From 9d7ef2ff82e8aef0ffa0d56f6fb0c6e2dd4a6f2d Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Dec 2021 14:43:34 -0500 Subject: [PATCH] Accounts Creation - 4.x rewrite (#4577) * started creating a few functions for account create * updating packages * adding create method for accounts * updated toCheckSumAddress and added testcases * added create and fromprivate functions for account * removing keythereum and implementing privatekeys with ethereum-cryptography * from private function complete and few tests added * cleaning up and updating changelog * rename fromPrivate to privateKeyToAccount * linter * fixing lint warnings * addressing feedback * fixing up * fixing function name * addressing feedback * addressing feedback * update changelog * fixing testcases --- CHANGELOG.md | 12 +++ packages/web3-common/src/constants.ts | 3 + packages/web3-common/src/errors.ts | 16 ++++ packages/web3-eth-accounts/package.json | 5 ++ packages/web3-eth-accounts/src/account.ts | 79 +++++++++++++++++++ packages/web3-eth-accounts/src/errors.ts | 0 packages/web3-eth-accounts/src/index.ts | 1 + packages/web3-eth-accounts/src/types.ts | 0 .../test/fixtures/account.ts | 31 ++++++++ .../test/unit/account.test.ts | 32 ++++++++ .../test/unit/constructor.test.ts | 20 ----- packages/web3-utils/src/converters.ts | 16 ++-- packages/web3-utils/src/hash.ts | 4 +- packages/web3-utils/src/index.ts | 1 + packages/web3-utils/src/random.ts | 26 ++++++ packages/web3-utils/src/validation.ts | 14 +++- .../web3-utils/test/fixtures/converters.ts | 5 ++ packages/web3-utils/test/fixtures/random.ts | 1 + .../web3-utils/test/fixtures/validation.ts | 24 +++--- .../web3-utils/test/unit/converters.test.ts | 12 +++ packages/web3-utils/test/unit/random.test.ts | 15 ++++ .../web3-utils/test/unit/validation.test.ts | 2 +- 22 files changed, 276 insertions(+), 43 deletions(-) create mode 100644 packages/web3-eth-accounts/src/account.ts delete mode 100644 packages/web3-eth-accounts/src/errors.ts delete mode 100644 packages/web3-eth-accounts/src/types.ts create mode 100644 packages/web3-eth-accounts/test/fixtures/account.ts create mode 100644 packages/web3-eth-accounts/test/unit/account.test.ts delete mode 100644 packages/web3-eth-accounts/test/unit/constructor.test.ts create mode 100644 packages/web3-utils/src/random.ts create mode 100644 packages/web3-utils/test/fixtures/random.ts create mode 100644 packages/web3-utils/test/unit/random.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d472dc03df7..b9a8a4e74a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -334,3 +334,15 @@ Released with 1.0.0-beta.37 code base. 1. The function `outputBigNumberFormatter` in `web3-core-helper` renamed to `outputBigIntFormatter` under `web3-common` 2. Removed `this.defaultBlock` context from `inputDefaultBlockNumberFormatter` in `web3-core-helper` and converted to additional parameter 3. Removed `this.defaultBlock` context from `inputTransactionFormatter` in `web3-core-helper` and converted to additional parameter + +#### web3-utils + +1. The following functions `soliditySha3` `soliditySha3Raw` `encodePacked` now includes type validation and requires type specficiation, instead of guessing the value type +2. The functions `soliditySha3` `soliditySha3Raw` `encodePacked` does not support BN and now supports `BigInt` +3. The functions `flattenTypes` and `jsonInterfaceMethodToString` moved to the `web3-eth-abi` package +4. The function `isAddress` now includes an optional parameter `checkChecksum` type boolean + +### web3-eth-accounts + +1. `create` function does not take in the optional parameter `entropy` +2. `ignoreLength` will be removed as an optional parameter for `privateKeyToAccount` diff --git a/packages/web3-common/src/constants.ts b/packages/web3-common/src/constants.ts index 6bbd947dfc4..51c4656dbdd 100644 --- a/packages/web3-common/src/constants.ts +++ b/packages/web3-common/src/constants.ts @@ -45,4 +45,7 @@ export const ERR_INVALID_PROVIDER = 601; export const ERR_INVALID_CLIENT = 602; export const ERR_SUBSCRIPTION = 603; +export const ERR_PRIVATE_KEY_LENGTH = 701; +export const ERR_INVALID_PRIVATE_KEY = 702; + export const GENESIS_BLOCK_NUMBER = '0x0'; diff --git a/packages/web3-common/src/errors.ts b/packages/web3-common/src/errors.ts index e6af77d884b..6926f5e3eab 100644 --- a/packages/web3-common/src/errors.ts +++ b/packages/web3-common/src/errors.ts @@ -35,6 +35,8 @@ import { ERR_OPERATION_TIMEOUT, ERR_OPERATION_ABORT, ERR_ABI_ENCODING, + ERR_INVALID_PRIVATE_KEY, + ERR_PRIVATE_KEY_LENGTH, } from './constants'; import { isResponseWithError } from './json_rpc'; @@ -393,3 +395,17 @@ export class OperationAbortError extends Web3Error { export class AbiError extends Web3Error { public code = ERR_ABI_ENCODING; } + +export class PrivateKeyLengthError extends Web3Error { + public code = ERR_PRIVATE_KEY_LENGTH; + public constructor(value: string | Buffer) { + super(`Invalid value given "${value.toString()}". Error: Private key must be 32 bytes.`); + } +} + +export class InvalidPrivateKeyError extends Web3Error { + public code = ERR_INVALID_PRIVATE_KEY; + public constructor(value: string | Buffer) { + super(`Invalid value given "${String(value)}". Error: not a valid string or buffer.`); + } +} diff --git a/packages/web3-eth-accounts/package.json b/packages/web3-eth-accounts/package.json index 3f14ec4e413..7e02d728347 100644 --- a/packages/web3-eth-accounts/package.json +++ b/packages/web3-eth-accounts/package.json @@ -37,5 +37,10 @@ "prettier": "^2.4.1", "ts-jest": "^27.0.7", "typescript": "^4.5.2" + }, + "dependencies": { + "ethereum-cryptography": "^0.2.0", + "web3-common": "^1.0.0-alpha.0", + "web3-utils": "^4.0.0-alpha.0" } } diff --git a/packages/web3-eth-accounts/src/account.ts b/packages/web3-eth-accounts/src/account.ts new file mode 100644 index 00000000000..29f546e94c0 --- /dev/null +++ b/packages/web3-eth-accounts/src/account.ts @@ -0,0 +1,79 @@ +import { utils, getPublicKey } from 'ethereum-cryptography/secp256k1'; +import { + toChecksumAddress, + bytesToHex, + sha3Raw, + HexString, + isBuffer, + isValidString, +} from 'web3-utils'; +import { InvalidPrivateKeyError, PrivateKeyLengthError } from 'web3-common'; + +// TODO Will be added later +export const encrypt = (): boolean => true; + +// TODO Will be added later +export const sign = (): boolean => true; + +// TODO Will be added later +export const signTransaction = (): boolean => true; + +/** + * Get account from private key + */ +export const privateKeyToAccount = ( + privateKey: string | Buffer, +): { + address: string; + privateKey: string; + signTransaction: () => boolean; // From 1.x + sign: () => boolean; + encrypt: () => boolean; +} => { + if (!(isValidString(privateKey) || isBuffer(privateKey))) { + throw new InvalidPrivateKeyError(privateKey); + } + + const stringPrivateKey = Buffer.isBuffer(privateKey) + ? Buffer.from(privateKey).toString('hex') + : privateKey; + + const stringPrivateKeyNoPrefix = stringPrivateKey.startsWith('0x') + ? stringPrivateKey.slice(2) + : stringPrivateKey; + + // TODO Replace with isHexString32Bytes function in web3-eth PR: + // Must be 64 hex characters + if (stringPrivateKeyNoPrefix.length !== 64) { + throw new PrivateKeyLengthError(stringPrivateKeyNoPrefix); + } + + const publicKey = getPublicKey(stringPrivateKeyNoPrefix); + + const publicKeyString = `0x${publicKey.slice(2)}`; + const publicHash = sha3Raw(publicKeyString); + const publicHashHex = bytesToHex(publicHash); + const address = toChecksumAddress(publicHashHex.slice(-40)); // To get the address, take the last 20 bytes of the public hash + return { address, privateKey: stringPrivateKey, signTransaction, sign, encrypt }; +}; + +/** + * Returns an acoount + */ +export const create = (): { + address: HexString; + privateKey: string; + signTransaction: () => boolean; // From 1.x + sign: () => boolean; + encrypt: () => boolean; +} => { + const privateKey = utils.randomPrivateKey(); + const address = getPublicKey(privateKey); + return { + privateKey: `0x${Buffer.from(privateKey).toString('hex')}`, + address: `0x${Buffer.from(address).toString('hex')}`, + signTransaction, + sign, + encrypt, + }; +}; diff --git a/packages/web3-eth-accounts/src/errors.ts b/packages/web3-eth-accounts/src/errors.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/web3-eth-accounts/src/index.ts b/packages/web3-eth-accounts/src/index.ts index e69de29bb2d..362a768e537 100644 --- a/packages/web3-eth-accounts/src/index.ts +++ b/packages/web3-eth-accounts/src/index.ts @@ -0,0 +1 @@ +export * from './account'; diff --git a/packages/web3-eth-accounts/src/types.ts b/packages/web3-eth-accounts/src/types.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/web3-eth-accounts/test/fixtures/account.ts b/packages/web3-eth-accounts/test/fixtures/account.ts new file mode 100644 index 00000000000..5f7ae663e9f --- /dev/null +++ b/packages/web3-eth-accounts/test/fixtures/account.ts @@ -0,0 +1,31 @@ +import { sign, signTransaction, encrypt } from '../../src/account'; + +export const validPrivateKeytoAccountData: [string, any][] = [ + [ + '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709', + { + address: '0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01', + privateKey: '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709', + sign, + signTransaction, + encrypt, + }, + ], + [ + '0x9e93921f9bca358a96aa66efcccbde12850473be95f63c1453e29656feafeb35', + { + address: '0x118C2E5F57FD62C2B5b46a5ae9216F4FF4011a07', + privateKey: '0x9e93921f9bca358a96aa66efcccbde12850473be95f63c1453e29656feafeb35', + sign, + signTransaction, + encrypt, + }, + ], +]; + +export const invalidPrivateKeytoAccountData: [any, string][] = [ + ['', 'Invalid value given "". Error: Private key must be 32 bytes.'], + [Buffer.from([]), 'Invalid value given "". Error: Private key must be 32 bytes.'], + [undefined, 'Invalid value given "undefined". Error: not a valid string or buffer.'], + [null, 'Invalid value given "null". Error: not a valid string or buffer.'], +]; diff --git a/packages/web3-eth-accounts/test/unit/account.test.ts b/packages/web3-eth-accounts/test/unit/account.test.ts new file mode 100644 index 00000000000..e7bb7e964cb --- /dev/null +++ b/packages/web3-eth-accounts/test/unit/account.test.ts @@ -0,0 +1,32 @@ +import { isHexStrict } from 'web3-utils'; +import { create, privateKeyToAccount } from '../../src/account'; +import { validPrivateKeytoAccountData, invalidPrivateKeytoAccountData } from '../fixtures/account'; + +describe('accounts', () => { + describe('create', () => { + describe('valid cases', () => { + it('%s', () => { + const account = create(); + expect(typeof account.privateKey).toBe('string'); + expect(typeof account.address).toBe('string'); + expect(isHexStrict(account.address)).toBe(true); + expect(typeof account.encrypt).toBe('function'); + expect(typeof account.sign).toBe('function'); + expect(typeof account.signTransaction).toBe('function'); + }); + }); + }); + + describe('privateKeyToAccount', () => { + describe('valid cases', () => { + it.each(validPrivateKeytoAccountData)('%s', (input, output) => { + expect(privateKeyToAccount(input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(invalidPrivateKeytoAccountData)('%s', (input, output) => { + expect(() => privateKeyToAccount(input)).toThrow(output); + }); + }); + }); +}); diff --git a/packages/web3-eth-accounts/test/unit/constructor.test.ts b/packages/web3-eth-accounts/test/unit/constructor.test.ts deleted file mode 100644 index 208d4432107..00000000000 --- a/packages/web3-eth-accounts/test/unit/constructor.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -// import Web3ProviderBase from '../../src/index' -// import {ProviderOptions} from '../../types' - -describe('constructs a PLACEHOLDER instance with expected properties', () => { - // let providerOptions: ProviderOptions - - beforeEach(() => { - // providerOptions = { - // providerUrl: 'http://127.0.0.1:8545' - // } - }); - - it('should construct with expected properties', () => { - // const web3ProviderBase = new Web3ProviderBase(providerOptions) - // expect(web3ProviderBase).toMatchObject({ - // _providerUrl: providerOptions.providerUrl - // }) - expect(true).toBeTruthy(); - }); -}); diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index be770a535c3..ef4e7e335fa 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -355,28 +355,30 @@ export const toWei = (number: Numbers, unit: EtherUnits): string => { }; export const toChecksumAddress = (address: Address): string => { - if (typeof address === 'undefined') return ''; - - if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { + if (!isAddress(address, false)) { throw new InvalidAddressError(address); } const lowerCaseAddress = address.toLowerCase().replace(/^0x/i, ''); - const hash = (keccak256(Buffer.from(lowerCaseAddress)) as Buffer).toString('hex'); + + const hash = bytesToHex(keccak256(Buffer.from(lowerCaseAddress))); + if ( hash === null || hash === 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) return ''; // // EIP-1052 if hash is equal to c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, keccak was given empty data + const addressHash = hash.replace(/^0x/i, ''); + let checksumAddress = '0x'; - for (let i = 0; i < address.length; i += 1) { + for (let i = 0; i < lowerCaseAddress.length; i += 1) { // If ith character is 8 to f then make it uppercase if (parseInt(addressHash[i], 16) > 7) { - checksumAddress += address[i].toUpperCase(); + checksumAddress += lowerCaseAddress[i].toUpperCase(); } else { - checksumAddress += address[i]; + checksumAddress += lowerCaseAddress[i]; } } return checksumAddress; diff --git a/packages/web3-utils/src/hash.ts b/packages/web3-utils/src/hash.ts index a15c356f807..a79b6ae55f7 100644 --- a/packages/web3-utils/src/hash.ts +++ b/packages/web3-utils/src/hash.ts @@ -18,7 +18,7 @@ const SHA3_EMPTY_BYTES = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfa /** * - * computes the Keccak-256 hash of the string input and returns a hexstring + * computes the Keccak-256 hash of the input and returns a hexstring */ export const sha3 = (data: Bytes): string | null => { const updatedData = typeof data === 'string' && isHexStrict(data) ? hexToBytes(data) : data; @@ -32,7 +32,7 @@ export const sha3 = (data: Bytes): string | null => { /** *Will calculate the sha3 of the input but does return the hash value instead of null if for example a empty string is passed. */ -export const sha3Raw = (data: string): string => { +export const sha3Raw = (data: Bytes): string => { const hash = sha3(data); if (hash === null) { return SHA3_EMPTY_BYTES; diff --git a/packages/web3-utils/src/index.ts b/packages/web3-utils/src/index.ts index 55db17df33d..7d2b828a72c 100644 --- a/packages/web3-utils/src/index.ts +++ b/packages/web3-utils/src/index.ts @@ -3,4 +3,5 @@ export * from './errors'; export * from './validation'; export * from './types'; export * from './hash'; +export * from './random'; export * from './string_manipulation'; diff --git a/packages/web3-utils/src/random.ts b/packages/web3-utils/src/random.ts new file mode 100644 index 00000000000..ee45413607f --- /dev/null +++ b/packages/web3-utils/src/random.ts @@ -0,0 +1,26 @@ +import { randomBytes as cryptoRandomBytes } from 'crypto'; + +/** + * Returns a random byte array by the given bytes size + * + * @param {Number} size + * @returns {Buffer} + */ + export const randomBytes = (byteSize: number): Buffer => { + const randomValues = + typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues + ? window.crypto.getRandomValues(new Uint8Array(byteSize)) + : cryptoRandomBytes(byteSize); + return Buffer.from(randomValues); +}; + + +/** + * Returns a random hex string by the given bytes size + * + * @param {Number} size + * @returns {string} + */ +export const randomHex = (byteSize: number): string => `0x${randomBytes(byteSize).toString('hex')}`; + + diff --git a/packages/web3-utils/src/validation.ts b/packages/web3-utils/src/validation.ts index f1aa9ff4817..28e7e702aa8 100644 --- a/packages/web3-utils/src/validation.ts +++ b/packages/web3-utils/src/validation.ts @@ -106,6 +106,16 @@ export const validateNumbersInput = ( } }; +/** + * checks input if typeof data is valid string input + */ +export const isValidString = (data: any) => typeof data === 'string'; + +/** + * checks input if typeof data is valid buffer input + */ +export const isBuffer = (data: any) => Buffer.isBuffer(data); + /** * checks input for valid string, otherwise throws error */ @@ -212,7 +222,7 @@ export const checkAddressCheckSum = (data: string): boolean => { /** * Checks if a given string is a valid Ethereum address. It will also check the checksum, if the address has upper and lowercase letters. */ -export const isAddress = (address: string): boolean => { +export const isAddress = (address: string, checkChecksum = true): boolean => { // check if it has the basic requirements of an address if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { return false; @@ -222,7 +232,7 @@ export const isAddress = (address: string): boolean => { return true; // Otherwise check each case } - return checkAddressCheckSum(address); + return checkChecksum ? checkAddressCheckSum(address) : true; }; /** diff --git a/packages/web3-utils/test/fixtures/converters.ts b/packages/web3-utils/test/fixtures/converters.ts index 1c25cd7f30b..f51bc4879e7 100644 --- a/packages/web3-utils/test/fixtures/converters.ts +++ b/packages/web3-utils/test/fixtures/converters.ts @@ -251,3 +251,8 @@ export const toWeiInvalidData: [[any, any], string][] = [ [['data', 'kwei'], 'Invalid value given "data". Error: not a valid number.'], [['1234', 'uwei'], 'Invalid value given "uwei". Error: invalid unit.'], ]; +export const toCheckSumValidData: [string, string][] = [ + ['0x0089d53f703f7e0843953d48133f74ce247184c2', '0x0089d53F703f7E0843953D48133f74cE247184c2'], + ['0x5fbc2b6c19ee3dd5f9af96ff337ddc89e30ceaef', '0x5FBc2b6C19EE3DD5f9Af96ff337DDC89e30ceAef'], + ['0xa54D3c09E34aC96807c1CC397404bF2B98DC4eFb', '0xa54d3c09E34aC96807c1CC397404bF2B98DC4eFb'], +]; diff --git a/packages/web3-utils/test/fixtures/random.ts b/packages/web3-utils/test/fixtures/random.ts new file mode 100644 index 00000000000..4f790808368 --- /dev/null +++ b/packages/web3-utils/test/fixtures/random.ts @@ -0,0 +1 @@ +export const randomHexData: number[] = [2,4,8,16,32,64,128,256] \ No newline at end of file diff --git a/packages/web3-utils/test/fixtures/validation.ts b/packages/web3-utils/test/fixtures/validation.ts index ee53dffa234..51138b1e409 100644 --- a/packages/web3-utils/test/fixtures/validation.ts +++ b/packages/web3-utils/test/fixtures/validation.ts @@ -101,17 +101,19 @@ export const checkAddressCheckSumValidData: [any, boolean][] = [ ['0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', true], ]; -export const isAddressValidData: [any, boolean][] = [ - ['0xc6d9d2cd449a754c494264e1809c50e34d64562b', true], - ['c6d9d2cd449a754c494264e1809c50e34d64562b', true], - ['0xE247A45c287191d435A8a5D72A7C8dc030451E9F', true], - ['0xE247a45c287191d435A8a5D72A7C8dc030451E9F', false], - ['0xe247a45c287191d435a8a5d72a7c8dc030451e9f', true], - ['0xE247A45C287191D435A8A5D72A7C8DC030451E9F', true], - ['0XE247A45C287191D435A8A5D72A7C8DC030451E9F', true], - ['0123', false], - ['0x12', false], - [123, false], +export const isAddressValidData: [[any, boolean], boolean][] = [ + [['0xc6d9d2cd449a754c494264e1809c50e34d64562b', true], true], + [['c6d9d2cd449a754c494264e1809c50e34d64562b', true], true], + [['0xE247A45c287191d435A8a5D72A7C8dc030451E9F', true], true], + [['0xE247a45c287191d435A8a5D72A7C8dc030451E9F', true], false], + [['0xe247a45c287191d435a8a5d72a7c8dc030451e9f', true], true], + [['0xE247A45C287191D435A8A5D72A7C8DC030451E9F', true], true], + [['0XE247A45C287191D435A8A5D72A7C8DC030451E9F', true], true], + [['0xa54D3c09E34aC96807c1CC397404bF2B98DC4eFb', false], true], + [['0xa54D3c09E34aC96807c1CC397404bF2B98DC4eFb', true], false], + [['0123', true], false], + [['0x12', true], false], + [[123, true], false], ]; export const compareBlockNumbersValidData: [[Numbers, Numbers], number][] = [ diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index 48727aac1af..cf5c5d23d89 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -20,6 +20,7 @@ import { toUtf8, toWei, utf8ToHex, + toChecksumAddress, } from '../../src/converters'; import { asciiToHexValidData, @@ -41,6 +42,7 @@ import { toWeiValidData, utf8ToHexInvalidData, utf8ToHexValidData, + toCheckSumValidData, } from '../fixtures/converters'; describe('converters', () => { @@ -321,4 +323,14 @@ describe('converters', () => { }); }); }); + describe('toChecksumAddress', () => { + describe('valid cases', () => { + it.each(toCheckSumValidData)('%s', (input, output) => { + expect(toChecksumAddress(input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.todo('should throw error for invalid cases'); + }); + }); }); diff --git a/packages/web3-utils/test/unit/random.test.ts b/packages/web3-utils/test/unit/random.test.ts new file mode 100644 index 00000000000..85006cbf26a --- /dev/null +++ b/packages/web3-utils/test/unit/random.test.ts @@ -0,0 +1,15 @@ +import { isHexStrict } from '../../src/validation'; +import { randomHex } from '../../src/random'; +import { randomHexData } from '../fixtures/random'; + +describe('random library tests', () => { + describe('randomHex', () => { + describe('valid cases', () => { + it.each(randomHexData)('%s', input => { + const hexResult = randomHex(input); + expect(isHexStrict(hexResult)).toBe(true); + expect(hexResult.length === input * 2 + 2).toBe(true); // bytes + 2 because of hex prefix '0x' + }); + }); + }); +}); diff --git a/packages/web3-utils/test/unit/validation.test.ts b/packages/web3-utils/test/unit/validation.test.ts index be61d13ce5f..6923a5c5817 100644 --- a/packages/web3-utils/test/unit/validation.test.ts +++ b/packages/web3-utils/test/unit/validation.test.ts @@ -90,7 +90,7 @@ describe('validation', () => { describe('isAddress', () => { describe('valid cases', () => { it.each(isAddressValidData)('%s', (input, output) => { - expect(isAddress(input)).toEqual(output); + expect(isAddress(...input)).toEqual(output); }); }); });