From c95ee8a53bf25bcf47777054af27cae1fbad8b2f Mon Sep 17 00:00:00 2001 From: "b00ste.lyx" <62855857+b00ste@users.noreply.github.com> Date: Mon, 24 Jul 2023 14:25:11 +0300 Subject: [PATCH] refactor!: change `ArrayLength` value from `uint256` to `uint128` (#287) * refactor: change ArrayLength value from uint256 to uint128 * refactor: fix decoding for array length as `uint128` * refactor!: encode value for `Array` length as `uint128` (16 bytes long) * test: fix mock schemas for tests * refactor: deprecate `ArrayLength` valueContent + use `encodeValueType` to encode Array length * test: update mock schemas for array length as left padded not abi-encoded * refactor: Update encoding/decoding for `uint128` * test: add more tests * chore: add sugested changes * refactor: update the encoding for `uint128` * chore: add spaces between `it` blocks * test: improve tests --------- Co-authored-by: CJ42 <31145285+CJ42@users.noreply.github.com> Co-authored-by: CJ42 --- src/index.test.ts | 2 +- src/lib/decodeData.test.ts | 3 +-- src/lib/decodeData.ts | 2 +- src/lib/encoder.test.ts | 26 +++++++++++++++++++ src/lib/encoder.ts | 36 +++++++++++++++++++------- src/lib/getData.ts | 2 +- src/lib/utils.test.ts | 53 +++++++++++++++++++++++++++++++++----- src/lib/utils.ts | 8 ++---- test/mockSchema.ts | 26 +++++++------------ 9 files changed, 116 insertions(+), 42 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 3a9ea942..614484b3 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -776,7 +776,7 @@ describe('Running @erc725/erc725.js tests...', () => { if (i === 0) { // Push the array length into the first element of results array results.push( - leftPad(numberToHex(schemaElement.expectedResult.length), 64), + leftPad(numberToHex(schemaElement.expectedResult.length), 32), ); } diff --git a/src/lib/decodeData.test.ts b/src/lib/decodeData.test.ts index 85cbd8b8..933b69b2 100644 --- a/src/lib/decodeData.test.ts +++ b/src/lib/decodeData.test.ts @@ -112,8 +112,7 @@ describe('decodeData', () => { value: [ { key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - value: - '0x0000000000000000000000000000000000000000000000000000000000000002', + value: '0x00000000000000000000000000000002', }, { key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', diff --git a/src/lib/decodeData.ts b/src/lib/decodeData.ts index 8471e696..e6107be2 100644 --- a/src/lib/decodeData.ts +++ b/src/lib/decodeData.ts @@ -207,7 +207,7 @@ export function decodeKey(schema: ERC725JSONSchema, value) { } const arrayLength = - decodeKeyValue('Number', 'uint256', valueElement.value, schema.name) || + decodeKeyValue('Number', 'uint128', valueElement.value, schema.name) || 0; // This will not run if no match or arrayLength diff --git a/src/lib/encoder.test.ts b/src/lib/encoder.test.ts index 90253546..51b8072d 100644 --- a/src/lib/encoder.test.ts +++ b/src/lib/encoder.test.ts @@ -46,6 +46,11 @@ describe('encoder', () => { encodedValue: '0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7', }, + { + valueType: 'uint128', + decodedValue: 11, + encodedValue: '0x0000000000000000000000000000000b', + }, { valueType: 'uint256', decodedValue: '1337', @@ -220,6 +225,27 @@ describe('encoder', () => { }); }); + it('throws when trying to encode a string as `uint128`', () => { + assert.throws(() => encodeValueType('uint128', 'helloWorld')); + }); + + it('throws when trying to encode a bytes17 as `uint128`', () => { + assert.throws(() => + encodeValueType('uint128', '340282366920938463463374607431768211456'), + ); + assert.throws(() => + encodeValueType('uint128', '0x0100000000000000000000000000000000'), + ); + }); + + it('throws when trying to decode a bytes17 as `uint128`', () => { + expect(() => + decodeValueType('uint128', '0x000000000000000000000000000000ffff'), + ).to.throw( + "Can't convert hex value 0x000000000000000000000000000000ffff to uint128. Too many bytes. 17 > 16", + ); + }); + describe('when encoding bytes[CompactBytesArray]', () => { it('should encode `0x` elements as `0x0000`', async () => { const testCase = { diff --git a/src/lib/encoder.ts b/src/lib/encoder.ts index 1720812b..91a5b070 100644 --- a/src/lib/encoder.ts +++ b/src/lib/encoder.ts @@ -37,8 +37,12 @@ import { toChecksumAddress, utf8ToHex, stripHexPrefix, + hexToBytes, + bytesToHex, } from 'web3-utils'; +import BigNumber from 'bignumber.js'; + import { JSONURLDataToEncode, URLDataWithHash } from '../types'; import { AssetURLEncode } from '../types/encodeData'; @@ -325,6 +329,28 @@ const valueTypeEncodingMap = { decode: (value: string) => abiCoder.decodeParameter('address', value), }, // NOTE: We could add conditional handling of numeric values here... + uint128: { + encode: (value: string | number) => { + const abiEncodedValue = abiCoder.encodeParameter('uint128', value); + const bytesArray = hexToBytes(abiEncodedValue); + return bytesToHex(bytesArray.slice(16)); + }, + decode: (value: string) => { + if (!isHex(value)) { + throw new Error(`Can't convert ${value} to uint128, value is not hex.`); + } + + if (value.length > 34) { + throw new Error( + `Can't convert hex value ${value} to uint128. Too many bytes. ${ + (value.length - 2) / 2 + } > 16`, + ); + } + + return BigNumber(value).toNumber(); + }, + }, uint256: { encode: (value: string | number) => abiCoder.encodeParameter('uint256', value), @@ -402,14 +428,6 @@ export const valueContentEncodingMap = (valueContent: string) => { decode: (value: string) => value, }; } - // NOTE: Deprecated. For reference/testing in future - case 'ArrayLength': { - return { - type: 'uint256', - encode: (value: number | string) => padLeft(numberToHex(value), 64), - decode: (value: string) => hexToNumber(value), - }; - } case 'Number': { return { type: 'uint256', @@ -424,7 +442,7 @@ export const valueContentEncodingMap = (valueContent: string) => { return padLeft(numberToHex(parsedValue), 64); }, - decode: (value) => '' + hexToNumber(value), + decode: (value) => hexToNumber(value).toString(), }; } // NOTE: This is not symmetrical, and always returns a checksummed address diff --git a/src/lib/getData.ts b/src/lib/getData.ts index d2bf6fcf..5d7cbc1e 100644 --- a/src/lib/getData.ts +++ b/src/lib/getData.ts @@ -41,7 +41,7 @@ const getArrayValues = async ( const arrayLength = await decodeKeyValue( 'Number', - 'uint256', + 'uint128', value.value, schema.name, ); // get the int array length diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 7799c73e..6b4aa282 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -59,8 +59,7 @@ describe('utils', () => { encodedValue: [ { key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', - value: - '0x0000000000000000000000000000000000000000000000000000000000000002', + value: '0x00000000000000000000000000000002', }, { key: '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', @@ -95,8 +94,7 @@ describe('utils', () => { encodedValue: [ { key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', - value: - '0x0000000000000000000000000000000000000000000000000000000000000002', + value: '0x00000000000000000000000000000002', }, { key: '0x9985edaf12cbacf5ac7d6ed54f0445cc00000000000000000000000000000000', @@ -512,12 +510,55 @@ describe('utils', () => { '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', ], values: [ - '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x00000000000000000000000000000001', '0xa3e6f38477d45727f6e6f853cdb479b0d60c0ac9', ], }); }); + it('encodes array', () => { + const addressArray = [ + '0x6413255d24b8fbf81d2d65214c485c694cb3d4b4', + '0xd6c68c2c94af899ce43ff1863693016a711ae7c7', + '0x79b698f4bc3051f18b5f94046f09d70823a8fd44', + '0x72bebf88546525a5888f188b390701bb0fd9b1a5', + '0x882aca051979e32e787e8815d9880759f91e7124', + '0x78827c8f8205072858a8cce39b8724d948327ba0', + '0xe27cd9c132677cdce2e9efa43b040de35ceff069', + '0x072616745957b45c8989e12b9563390fafac4ebe', + '0xfd5a7c50c0cf665a772407af3f05522784589c44', + '0x13de082cf8a499eee75b0681cfa0141a145f15d9', + '0xe3610d0eb167fe7a7b7c25d0aee8874eb8b113ef', + ]; + const encodedDataWithMultipleKeys = encodeData( + [ + { + keyName: 'LSP3IssuedAssets[]', + value: addressArray, + }, + ], + schemas, + ); + + assert.deepStrictEqual(encodedDataWithMultipleKeys, { + keys: [ + '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000002', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000003', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000004', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000005', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000006', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000007', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000008', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000009', + '0x3a47ab5bd3a594c3a8995f8fa58d08760000000000000000000000000000000a', + ], + values: ['0x0000000000000000000000000000000b', ...addressArray], + }); + }); + it('encodes multiple keys', () => { const encodedMultipleKeys = encodeData( [ @@ -554,7 +595,7 @@ describe('utils', () => { ], values: [ '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361697066733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', - '0x0000000000000000000000000000000000000000000000000000000000000002', + '0x00000000000000000000000000000002', '0xd94353d9b005b3c0a9da169b768a31c57844e490', '0xdaea594e385fc724449e3118b2db7e86dfba1826', '0x1183790f29be3cdfd0a102862fea1a4a30b3adab', diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e709238a..bcd607e8 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -259,14 +259,10 @@ export function encodeKey( const dataElement = value[index]; if (index === 0) { // This is arrayLength as the first element in the raw array + // encoded as uint128 results.push({ key: schema.key, - value: encodeKeyValue( - 'Number', - 'uint256', - value.length.toString(), - schema.name, - ) as string, + value: encodeValueType('uint128', value.length), }); } diff --git a/test/mockSchema.ts b/test/mockSchema.ts index ef438805..97b41d2b 100644 --- a/test/mockSchema.ts +++ b/test/mockSchema.ts @@ -2,7 +2,7 @@ // make one schema that tests every single type import AbiCoder from 'web3-eth-abi'; -import { utf8ToHex } from 'web3-utils'; +import { leftPad, utf8ToHex } from 'web3-utils'; import { ERC725JSONSchema } from '../src/types/ERC725JSONSchema'; @@ -185,7 +185,7 @@ export const mockSchema: (ERC725JSONSchema & { // testing data // the full array of values returnRawData: [ - abiCoder.encodeParameter('bytes', abiCoder.encodeParameter('uint256', 2)), // array length + abiCoder.encodeParameter('bytes', leftPad(2, 32)), // array length abiCoder.encodeParameter( 'bytes', '0xc444009d38d3046bb0cf81fa2cd295ce46a67c78', @@ -196,9 +196,7 @@ export const mockSchema: (ERC725JSONSchema & { ), ], returnRawDataArray: [ - abiCoder.encodeParameter('bytes[]', [ - abiCoder.encodeParameter('uint256', 2), - ]), + abiCoder.encodeParameter('bytes[]', [leftPad(2, 32)]), // array length abiCoder.encodeParameter('bytes[]', [ '0xc444009d38d3046bb0cf81fa2cd295ce46a67c78', ]), @@ -207,7 +205,7 @@ export const mockSchema: (ERC725JSONSchema & { ]), ], returnGraphData: [ - '0x0000000000000000000000000000000000000000000000000000000000000002', // array length + leftPad(2, 32), // array length '0xc444009d38d3046bb0cf81fa2cd295ce46a67c78', '0x4febc3491230571f6e1829e46602e3b110215a2e', ], @@ -226,7 +224,7 @@ export const mockSchema: (ERC725JSONSchema & { // testing data // the full array of values returnRawData: [ - abiCoder.encodeParameter('bytes', abiCoder.encodeParameter('uint256', 2)), // array length + abiCoder.encodeParameter('bytes', leftPad(2, 32)), // array length abiCoder.encodeParameter('bytes', '0x'), abiCoder.encodeParameter( 'bytes', @@ -234,16 +232,14 @@ export const mockSchema: (ERC725JSONSchema & { ), ], returnRawDataArray: [ - abiCoder.encodeParameter('bytes[]', [ - abiCoder.encodeParameter('uint256', 2), - ]), + abiCoder.encodeParameter('bytes[]', [leftPad(2, 32)]), abiCoder.encodeParameter('bytes[]', ['0x']), abiCoder.encodeParameter('bytes[]', [ '0x4febc3491230571f6e1829e46602e3b110215a2e', ]), ], returnGraphData: [ - '0x0000000000000000000000000000000000000000000000000000000000000002', // array length + leftPad(2, 32), // array length '0x', '0x4febc3491230571f6e1829e46602e3b110215a2e', ], @@ -260,7 +256,7 @@ export const mockSchema: (ERC725JSONSchema & { // testing data // the full array of values returnRawData: [ - abiCoder.encodeParameter('bytes', abiCoder.encodeParameter('uint256', 2)), // array length + abiCoder.encodeParameter('bytes', leftPad(2, 32)), // array length abiCoder.encodeParameter( 'bytes', '0x6f357c6a733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a6264', @@ -271,9 +267,7 @@ export const mockSchema: (ERC725JSONSchema & { ), ], returnRawDataArray: [ - abiCoder.encodeParameter('bytes[]', [ - abiCoder.encodeParameter('uint256', 2), - ]), + abiCoder.encodeParameter('bytes[]', [leftPad(2, 32)]), abiCoder.encodeParameter('bytes[]', [ '0x6f357c6a733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a6264', ]), @@ -282,7 +276,7 @@ export const mockSchema: (ERC725JSONSchema & { ]), ], returnGraphData: [ - '0x0000000000000000000000000000000000000000000000000000000000000002', // array length + leftPad(2, 32), // array length '0x6f357c6a733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a6264', '0x6f357c6a81bd0b7ed5ac354abbf24619ce16933f00a4bdfa8fcaf3791d25f69b497abf88697066733a2f2f516d6245724b6833466a7378787878787878787878787878787878787878787878787878787639414a4a765a6264', ],