Skip to content

Commit

Permalink
refactor: remove left zero padding in dynamicName and `dynamicKeyPa…
Browse files Browse the repository at this point in the history
…rt` for `Mapping`
  • Loading branch information
CJ42 committed May 2, 2024
1 parent 902b2be commit 693e2c9
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/lib/encodeKeyName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { DynamicKeyParts } from '../types/dynamicKeys';
const dynamicTypes = ['<string>', '<address>', '<bool>'];

// 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+)>/;

/**
*
Expand Down
68 changes: 50 additions & 18 deletions src/lib/schemaParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

import assert from 'assert';
import { DynamicNameSchema } from '../types/ERC725JSONSchema';
import { ERC725JSONSchema } from '../types/ERC725JSONSchema';

import { getSchema } from './schemaParser';

Expand Down Expand Up @@ -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',
});
});
Expand All @@ -118,41 +116,48 @@ 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',
};

const schema = getSchema(key, [extraSchema]);

assert.deepStrictEqual(schema, extraSchema);
assert.deepStrictEqual(schema, {
...extraSchema,
dynamicKeyPart: `0x${address}`,
dynamicName,
});
});

it('finds known SomeBytes32Mapping:<bytes32>', () => {
const bytes32Value =
'1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff';
const name = 'SomeBytes32Mapping:<bytes32>';
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}`;

SomeBytes32Mapping: 0x1111222233334444555566667777888899990000aa;

const extraSchema: DynamicNameSchema = {
const extraSchema: ERC725JSONSchema = {
name,
dynamicName,
key,
dynamicKeyPart: `0x${bytes32Value}`,
keyType: 'Mapping',
valueContent: 'Address',
valueType: 'address',
};

const schema = getSchema(key, [extraSchema]);

assert.deepStrictEqual(schema, extraSchema);
assert.deepStrictEqual(schema, {
...extraSchema,
dynamicName,
dynamicKeyPart: `0x${dynamicPart}`,
});
});

it('finds known SomeSelectorMap:<bytes4>', () => {
Expand All @@ -161,19 +166,46 @@ 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)',
};

const schema = getSchema(key, [extraSchema]);

assert.deepStrictEqual(schema, extraSchema);
assert.deepStrictEqual(schema, {
...extraSchema,
dynamicName,
dynamicKeyPart: `0x${bytes4Value}`,
});
});

it('finds Known LSP1UniversalReceiverDelegate:<bytes32> ', () => {
const bytes32value =
'cafecafecafecafecafecafecafecafecafecafef00df00df00df00df00df00d';
const name = 'LSP1UniversalReceiverDelegate:<bytes32>';
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}`,
});
});
});

Expand Down
95 changes: 60 additions & 35 deletions src/lib/schemaParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
* @author Hugo Masclet <@Hugoo>
* @date 2022
*/

import { keccak256 } from 'web3-utils';
import allSchemas from '../schemas';

Expand All @@ -25,7 +24,7 @@ import {
ERC725JSONSchema,
ERC725JSONSchemaKeyType,
} from '../types/ERC725JSONSchema';
import { isDynamicKeyName } from './encodeKeyName';
import { dynamicTypesRegex, isDynamicKeyName } from './encodeKeyName';

const getSchemasByKeyType = (
schemas: ERC725JSONSchema[],
Expand All @@ -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: <address>, <bytes32>) 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[],
Expand Down Expand Up @@ -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,
Expand All @@ -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: <address>, <bytes32>) 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 = (
Expand All @@ -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}`;
}

Expand Down

0 comments on commit 693e2c9

Please sign in to comment.