diff --git a/packages/web3-eth-accounts/test/unit/account_dom.test.ts b/packages/web3-eth-accounts/test/unit/account_dom.test.ts new file mode 100644 index 00000000000..587996a721c --- /dev/null +++ b/packages/web3-eth-accounts/test/unit/account_dom.test.ts @@ -0,0 +1,248 @@ +/** + * @jest-environment jsdom + */ + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// this file contains the unit test for the event emitter in the DOM environment +// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) + +// ignore the following rule to allow keeping `@jest-environment jsdom` on top: +// eslint-disable-next-line header/header +import { TextEncoder } from 'util'; +import crypto from 'crypto'; +// polyfill for jsdom +global.TextEncoder = TextEncoder; +// @ts-expect-error "Cannot assign to 'subtle' because it is a read-only property." +global.crypto.subtle = crypto.webcrypto.subtle; + +/* eslint-disable import/first */ +import { Address } from 'web3-types'; +import { Web3ValidatorError, isHexStrict } from 'web3-validator'; +import { + create, + decrypt, + encrypt, + hashMessage, + privateKeyToAccount, + privateKeyToAddress, + recover, + recoverTransaction, + sign, + signTransaction, + privateKeyToPublicKey, +} from '../../src/account'; +import { + invalidDecryptData, + invalidEncryptData, + invalidKeyStore, + invalidPrivateKeytoAccountData, + invalidPrivateKeyToAddressData, + signatureRecoverData, + transactionsTestData, + validDecryptData, + validEncryptData, + validHashMessageData, + validPrivateKeytoAccountData, + validPrivateKeyToAddressData, + validPrivateKeyToPublicKeyData, + validRecover, +} from '../fixtures/account'; +import { TransactionFactory } from '../../src/tx/transactionFactory'; +import { TxData } from '../../src/tx/types'; + +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('privateKeyToAddress', () => { + describe('valid cases', () => { + it.each(validPrivateKeyToAddressData)('%s', (input, output) => { + expect(privateKeyToAddress(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(invalidPrivateKeyToAddressData)('%s', (input, output) => { + expect(() => privateKeyToAddress(input)).toThrow(output); + }); + }); + }); + + describe('privateKeyToAccount', () => { + describe('valid cases', () => { + it.each(validPrivateKeytoAccountData)('%s', (input, output) => { + expect(JSON.stringify(privateKeyToAccount(input.address, input.ignoreLength))).toEqual( + JSON.stringify(output), + ); + }); + }); + + describe('invalid cases', () => { + it.each(invalidPrivateKeytoAccountData)('%s', (input, output) => { + expect(() => privateKeyToAccount(input)).toThrow(output); + }); + }); + }); + describe('privateKeyToPublicKey', () => { + describe('valid cases', () => { + it.each(validPrivateKeyToPublicKeyData)('%s', (privateKey, isCompressed, output) => { + expect(privateKeyToPublicKey(privateKey, isCompressed)).toEqual(output); + }); + }); + }); + + describe('Signing and Recovery of Transaction', () => { + it.each(transactionsTestData)('sign transaction', async txData => { + const account = create(); + + const signedResult = await signTransaction( + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + TransactionFactory.fromTxData(txData as unknown as TxData), + account.privateKey, + ); + expect(signedResult).toBeDefined(); + expect(signedResult.messageHash).toBeDefined(); + expect(signedResult.rawTransaction).toBeDefined(); + expect(signedResult.transactionHash).toBeDefined(); + expect(signedResult.r).toMatch(/0[xX][0-9a-fA-F]{64}/); + expect(signedResult.s).toMatch(/0[xX][0-9a-fA-F]{64}/); + expect(signedResult.v).toMatch(/0[xX][0-9a-fA-F]+/); + }); + + it.each(transactionsTestData)('Recover transaction', async txData => { + const account = create(); + const txObj = { ...txData, from: account.address }; + const signedResult = await signTransaction( + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + TransactionFactory.fromTxData(txObj), + account.privateKey, + ); + expect(signedResult).toBeDefined(); + + const address: Address = recoverTransaction(signedResult.rawTransaction); + expect(address).toBeDefined(); + expect(address).toEqual(account.address); + }); + }); + + describe('Hash Message', () => { + it.each(validHashMessageData)('%s', (message, hash) => { + expect(hashMessage(message)).toEqual(hash); + }); + }); + + describe('Sign Message', () => { + describe('sign', () => { + it.each(signatureRecoverData)('%s', (data, testObj) => { + const result = sign(data, testObj.privateKey); + expect(result.signature).toEqual(testObj.signature || testObj.signatureOrV); // makes sure we get signature and not V value + expect(result.r).toEqual(testObj.r); + expect(result.s).toEqual(testObj.s); + }); + }); + + describe('recover', () => { + it.each(signatureRecoverData)('%s', (data, testObj) => { + const address = recover(data, testObj.signatureOrV, testObj.prefixedOrR, testObj.s); + expect(address).toEqual(testObj.address); + }); + }); + }); + + describe('encrypt', () => { + describe('valid cases', () => { + it.each(validEncryptData)('%s', async (input, output) => { + const result = await encrypt(input[0], input[1], input[2]).catch(err => { + throw err; + }); + expect(result.version).toBe(output.version); + expect(result.address).toBe(output.address); + expect(result.crypto.ciphertext).toBe(output.crypto.ciphertext); + expect(result.crypto.cipherparams).toEqual(output.crypto.cipherparams); + expect(result.crypto.cipher).toEqual(output.crypto.cipher); + expect(result.crypto.kdf).toBe(output.crypto.kdf); + expect(result.crypto.kdfparams).toEqual(output.crypto.kdfparams); + expect(typeof result.version).toBe('number'); + expect(typeof result.id).toBe('string'); + expect(typeof result.crypto.mac).toBe('string'); + }); + }); + + describe('invalid cases', () => { + it.each(invalidEncryptData)('%s', async (input, output) => { + const result = encrypt(input[0], input[1], input[2]); + await expect(result).rejects.toThrow(output); + }); + }); + }); + + describe('decrypt', () => { + describe('valid cases', () => { + it.each(validDecryptData)('%s', async input => { + const keystore = await encrypt(input[0], input[1], input[2]).catch(err => { + throw err; + }); + + // make sure decrypt does not throw invalid password error + const result = await decrypt(keystore, input[1]); + + expect(JSON.stringify(result)).toEqual(JSON.stringify(privateKeyToAccount(input[3]))); + + const keystoreString = JSON.stringify(keystore); + + const stringResult = await decrypt(keystoreString, input[1], true); + + expect(JSON.stringify(stringResult)).toEqual(JSON.stringify(privateKeyToAccount(input[3]))); + }); + }); + + describe('invalid cases', () => { + it.each(invalidDecryptData)('%s', async (input, output) => { + const result = decrypt(input[0], input[1]); + + await expect(result).rejects.toThrow(output); + }); + }); + + describe('invalid keystore, fails validation', () => { + it.each(invalidKeyStore)('%s', async input => { + const result = decrypt(input[0], input[1]); + + await expect(result).rejects.toThrow(Web3ValidatorError); + }); + }); + + describe('valid signatures for recover', () => { + it.each(validRecover)('&s', (data, signature) => { + recover(data, signature); + }); + }); + }); +}); diff --git a/packages/web3-utils/src/uint8array.ts b/packages/web3-utils/src/uint8array.ts index 772e648920c..5fa8b952711 100644 --- a/packages/web3-utils/src/uint8array.ts +++ b/packages/web3-utils/src/uint8array.ts @@ -21,6 +21,8 @@ export function isUint8Array(data: unknown | Uint8Array): data is Uint8Array { (data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array' ); } + +// @TODO: Remove this function and its usages once all sub dependencies uses version 1.3.3 or above of @noble/hashes export function ensureIfUint8Array(data: T) { if ( !(data instanceof Uint8Array) && diff --git a/packages/web3-utils/test/unit/converters_dom.test.ts b/packages/web3-utils/test/unit/converters_dom.test.ts new file mode 100644 index 00000000000..e7ed91a9573 --- /dev/null +++ b/packages/web3-utils/test/unit/converters_dom.test.ts @@ -0,0 +1,439 @@ +/** + * @jest-environment jsdom + */ + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// this file contains the unit test for the event emitter in the DOM environment +// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) + +// ignore the following rule to allow keeping `@jest-environment jsdom` on top: +// eslint-disable-next-line header/header +import { TextDecoder, TextEncoder } from 'util'; +// polyfill for jsdom +// @ts-expect-error ignore the error 'Type 'typeof TextDecoder' is not assignable to type ...' +global.TextDecoder = TextDecoder; +global.TextEncoder = TextEncoder; + +/* eslint-disable import/first */ +import { + asciiToHex, + bytesToHex, + fromAscii, + fromDecimal, + fromUtf8, + fromWei, + hexToAscii, + hexToBytes, + hexToNumber, + hexToNumberString, + hexToString, + hexToUtf8, + numberToHex, + stringToHex, + toAscii, + toDecimal, + toHex, + toNumber, + toUtf8, + toWei, + utf8ToHex, + toChecksumAddress, + bytesToUint8Array, + toBigInt, + toBool, +} from '../../src/converters'; + +import { + asciiToHexValidData, + bytesToHexInvalidData, + bytesToHexValidData, + fromWeiInvalidData, + fromWeiValidData, + hexToAsciiValidData, + hexToBytesInvalidData, + hexToBytesValidData, + hexToNumberInvalidData, + hexToNumberValidData, + hexToUtf8InvalidData, + hexToUtf8ValidData, + toUtf8ValidData, + numberToHexInvalidData, + numberToHexValidData, + toHexValidData, + toHexInvalidData, + toWeiInvalidData, + toWeiValidData, + utf8ToHexInvalidData, + utf8ToHexValidData, + toCheckSumValidData, + bytesToUint8ArrayInvalidData, + bytesToUint8ArrayValidData, + toBigIntValidData, + toBigIntInvalidData, + toCheckSumInvalidData, + numberToHexstrictValidData, + toBoolValidData, + toBoolInvalidData, +} from '../fixtures/converters'; + +describe('converters', () => { + describe('bytesToHex', () => { + describe('valid cases', () => { + it.each(bytesToHexValidData)('%s', (input, output) => { + expect(bytesToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(bytesToHexInvalidData)('%s', (input, output) => { + expect(() => bytesToHex(input)).toThrow(output); + }); + }); + }); + + describe('hexToBytes', () => { + describe('valid cases', () => { + it.each(hexToBytesValidData)('%s', (input, output) => { + expect(hexToBytes(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToBytesInvalidData)('%s', (input, output) => { + expect(() => hexToBytes(input)).toThrow(output); + }); + }); + }); + + describe('numberToHex', () => { + describe('valid cases', () => { + it.each(numberToHexValidData)('%s', (input, output) => { + expect(numberToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(numberToHexInvalidData)('%s', (input, output) => { + expect(() => numberToHex(input)).toThrow(output); + }); + }); + + describe('valid hexstrict cases', () => { + it.each(numberToHexstrictValidData)('%s', (input, output) => { + expect(numberToHex(input, true)).toEqual(output); + }); + }); + }); + + describe('fromDecimal', () => { + describe('valid cases', () => { + it.each(numberToHexValidData)('%s', (input, output) => { + expect(fromDecimal(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(numberToHexInvalidData)('%s', (input, output) => { + expect(() => fromDecimal(input)).toThrow(output); + }); + }); + }); + + describe('hexToNumber', () => { + describe('valid cases', () => { + it.each(hexToNumberValidData)('%s', (input, output) => { + expect(hexToNumber(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToNumberInvalidData)('%s', (input, output) => { + expect(() => hexToNumber(input)).toThrow(output); + }); + }); + }); + + describe('toDecimal', () => { + describe('valid cases', () => { + it.each(hexToNumberValidData)('%s', (input, output) => { + expect(toDecimal(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToNumberInvalidData)('%s', (input, output) => { + expect(() => toDecimal(input)).toThrow(output); + }); + }); + }); + + describe('hexToNumberString', () => { + it.each(hexToNumberValidData)('%s', (input, output) => { + expect(hexToNumberString(input)).toEqual(output.toString()); + }); + }); + + describe('utf8ToHex', () => { + describe('valid cases', () => { + it.each(utf8ToHexValidData)('%s', (input, output) => { + expect(utf8ToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => utf8ToHex(input)).toThrow(output); + }); + }); + }); + + describe('fromUtf8', () => { + describe('valid cases', () => { + it.each(utf8ToHexValidData)('%s', (input, output) => { + expect(fromUtf8(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => fromUtf8(input)).toThrow(output); + }); + }); + }); + + describe('stringToHex', () => { + describe('valid cases', () => { + it.each(utf8ToHexValidData)('%s', (input, output) => { + expect(stringToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => stringToHex(input)).toThrow(output); + }); + }); + }); + + describe('hexToUtf8', () => { + describe('valid cases', () => { + it.each(hexToUtf8ValidData)('%s', (input, output) => { + expect(hexToUtf8(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => hexToUtf8(input)).toThrow(output); + }); + }); + }); + + describe('toUtf8', () => { + describe('valid cases', () => { + it.each(toUtf8ValidData)('%s', (input, output) => { + expect(toUtf8(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => toUtf8(input)).toThrow(output); + }); + }); + }); + + describe('hexToString', () => { + describe('valid cases', () => { + it.each(hexToUtf8ValidData)('%s', (input, output) => { + expect(hexToString(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => hexToString(input)).toThrow(output); + }); + }); + }); + + describe('asciiToHex', () => { + describe('valid cases', () => { + it.each(asciiToHexValidData)('%s', (input, output) => { + expect(asciiToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => asciiToHex(input)).toThrow(output); + }); + }); + }); + + describe('fromAscii', () => { + describe('valid cases', () => { + it.each(asciiToHexValidData)('%s', (input, output) => { + expect(fromAscii(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => fromAscii(input)).toThrow(output); + }); + }); + }); + + describe('hexToAscii', () => { + describe('valid cases', () => { + it.each(hexToAsciiValidData)('%s', (input, output) => { + expect(hexToAscii(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => hexToAscii(input)).toThrow(output); + }); + }); + }); + + describe('toAscii', () => { + describe('valid cases', () => { + it.each(hexToAsciiValidData)('%s', (input, output) => { + expect(toAscii(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => toAscii(input)).toThrow(output); + }); + }); + }); + + describe('toHex', () => { + describe('return value', () => { + it.each(toHexValidData)('%s', (input, output) => { + expect(toHex(input)).toEqual(output[0]); + }); + }); + + describe('return type', () => { + it.each(toHexValidData)('%s', (input, output) => { + expect(toHex(input, true)).toEqual(output[1]); + }); + }); + + describe('invalid cases', () => { + it.each(toHexInvalidData)('%s', (input, output) => { + expect(() => toHex(input)).toThrow(output); + }); + }); + }); + + describe('toNumber', () => { + it.each([...hexToNumberValidData, [123, 123], ['123', 123]])('%s', (input, output) => { + expect(toNumber(input)).toEqual(output); + }); + }); + + describe('fromWei', () => { + describe('valid cases', () => { + it.each(fromWeiValidData)('%s', (input, output) => { + expect(fromWei(input[0], input[1])).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(fromWeiInvalidData)('%s', (input, output) => { + expect(() => fromWei(input[0], input[1])).toThrow(output); + }); + }); + }); + + describe('toWei', () => { + describe('valid cases', () => { + it.each(toWeiValidData)('%s', (input, output) => { + expect(toWei(output, input[1])).toEqual(input[0].toString()); + }); + }); + + describe('invalid cases', () => { + it.each(toWeiInvalidData)('%s', (input, output) => { + expect(() => toWei(input[0], input[1])).toThrow(output); + }); + }); + }); + describe('toChecksumAddress', () => { + describe('valid cases', () => { + it.each(toCheckSumValidData)('%s', (input, output) => { + expect(toChecksumAddress(input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(toCheckSumInvalidData)('%s', (input, output) => { + expect(() => toChecksumAddress(input)).toThrow(output); + }); + }); + }); + describe('bytesToUint8Array', () => { + describe('bytesToUint8Array', () => { + describe('valid cases', () => { + it.each(bytesToUint8ArrayValidData)('%s', (input, output) => { + expect(bytesToUint8Array(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(bytesToUint8ArrayInvalidData)('%s', (input, output) => { + expect(() => bytesToUint8Array(input)).toThrow(output); + }); + }); + }); + }); + describe('toBigInt', () => { + describe('valid cases', () => { + it.each(toBigIntValidData)('%s', (input, output) => { + expect(toBigInt(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(toBigIntInvalidData)('%s', (input, output) => { + expect(() => toBigInt(input)).toThrow(output); + }); + }); + }); + + describe('toBool', () => { + describe('valid cases', () => { + it.each(toBoolValidData)('%s', (input, output) => { + expect(toBool(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(toBoolInvalidData)('%s', (input, output) => { + expect(() => toBool(input)).toThrow(output); + }); + }); + }); +}); diff --git a/packages/web3-utils/test/unit/hash_dom.test.ts b/packages/web3-utils/test/unit/hash_dom.test.ts new file mode 100644 index 00000000000..1e4dd67cf0c --- /dev/null +++ b/packages/web3-utils/test/unit/hash_dom.test.ts @@ -0,0 +1,166 @@ +/** + * @jest-environment jsdom + */ + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// this file contains the unit test for the event emitter in the DOM environment +// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) + +// ignore the following rule to allow keeping `@jest-environment jsdom` on top: +// eslint-disable-next-line header/header +import { TextEncoder } from 'util'; +// polyfill for jsdom +global.TextEncoder = TextEncoder; + +/* eslint-disable import/first */ +import { keccak256 } from 'js-sha3'; +import { + sha3, + sha3Raw, + soliditySha3, + soliditySha3Raw, + encodePacked, + keccak256 as web3keccak256, +} from '../../src/hash'; +import { + sha3Data, + sha3ValidData, + soliditySha3RawValidData, + sha3RawValidData, + soliditySha3ValidData, + soliditySha3InvalidData, + compareSha3JSValidData, + compareSha3JSRawValidData, + encodePackData, + encodePackedInvalidData, + keccak256ValidData, + soliditySha3BigIntValidData, +} from '../fixtures/hash'; + +describe('hash', () => { + describe('sha3', () => { + describe('valid cases', () => { + it.each(sha3ValidData)('%s', (input, output) => { + expect(sha3(input)).toEqual(output); + }); + }); + + describe('compare with js-sha3 normal cases', () => { + it.each(sha3Data)('%s', input => { + expect(sha3(input)).toBe(`0x${keccak256(input)}`); + }); + }); + + describe('compare with js-sha3 uint8array cases', () => { + it.each(compareSha3JSValidData)('%s', (input, output) => { + expect(sha3(input)).toBe(`0x${keccak256(output)}`); + }); + }); + }); + + describe('sha3Raw', () => { + describe('valid cases', () => { + it.each(sha3RawValidData)('%s', (input, output) => { + expect(sha3Raw(input)).toEqual(output); + }); + }); + describe('comparing with js-sha3 cases', () => { + it.each(compareSha3JSRawValidData)('%s', (input, output) => { + expect(sha3Raw(input)).toBe(`0x${keccak256(output)}`); + }); + }); + }); + + describe('soliditySha3', () => { + describe('valid cases', () => { + it.each(soliditySha3ValidData)('%s', (input, output) => { + expect(soliditySha3(...input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(soliditySha3InvalidData)('%s', (input, output) => { + expect(() => soliditySha3(input)).toThrow(output); + }); + }); + }); + + describe('soliditySha3Raw', () => { + describe('valid cases', () => { + it.each(soliditySha3RawValidData)('%s', (input, output) => { + expect(soliditySha3Raw(...input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(soliditySha3InvalidData)('%s', (input, output) => { + expect(() => soliditySha3Raw(input)).toThrow(output); + }); + }); + }); + + describe('encodePacked', () => { + describe('valid cases', () => { + it.each(encodePackData)('%s', (input, output) => { + expect(encodePacked(...input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(encodePackedInvalidData)('%s', (input, output) => { + expect(() => encodePacked(input)).toThrow(output); + }); + }); + }); + describe('keccak256', () => { + describe('valid cases', () => { + it.each(keccak256ValidData)('%s', (input, output) => { + expect(web3keccak256(input)).toEqual(output); + }); + }); + }); + + describe('extra types supporting', () => { + it('object', () => { + const res = soliditySha3({ + historicBlock: { + hash: '0xcba0b90a5e65512202091c12a2e3b328f374715b9f1c8f32cb4600c726fe2aa6', + height: 1, + }, + networkId: 5777, + }); + expect(res).toBe('0x00203462b63e3a8ca15da715e490c676b0e370f47823e31383fe43c25da3b78d'); + }); + it('object in string', () => { + const res = soliditySha3( + '{"contents":"pragma solidity >=0.4.21 <0.6.0;\\n\\ncontract Migrations {\\n address public owner;\\n uint public last_completed_migration;\\n\\n constructor() public {\\n owner = msg.sender;\\n }\\n\\n modifier restricted() {\\n if (msg.sender == owner) _;\\n }\\n\\n function setCompleted(uint completed) public restricted {\\n last_completed_migration = completed;\\n }\\n\\n function upgrade(address new_address) public restricted {\\n Migrations upgraded = Migrations(new_address);\\n upgraded.setCompleted(last_completed_migration);\\n }\\n}\\n","sourcePath":"/Users/gnidan/src/work/reproduce/2019/01/21/artifacts/contracts/Migrations.sol"}', + ); + expect(res).toBe('0xdb092e2751b8dcb7c8509baade3c0ac290414a71685823c3cbeb28667970b0bd'); + }); + it('another object in string', () => { + const res = soliditySha3( + '{"bytes":"608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610314806100606000396000f3fe608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100b85780638da5cb5b146100e3578063fdacd5761461013a575b600080fd5b34801561007357600080fd5b506100b66004803603602081101561008a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610175565b005b3480156100c457600080fd5b506100cd61025d565b6040518082815260200191505060405180910390f35b3480156100ef57600080fd5b506100f8610263565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561014657600080fd5b506101736004803603602081101561015d57600080fd5b8101908080359060200190929190505050610288565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561025a5760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561024057600080fd5b505af1158015610254573d6000803e3d6000fd5b50505050505b50565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102e557806001819055505b5056fea165627a7a7230582013359aba5684f88626fb6a58a003236e309ef1462172af4afb4afb9bd2532b510029","linkReferences":[]}', + ); + expect(res).toBe('0x46e99868594ceb46b7cd37e4b33d635f12a7751671f8c51dd8218fa0dcf82901'); + }); + + describe('BigInt soliditySha3', () => { + it.each(soliditySha3BigIntValidData)('%s', (input, output) => { + expect(soliditySha3(...input)).toEqual(output); + }); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 18cbb781cec..90bbf007848 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1386,12 +1386,12 @@ dependencies: "@noble/hashes" "1.3.1" -"@noble/hashes@1.1.2", "@noble/hashes@~1.1.1": +"@noble/hashes@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== -"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": +"@noble/hashes@1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== @@ -1401,10 +1401,15 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== -"@noble/hashes@~1.3.1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@noble/hashes@~1.1.1": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.5.tgz#1a0377f3b9020efe2fae03290bd2a12140c95c11" + integrity sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== "@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": version "1.6.3" @@ -2301,6 +2306,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/benchmark@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/benchmark/-/benchmark-2.1.5.tgz#940c1850c18fdfdaee3fd6ed29cd92ae0d445b45" + integrity sha512-cKio2eFB3v7qmKcvIHLUMw/dIx/8bhWPuzpzRT4unCPRTD8VdA9Zb0afxpcxOqR4PixRS7yT42FqGS8BYL8g1w== + "@types/bn.js@^4.11.3": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -3505,6 +3515,14 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ== + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + big-integer@^1.6.44: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" @@ -8913,7 +8931,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10463,6 +10481,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +platform@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"