From b2c62faf305745a0a0a3ce0478f4cd15e9ed6f84 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Wed, 25 Oct 2023 08:32:02 +0100 Subject: [PATCH] feat(wip): allow to encode LSP2 Array length only --- src/index.ts | 2 +- src/lib/encoder.ts | 37 ++--- src/lib/utils.test.ts | 322 +++++++++++++++++++++++------------------- src/lib/utils.ts | 5 + 4 files changed, 194 insertions(+), 172 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6a07e404..80e33d21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -118,7 +118,7 @@ export class ERC725 { } /** - * To prevent weird behovior from the lib, we must make sure all the schemas are correct before loading them. + * To prevent weird behavior from the lib, we must make sure all the schemas are correct before loading them. * * @param schemas * @returns diff --git a/src/lib/encoder.ts b/src/lib/encoder.ts index bde305fc..19a90123 100644 --- a/src/lib/encoder.ts +++ b/src/lib/encoder.ts @@ -324,30 +324,6 @@ const returnTypesOfUintNCompactBytesArray = () => { return types; }; -/** - * Encodes any set of strings to string[CompactBytesArray] - * - * @param values An array of non restricted strings - * @returns string[CompactBytesArray] - */ -const encodeStringCompactBytesArray = (values: string[]): string => { - const hexValues: string[] = values.map((element) => utf8ToHex(element)); - - return encodeCompactBytesArray(hexValues); -}; - -/** - * Decode a string[CompactBytesArray] to an array of strings - * @param compactBytesArray A string[CompactBytesArray] - * @returns An array of strings - */ -const decodeStringCompactBytesArray = (compactBytesArray: string): string[] => { - const hexValues: string[] = decodeCompactBytesArray(compactBytesArray); - const stringValues: string[] = hexValues.map((element) => hexToUtf8(element)); - - return stringValues; -}; - const valueTypeEncodingMap = { bool: { encode: (value: boolean) => (value ? '0x01' : '0x00'), @@ -486,8 +462,17 @@ const valueTypeEncodingMap = { decode: (value: string) => decodeCompactBytesArray(value), }, 'string[CompactBytesArray]': { - encode: (value: string[]) => encodeStringCompactBytesArray(value), - decode: (value: string) => decodeStringCompactBytesArray(value), + encode: (values: string[]) => { + const hexValues: string[] = values.map((element) => utf8ToHex(element)); + return encodeCompactBytesArray(hexValues); + }, + decode: (value: string) => { + const hexValues: string[] = decodeCompactBytesArray(value); + const stringValues: string[] = hexValues.map((element) => + hexToUtf8(element), + ); + return stringValues; + }, }, ...returnTypesOfBytesNCompactBytesArray(), ...returnTypesOfUintNCompactBytesArray(), diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 3365d600..7d3d384a 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -42,8 +42,9 @@ import { isDynamicKeyName } from './encodeKeyName'; import { decodeKey } from './decodeData'; describe('utils', () => { - describe('encodeKey/decodeKey', () => { + describe.only('encodeKey/decodeKey', () => { const testCases = [ + // test encoding an array of address { schema: { name: 'LSP3IssuedAssets[]', @@ -71,157 +72,172 @@ describe('utils', () => { }, ], }, - { - schema: { - name: 'TestObjArray[]', - key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', - keyType: 'Array', - valueContent: 'JSONURL', - valueType: 'bytes', - }, - decodedValue: [ - { - hashFunction: SUPPORTED_HASH_FUNCTION_STRINGS.KECCAK256_UTF8, - hash: '0x733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d', - url: 'ipfs://QmbErKh3FjsAR6YjsTjHZNm6McDp6aRt82Ftcv9AJJvZbd', - }, - { - hashFunction: SUPPORTED_HASH_FUNCTION_STRINGS.KECCAK256_UTF8, - hash: '0x81bd0b7ed5ac354abbf24619ce16933f00a4bdfa8fcaf3791d25f69b497abf88', - url: 'ipfs://QmbErKh3Fjsxxxxxxxxxxxxxxxxxxxxxxxxxxv9AJJvZbd', - }, - ], - encodedValue: [ - { - key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', - value: '0x00000000000000000000000000000002', - }, - { - key: '0x9985edaf12cbacf5ac7d6ed54f0445cc00000000000000000000000000000000', - value: - '0x6f357c6a733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a6264', - }, - { - key: '0x9985edaf12cbacf5ac7d6ed54f0445cc00000000000000000000000000000001', - value: - '0x6f357c6a81bd0b7ed5ac354abbf24619ce16933f00a4bdfa8fcaf3791d25f69b497abf88697066733a2f2f516d6245724b6833466a7378787878787878787878787878787878787878787878787878787639414a4a765a6264', - }, - ], - }, - { - schema: { - name: 'TupleType', - key: '0x50f2bd37dff422565ef30f885bf0ee0a36a2a2bcf9b0146ac9c646679c38dfd8', - keyType: 'Singleton', - valueType: '(bytes4,bytes8)', - valueContent: '(Bytes4,Number)', - }, - decodedValue: ['0xcafecafe', 11], - encodedValue: '0xcafecafe000000000000000b', - }, - { - schema: { - name: 'TupleMultiType', - key: '1e1bc4abe01b7baa7d4a359c0f460e632ef34b3f16f5722bd8892f2dae913022', - keyType: 'Singleton', - valueType: '(bytes4,bytes8,bytes4)', - valueContent: '(Bytes4,Number,Number)', - }, - decodedValue: ['0xcafecafe', 11, 8], - encodedValue: '0xcafecafe000000000000000b00000008', - }, - { - schema: { - name: 'TupleMultiType', - key: '1e1bc4abe01b7baa7d4a359c0f460e632ef34b3f16f5722bd8892f2dae913022', - keyType: 'Singleton', - valueType: '(bytes4,address,bytes2)[CompactBytesArray]', - valueContent: '(Bytes4,Address,Bytes)', - }, - decodedValue: [ - [ - '0xcafecafe', - '0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5', - '0xcafe', - ], - [ - '0xbeefbeef', - '0xFE31320faF8Da1492Eadf8Deb79bd264D7cF2141', - '0xbeef', - ], - [ - '0xf00df00d', - '0xc527702b14BF2f79F70B32e09F62B6A74cADFd80', - '0xf00d', - ], - ], - encodedValue: - '0x001acafecafedafea492d9c6733ae3d56b7ed1adb60692c98bc5cafe001abeefbeeffe31320faf8da1492eadf8deb79bd264d7cf2141beef001af00df00dc527702b14bf2f79f70b32e09f62b6a74cadfd80f00d', - }, - { - schema: { - name: 'TupleMultiType', - key: '1e1bc4abe01b7baa7d4a359c0f460e632ef34b3f16f5722bd8892f2dae913022', - keyType: 'Singleton', - valueType: '(bytes4,bytes8,bytes16)[CompactBytesArray]', - valueContent: '(Bytes4,Bytes8,Bytes16)', - }, - decodedValue: [ - [ - '0xcafecafe', - '0x951a5d121531bba8', - '0xdafea492d9c6733ae3d56b7ed1adb606', - ], - [ - '0xbeefbeef', - '0x8a483080f5db1105', - '0xfe31320faf8da1492eadf8deb79bd264', - ], - [ - '0xf00df00d', - '0x2fe92a11caf28ab2', - '0xc527702b14bf2f79f70b32e09f62b6a7', - ], - ], - encodedValue: - '0x001ccafecafe951a5d121531bba8dafea492d9c6733ae3d56b7ed1adb606001cbeefbeef8a483080f5db1105fe31320faf8da1492eadf8deb79bd264001cf00df00d2fe92a11caf28ab2c527702b14bf2f79f70b32e09f62b6a7', - }, - { - schema: { - name: 'AddressPermissions:AllowedCalls:
', - key: '0x4b80742de2bf393a64c70000
', - keyType: 'MappingWithGrouping', - valueType: '(bytes4,address,bytes4)[CompactBytesArray]', - valueContent: '(Bytes4,Address,Bytes4)', - }, - decodedValue: [ - [ - '0xcafecafe', - '0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5', - '0xcafecafe', - ], - [ - '0xbeefbeef', - '0xFE31320faF8Da1492Eadf8Deb79bd264D7cF2141', - '0xbeefbeef', - ], - [ - '0xf00df00d', - '0xc527702b14BF2f79F70B32e09F62B6A74cADFd80', - '0xf00df00d', - ], - ], - encodedValue: - '0x001ccafecafeDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5cafecafe001cbeefbeefFE31320faF8Da1492Eadf8Deb79bd264D7cF2141beefbeef001cf00df00dc527702b14BF2f79F70B32e09F62B6A74cADFd80f00df00d'.toLowerCase(), - }, + // { + // schema: { + // name: 'TestObjArray[]', + // key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', + // keyType: 'Array', + // valueContent: 'JSONURL', + // valueType: 'bytes', + // }, + // decodedValue: [ + // { + // hashFunction: SUPPORTED_HASH_FUNCTION_STRINGS.KECCAK256_UTF8, + // hash: '0x733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d', + // url: 'ipfs://QmbErKh3FjsAR6YjsTjHZNm6McDp6aRt82Ftcv9AJJvZbd', + // }, + // { + // hashFunction: SUPPORTED_HASH_FUNCTION_STRINGS.KECCAK256_UTF8, + // hash: '0x81bd0b7ed5ac354abbf24619ce16933f00a4bdfa8fcaf3791d25f69b497abf88', + // url: 'ipfs://QmbErKh3Fjsxxxxxxxxxxxxxxxxxxxxxxxxxxv9AJJvZbd', + // }, + // ], + // encodedValue: [ + // { + // key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', + // value: '0x00000000000000000000000000000002', + // }, + // { + // key: '0x9985edaf12cbacf5ac7d6ed54f0445cc00000000000000000000000000000000', + // value: + // '0x6f357c6a733e78f2fc4a3304c141e8424d02c9069fe08950c6514b27289ead8ef4faa49d697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a6264', + // }, + // { + // key: '0x9985edaf12cbacf5ac7d6ed54f0445cc00000000000000000000000000000001', + // value: + // '0x6f357c6a81bd0b7ed5ac354abbf24619ce16933f00a4bdfa8fcaf3791d25f69b497abf88697066733a2f2f516d6245724b6833466a7378787878787878787878787878787878787878787878787878787639414a4a765a6264', + // }, + // ], + // }, + // { + // schema: { + // name: 'TupleType', + // key: '0x50f2bd37dff422565ef30f885bf0ee0a36a2a2bcf9b0146ac9c646679c38dfd8', + // keyType: 'Singleton', + // valueType: '(bytes4,bytes8)', + // valueContent: '(Bytes4,Number)', + // }, + // decodedValue: ['0xcafecafe', 11], + // encodedValue: '0xcafecafe000000000000000b', + // }, + // { + // schema: { + // name: 'TupleMultiType', + // key: '1e1bc4abe01b7baa7d4a359c0f460e632ef34b3f16f5722bd8892f2dae913022', + // keyType: 'Singleton', + // valueType: '(bytes4,bytes8,bytes4)', + // valueContent: '(Bytes4,Number,Number)', + // }, + // decodedValue: ['0xcafecafe', 11, 8], + // encodedValue: '0xcafecafe000000000000000b00000008', + // }, + // { + // schema: { + // name: 'TupleMultiType', + // key: '1e1bc4abe01b7baa7d4a359c0f460e632ef34b3f16f5722bd8892f2dae913022', + // keyType: 'Singleton', + // valueType: '(bytes4,address,bytes2)[CompactBytesArray]', + // valueContent: '(Bytes4,Address,Bytes)', + // }, + // decodedValue: [ + // [ + // '0xcafecafe', + // '0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5', + // '0xcafe', + // ], + // [ + // '0xbeefbeef', + // '0xFE31320faF8Da1492Eadf8Deb79bd264D7cF2141', + // '0xbeef', + // ], + // [ + // '0xf00df00d', + // '0xc527702b14BF2f79F70B32e09F62B6A74cADFd80', + // '0xf00d', + // ], + // ], + // encodedValue: + // '0x001acafecafedafea492d9c6733ae3d56b7ed1adb60692c98bc5cafe001abeefbeeffe31320faf8da1492eadf8deb79bd264d7cf2141beef001af00df00dc527702b14bf2f79f70b32e09f62b6a74cadfd80f00d', + // }, + // { + // schema: { + // name: 'TupleMultiType', + // key: '1e1bc4abe01b7baa7d4a359c0f460e632ef34b3f16f5722bd8892f2dae913022', + // keyType: 'Singleton', + // valueType: '(bytes4,bytes8,bytes16)[CompactBytesArray]', + // valueContent: '(Bytes4,Bytes8,Bytes16)', + // }, + // decodedValue: [ + // [ + // '0xcafecafe', + // '0x951a5d121531bba8', + // '0xdafea492d9c6733ae3d56b7ed1adb606', + // ], + // [ + // '0xbeefbeef', + // '0x8a483080f5db1105', + // '0xfe31320faf8da1492eadf8deb79bd264', + // ], + // [ + // '0xf00df00d', + // '0x2fe92a11caf28ab2', + // '0xc527702b14bf2f79f70b32e09f62b6a7', + // ], + // ], + // encodedValue: + // '0x001ccafecafe951a5d121531bba8dafea492d9c6733ae3d56b7ed1adb606001cbeefbeef8a483080f5db1105fe31320faf8da1492eadf8deb79bd264001cf00df00d2fe92a11caf28ab2c527702b14bf2f79f70b32e09f62b6a7', + // }, + // { + // schema: { + // name: 'AddressPermissions:AllowedCalls:
', + // key: '0x4b80742de2bf393a64c70000
', + // keyType: 'MappingWithGrouping', + // valueType: '(bytes4,address,bytes4)[CompactBytesArray]', + // valueContent: '(Bytes4,Address,Bytes4)', + // }, + // decodedValue: [ + // [ + // '0xcafecafe', + // '0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5', + // '0xcafecafe', + // ], + // [ + // '0xbeefbeef', + // '0xFE31320faF8Da1492Eadf8Deb79bd264D7cF2141', + // '0xbeefbeef', + // ], + // [ + // '0xf00df00d', + // '0xc527702b14BF2f79F70B32e09F62B6A74cADFd80', + // '0xf00df00d', + // ], + // ], + // encodedValue: + // '0x001ccafecafeDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5cafecafe001cbeefbeefFE31320faF8Da1492Eadf8Deb79bd264D7cF2141beefbeef001cf00df00dc527702b14BF2f79F70B32e09F62B6A74cADFd80f00df00d'.toLowerCase(), + // }, ]; testCases.forEach((testCase) => { it(`encodes/decodes keyType Array / tuples (valueContent: ${testCase.schema.valueContent}, valueType: ${testCase.schema.valueType}`, () => { + console.log( + 'encodeKey(testCase.schema as ERC725JSONSchema, testCase.decodedValue)', + ); + console.log( + encodeKey(testCase.schema as ERC725JSONSchema, testCase.decodedValue), + ); + assert.deepStrictEqual( encodeKey(testCase.schema as ERC725JSONSchema, testCase.decodedValue), testCase.encodedValue, ); + + console.log( + 'decodeKey(testCase.schema as ERC725JSONSchema, testCase.encodedValue)', + ); + console.log( + decodeKey(testCase.schema as ERC725JSONSchema, testCase.encodedValue), + ); + assert.deepStrictEqual( decodeKey(testCase.schema as ERC725JSONSchema, testCase.encodedValue), testCase.decodedValue, @@ -382,7 +398,7 @@ describe('utils', () => { }); }); - describe('encodeArrayKey', () => { + describe.only('encodeArrayKey', () => { it('encodes array key correctly', () => { const key = '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0'; @@ -397,6 +413,22 @@ describe('utils', () => { assert.strictEqual(encodeArrayKey(key, index), expectedValue); }); }); + + it('should encode the array length only if passing a number', async () => { + // test encoding only the length + const schema: ERC725JSONSchema = { + name: 'LSP3IssuedAssets[]', + key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + keyType: 'Array', + valueContent: 'Address', + valueType: 'address', + }; + + const decodedValue = 3; + const encodedValue = '0x00000000000000000000000000000003'; + + assert.equal(encodeKey(schema, decodedValue), encodedValue); + }); }); describe('encodeData', () => { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f289fb01..f88b5434 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -248,6 +248,11 @@ export function encodeKey( switch (lowerCaseKeyType) { case 'array': { + // if we are encoding only the Array length + if (typeof value === 'number') { + return encodeValueType('uint128', value); + } + if (!Array.isArray(value)) { console.error("Can't encode a non array for key of type array"); return null;