From f8dc5ae9651ce953b2ff9808dfb3671126adace0 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 25 Apr 2024 10:43:29 +0100 Subject: [PATCH] fix: encode `uintN` with correct padding and bytes length --- src/index.test.ts | 54 +++++++++++++++++++++++++++++++++++++++++++ src/lib/utils.test.ts | 6 +++++ src/lib/utils.ts | 34 +++++++++++++-------------- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 22913248..a872ac31 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -889,6 +889,60 @@ describe('Running @erc725/erc725.js tests...', () => { }); }); + describe('Testing `encodeData`', () => { + describe('for `uintN` as `Number`', () => { + [ + { valueType: 'uint8', valueToEncode: 10, expectedEncodedValue: '0x0a' }, + { + valueType: 'uint16', + valueToEncode: 10, + expectedEncodedValue: '0x000a', + }, + { + valueType: 'uint24', + valueToEncode: 10, + expectedEncodedValue: '0x00000a', + }, + { + valueType: 'uint32', + valueToEncode: 10, + expectedEncodedValue: '0x0000000a', + }, + { + valueType: 'uint128', + valueToEncode: 10, + expectedEncodedValue: '0x0000000000000000000000000000000a', + }, + { + valueType: 'uint256', + valueToEncode: 10, + expectedEncodedValue: + '0x000000000000000000000000000000000000000000000000000000000000000a', + }, + ].forEach((testCase) => { + it('should encode a valueType `uintN` as valueContent `Number` correctly with the right padding', () => { + const schema = { + name: 'ExampleUintN', + key: '0x512cddbe2654abd240fafbed308d91e82ff977301943f08ea825ba3e435bfa57', + keyType: 'Singleton', + valueType: testCase.valueType, + valueContent: 'Number', + }; + const erc725js = new ERC725([schema]); + const result = erc725js.encodeData([ + { keyName: schema.name, value: testCase.valueToEncode }, + ]); + + assert.equal(result.values[0], testCase.expectedEncodedValue); + }); + }); + }); + }); + + describe('Testing `decodeData`', () => { + // ... + }); + describe('Testing utility encoding & decoding functions', () => { const allGraphData = generateAllData(mockSchema) as any; /* **************************************** */ diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 90212974..088dfe19 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -414,6 +414,12 @@ describe('utils', () => { encodedValue: '0xdeadbeaf0000000000000010', decodedValue: ['0xdeadbeaf', 16], }, + { + valueContent: '(Bytes4,Number)', + valueType: '(bytes4,uint128)', + encodedValue: '0xdeadbeaf00000000000000000000000000000020', + decodedValue: ['0xdeadbeaf', 32], + }, ]; // we may need to add more test cases! Address, etc. testCases.forEach((testCase) => { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 155c0b72..c7fb78db 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -61,6 +61,10 @@ import { EncodeDataInput } from '../types/decodeData'; import { GetDataDynamicKey } from '../types/GetData'; import { isValidTuple } from './decodeData'; +function isValueContentLiteralHex(valueContent: string): boolean { + return valueContent.slice(0, 2) === '0x'; +} + /** * * @param {string} valueContent as per ERC725Schema definition @@ -84,17 +88,23 @@ export function encodeKeyValue( name?: string, ): string | false { const isSupportedValueContent = - !!valueContentMap(valueContent) || valueContent.slice(0, 2) === '0x'; + !!valueContentMap(valueContent) || isValueContentLiteralHex(valueContent); if (!isSupportedValueContent) { throw new Error( - `The valueContent '${valueContent}' - for ${name} is not supported.`, + `The valueContent '${valueContent}' for ${name} is not supported.`, ); } const isValueTypeArray = valueType.slice(valueType.length - 2) === '[]'; + if ( + (valueType.startsWith('uint') || valueType.startsWith('bytes')) && + typeof decodedValue !== 'object' + ) { + return encodeValueType(valueType, decodedValue); + } + if (!isValueTypeArray && !Array.isArray(decodedValue)) { // Straight forward encode return encodeValueContent(valueContent, decodedValue); @@ -187,8 +197,8 @@ export function guessKeyTypeFromKeyName( } export const encodeTupleKeyValue = ( - valueContent: string, // i.e. (bytes4,Number,bytes16) - valueType: string, // i.e. (bytes4,bytes8,bytes16) + valueContent: string, // i.e. (Bytes4,Number,Bytes16,Address) + valueType: string, // i.e. (bytes4,uint128,bytes16,address) decodedValues: Array, ) => { // We assume data has already been validated at this stage @@ -196,6 +206,7 @@ export const encodeTupleKeyValue = ( const valueTypeParts = valueType .substring(1, valueType.length - 1) .split(','); + const valueContentParts = valueContent .substring(1, valueContent.length - 1) .split(','); @@ -218,18 +229,7 @@ export const encodeTupleKeyValue = ( return ''; // may cause issues? } - const numberOfBytes = Number.parseInt(valueTypeParts[i].substring(5), 10); // bytes50 -> 50 - - // If the encoded value is too large for the expected valueType, we shrink it from the left - // i.e. number are encoded on 32bytes - // TODO: might be missing cases ! - if (encodedKeyValue.length > 2 + numberOfBytes * 2) { - return encodedKeyValue.slice( - encodedKeyValue.length - numberOfBytes * 2, - ); - } - - return padLeft(encodedKeyValue, numberOfBytes * 2).replace('0x', ''); + return stripHexPrefix(encodedKeyValue); }) .join('')}`;