diff --git a/src/lib/encodeKeyName.ts b/src/lib/encodeKeyName.ts index 24328a64..7cf4c0da 100644 --- a/src/lib/encodeKeyName.ts +++ b/src/lib/encodeKeyName.ts @@ -34,7 +34,7 @@ import { DynamicKeyParts } from '../types/dynamicKeys'; const dynamicTypes = ['', '
', '']; // https://docs.soliditylang.org/en/v0.8.14/abi-spec.html#types -const dynamicTypesRegex = /<(uint|int|bytes)(\d+)>/; +export const dynamicTypesRegex = /<(uint|int|bytes)(\d+)>/; /** * diff --git a/src/lib/schemaParser.test.ts b/src/lib/schemaParser.test.ts index 8340b89a..58960c7a 100644 --- a/src/lib/schemaParser.test.ts +++ b/src/lib/schemaParser.test.ts @@ -13,7 +13,7 @@ */ import assert from 'assert'; -import { DynamicNameSchema } from '../types/ERC725JSONSchema'; +import { ERC725JSONSchema } from '../types/ERC725JSONSchema'; import { getSchema } from './schemaParser'; @@ -101,13 +101,11 @@ describe('schemaParser getSchema', () => { '0xeafec4d89fa9619884b60000f4d7faed14a1ab658d46d385bc29fb1eeaa56d0b', ); - console.log('schema ', schema); - assert.deepStrictEqual(schema, { name: 'SupportedStandards:??????', key: '0xeafec4d89fa9619884b60000f4d7faed14a1ab658d46d385bc29fb1eeaa56d0b', keyType: 'Mapping', - valueContent: '?', + valueContent: '0x5ef83ad9', valueType: 'bytes4', }); }); @@ -118,11 +116,9 @@ describe('schemaParser getSchema', () => { const dynamicName = `MyCoolAddress:0x${address}`; const key = `0x22496f48a493035f00000000${address}`; - const extraSchema: DynamicNameSchema = { + const extraSchema: ERC725JSONSchema = { name, - dynamicName, key, - dynamicKeyPart: `0x${address}`, keyType: 'Mapping', valueContent: 'Address', valueType: 'address', @@ -130,21 +126,24 @@ describe('schemaParser getSchema', () => { const schema = getSchema(key, [extraSchema]); - assert.deepStrictEqual(schema, extraSchema); + assert.deepStrictEqual(schema, { + ...extraSchema, + dynamicKeyPart: `0x${address}`, + dynamicName, + }); }); it('finds known SomeBytes32Mapping:', () => { const bytes32Value = '1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff'; const name = 'SomeBytes32Mapping:'; - const dynamicName = `SomeBytes32Mapping:0x${bytes32Value}`; - const key = `0x0cfc51aec37c55a4d0b10000${bytes32Value.slice(0, 42)}`; + const dynamicPart = bytes32Value.slice(0, 40); + const dynamicName = `SomeBytes32Mapping:0x${dynamicPart}`; + const key = `0x0cfc51aec37c55a4d0b10000${dynamicPart}`; - const extraSchema: DynamicNameSchema = { + const extraSchema: ERC725JSONSchema = { name, - dynamicName, key, - dynamicKeyPart: `0x${bytes32Value}`, keyType: 'Mapping', valueContent: 'Address', valueType: 'address', @@ -152,7 +151,11 @@ describe('schemaParser getSchema', () => { const schema = getSchema(key, [extraSchema]); - assert.deepStrictEqual(schema, extraSchema); + assert.deepStrictEqual(schema, { + ...extraSchema, + dynamicName, + dynamicKeyPart: `0x${dynamicPart}`, + }); }); it('finds known SomeSelectorMap:', () => { @@ -161,11 +164,9 @@ describe('schemaParser getSchema', () => { const dynamicName = `SomeSelectorMap:0x${bytes4Value}`; const key = `0x0cfc51aec37c55a4d0b10000${bytes4Value}00000000000000000000000000000000`; - const extraSchema: DynamicNameSchema = { + const extraSchema: ERC725JSONSchema = { name, - dynamicName, key, - dynamicKeyPart: `0x${bytes4Value}`, keyType: 'Mapping', valueContent: '(Address,bool)', valueType: '(address,bool)', @@ -173,7 +174,36 @@ describe('schemaParser getSchema', () => { const schema = getSchema(key, [extraSchema]); - assert.deepStrictEqual(schema, extraSchema); + assert.deepStrictEqual(schema, { + ...extraSchema, + dynamicName, + dynamicKeyPart: `0x${bytes4Value}`, + }); + }); + + it('finds Known LSP1UniversalReceiverDelegate: ', () => { + const bytes32value = + 'cafecafecafecafecafecafecafecafecafecafef00df00df00df00df00df00d'; + const name = 'LSP1UniversalReceiverDelegate:'; + const dynamicPart = bytes32value.slice(0, 40); + const dynamicName = `LSP1UniversalReceiverDelegate:0x${dynamicPart}`; + const key = `0x0cfc51aec37c55a4d0b10000${dynamicPart}`; + + const extraSchema: ERC725JSONSchema = { + name, + key, + keyType: 'Mapping', + valueContent: 'Address', + valueType: 'address', + }; + + const schema = getSchema(key, [extraSchema]); + + assert.deepStrictEqual(schema, { + ...extraSchema, + dynamicName, + dynamicKeyPart: `0x${dynamicPart}`, + }); }); }); diff --git a/src/lib/schemaParser.ts b/src/lib/schemaParser.ts index 889fb579..1041771b 100644 --- a/src/lib/schemaParser.ts +++ b/src/lib/schemaParser.ts @@ -16,7 +16,6 @@ * @author Hugo Masclet <@Hugoo> * @date 2022 */ - import { keccak256 } from 'web3-utils'; import allSchemas from '../schemas'; @@ -25,7 +24,7 @@ import { ERC725JSONSchema, ERC725JSONSchemaKeyType, } from '../types/ERC725JSONSchema'; -import { isDynamicKeyName } from './encodeKeyName'; +import { dynamicTypesRegex, isDynamicKeyName } from './encodeKeyName'; const getSchemasByKeyType = ( schemas: ERC725JSONSchema[], @@ -40,6 +39,59 @@ const getSchemasByKeyType = ( }; }; +const fillDynamicKeyPart = ( + key: string, + keySchema: ERC725JSONSchema, +): ERC725JSONSchema | DynamicNameSchema => { + const result: ERC725JSONSchema | DynamicNameSchema = { ...keySchema, key }; + + const keyNameParts = keySchema.name.split(':'); + const secondWordHex = key.substring(26); + + // 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; + + let dynamicName = `${keyNameParts[0]}:0x${dynamicPartName}`; + let dynamicKeyPart = `0x${secondWordHex}`; + + const dynamicPartType = keyNameParts[1].match(dynamicTypesRegex); + + if (dynamicPartType) { + const byteSize = + dynamicPartType[1] == 'uint' || dynamicPartType[1] == 'int' + ? parseInt(dynamicPartType[2]) / 8 // e.g: uint128 -> 128 / 8 -> 16 bytes + : parseInt(dynamicPartType[2]); // e.g: bytes8 -> 8 bytes + + if (byteSize < 20) { + dynamicName = `${keyNameParts[0]}:0x${dynamicPartName.slice( + 0, + byteSize * 2, + )}`; + + dynamicKeyPart = `0x${secondWordHex.slice(0, byteSize * 2)}`; + } + } + + (result as DynamicNameSchema).dynamicName = dynamicName; + (result as DynamicNameSchema).dynamicKeyPart = dynamicKeyPart; + + 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]; + } + result.name = `${keyNameParts[0]}:${dynamicPartName}`; + + return result; +}; + const findSingletonSchemaForKey = ( key: string, schemas: ERC725JSONSchema[], @@ -90,19 +142,15 @@ const findMappingSchemaForKey = ( schemas: ERC725JSONSchema[], ): ERC725JSONSchema | DynamicNameSchema | null => { const firstWordHex = key.substring(0, 26); - const secondWordHex = key.substring(26); // Should detect: - // 1. Known/defined mapping + // Known/defined mapping let keySchema = schemas.find((schema) => schema.key === key) || null; if (keySchema) { - return keySchema; + return fillDynamicKeyPart(key, keySchema); } - // 2. "Semi defined mappings" i.e. "SupportedStandards:??????" - let dynamicPartName = '??????'; // default for "unknown" - keySchema = schemas.find( (schema) => `${schema.key.substring(0, 22)}0000` === firstWordHex, @@ -112,31 +160,7 @@ const findMappingSchemaForKey = ( return null; } - const keyNameParts = keySchema.name.split(':'); - - const result = { - ...keySchema, - name: `${keyNameParts[0]}:${dynamicPartName}`, - valueContent: '?', - key, - }; - - // 3. mappings with dynamic key part - // replace dynamic placeholder in the map part (e.g:
, ) with the hex value - if (isDynamicKeyName(keySchema.name)) { - dynamicPartName = secondWordHex; - (result as DynamicNameSchema).dynamicName = - `${keyNameParts[0]}:0x${dynamicPartName}`; - (result as DynamicNameSchema).dynamicKeyPart = `0x${secondWordHex}`; - } - - // 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, 26) === secondWordHex) { - [, dynamicPartName] = `0x${keyNameParts}`; - } - - return result; + return fillDynamicKeyPart(key, keySchema); }; const findMappingWithGroupingSchemaForKey = ( @@ -154,8 +178,9 @@ const findMappingWithGroupingSchemaForKey = ( const dynamicKeyPart = key.substring(26); if (isDynamicKeyName(keySchema.name)) { - (keySchema as DynamicNameSchema).dynamicName = - `${keyNameParts[0]}:${keyNameParts[1]}:0x${dynamicKeyPart}`; + ( + keySchema as DynamicNameSchema + ).dynamicName = `${keyNameParts[0]}:${keyNameParts[1]}:0x${dynamicKeyPart}`; (keySchema as DynamicNameSchema).dynamicKeyPart = `0x${dynamicKeyPart}`; }