From 95dabb22c3958b94014eed2b460a5e2dcce4684d Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Mon, 1 Jul 2024 06:56:03 -0400 Subject: [PATCH 1/4] fix: Normalize tests and add name as a constant in the schema. Add nonDynamicName to capture resolved schema names. --- src/index.test.ts | 38 ++++++++++++++---- src/lib/decodeData.test.ts | 2 +- src/lib/decodeData.ts | 7 +++- src/lib/decodeMappingKey.test.ts | 20 ++++++---- src/lib/decodeMappingKey.ts | 18 ++++----- src/lib/encodeKeyName.test.ts | 38 +++++++++--------- src/lib/encodeKeyName.ts | 67 ++++++++++++++++---------------- src/lib/getSchemaElement.test.ts | 5 ++- src/lib/getSchemaElement.ts | 29 ++++++++++++-- src/lib/schemaParser.test.ts | 2 +- src/lib/schemaParser.ts | 5 ++- src/lib/utils.test.ts | 10 ++--- src/lib/utils.ts | 8 +--- src/types/ERC725JSONSchema.ts | 4 ++ src/types/decodeData.ts | 4 ++ test/mockSchema.ts | 35 ++++++++++++++++- test/testHelpers.ts | 6 ++- 17 files changed, 200 insertions(+), 98 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 3a0fef4d..e99cc3df 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -223,6 +223,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'ThisKeyDoesNotExist', key: '0xb12a0af5f83066646eb63c96bf29dcb827024d9a33189f5a61652a03951d1fbe', value: null, + nonDynamicName: 'ThisKeyDoesNotExist', }; assert.deepStrictEqual(data, expectedResult); @@ -240,6 +241,7 @@ describe('Running @erc725/erc725.js tests...', () => { keyType: 'Array', valueContent: 'Address', valueType: 'address', + nonDynamicName: 'NonExistingArray[]', }, ], ERC725_CONTRACT_ADDRESS, @@ -251,6 +253,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'NonExistingArray[]', key: '0xd6cbdbfc8d25c9ce4720b5fe6fa8fc536803944271617bf5425b4bd579195840', value: [], + nonDynamicName: 'NonExistingArray[]', }); const dataArray = await erc725.getData(['NonExistingArray[]']); @@ -259,6 +262,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'NonExistingArray[]', key: '0xd6cbdbfc8d25c9ce4720b5fe6fa8fc536803944271617bf5425b4bd579195840', value: [], + nonDynamicName: 'NonExistingArray[]', }, ]); }); @@ -284,6 +288,7 @@ describe('Running @erc725/erc725.js tests...', () => { { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + nonDynamicName: 'LSP3Profile', value: { verification: { method: 'keccak256(utf8)', @@ -296,6 +301,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP1UniversalReceiverDelegate', key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313', + nonDynamicName: 'LSP1UniversalReceiverDelegate', }, ]; @@ -359,6 +365,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP1UniversalReceiverDelegate', key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313', + nonDynamicName: 'LSP1UniversalReceiverDelegate', }); }); }); @@ -400,7 +407,9 @@ describe('Running @erc725/erc725.js tests...', () => { ]); assert.deepStrictEqual(data[0], { key: '0x4b80742de2bf82acb36300009139def55c73c12bcda9c44f12326686e3948634', - name: 'AddressPermissions:Permissions:9139def55c73c12bcda9c44f12326686e3948634', + name: 'AddressPermissions:Permissions:
', + nonDynamicName: + 'AddressPermissions:Permissions:9139def55c73c12bcda9c44f12326686e3948634', value: '0x0000000000000000000000000000000000000000000000000000000000000002', }); @@ -444,7 +453,9 @@ describe('Running @erc725/erc725.js tests...', () => { ]); assert.deepStrictEqual(data[0], { key: '0x6de85eaf5d982b4e5da000009139def55c73c12bcda9c44f12326686e3948634', - name: 'LSP4CreatorsMap:9139def55c73c12bcda9c44f12326686e3948634', + name: 'LSP4CreatorsMap:
', + nonDynamicName: + 'LSP4CreatorsMap:9139def55c73c12bcda9c44f12326686e3948634', value: ['0x24871b3d', 0], }); }); @@ -472,6 +483,7 @@ describe('Running @erc725/erc725.js tests...', () => { { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + nonDynamicName: 'LSP3Profile', value: { verification: { method: 'keccak256(utf8)', @@ -484,6 +496,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP1UniversalReceiverDelegate', key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313', + nonDynamicName: 'LSP1UniversalReceiverDelegate', }, ]; @@ -532,6 +545,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: null, + nonDynamicName: 'LSP3Profile', }); }); @@ -599,16 +613,20 @@ describe('Running @erc725/erc725.js tests...', () => { { key: '0x48643a15ac5407a175674ab0f8c92df5ae90694dac72ebf0a058fb2599e3b06a', name: 'MyURL', + nonDynamicName: 'MyURL', value: 'ipfs://QmbErKh3FjsAR6YjsTjHZNm6McDp6aRt82Ftcv9AJJvZbd', }, { key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', - name: 'LSP12IssuedAssetsMap:b74a88C43BCf691bd7A851f6603cb1868f6fc147', + name: 'LSP12IssuedAssetsMap:
', + nonDynamicName: + 'LSP12IssuedAssetsMap:b74a88C43BCf691bd7A851f6603cb1868f6fc147', value: '0x1098603B193d276f5fA176CC02007B609F9DAE6b', }, { key: '0xeafec4d89fa9619884b600005ef83ad9559033e6e941db7d7c495acdce616347', name: 'SupportedStandards:LSP3Profile', + nonDynamicName: 'SupportedStandards:LSP3Profile', value: '0x5ef83ad9', }, ]); @@ -704,6 +722,7 @@ describe('Running @erc725/erc725.js tests...', () => { key: testJSONURLSchema.key, name: testJSONURLSchema.name, value: JSON.parse(jsonString), + nonDynamicName: testJSONURLSchema.name, }); }); @@ -743,7 +762,9 @@ describe('Running @erc725/erc725.js tests...', () => { }); assert.deepStrictEqual(result, { - name: 'JSONForAddress:cafecafecafecafecafecafecafecafecafecafe', + name: 'JSONForAddress:
', + nonDynamicName: + 'JSONForAddress:cafecafecafecafecafecafecafecafecafecafe', key: '0x84b02f6e50a0a0819a4f0000cafecafecafecafecafecafecafecafecafecafe', value: JSON.parse(jsonString), }); @@ -817,7 +838,7 @@ describe('Running @erc725/erc725.js tests...', () => { const result = await erc725.getData( schemaElement.dynamicKeyParts ? { - keyName: schemaElement.key, + keyName: schemaElement.name, dynamicKeyParts: schemaElement.dynamicKeyParts, } : schemaElement.key, @@ -826,6 +847,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: schemaElement.name, key: schemaElement.key, value: schemaElement.expectedResult, + nonDynamicName: schemaElement.nonDynamicName, }); }); @@ -838,7 +860,7 @@ describe('Running @erc725/erc725.js tests...', () => { const result = await erc725.getData( schemaElement.dynamicKeyParts ? { - keyName: schemaElement.key, + keyName: schemaElement.name, dynamicKeyParts: schemaElement.dynamicKeyParts, } : schemaElement.key, @@ -847,6 +869,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: schemaElement.name, key: schemaElement.key, value: schemaElement.expectedResult, + nonDynamicName: schemaElement.nonDynamicName, }); }); }); @@ -1136,6 +1159,7 @@ describe('Running @erc725/erc725.js tests...', () => { const result = erc725.encodeData([ { keyName: schemaElement.name, + dynamicKeyParts: schemaElement.dynamicKeyParts, value: schemaElement.expectedResult, }, ]); @@ -1979,7 +2003,7 @@ describe('decodeMappingKey', () => { [ { type: 'bytes16', - value: '00000000000000000000000012345678', + value: '0x12345678000000000000000000000000', }, ], ); diff --git a/src/lib/decodeData.test.ts b/src/lib/decodeData.test.ts index f323cd76..c6e48bbd 100644 --- a/src/lib/decodeData.test.ts +++ b/src/lib/decodeData.test.ts @@ -338,7 +338,7 @@ describe('decodeData', () => { schemas, ); - expect(decodedData.map(({ name }) => name)).to.eql([ + expect(decodedData.map(({ nonDynamicName }) => nonDynamicName)).to.eql([ 'MyKeyName2:aaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa:true', 'MyDynamicKey2:cafecafecafecafecafecafecafecafecafecafe', 'KeyTwo', diff --git a/src/lib/decodeData.ts b/src/lib/decodeData.ts index a6b7cefd..bf7b51f8 100644 --- a/src/lib/decodeData.ts +++ b/src/lib/decodeData.ts @@ -338,9 +338,12 @@ export function decodeData( } console.error(error); } + const { key, name, nonDynamicName } = schemaElement; return { - key: schemaElement.key, - name: schemaElement.name, + key, + name, + ...(nonDynamicName ? { nonDynamicName } : { nonDynamicName: name }), + ...(dynamicKeyParts ? { dynamicKeyParts } : {}), value: decodedValue, }; }; diff --git a/src/lib/decodeMappingKey.test.ts b/src/lib/decodeMappingKey.test.ts index 3f804b90..fe143543 100644 --- a/src/lib/decodeMappingKey.test.ts +++ b/src/lib/decodeMappingKey.test.ts @@ -44,9 +44,9 @@ describe('decodeDynamicKeyParts', () => { key: { name: 'MyKeyName:', encoded: - '0x35e6950bc8d21a1699e5000000000000000000000000000000000000abcd1234', + '0x35e6950bc8d21a1699e50000abcd123400000000000000000000000000000000', }, - dynamicKeyParts: [{ type: 'bytes4', value: 'abcd1234' }], + dynamicKeyParts: [{ type: 'bytes4', value: '0xabcd1234' }], }, { key: { @@ -57,7 +57,8 @@ describe('decodeDynamicKeyParts', () => { dynamicKeyParts: [ { type: 'bytes32', - value: 'aaaabbbbccccddddeeeeffff1111222233334444', + value: + '0xaaaabbbbccccddddeeeeffff1111222233334444000000000000000000000000', }, ], }, @@ -94,10 +95,10 @@ describe('decodeDynamicKeyParts', () => { key: { name: 'MyKeyName::', encoded: - '0x35e6950bc8d20000ffff000000000000000000000000000000000000f342d33d', + '0x35e6950bc8d2ffff0000000000000000000000000000000000000000f342d33d', }, dynamicKeyParts: [ - { type: 'bytes2', value: 'ffff' }, + { type: 'bytes2', value: '0xffff' }, { type: 'uint32', value: 4081242941 }, ], }, @@ -127,7 +128,8 @@ describe('decodeDynamicKeyParts', () => { dynamicKeyParts: [ { type: 'bytes32', - value: 'aaaabbbbccccddddeeeeffff1111222233334444', + value: + '0xaaaabbbbccccddddeeeeffff1111222233334444000000000000000000000000', }, ], }, @@ -138,7 +140,11 @@ describe('decodeDynamicKeyParts', () => { '0x35e6950bc8d2aaaabbbb00000000000000000000000000000000000000000001', }, dynamicKeyParts: [ - { type: 'bytes32', value: 'aaaabbbb' }, + { + type: 'bytes32', + value: + '0xaaaabbbb00000000000000000000000000000000000000000000000000000000', + }, { type: 'bool', value: true }, ], }, diff --git a/src/lib/decodeMappingKey.ts b/src/lib/decodeMappingKey.ts index 7e3d40ac..bb1f4bdb 100644 --- a/src/lib/decodeMappingKey.ts +++ b/src/lib/decodeMappingKey.ts @@ -18,7 +18,7 @@ * @date 2022 */ -import { padLeft } from 'web3-utils'; +import { padLeft, padRight } from 'web3-utils'; import { isHex } from 'web3-validator'; import { decodeValueType } from './encoder'; import { ERC725JSONSchema } from '../types/ERC725JSONSchema'; @@ -45,18 +45,16 @@ function decodeKeyPart( let decodedKey: string | number | boolean | undefined; const type = keyPartName.slice(1, keyPartName.length - 1); - - if (type === 'bool') + if (type === 'bool') { decodedKey = encodedKeyPart.slice(encodedKeyPart.length - 1) === '1'; - else if (type.includes('uint')) + } else if (type.includes('uint')) decodedKey = Number.parseInt(encodedKeyPart, 16); else if (type.includes('bytes')) { - const bytesLength = Number.parseInt(type.replace('bytes', ''), 10) * 2; - const sliceFrom = - encodedKeyPart.length - bytesLength < 0 - ? 0 - : encodedKeyPart.length - bytesLength; - decodedKey = encodedKeyPart.slice(sliceFrom); + const charLength = Number.parseInt(type.replace('bytes', ''), 10) * 2; + decodedKey = padRight( + `0x${encodedKeyPart.slice(0, charLength)}`, + charLength, + ); } else if (type === 'address') { // this is required if the 2nd word is an address in a MappingWithGrouping const leftPaddedAddress = padLeft(`0x${encodedKeyPart}`, 40); diff --git a/src/lib/encodeKeyName.test.ts b/src/lib/encodeKeyName.test.ts index 05dfa044..3d996695 100644 --- a/src/lib/encodeKeyName.test.ts +++ b/src/lib/encodeKeyName.test.ts @@ -53,18 +53,19 @@ describe('encodeKeyName', () => { '0x22496f48a493035f0ab40000cafecafecafecafecafecafecafecafecafecafe', }, { - keyName: 'MyCoolAddress:cafecafecafecafecafecafecafecafecafecafe', + keyName: 'MyCoolAddress:0xcafecafecafecafecafecafecafecafecafecafe', expectedKey: '0x22496f48a493035f0ab40000cafecafecafecafecafecafecafecafecafecafe', }, { - keyName: 'LSP12IssuedAssetsMap:b74a88C43BCf691bd7A851f6603cb1868f6fc147', + keyName: + 'LSP12IssuedAssetsMap:0xb74a88C43BCf691bd7A851f6603cb1868f6fc147', expectedKey: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', }, { keyName: - 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', + 'AddressPermissions:Permissions:0xcafecafecafecafecafecafecafecafecafecafe', expectedKey: '0x4b80742de2bf82acb3630000cafecafecafecafecafecafecafecafecafecafe', }, @@ -119,7 +120,7 @@ describe('encodeKeyName', () => { { keyName: 'MyKeyName:', expectedKey: - '0x35e6950bc8d21a1699e5000000000000000000000000000000000000abcd1234', + '0x35e6950bc8d21a1699e50000abcd123400000000000000000000000000000000', // |--keccak256 bytes12-||--||---bytes20: keccak256()---------------| dynamicKeyParts: '0xabcd1234', }, @@ -163,14 +164,14 @@ describe('encodeKeyName', () => { { keyName: 'MyKeyName::', expectedKey: - '0x35e6950bc8d20000ffff000000000000000000000000000000000000f342d33d', + '0x35e6950bc8d2ffff0000000000000000000000000000000000000000f342d33d', // |-- bytes6 --||------||--||------------ bytes20 -----------------| dynamicKeyParts: ['ffff', '4081242941'], }, { keyName: 'MyKeyName::', expectedKey: - '0x35e6950bc8d20000ffff000000000000000000000000000000000000f342d33d', + '0x35e6950bc8d2ffff0000000000000000000000000000000000000000f342d33d', // |-- bytes6 --||------||--||------------ bytes20 -----------------| dynamicKeyParts: ['ffff', '0xf342d33d'], }, @@ -189,9 +190,7 @@ describe('encodeKeyName', () => { expectedKey: '0x35e6950bc8d275060e3c0000aaaabbbbccccddddeeeeffff1111222233334444', // |-- bytes6 --||------||--||------------ bytes20 -----------------| - dynamicKeyParts: [ - '0xaaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa', - ], + dynamicKeyParts: ['0xaaaabbbbccccddddeeeeffff1111222233334444'], }, { keyName: 'MyKeyName::', @@ -367,19 +366,19 @@ describe('encodeDynamicKeyPart', () => { type: '', value: '0xd1b2917d26eeeaad', bytes: 12, - expectedEncoding: '00000000d1b2917d26eeeaad', // left padded + expectedEncoding: 'd1b2917d26eeeaad00000000', // right padded/cut }, { type: '', value: 'd1b2917d26eeeaad', // test without 0x prefix bytes: 12, - expectedEncoding: '00000000d1b2917d26eeeaad', // left padded + expectedEncoding: 'd1b2917d26eeeaad00000000', // right padded/cut }, { type: '', - value: 'd1b2917d26eeeaad', + value: '0xd1b2917d26eeeaad', bytes: 4, - expectedEncoding: 'd1b2917d', // right cut + expectedEncoding: 'd1b2917d', // right padded/cut }, ]; @@ -397,11 +396,14 @@ describe('encodeDynamicKeyPart', () => { encodeDynamicKeyPart('', 'thisisnotanhexstr', 20), ); }); - it('throws if is called with wrong number of bytes', () => { - assert.throws(() => - encodeDynamicKeyPart('', '0xd1b2917d26eeeaad1234', 20), - ); - }); + + // Since we're allowing things to be truncated and right and left padded + // This doesn't seem an ideal test case. + // it('throws if is called with wrong number of bytes', () => { + // assert.throws(() => + // encodeDynamicKeyPart('', '0xd1b2917d26eeeaad1234', 20), + // ); + // }); }); describe('generateDynamicKeyName', () => { diff --git a/src/lib/encodeKeyName.ts b/src/lib/encodeKeyName.ts index 8c3c76a4..98d109c0 100644 --- a/src/lib/encodeKeyName.ts +++ b/src/lib/encodeKeyName.ts @@ -18,8 +18,7 @@ */ import { isAddress, isHex } from 'web3-validator'; -import { keccak256, leftPad, numberToHex, padLeft } from 'web3-utils'; -import { stripHexPrefix } from 'web3-eth-accounts'; +import { keccak256, leftPad, numberToHex, padLeft, padRight } from 'web3-utils'; import { encodeArrayKey, guessKeyTypeFromKeyName } from './utils'; import { DynamicKeyParts } from '../types/dynamicKeys'; @@ -39,11 +38,12 @@ export const dynamicTypesRegex = /<(uint|int|bytes)(\d+)>/; */ export const encodeDynamicKeyPart = ( type: string, - value: string, + value_: string, bytes: number, ) => { let baseType = ''; let size = 0; + let value = value_; if (dynamicTypes.includes(type)) { baseType = type.slice(1, -1); @@ -68,23 +68,24 @@ export const encodeDynamicKeyPart = ( `Wrong value: ${value} for dynamic key with type: . Expected "true" or "false".`, ); } - return leftPad(+(value === 'true'), bytes * 2).slice(2); + return leftPad( + // In theory passing in 0x1 should also work + value === 'true' || Number(value) === 1 ? 1 : 0, + bytes * 2, + ).slice(2); } case 'address': { + if (!value.startsWith('0x')) { + value = `0x${value}`; + } if (!isAddress(value)) { throw new Error( `Wrong value: ${value} for dynamic key with type:
. Value is not an address.`, ); } - - if (bytes > 20) { - return leftPad(value.replace('0x', ''), bytes * 2).toLowerCase(); - } - - return value - .replace('0x', '') - .slice(0, bytes * 2) - .toLowerCase(); + return padLeft(value.slice(0, 2 + bytes * 2), bytes * 2) + .slice(2) + .toLowerCase(); // keys should not contain upper case chars (i.e. original checksummed stuff) } case 'uint': { if (size > 256 || size % 8 !== 0) { @@ -96,34 +97,27 @@ export const encodeDynamicKeyPart = ( // NOTE: we could verify if the number given is not too big for the given size. // e.g.: uint8 max value is 255, uint16 is 65535... - const hexNumber = numberToHex(value).slice(2); - if (hexNumber.length <= bytes * 2) { - return padLeft(hexNumber, bytes * 2); + let hex = numberToHex(value).slice(2); + if (hex.length > bytes * 2) { + hex = `0x${hex.slice(-bytes * 2)}`; + } else { + hex = `0x${hex}`; } - - return hexNumber.slice(-bytes * 2); + return padLeft(hex, bytes * 2).slice(2); } case 'int': // TODO: throw new Error('The encoding of has not been implemented yet.'); case 'bytes': { - if (!isHex(value)) { - throw new Error( - `Wrong value: ${value} for dynamic key with type: ${type}. Value is not in hex.`, - ); + if (!value.startsWith('0x')) { + value = `0x${value}`; } - const valueWithoutPrefix = stripHexPrefix(value); - if (valueWithoutPrefix.length > size * 2) { + if (!isHex(value)) { throw new Error( - `Wrong value: ${value} for dynamic key with type: ${type}. Value longer than ${size} bytes.`, + `Wrong value: ${value} for dynamic key with type: $type. Value is not in hex.`, ); } - - if (valueWithoutPrefix.length > bytes * 2) { - return valueWithoutPrefix.slice(0, bytes * 2); // right cut - } - - return stripHexPrefix(leftPad(value, bytes * 2).toLowerCase()); + return padRight(value.slice(0, 2 + bytes * 2), bytes * 2).slice(2); } default: throw new Error(`Dynamic key: ${type} is not supported`); @@ -132,6 +126,9 @@ export const encodeDynamicKeyPart = ( // This function does not support multi dynamic types such as MyName: export function isDynamicKeyName(name: string) { + if (name.startsWith('0x') && name.includes('<')) { + return true; + } const keyNameParts = name.split(':'); for (let i = 0; i < keyNameParts.length; i++) { @@ -205,14 +202,18 @@ const encodeDynamicMappingWithGrouping = ( const firstPart = keccak256(keyNameSplit[0]).slice(0, 14); let secondPart = ''; - if (isDynamicKeyName(keyNameSplit[1])) { + if (keyNameSplit[1].startsWith('0x')) { + secondPart = padRight(keyNameSplit[1].slice(0, 2 + 4 * 2), 4 * 2).slice(2); + } else if (isDynamicKeyName(keyNameSplit[1])) { secondPart = encodeDynamicKeyPart(keyNameSplit[1], dynamicKeyParts[0], 4); } else { secondPart = keccak256(keyNameSplit[1]).slice(2, 2 + 4 * 2); } let lastPart = ''; - if (isDynamicKeyName(keyNameSplit[2])) { + if (keyNameSplit[2].startsWith('0x')) { + lastPart = padRight(keyNameSplit[2].slice(0, 2 + 20 * 2), 20 * 2).slice(2); + } else if (isDynamicKeyName(keyNameSplit[2])) { lastPart = encodeDynamicKeyPart( keyNameSplit[2], dynamicKeyParts[dynamicKeyParts.length - 1], diff --git a/src/lib/getSchemaElement.test.ts b/src/lib/getSchemaElement.test.ts index 0469391c..53fedb26 100644 --- a/src/lib/getSchemaElement.test.ts +++ b/src/lib/getSchemaElement.test.ts @@ -68,11 +68,14 @@ describe('getSchemaElement', () => { ['0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac'], ), { - name: 'LSP12IssuedAssetsMap:2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', + nonDynamicName: + 'LSP12IssuedAssetsMap:2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', + name: 'LSP12IssuedAssetsMap:
', key: '0x74ac2555c10b9349e78f00002ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', keyType: 'Mapping', valueType: '(bytes4,uint128)', valueContent: '(Bytes4,Number)', + dynamicKeyParts: ['0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac'], }, ); }); diff --git a/src/lib/getSchemaElement.ts b/src/lib/getSchemaElement.ts index 81d3b9fb..496cac56 100644 --- a/src/lib/getSchemaElement.ts +++ b/src/lib/getSchemaElement.ts @@ -25,6 +25,7 @@ import { generateDynamicKeyName, isDynamicKeyName, } from './encodeKeyName'; +import { decodeMappingKey } from './decodeMappingKey'; /** * @@ -51,12 +52,16 @@ const getSchemaElementForDynamicKeyName = ( // once we have the schemaElement with dynamic parts, we need to replace the name and the key: const key = encodeKeyName(namedDynamicKey, dynamicKeyParts); - const name = generateDynamicKeyName(namedDynamicKey, dynamicKeyParts); + const nonDynamicName = generateDynamicKeyName( + namedDynamicKey, + dynamicKeyParts, + ); return { ...schemaElement, + nonDynamicName, key, - name, + dynamicKeyParts, }; }; @@ -74,7 +79,25 @@ export function getSchemaElement( dynamicKeyParts?: DynamicKeyParts, ): ERC725JSONSchema { let keyHash: string; - + if (namedOrHashedKey.startsWith('0x')) { + const index = namedOrHashedKey.indexOf('<'); + if (index !== -1) { + const partial = namedOrHashedKey.slice(0, index); + const schemaElement = schemas.find( + (e) => e.key.slice(0, index) === partial, + ); + const dynamicKeyParts = decodeMappingKey( + namedOrHashedKey, + schemaElement as ERC725JSONSchema, + ) as unknown as DynamicKeyParts; + if (schemaElement) { + return { + ...schemaElement, + ...(dynamicKeyParts ? { dynamicKeyParts } : {}), + }; + } + } + } if (isDynamicKeyName(namedOrHashedKey)) { if (!dynamicKeyParts) { throw new Error( diff --git a/src/lib/schemaParser.test.ts b/src/lib/schemaParser.test.ts index dcd8d60f..f10f188a 100644 --- a/src/lib/schemaParser.test.ts +++ b/src/lib/schemaParser.test.ts @@ -106,7 +106,7 @@ describe('schemaParser getSchema', () => { ); assert.deepStrictEqual(schema, { - name: 'SupportedStandards:??????', + name: 'SupportedStandards:LSP3Profile', key: '0xeafec4d89fa9619884b60000f4d7faed14a1ab658d46d385bc29fb1eeaa56d0b', keyType: 'Mapping', valueContent: '0x5ef83ad9', diff --git a/src/lib/schemaParser.ts b/src/lib/schemaParser.ts index 3cdde5ec..2f2ff26e 100644 --- a/src/lib/schemaParser.ts +++ b/src/lib/schemaParser.ts @@ -87,7 +87,10 @@ const fillDynamicKeyPart = ( if (keccak256(keyNameParts[1]).substring(0, 42) === `0x${secondWordHex}`) { dynamicPartName = keyNameParts[1]; } - result.name = `${keyNameParts[0]}:${dynamicPartName}`; + + // DO NOT MODIFY THE NAME OF THE SCHEMA ITEM + // OTHERWISE WE CAN NO LONGER FIND THE CORRESPONDING SCHEMA BY NAME. + // result.name = `${keyNameParts[0]}:${dynamicPartName}`; return result; }; diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 5ceff6d1..2fabaed1 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -375,8 +375,8 @@ describe('utils', () => { encodedValue: '0x74657374', }, { - valueContent: '0xc9aaAE3201F40fd0fF04D9c885769d8256A456ab', - valueType: 'bytes', + valueContent: 'Address', + valueType: 'address', decodedValue: '0xc9aaAE3201F40fd0fF04D9c885769d8256A456ab', encodedValue: '0xc9aaae3201f40fd0ff04d9c885769d8256a456ab', }, @@ -929,7 +929,7 @@ describe('utils', () => { }, { keyType: 'Mapping', - keyName: 'MyCoolAddress:0xcafecafecafecafecafecafecafecafecafecafe', + keyName: 'MyCoolAddress:cafecafecafecafecafecafecafecafecafecafe', }, { keyType: 'Mapping', @@ -943,7 +943,7 @@ describe('utils', () => { { keyType: 'MappingWithGrouping', keyName: - 'AddressPermissions:Permissions:0xcafecafecafecafecafecafecafecafecafecafe', + 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', }, ]; @@ -1026,7 +1026,7 @@ describe('utils', () => { generatedSchemas.forEach((schema) => { expect( - isDynamicKeyName(schema.name), + isDynamicKeyName(schema.key), 'generated schema key should not be dynamic', ).to.be.false; }); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index cb6ccc20..eba20a82 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -177,7 +177,7 @@ export function guessKeyTypeFromKeyName( // This function could not work with subsequents keys of an Array // It will always assume the given key, if array, is the initial array key. - const splittedKeyName = keyName.split(':'); + const splittedKeyName = keyName.replace(/[^:] } | Record; + dynamicKeyParts?: DynamicKeyParts; + nonDynamicName?: string; name: string; key: string; } diff --git a/test/mockSchema.ts b/test/mockSchema.ts index 295d8ddc..b0e2b9f8 100644 --- a/test/mockSchema.ts +++ b/test/mockSchema.ts @@ -16,6 +16,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 1 { name: 'SupportedStandards:LSP3Profile', + nonDynamicName: 'SupportedStandards:LSP3Profile', key: '0xeafec4d89fa9619884b600005ef83ad9559033e6e941db7d7c495acdce616347', keyType: 'Mapping', valueContent: '0x5ef83ad9', @@ -30,6 +31,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 2 { name: 'TestJSONURL', + nonDynamicName: 'TestJSONURL', key: '0xd154e1e44d32870ff5ade9e8726fd06d0ed6c996f5946dabfdfd46aa6dd2ea99', keyType: 'Singleton', valueContent: 'JSONURL', @@ -56,6 +58,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 3 { name: 'TestAssetURL', + nonDynamicName: 'TestAssetURL', key: '0xf18290c9b373d751e12c5ec807278267a807c35c3806255168bc48a85757ceee', keyType: 'Singleton', valueContent: 'AssetURL', @@ -82,6 +85,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 4 { name: 'TestKeccak256', + nonDynamicName: 'TestKeccak256', key: '0xd6c7198ea09a1d3357688e1dbdf0e07f6cfaf94359e0a4fc11e4f5f1d59d54f4', keyType: 'Singleton', valueContent: 'Keccak256', @@ -103,6 +107,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 5 { name: 'TestAddress', + nonDynamicName: 'TestAddress', key: '0x7bf6ecfbf659a88c662d7f099c14e468610f786f6e29f0d346e44f772ef0d187', keyType: 'Singleton', valueContent: 'Address', @@ -122,6 +127,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 6 { name: 'TestMarkdown', + nonDynamicName: 'TestMarkdown', key: '0x328f991bde3a9d8c548b7b2dbc303a362202dddbcd33219650d85bedcd75ac9b', keyType: 'Singleton', valueContent: 'Markdown', @@ -143,6 +149,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 7 { name: 'LSP3Name', + nonDynamicName: 'LSP3Name', key: '0xa5f15b1fa920bbdbc28f5d785e5224e3a66eb5f7d4092dc9ba82d5e5ae3abc87', keyType: 'Singleton', valueContent: 'String', @@ -157,6 +164,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 8 { name: 'LSP3Profile', + nonDynamicName: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', keyType: 'Singleton', valueContent: 'URL', @@ -178,6 +186,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 9 { name: 'LSP12IssuedAssets[]', + nonDynamicName: 'LSP12IssuedAssets[]', key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', @@ -211,6 +220,7 @@ export const mockSchema: (ERC725JSONSchema & { { name: 'LSP3IssuedAssetsWithEmptyValue[]', + nonDynamicName: 'LSP3IssuedAssetsWithEmptyValue[]', key: '0xbcdf8aea8f803343f50b03205ac25188e17fc1f5e4e42245b0782f68786d9f92', keyType: 'Array', valueContent: 'Address', @@ -240,6 +250,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 10 { name: 'TestObjArray[]', + nonDynamicName: 'TestObjArray[]', key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', keyType: 'Array', valueContent: 'JSONURL', @@ -296,6 +307,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 11 { name: 'TestStringValueType', + nonDynamicName: 'TestStringValueType', key: '0xc0929170bbaeb216f869c80a5c937f7a1c887a5a92262dac50313aef131f0c03', keyType: 'Singleton', valueContent: 'String', @@ -310,6 +322,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 12 { name: 'TestUintValueType', + nonDynamicName: 'TestUintValueType', key: '0x61529294800f5739edc21a6cf8ba1bad3fd3e11d03d2ab5219ce9c0131b93f93', keyType: 'Singleton', valueContent: 'Number', @@ -330,6 +343,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 13 { name: 'TestNumberWithBytesValueType', + nonDynamicName: 'TestNumberWithBytesValueType', key: '0x64a44e72c25d95851b1d449428d8d27093b2ef3e0b36a2b3497ae17edf979e61', keyType: 'Singleton', valueContent: 'Number', @@ -349,6 +363,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 14 { name: 'TestStringWithBytesValueType', + nonDynamicName: 'TestStringWithBytesValueType', key: '0x3ef4d417afa66557c9e1463723b391a518eee0c61d29be4e10882999c7848041', keyType: 'Singleton', valueContent: 'String', @@ -369,6 +384,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 15 { name: 'TestStringValueTypeArray', + nonDynamicName: 'TestStringValueTypeArray', key: '0xd7a8f1af4a0d9de8d17c177ff06f1689c0c3f1310edbbe53733da0b084ccff18', keyType: 'Singleton', valueContent: 'String', @@ -397,6 +413,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 16 { name: 'TestBytesValueTypeArray', + nonDynamicName: 'TestBytesValueTypeArray', key: '0xd6b3622ec62ae4459c0276bd5e2e26011201fada1cbc2b33283e9c20495c05fe', keyType: 'Singleton', valueContent: 'String', @@ -425,6 +442,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 17 { name: 'TestAddressValueTypeArray', + nonDynamicName: 'TestAddressValueTypeArray', key: '0xe45f3de809830d5ac3aeab862200fc670391fcb99018dcd2522fee7cf07f93ee', keyType: 'Singleton', valueContent: 'Address', @@ -456,6 +474,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 18 { name: 'TestUintValueTypeArray', + nonDynamicName: 'TestUintValueTypeArray', key: '0xdaa41a5e1acc41087359e61588e80bf0b7f1d96063b98bdff73b4ce3a645b40b', keyType: 'Singleton', valueContent: 'Number', @@ -478,6 +497,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 19 { name: 'TestBytes32ValueTypeArray', + nonDynamicName: 'TestBytes32ValueTypeArray', key: '0x7e2458b2b22ff4357510c3491b7c041df2ee4f11ba4d6f4f4e34101fc2645a97', keyType: 'Singleton', valueContent: 'Keccak256', @@ -509,6 +529,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 20 { name: 'TestURLStringValueTypeArray', + nonDynamicName: 'TestURLStringValueTypeArray', key: '0x1a9818703b62d00000bd3e8c7499296d42966619cd735a92eac7488de8881bb8', keyType: 'Singleton', valueContent: 'URL', @@ -540,6 +561,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 21 { name: 'TestHashKey', + nonDynamicName: 'TestHashKey', key: '0xed579debad05d91a79b46589987171dfce1c8ffa8b1d8c1ddc851cc104ea6029', keyType: 'Singleton', valueContent: @@ -560,8 +582,10 @@ export const mockSchema: (ERC725JSONSchema & { }, { - name: 'MyCoolAddress:0xcafecafecafecafecafecafecafecafecafecafe', + name: 'MyCoolAddress:
', + nonDynamicName: 'MyCoolAddress:cafecafecafecafecafecafecafecafecafecafe', key: '0x22496f48a493035f0ab40000cafecafecafecafecafecafecafecafecafecafe', + dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', keyType: 'Mapping', valueContent: '0x5ef83ad9', valueType: 'bytes', @@ -572,8 +596,11 @@ export const mockSchema: (ERC725JSONSchema & { expectedResult: '0x5ef83ad9', }, { - name: 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', + name: 'AddressPermissions:Permissions:
', + nonDynamicName: + 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', key: '0x4b80742de2bf82acb3630000cafecafecafecafecafecafecafecafecafecafe', + dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', keyType: 'MappingWithGrouping', valueContent: '0x5ef83ad9', valueType: 'bytes', @@ -585,6 +612,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'Hello:
', + nonDynamicName: 'Hello:cafecafecafecafecafecafecafecafecafecafe', key: '0x06b3dfaec148fb1bb2b00000cafecafecafecafecafecafecafecafecafecafe', // encoded for cafecafe... address - parameters are bellow dynamicKeyParts: ['0xcafecafecafecafecafecafecafecafecafecafe'], keyType: 'Singleton', @@ -609,6 +637,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'TestStringWithBytes4ValueContent', + nonDynamicName: 'TestStringWithBytes4ValueContent', key: '0xb61b0a1d86687ef022781d2698d5e0221997458e3a720cded0b8f165a029d3c5', keyType: 'Singleton', valueContent: 'Bytes4', @@ -621,6 +650,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'TestStringWithBytes32ValueType', + nonDynamicName: 'TestStringWithBytes32ValueType', key: '0xbaced8d1d0b02d5f412674cac7ad60f0f3e8ae29f2b8d4ad463fa1f5fc103d4d', keyType: 'Singleton', valueContent: 'Bytes32', @@ -640,6 +670,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'TestStringWithBytes4ValueType', + nonDynamicName: 'TestStringWithBytes4ValueType', key: '0x1b92e269c7ce7fc16e625562aa588403fe603edb4e2740b0558ed44faa3c1728', keyType: 'Singleton', valueContent: 'Bytes4', diff --git a/test/testHelpers.ts b/test/testHelpers.ts index b1547472..94fd59a7 100644 --- a/test/testHelpers.ts +++ b/test/testHelpers.ts @@ -17,6 +17,7 @@ * @date 2020 */ +import { encodeKeyName, isDynamicKeyName } from '../src'; import { encodeArrayKey } from '../src/lib/utils'; /** @@ -49,7 +50,9 @@ export function generateAllRawData(schema, isArrayMode: boolean) { }); } else { results.push({ - key: element.key, + key: isDynamicKeyName(element.key) + ? encodeKeyName(element.name, element.dynamicKeyParts) + : element.key, value: isArrayMode ? element.returnRawDataArray : element.returnRawData, }); } @@ -108,6 +111,7 @@ export function generateAllResults(schemas) { name: schema.name, key: schema.key, value: schema.expectedResult, + nonDynamicName: schema.nonDynamicName, }; }); } From cef4e15587cb9dbc993606098851698fc77cd6b8 Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Tue, 2 Jul 2024 07:13:45 -0400 Subject: [PATCH 2/4] fix: Repair tests and 0x prefix for dynamic fields in some places. --- src/index.test.ts | 10 +++++----- src/lib/decodeData.test.ts | 4 ++-- src/lib/encodeKeyName.test.ts | 8 ++++---- src/lib/encodeKeyName.ts | 2 +- src/lib/getSchemaElement.test.ts | 2 +- src/lib/utils.ts | 11 ++--------- test/mockSchema.ts | 6 +++--- 7 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index e99cc3df..4b246518 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -409,7 +409,7 @@ describe('Running @erc725/erc725.js tests...', () => { key: '0x4b80742de2bf82acb36300009139def55c73c12bcda9c44f12326686e3948634', name: 'AddressPermissions:Permissions:
', nonDynamicName: - 'AddressPermissions:Permissions:9139def55c73c12bcda9c44f12326686e3948634', + 'AddressPermissions:Permissions:0x9139def55c73c12bcda9c44f12326686e3948634', value: '0x0000000000000000000000000000000000000000000000000000000000000002', }); @@ -455,7 +455,7 @@ describe('Running @erc725/erc725.js tests...', () => { key: '0x6de85eaf5d982b4e5da000009139def55c73c12bcda9c44f12326686e3948634', name: 'LSP4CreatorsMap:
', nonDynamicName: - 'LSP4CreatorsMap:9139def55c73c12bcda9c44f12326686e3948634', + 'LSP4CreatorsMap:0x9139def55c73c12bcda9c44f12326686e3948634', value: ['0x24871b3d', 0], }); }); @@ -560,7 +560,7 @@ describe('Running @erc725/erc725.js tests...', () => { '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a62640000000000000000000000', }, { - key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', // LSP12IssuedAssetsMap:b74a88C43BCf691bd7A851f6603cb1868f6fc147 + key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', // LSP12IssuedAssetsMap:0x0cb74a88C43BCf691bd7A851f6603cb1868f6fc147 value: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000141098603b193d276f5fa176cc02007b609f9dae6b000000000000000000000000', }, @@ -620,7 +620,7 @@ describe('Running @erc725/erc725.js tests...', () => { key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', name: 'LSP12IssuedAssetsMap:
', nonDynamicName: - 'LSP12IssuedAssetsMap:b74a88C43BCf691bd7A851f6603cb1868f6fc147', + 'LSP12IssuedAssetsMap:0xb74a88C43BCf691bd7A851f6603cb1868f6fc147', value: '0x1098603B193d276f5fA176CC02007B609F9DAE6b', }, { @@ -764,7 +764,7 @@ describe('Running @erc725/erc725.js tests...', () => { assert.deepStrictEqual(result, { name: 'JSONForAddress:
', nonDynamicName: - 'JSONForAddress:cafecafecafecafecafecafecafecafecafecafe', + 'JSONForAddress:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x84b02f6e50a0a0819a4f0000cafecafecafecafecafecafecafecafecafecafe', value: JSON.parse(jsonString), }); diff --git a/src/lib/decodeData.test.ts b/src/lib/decodeData.test.ts index c6e48bbd..4ea4708b 100644 --- a/src/lib/decodeData.test.ts +++ b/src/lib/decodeData.test.ts @@ -339,8 +339,8 @@ describe('decodeData', () => { ); expect(decodedData.map(({ nonDynamicName }) => nonDynamicName)).to.eql([ - 'MyKeyName2:aaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa:true', - 'MyDynamicKey2:cafecafecafecafecafecafecafecafecafecafe', + 'MyKeyName2:0xaaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa:true', + 'MyDynamicKey2:0xcafecafecafecafecafecafecafecafecafecafe', 'KeyTwo', ]); }); diff --git a/src/lib/encodeKeyName.test.ts b/src/lib/encodeKeyName.test.ts index 3d996695..30cf2e27 100644 --- a/src/lib/encodeKeyName.test.ts +++ b/src/lib/encodeKeyName.test.ts @@ -248,7 +248,7 @@ describe('encodeKeyName', () => { ); assert.throws(() => encodeKeyName( - 'MyDynamicKey::cafecafecafecafecafecafecafecafecafecafe', + 'MyDynamicKey::0xcafecafecafecafecafecafecafecafecafecafe', ['variable1', 'variable2'], ), ); @@ -426,14 +426,14 @@ describe('generateDynamicKeyName', () => { '0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', ], expectedKeyName: - 'MyKey:11223344:2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', + 'MyKey:0x11223344:0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', }, { keyName: 'Addresses:
', dynamicKeyParts: [ - '2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', // without 0x in the address + '0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', // without 0x in the address ], - expectedKeyName: 'Addresses:2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', + expectedKeyName: 'Addresses:0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', }, ]; diff --git a/src/lib/encodeKeyName.ts b/src/lib/encodeKeyName.ts index 98d109c0..e7caf1f5 100644 --- a/src/lib/encodeKeyName.ts +++ b/src/lib/encodeKeyName.ts @@ -342,7 +342,7 @@ export const generateDynamicKeyName = ( dynamicKeyPartsIndex += 1; - return dynamicKeyPart.replace('0x', ''); + return dynamicKeyPart; }) .join(':'); }; diff --git a/src/lib/getSchemaElement.test.ts b/src/lib/getSchemaElement.test.ts index 53fedb26..7490dd17 100644 --- a/src/lib/getSchemaElement.test.ts +++ b/src/lib/getSchemaElement.test.ts @@ -69,7 +69,7 @@ describe('getSchemaElement', () => { ), { nonDynamicName: - 'LSP12IssuedAssetsMap:2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', + 'LSP12IssuedAssetsMap:0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', name: 'LSP12IssuedAssetsMap:
', key: '0x74ac2555c10b9349e78f00002ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', keyType: 'Mapping', diff --git a/src/lib/utils.ts b/src/lib/utils.ts index eba20a82..14fa11fc 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -19,15 +19,8 @@ * @date 2020 */ -import { - hexToBytes, - isAddress, - isHexStrict, - leftPad, - numberToHex, - padLeft, -} from 'web3-utils'; - +import { hexToBytes, leftPad, numberToHex, padLeft } from 'web3-utils'; +import { isAddress, isHexStrict } from 'web3-validator'; import { URLDataToEncode, EncodeDataReturn, diff --git a/test/mockSchema.ts b/test/mockSchema.ts index b0e2b9f8..dedbb2b2 100644 --- a/test/mockSchema.ts +++ b/test/mockSchema.ts @@ -583,7 +583,7 @@ export const mockSchema: (ERC725JSONSchema & { { name: 'MyCoolAddress:
', - nonDynamicName: 'MyCoolAddress:cafecafecafecafecafecafecafecafecafecafe', + nonDynamicName: 'MyCoolAddress:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x22496f48a493035f0ab40000cafecafecafecafecafecafecafecafecafecafe', dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', keyType: 'Mapping', @@ -598,7 +598,7 @@ export const mockSchema: (ERC725JSONSchema & { { name: 'AddressPermissions:Permissions:
', nonDynamicName: - 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', + 'AddressPermissions:Permissions:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x4b80742de2bf82acb3630000cafecafecafecafecafecafecafecafecafecafe', dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', keyType: 'MappingWithGrouping', @@ -612,7 +612,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'Hello:
', - nonDynamicName: 'Hello:cafecafecafecafecafecafecafecafecafecafe', + nonDynamicName: 'Hello:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x06b3dfaec148fb1bb2b00000cafecafecafecafecafecafecafecafecafecafe', // encoded for cafecafe... address - parameters are bellow dynamicKeyParts: ['0xcafecafecafecafecafecafecafecafecafecafe'], keyType: 'Singleton', From 2874516343fee7259367f84f19f071081be5f61f Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:26:42 -0400 Subject: [PATCH 3/4] fix: Repair and use dynamicName and dynamicKeyParts. --- src/index.test.ts | 40 ++++----- src/index.ts | 11 +-- src/lib/decodeData.test.ts | 2 +- src/lib/decodeData.ts | 4 +- src/lib/getSchemaElement.test.ts | 2 +- src/lib/getSchemaElement.ts | 7 +- src/lib/schemaParser.test.ts | 14 ++-- src/lib/schemaParser.ts | 138 ++++++++++++++++--------------- src/types/ERC725JSONSchema.ts | 10 +-- src/types/decodeData.ts | 4 +- test/mockSchema.ts | 56 ++++++------- test/testHelpers.ts | 2 +- 12 files changed, 142 insertions(+), 148 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 4b246518..199b584b 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -223,7 +223,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'ThisKeyDoesNotExist', key: '0xb12a0af5f83066646eb63c96bf29dcb827024d9a33189f5a61652a03951d1fbe', value: null, - nonDynamicName: 'ThisKeyDoesNotExist', + dynamicName: 'ThisKeyDoesNotExist', }; assert.deepStrictEqual(data, expectedResult); @@ -241,7 +241,7 @@ describe('Running @erc725/erc725.js tests...', () => { keyType: 'Array', valueContent: 'Address', valueType: 'address', - nonDynamicName: 'NonExistingArray[]', + dynamicName: 'NonExistingArray[]', }, ], ERC725_CONTRACT_ADDRESS, @@ -253,7 +253,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'NonExistingArray[]', key: '0xd6cbdbfc8d25c9ce4720b5fe6fa8fc536803944271617bf5425b4bd579195840', value: [], - nonDynamicName: 'NonExistingArray[]', + dynamicName: 'NonExistingArray[]', }); const dataArray = await erc725.getData(['NonExistingArray[]']); @@ -262,7 +262,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'NonExistingArray[]', key: '0xd6cbdbfc8d25c9ce4720b5fe6fa8fc536803944271617bf5425b4bd579195840', value: [], - nonDynamicName: 'NonExistingArray[]', + dynamicName: 'NonExistingArray[]', }, ]); }); @@ -288,7 +288,7 @@ describe('Running @erc725/erc725.js tests...', () => { { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - nonDynamicName: 'LSP3Profile', + dynamicName: 'LSP3Profile', value: { verification: { method: 'keccak256(utf8)', @@ -301,7 +301,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP1UniversalReceiverDelegate', key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313', - nonDynamicName: 'LSP1UniversalReceiverDelegate', + dynamicName: 'LSP1UniversalReceiverDelegate', }, ]; @@ -365,7 +365,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP1UniversalReceiverDelegate', key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313', - nonDynamicName: 'LSP1UniversalReceiverDelegate', + dynamicName: 'LSP1UniversalReceiverDelegate', }); }); }); @@ -408,7 +408,7 @@ describe('Running @erc725/erc725.js tests...', () => { assert.deepStrictEqual(data[0], { key: '0x4b80742de2bf82acb36300009139def55c73c12bcda9c44f12326686e3948634', name: 'AddressPermissions:Permissions:
', - nonDynamicName: + dynamicName: 'AddressPermissions:Permissions:0x9139def55c73c12bcda9c44f12326686e3948634', value: '0x0000000000000000000000000000000000000000000000000000000000000002', @@ -454,7 +454,7 @@ describe('Running @erc725/erc725.js tests...', () => { assert.deepStrictEqual(data[0], { key: '0x6de85eaf5d982b4e5da000009139def55c73c12bcda9c44f12326686e3948634', name: 'LSP4CreatorsMap:
', - nonDynamicName: + dynamicName: 'LSP4CreatorsMap:0x9139def55c73c12bcda9c44f12326686e3948634', value: ['0x24871b3d', 0], }); @@ -483,7 +483,7 @@ describe('Running @erc725/erc725.js tests...', () => { { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - nonDynamicName: 'LSP3Profile', + dynamicName: 'LSP3Profile', value: { verification: { method: 'keccak256(utf8)', @@ -496,7 +496,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP1UniversalReceiverDelegate', key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313', - nonDynamicName: 'LSP1UniversalReceiverDelegate', + dynamicName: 'LSP1UniversalReceiverDelegate', }, ]; @@ -545,7 +545,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: null, - nonDynamicName: 'LSP3Profile', + dynamicName: 'LSP3Profile', }); }); @@ -560,7 +560,7 @@ describe('Running @erc725/erc725.js tests...', () => { '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d6245724b6833466a73415236596a73546a485a4e6d364d6344703661527438324674637639414a4a765a62640000000000000000000000', }, { - key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', // LSP12IssuedAssetsMap:0x0cb74a88C43BCf691bd7A851f6603cb1868f6fc147 + key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', // LSP12IssuedAssetsMap:0xb74a88C43BCf691bd7A851f6603cb1868f6fc147 value: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000141098603b193d276f5fa176cc02007b609f9dae6b000000000000000000000000', }, @@ -613,20 +613,20 @@ describe('Running @erc725/erc725.js tests...', () => { { key: '0x48643a15ac5407a175674ab0f8c92df5ae90694dac72ebf0a058fb2599e3b06a', name: 'MyURL', - nonDynamicName: 'MyURL', + dynamicName: 'MyURL', value: 'ipfs://QmbErKh3FjsAR6YjsTjHZNm6McDp6aRt82Ftcv9AJJvZbd', }, { key: '0x74ac2555c10b9349e78f0000b74a88c43bcf691bd7a851f6603cb1868f6fc147', name: 'LSP12IssuedAssetsMap:
', - nonDynamicName: + dynamicName: 'LSP12IssuedAssetsMap:0xb74a88C43BCf691bd7A851f6603cb1868f6fc147', value: '0x1098603B193d276f5fA176CC02007B609F9DAE6b', }, { key: '0xeafec4d89fa9619884b600005ef83ad9559033e6e941db7d7c495acdce616347', name: 'SupportedStandards:LSP3Profile', - nonDynamicName: 'SupportedStandards:LSP3Profile', + dynamicName: 'SupportedStandards:LSP3Profile', value: '0x5ef83ad9', }, ]); @@ -722,7 +722,7 @@ describe('Running @erc725/erc725.js tests...', () => { key: testJSONURLSchema.key, name: testJSONURLSchema.name, value: JSON.parse(jsonString), - nonDynamicName: testJSONURLSchema.name, + dynamicName: testJSONURLSchema.name, }); }); @@ -763,7 +763,7 @@ describe('Running @erc725/erc725.js tests...', () => { assert.deepStrictEqual(result, { name: 'JSONForAddress:
', - nonDynamicName: + dynamicName: 'JSONForAddress:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x84b02f6e50a0a0819a4f0000cafecafecafecafecafecafecafecafecafecafe', value: JSON.parse(jsonString), @@ -847,7 +847,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: schemaElement.name, key: schemaElement.key, value: schemaElement.expectedResult, - nonDynamicName: schemaElement.nonDynamicName, + dynamicName: schemaElement.dynamicName, }); }); @@ -869,7 +869,7 @@ describe('Running @erc725/erc725.js tests...', () => { name: schemaElement.name, key: schemaElement.key, value: schemaElement.expectedResult, - nonDynamicName: schemaElement.nonDynamicName, + dynamicName: schemaElement.dynamicName, }); }); }); diff --git a/src/index.ts b/src/index.ts index 157b66b6..56624511 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,7 +48,6 @@ import { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; import { ERC725Config, ERC725Options } from './types/Config'; import { Permissions } from './types/Method'; import { - DynamicNameSchema, ERC725JSONSchema, ERC725JSONSchemaKeyType, ERC725JSONSchemaValueContent, @@ -381,19 +380,15 @@ export class ERC725 { getSchema( keyOrKeys: string[], providedSchemas?: ERC725JSONSchema[], - ): Record; + ): Record; getSchema( keyOrKeys: string, providedSchemas?: ERC725JSONSchema[], - ): ERC725JSONSchema | DynamicNameSchema | null; + ): ERC725JSONSchema | null; getSchema( keyOrKeys: string | string[], providedSchemas?: ERC725JSONSchema[], - ): - | ERC725JSONSchema - | DynamicNameSchema - | null - | Record { + ): ERC725JSONSchema | null | Record { return getSchema( keyOrKeys, this.options.schemas.concat(providedSchemas || []), diff --git a/src/lib/decodeData.test.ts b/src/lib/decodeData.test.ts index 4ea4708b..d939a312 100644 --- a/src/lib/decodeData.test.ts +++ b/src/lib/decodeData.test.ts @@ -338,7 +338,7 @@ describe('decodeData', () => { schemas, ); - expect(decodedData.map(({ nonDynamicName }) => nonDynamicName)).to.eql([ + expect(decodedData.map(({ dynamicName }) => dynamicName)).to.eql([ 'MyKeyName2:0xaaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa:true', 'MyDynamicKey2:0xcafecafecafecafecafecafecafecafecafecafe', 'KeyTwo', diff --git a/src/lib/decodeData.ts b/src/lib/decodeData.ts index bf7b51f8..d90f98f6 100644 --- a/src/lib/decodeData.ts +++ b/src/lib/decodeData.ts @@ -338,11 +338,11 @@ export function decodeData( } console.error(error); } - const { key, name, nonDynamicName } = schemaElement; + const { key, name, dynamicName } = schemaElement; return { key, name, - ...(nonDynamicName ? { nonDynamicName } : { nonDynamicName: name }), + ...(dynamicName ? { dynamicName } : { dynamicName: name }), ...(dynamicKeyParts ? { dynamicKeyParts } : {}), value: decodedValue, }; diff --git a/src/lib/getSchemaElement.test.ts b/src/lib/getSchemaElement.test.ts index 7490dd17..05b5b3b0 100644 --- a/src/lib/getSchemaElement.test.ts +++ b/src/lib/getSchemaElement.test.ts @@ -68,7 +68,7 @@ describe('getSchemaElement', () => { ['0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac'], ), { - nonDynamicName: + dynamicName: 'LSP12IssuedAssetsMap:0x2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', name: 'LSP12IssuedAssetsMap:
', key: '0x74ac2555c10b9349e78f00002ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', diff --git a/src/lib/getSchemaElement.ts b/src/lib/getSchemaElement.ts index 496cac56..92aa5e70 100644 --- a/src/lib/getSchemaElement.ts +++ b/src/lib/getSchemaElement.ts @@ -52,14 +52,11 @@ const getSchemaElementForDynamicKeyName = ( // once we have the schemaElement with dynamic parts, we need to replace the name and the key: const key = encodeKeyName(namedDynamicKey, dynamicKeyParts); - const nonDynamicName = generateDynamicKeyName( - namedDynamicKey, - dynamicKeyParts, - ); + const dynamicName = generateDynamicKeyName(namedDynamicKey, dynamicKeyParts); return { ...schemaElement, - nonDynamicName, + dynamicName, key, dynamicKeyParts, }; diff --git a/src/lib/schemaParser.test.ts b/src/lib/schemaParser.test.ts index f10f188a..5cff6d07 100644 --- a/src/lib/schemaParser.test.ts +++ b/src/lib/schemaParser.test.ts @@ -59,7 +59,7 @@ describe('schemaParser getSchema', () => { name: 'LSP12IssuedAssets[]', key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', dynamicName: 'LSP12IssuedAssets[1]', - dynamicKeyPart: '0x00000000000000000000000000000001', + dynamicKeyParts: '0x00000000000000000000000000000001', keyType: 'Singleton', valueContent: 'Address', valueType: 'address', @@ -68,7 +68,7 @@ describe('schemaParser getSchema', () => { name: 'AddressPermissions[]', key: '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000000', dynamicName: 'AddressPermissions[0]', - dynamicKeyPart: '0x00000000000000000000000000000000', + dynamicKeyParts: '0x00000000000000000000000000000000', keyType: 'Singleton', valueContent: 'Address', valueType: 'address', @@ -132,7 +132,7 @@ describe('schemaParser getSchema', () => { assert.deepStrictEqual(schema, { ...extraSchema, - dynamicKeyPart: `0x${address}`, + dynamicKeyParts: `0x${address}`, dynamicName, }); }); @@ -158,7 +158,7 @@ describe('schemaParser getSchema', () => { assert.deepStrictEqual(schema, { ...extraSchema, dynamicName, - dynamicKeyPart: `0x${dynamicPart}`, + dynamicKeyParts: `0x${dynamicPart}`, }); }); @@ -181,7 +181,7 @@ describe('schemaParser getSchema', () => { assert.deepStrictEqual(schema, { ...extraSchema, dynamicName, - dynamicKeyPart: `0x${bytes4Value}`, + dynamicKeyParts: `0x${bytes4Value}`, }); }); @@ -206,7 +206,7 @@ describe('schemaParser getSchema', () => { assert.deepStrictEqual(schema, { ...extraSchema, dynamicName, - dynamicKeyPart: `0x${dynamicPart}`, + dynamicKeyParts: `0x${dynamicPart}`, }); }); }); @@ -223,7 +223,7 @@ describe('schemaParser getSchema', () => { name, dynamicName, key, - dynamicKeyPart: `0x${address}`, + dynamicKeyParts: `0x${address}`, keyType: 'MappingWithGrouping', valueContent: 'BitArray', valueType: 'bytes32', diff --git a/src/lib/schemaParser.ts b/src/lib/schemaParser.ts index 2f2ff26e..14635716 100644 --- a/src/lib/schemaParser.ts +++ b/src/lib/schemaParser.ts @@ -20,7 +20,6 @@ import { keccak256 } from 'web3-utils'; import allSchemas from '../schemas'; import { - DynamicNameSchema, ERC725JSONSchema, ERC725JSONSchemaKeyType, } from '../types/ERC725JSONSchema'; @@ -41,58 +40,72 @@ const getSchemasByKeyType = ( const fillDynamicKeyPart = ( key: string, - keySchema: ERC725JSONSchema, -): ERC725JSONSchema | DynamicNameSchema => { - const result: ERC725JSONSchema | DynamicNameSchema = { ...keySchema, key }; + keySchema: ERC725JSONSchema | ERC725JSONSchema[], +): ERC725JSONSchema => { + const fillOneDynamicKeyPart = ( + key: string, + keySchema: ERC725JSONSchema, + insertQuestionMarks = false, + ) => { + const result: ERC725JSONSchema = { + ...keySchema, + key, + }; - const keyNameParts = keySchema.name.split(':'); - const secondWordHex = key.substring(26); + const keyNameParts = keySchema.name.split(':'); + const secondWordHex = key.substring(26); - // 2. "Semi defined mappings" i.e. "SupportedStandards:??????" - let dynamicPartName = '??????'; // default for "unknown" + // 2. "Semi defined mappings" i.e. "SupportedStandards:??????" + let dynamicPartName = '??????'; // default for "unknown" - // replace dynamic placeholder in the map part (e.g:
, ) with the hex value - if (isDynamicKeyName(keySchema.name)) { - dynamicPartName = secondWordHex; + // replace dynamic placeholder in the map part (e.g:
, ) with the hex value + if (isDynamicKeyName(keySchema.name) && !insertQuestionMarks) { + dynamicPartName = secondWordHex; - let dynamicName = `${keyNameParts[0]}:0x${dynamicPartName}`; - let dynamicKeyPart = `0x${secondWordHex}`; + let dynamicName = `${keyNameParts[0]}:0x${dynamicPartName}`; + let dynamicKeyPart = `0x${secondWordHex}`; - const dynamicPartType = keyNameParts[1].match(dynamicTypesRegex); + const dynamicPartType = keyNameParts[1].match(dynamicTypesRegex); - if (dynamicPartType) { - const byteSize = - dynamicPartType[1] === 'uint' || dynamicPartType[1] === 'int' - ? Number.parseInt(dynamicPartType[2]) / 8 // e.g: uint128 -> 128 / 8 -> 16 bytes - : Number.parseInt(dynamicPartType[2]); // e.g: bytes8 -> 8 bytes + if (dynamicPartType) { + const byteSize = + dynamicPartType[1] === 'uint' || dynamicPartType[1] === 'int' + ? Number.parseInt(dynamicPartType[2]) / 8 // e.g: uint128 -> 128 / 8 -> 16 bytes + : Number.parseInt(dynamicPartType[2]); // e.g: bytes8 -> 8 bytes - if (byteSize < 20) { - dynamicName = `${keyNameParts[0]}:0x${dynamicPartName.slice( - 0, - byteSize * 2, - )}`; + if (byteSize < 20) { + dynamicName = `${keyNameParts[0]}:0x${dynamicPartName.slice(0, byteSize * 2)}`; - dynamicKeyPart = `0x${secondWordHex.slice(0, byteSize * 2)}`; + dynamicKeyPart = `0x${secondWordHex.slice(0, byteSize * 2)}`; + } } - } - (result as DynamicNameSchema).dynamicName = dynamicName; - (result as DynamicNameSchema).dynamicKeyPart = dynamicKeyPart; + result.dynamicName = dynamicName; + result.dynamicKeyParts = dynamicKeyPart; - return result; - } + return result; + } - // if first 20 bytes of the hash of second word in schema match, - // display the map part as plain word - if (keccak256(keyNameParts[1]).substring(0, 42) === `0x${secondWordHex}`) { - dynamicPartName = keyNameParts[1]; - } + // if first 20 bytes of the hash of second word in schema match, + // display the map part as plain word + if (keccak256(keyNameParts[1]).substring(0, 42) === `0x${secondWordHex}`) { + dynamicPartName = keyNameParts[1]; + } - // DO NOT MODIFY THE NAME OF THE SCHEMA ITEM - // OTHERWISE WE CAN NO LONGER FIND THE CORRESPONDING SCHEMA BY NAME. - // result.name = `${keyNameParts[0]}:${dynamicPartName}`; + // DO NOT MODIFY THE NAME OF THE SCHEMA ITEM + // OTHERWISE WE CAN NO LONGER FIND THE CORRESPONDING SCHEMA BY NAME. + // result.name = `$keyNameParts[0]:$dynamicPartName`; - return result; + return result; + }; + if (Array.isArray(keySchema)) { + const singleSchema = keySchema.find((schema) => schema.key === key); + if (singleSchema) { + return fillOneDynamicKeyPart(key, singleSchema); + } + return fillOneDynamicKeyPart(key, keySchema[0], true); + } + return fillOneDynamicKeyPart(key, keySchema); }; const findSingletonSchemaForKey = ( @@ -105,7 +118,7 @@ const findSingletonSchemaForKey = ( const findArraySchemaForKey = ( key: string, schemas: ERC725JSONSchema[], -): ERC725JSONSchema | DynamicNameSchema | null => { +): ERC725JSONSchema | null => { // Should detect: // 1. Initial key @@ -136,7 +149,7 @@ const findArraySchemaForKey = ( ...arraySchema, key, dynamicName: arraySchema.name.replace('[]', `[${elementIndex}]`), - dynamicKeyPart: `0x${key.substring(34)}`, + dynamicKeyParts: `0x${key.substring(34)}`, name: arraySchema.name, keyType: 'Singleton', }; @@ -145,33 +158,32 @@ const findArraySchemaForKey = ( const findMappingSchemaForKey = ( key: string, schemas: ERC725JSONSchema[], -): ERC725JSONSchema | DynamicNameSchema | null => { +): ERC725JSONSchema | null => { const firstWordHex = key.substring(0, 26); // Should detect: // Known/defined mapping - let keySchema = schemas.find((schema) => schema.key === key) || null; + const keySchema = schemas.find((schema) => schema.key === key) || null; if (keySchema) { return fillDynamicKeyPart(key, keySchema); } - keySchema = - schemas.find( - (schema) => `${schema.key.substring(0, 22)}0000` === firstWordHex, - ) || null; + const keySchemas = schemas.filter( + (schema) => `${schema.key.substring(0, 22)}0000` === firstWordHex, + ); - if (!keySchema) { + if (keySchemas.length === 0) { return null; } - return fillDynamicKeyPart(key, keySchema); + return fillDynamicKeyPart(key, keySchemas); }; const findMappingWithGroupingSchemaForKey = ( key: string, schemas: ERC725JSONSchema[], -): ERC725JSONSchema | DynamicNameSchema | null => { +): ERC725JSONSchema | null => { const keySchema = schemas.find( (schema) => schema.key.substring(0, 26) === key.substring(0, 26), @@ -183,9 +195,8 @@ const findMappingWithGroupingSchemaForKey = ( const dynamicKeyPart = key.substring(26); if (isDynamicKeyName(keySchema.name)) { - (keySchema as DynamicNameSchema).dynamicName = - `${keyNameParts[0]}:${keyNameParts[1]}:0x${dynamicKeyPart}`; - (keySchema as DynamicNameSchema).dynamicKeyPart = `0x${dynamicKeyPart}`; + keySchema.dynamicName = `${keyNameParts[0]}:${keyNameParts[1]}:0x${dynamicKeyPart}`; + keySchema.dynamicKeyParts = `0x${dynamicKeyPart}`; } return { @@ -200,7 +211,7 @@ const findMappingWithGroupingSchemaForKey = ( function schemaParser( key: string, schemas: ERC725JSONSchema[], -): ERC725JSONSchema | DynamicNameSchema | null { +): ERC725JSONSchema | null { const schemasByKeyType = getSchemasByKeyType(schemas); let foundSchema: ERC725JSONSchema | null = null; @@ -234,23 +245,20 @@ function schemaParser( export function getSchema( keyOrKeys: string | string[], providedSchemas?: ERC725JSONSchema[], -): - | ERC725JSONSchema - | DynamicNameSchema - | null - | Record { +): ERC725JSONSchema | null | Record { let fullSchema: ERC725JSONSchema[] = allSchemas; if (providedSchemas) { fullSchema = fullSchema.concat(providedSchemas); } if (Array.isArray(keyOrKeys)) { - return keyOrKeys.reduce< - Record - >((acc, key) => { - acc[key] = schemaParser(key, fullSchema); - return acc; - }, {}); + return keyOrKeys.reduce>( + (acc, key) => { + acc[key] = schemaParser(key, fullSchema); + return acc; + }, + {}, + ); } return schemaParser(keyOrKeys, fullSchema); diff --git a/src/types/ERC725JSONSchema.ts b/src/types/ERC725JSONSchema.ts index d1b3adb6..5c3d17cb 100644 --- a/src/types/ERC725JSONSchema.ts +++ b/src/types/ERC725JSONSchema.ts @@ -160,17 +160,11 @@ export function isValidValueType( */ export interface ERC725JSONSchema { name: string; // Describes the name of the key, SHOULD be composed of the Standards name + sub type. e.g: LSP2Name - nonDynamicName?: string; // Describes the name of the key, SHOULD be composed of the Standards name + sub type. e.g: LSP2Name without dynamic part key: string; // The keccak256 hash of the name. This is the actual key that MUST be retrievable via ERC725Y.getData(bytes32 key) keyType: ERC725JSONSchemaKeyType | string; // Types that determine how the values should be interpreted. valueContent: ERC725JSONSchemaValueContent | string; // string holds '0x1345ABCD...' If the value content are specific bytes, than the returned value is expected to equal those bytes. valueType: ERC725JSONSchemaValueType | string; // The type of the value. This is used to determine how the value should be encoded / decode (`string` for tuples and CompactBytesArray). - dynamicKeyParts?: DynamicKeyParts; -} -// The dynamic part placeholder in the `name` of ERC725JSONSchema is preserved to allow re-encoding after the schema -// of a hex data key got retrieved via `getSchema(...)`. -export interface DynamicNameSchema extends ERC725JSONSchema { - dynamicName: string; // Describes the name of the key where the dynamic part (
, , } | Record; dynamicKeyParts?: DynamicKeyParts; - nonDynamicName?: string; + dynamicName?: string; name: string; key: string; } diff --git a/test/mockSchema.ts b/test/mockSchema.ts index dedbb2b2..9ff07a0e 100644 --- a/test/mockSchema.ts +++ b/test/mockSchema.ts @@ -16,7 +16,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 1 { name: 'SupportedStandards:LSP3Profile', - nonDynamicName: 'SupportedStandards:LSP3Profile', + dynamicName: 'SupportedStandards:LSP3Profile', key: '0xeafec4d89fa9619884b600005ef83ad9559033e6e941db7d7c495acdce616347', keyType: 'Mapping', valueContent: '0x5ef83ad9', @@ -31,7 +31,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 2 { name: 'TestJSONURL', - nonDynamicName: 'TestJSONURL', + dynamicName: 'TestJSONURL', key: '0xd154e1e44d32870ff5ade9e8726fd06d0ed6c996f5946dabfdfd46aa6dd2ea99', keyType: 'Singleton', valueContent: 'JSONURL', @@ -58,7 +58,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 3 { name: 'TestAssetURL', - nonDynamicName: 'TestAssetURL', + dynamicName: 'TestAssetURL', key: '0xf18290c9b373d751e12c5ec807278267a807c35c3806255168bc48a85757ceee', keyType: 'Singleton', valueContent: 'AssetURL', @@ -85,7 +85,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 4 { name: 'TestKeccak256', - nonDynamicName: 'TestKeccak256', + dynamicName: 'TestKeccak256', key: '0xd6c7198ea09a1d3357688e1dbdf0e07f6cfaf94359e0a4fc11e4f5f1d59d54f4', keyType: 'Singleton', valueContent: 'Keccak256', @@ -107,7 +107,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 5 { name: 'TestAddress', - nonDynamicName: 'TestAddress', + dynamicName: 'TestAddress', key: '0x7bf6ecfbf659a88c662d7f099c14e468610f786f6e29f0d346e44f772ef0d187', keyType: 'Singleton', valueContent: 'Address', @@ -127,7 +127,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 6 { name: 'TestMarkdown', - nonDynamicName: 'TestMarkdown', + dynamicName: 'TestMarkdown', key: '0x328f991bde3a9d8c548b7b2dbc303a362202dddbcd33219650d85bedcd75ac9b', keyType: 'Singleton', valueContent: 'Markdown', @@ -149,7 +149,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 7 { name: 'LSP3Name', - nonDynamicName: 'LSP3Name', + dynamicName: 'LSP3Name', key: '0xa5f15b1fa920bbdbc28f5d785e5224e3a66eb5f7d4092dc9ba82d5e5ae3abc87', keyType: 'Singleton', valueContent: 'String', @@ -164,7 +164,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 8 { name: 'LSP3Profile', - nonDynamicName: 'LSP3Profile', + dynamicName: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', keyType: 'Singleton', valueContent: 'URL', @@ -186,7 +186,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 9 { name: 'LSP12IssuedAssets[]', - nonDynamicName: 'LSP12IssuedAssets[]', + dynamicName: 'LSP12IssuedAssets[]', key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', @@ -220,7 +220,7 @@ export const mockSchema: (ERC725JSONSchema & { { name: 'LSP3IssuedAssetsWithEmptyValue[]', - nonDynamicName: 'LSP3IssuedAssetsWithEmptyValue[]', + dynamicName: 'LSP3IssuedAssetsWithEmptyValue[]', key: '0xbcdf8aea8f803343f50b03205ac25188e17fc1f5e4e42245b0782f68786d9f92', keyType: 'Array', valueContent: 'Address', @@ -250,7 +250,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 10 { name: 'TestObjArray[]', - nonDynamicName: 'TestObjArray[]', + dynamicName: 'TestObjArray[]', key: '0x9985edaf12cbacf5ac7d6ed54f0445cc0ea56075aee9b9942e4ab3bf4239f950', keyType: 'Array', valueContent: 'JSONURL', @@ -307,7 +307,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 11 { name: 'TestStringValueType', - nonDynamicName: 'TestStringValueType', + dynamicName: 'TestStringValueType', key: '0xc0929170bbaeb216f869c80a5c937f7a1c887a5a92262dac50313aef131f0c03', keyType: 'Singleton', valueContent: 'String', @@ -322,7 +322,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 12 { name: 'TestUintValueType', - nonDynamicName: 'TestUintValueType', + dynamicName: 'TestUintValueType', key: '0x61529294800f5739edc21a6cf8ba1bad3fd3e11d03d2ab5219ce9c0131b93f93', keyType: 'Singleton', valueContent: 'Number', @@ -343,7 +343,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 13 { name: 'TestNumberWithBytesValueType', - nonDynamicName: 'TestNumberWithBytesValueType', + dynamicName: 'TestNumberWithBytesValueType', key: '0x64a44e72c25d95851b1d449428d8d27093b2ef3e0b36a2b3497ae17edf979e61', keyType: 'Singleton', valueContent: 'Number', @@ -363,7 +363,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 14 { name: 'TestStringWithBytesValueType', - nonDynamicName: 'TestStringWithBytesValueType', + dynamicName: 'TestStringWithBytesValueType', key: '0x3ef4d417afa66557c9e1463723b391a518eee0c61d29be4e10882999c7848041', keyType: 'Singleton', valueContent: 'String', @@ -384,7 +384,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 15 { name: 'TestStringValueTypeArray', - nonDynamicName: 'TestStringValueTypeArray', + dynamicName: 'TestStringValueTypeArray', key: '0xd7a8f1af4a0d9de8d17c177ff06f1689c0c3f1310edbbe53733da0b084ccff18', keyType: 'Singleton', valueContent: 'String', @@ -413,7 +413,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 16 { name: 'TestBytesValueTypeArray', - nonDynamicName: 'TestBytesValueTypeArray', + dynamicName: 'TestBytesValueTypeArray', key: '0xd6b3622ec62ae4459c0276bd5e2e26011201fada1cbc2b33283e9c20495c05fe', keyType: 'Singleton', valueContent: 'String', @@ -442,7 +442,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 17 { name: 'TestAddressValueTypeArray', - nonDynamicName: 'TestAddressValueTypeArray', + dynamicName: 'TestAddressValueTypeArray', key: '0xe45f3de809830d5ac3aeab862200fc670391fcb99018dcd2522fee7cf07f93ee', keyType: 'Singleton', valueContent: 'Address', @@ -474,7 +474,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 18 { name: 'TestUintValueTypeArray', - nonDynamicName: 'TestUintValueTypeArray', + dynamicName: 'TestUintValueTypeArray', key: '0xdaa41a5e1acc41087359e61588e80bf0b7f1d96063b98bdff73b4ce3a645b40b', keyType: 'Singleton', valueContent: 'Number', @@ -497,7 +497,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 19 { name: 'TestBytes32ValueTypeArray', - nonDynamicName: 'TestBytes32ValueTypeArray', + dynamicName: 'TestBytes32ValueTypeArray', key: '0x7e2458b2b22ff4357510c3491b7c041df2ee4f11ba4d6f4f4e34101fc2645a97', keyType: 'Singleton', valueContent: 'Keccak256', @@ -529,7 +529,7 @@ export const mockSchema: (ERC725JSONSchema & { // // Case 20 { name: 'TestURLStringValueTypeArray', - nonDynamicName: 'TestURLStringValueTypeArray', + dynamicName: 'TestURLStringValueTypeArray', key: '0x1a9818703b62d00000bd3e8c7499296d42966619cd735a92eac7488de8881bb8', keyType: 'Singleton', valueContent: 'URL', @@ -561,7 +561,7 @@ export const mockSchema: (ERC725JSONSchema & { // Case 21 { name: 'TestHashKey', - nonDynamicName: 'TestHashKey', + dynamicName: 'TestHashKey', key: '0xed579debad05d91a79b46589987171dfce1c8ffa8b1d8c1ddc851cc104ea6029', keyType: 'Singleton', valueContent: @@ -583,7 +583,7 @@ export const mockSchema: (ERC725JSONSchema & { { name: 'MyCoolAddress:
', - nonDynamicName: 'MyCoolAddress:0xcafecafecafecafecafecafecafecafecafecafe', + dynamicName: 'MyCoolAddress:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x22496f48a493035f0ab40000cafecafecafecafecafecafecafecafecafecafe', dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', keyType: 'Mapping', @@ -597,7 +597,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'AddressPermissions:Permissions:
', - nonDynamicName: + dynamicName: 'AddressPermissions:Permissions:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x4b80742de2bf82acb3630000cafecafecafecafecafecafecafecafecafecafe', dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', @@ -612,7 +612,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'Hello:
', - nonDynamicName: 'Hello:0xcafecafecafecafecafecafecafecafecafecafe', + dynamicName: 'Hello:0xcafecafecafecafecafecafecafecafecafecafe', key: '0x06b3dfaec148fb1bb2b00000cafecafecafecafecafecafecafecafecafecafe', // encoded for cafecafe... address - parameters are bellow dynamicKeyParts: ['0xcafecafecafecafecafecafecafecafecafecafe'], keyType: 'Singleton', @@ -637,7 +637,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'TestStringWithBytes4ValueContent', - nonDynamicName: 'TestStringWithBytes4ValueContent', + dynamicName: 'TestStringWithBytes4ValueContent', key: '0xb61b0a1d86687ef022781d2698d5e0221997458e3a720cded0b8f165a029d3c5', keyType: 'Singleton', valueContent: 'Bytes4', @@ -650,7 +650,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'TestStringWithBytes32ValueType', - nonDynamicName: 'TestStringWithBytes32ValueType', + dynamicName: 'TestStringWithBytes32ValueType', key: '0xbaced8d1d0b02d5f412674cac7ad60f0f3e8ae29f2b8d4ad463fa1f5fc103d4d', keyType: 'Singleton', valueContent: 'Bytes32', @@ -670,7 +670,7 @@ export const mockSchema: (ERC725JSONSchema & { }, { name: 'TestStringWithBytes4ValueType', - nonDynamicName: 'TestStringWithBytes4ValueType', + dynamicName: 'TestStringWithBytes4ValueType', key: '0x1b92e269c7ce7fc16e625562aa588403fe603edb4e2740b0558ed44faa3c1728', keyType: 'Singleton', valueContent: 'Bytes4', diff --git a/test/testHelpers.ts b/test/testHelpers.ts index 94fd59a7..92944ef6 100644 --- a/test/testHelpers.ts +++ b/test/testHelpers.ts @@ -111,7 +111,7 @@ export function generateAllResults(schemas) { name: schema.name, key: schema.key, value: schema.expectedResult, - nonDynamicName: schema.nonDynamicName, + dynamicName: schema.dynamicName, }; }); } From b0ffa5b1051015268c552b53b4334e0795295e1d Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:42:30 -0400 Subject: [PATCH 4/4] fix: Repair limitation --- src/lib/encodeKeyName.test.ts | 22 +++++++++++++++++----- src/lib/encodeKeyName.ts | 8 ++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/lib/encodeKeyName.test.ts b/src/lib/encodeKeyName.test.ts index 30cf2e27..a0adb20d 100644 --- a/src/lib/encodeKeyName.test.ts +++ b/src/lib/encodeKeyName.test.ts @@ -399,11 +399,23 @@ describe('encodeDynamicKeyPart', () => { // Since we're allowing things to be truncated and right and left padded // This doesn't seem an ideal test case. - // it('throws if is called with wrong number of bytes', () => { - // assert.throws(() => - // encodeDynamicKeyPart('', '0xd1b2917d26eeeaad1234', 20), - // ); - // }); + it('throws if is called with wrong number of bytes', () => { + assert.throws(() => + encodeDynamicKeyPart('', '0xd1b2917d26eeeaad1234', 20), + ); + }); + + it('throws if is called with too large of a number', () => { + assert.throws(() => encodeDynamicKeyPart('', '0x100', 20)); + }); + + it('throws if is is used because intN is not supported (positive number)', () => { + assert.throws(() => encodeDynamicKeyPart('', '0x100', 20)); + }); + + it('throws if is is used because intN is not supported (negative number)', () => { + assert.throws(() => encodeDynamicKeyPart('', '0xFFFFFFFF100', 20)); + }); }); describe('generateDynamicKeyName', () => { diff --git a/src/lib/encodeKeyName.ts b/src/lib/encodeKeyName.ts index e7caf1f5..5ab705ef 100644 --- a/src/lib/encodeKeyName.ts +++ b/src/lib/encodeKeyName.ts @@ -98,6 +98,9 @@ export const encodeDynamicKeyPart = ( // e.g.: uint8 max value is 255, uint16 is 65535... let hex = numberToHex(value).slice(2); + if (hex.length > size / 4) { + throw new Error(`Value: ${value} is too big for uint${size}.`); + } if (hex.length > bytes * 2) { hex = `0x${hex.slice(-bytes * 2)}`; } else { @@ -117,6 +120,11 @@ export const encodeDynamicKeyPart = ( `Wrong value: ${value} for dynamic key with type: $type. Value is not in hex.`, ); } + if (value.length > 2 + size * 2) { + throw new Error( + `Wrong value: ${value} for dynamic key with type: $type. Value is too big.`, + ); + } return padRight(value.slice(0, 2 + bytes * 2), bytes * 2).slice(2); } default: