From 3a6be551d889904b7d95e2630ab637f2a31feb50 Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:04:09 +0000 Subject: [PATCH] feat: allow to encode LSP2 Array length only (#326) * feat(wip): allow to encode LSP2 Array length only * refactor: add typing `number` for `EncodeDataType` * docs: add docs for encode array length --------- Co-authored-by: Hugo Masclet --- docs/classes/ERC725.md | 24 +++++++++++++ src/index.ts | 2 +- src/lib/encoder.ts | 37 ++++++------------- src/lib/utils.test.ts | 63 ++++++++++++++++++++++++++------- src/lib/utils.ts | 5 +++ src/types/encodeData/JSONURL.ts | 7 +++- 6 files changed, 97 insertions(+), 41 deletions(-) diff --git a/docs/classes/ERC725.md b/docs/classes/ERC725.md index 269870ce..2fceb610 100644 --- a/docs/classes/ERC725.md +++ b/docs/classes/ERC725.md @@ -676,6 +676,30 @@ myErc725.encodeData([ +
+ Encode array length + +If the key is of type Array and you pass an integer as a value (for instance, the array length), it will be encoded accordingly. + +```javascript title="Encode the length of an array" +myErc725.encodeData([ + { + keyName: 'LSP3IssuedAssets[]', + value: 5, + }, +]); +/** +{ + keys: [ + '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + ], + values: ['0x00000000000000000000000000000005'], +} +*/ +``` + +
+ --- ## encodePermissions diff --git a/src/index.ts b/src/index.ts index 957d5c50..7287a2e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -125,7 +125,7 @@ export class ERC725 { } /** - * To prevent weird behovior from the lib, we must make sure all the schemas are correct before loading them. + * To prevent weird behavior from the lib, we must make sure all the schemas are correct before loading them. * * @param schemas * @returns diff --git a/src/lib/encoder.ts b/src/lib/encoder.ts index 42f93890..0944edd6 100644 --- a/src/lib/encoder.ts +++ b/src/lib/encoder.ts @@ -332,30 +332,6 @@ const returnTypesOfUintNCompactBytesArray = () => { return types; }; -/** - * Encodes any set of strings to string[CompactBytesArray] - * - * @param values An array of non restricted strings - * @returns string[CompactBytesArray] - */ -const encodeStringCompactBytesArray = (values: string[]): string => { - const hexValues: string[] = values.map((element) => utf8ToHex(element)); - - return encodeCompactBytesArray(hexValues); -}; - -/** - * Decode a string[CompactBytesArray] to an array of strings - * @param compactBytesArray A string[CompactBytesArray] - * @returns An array of strings - */ -const decodeStringCompactBytesArray = (compactBytesArray: string): string[] => { - const hexValues: string[] = decodeCompactBytesArray(compactBytesArray); - const stringValues: string[] = hexValues.map((element) => hexToUtf8(element)); - - return stringValues; -}; - const valueTypeEncodingMap = { bool: { encode: (value: boolean) => (value ? '0x01' : '0x00'), @@ -494,8 +470,17 @@ const valueTypeEncodingMap = { decode: (value: string) => decodeCompactBytesArray(value), }, 'string[CompactBytesArray]': { - encode: (value: string[]) => encodeStringCompactBytesArray(value), - decode: (value: string) => decodeStringCompactBytesArray(value), + encode: (values: string[]) => { + const hexValues: string[] = values.map((element) => utf8ToHex(element)); + return encodeCompactBytesArray(hexValues); + }, + decode: (value: string) => { + const hexValues: string[] = decodeCompactBytesArray(value); + const stringValues: string[] = hexValues.map((element) => + hexToUtf8(element), + ); + return stringValues; + }, }, ...returnTypesOfBytesNCompactBytesArray(), ...returnTypesOfUintNCompactBytesArray(), diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 0ae455ad..57a5add0 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -29,7 +29,6 @@ import { SUPPORTED_VERIFICATION_METHOD_STRINGS } from '../constants/constants'; import { guessKeyTypeFromKeyName, isDataAuthentic, - encodeArrayKey, encodeKeyValue, decodeKeyValue, encodeKey, @@ -44,6 +43,7 @@ import { decodeKey } from './decodeData'; describe('utils', () => { describe('encodeKey/decodeKey', () => { const testCases = [ + // test encoding an array of address { schema: { name: 'LSP3IssuedAssets[]', @@ -226,12 +226,28 @@ describe('utils', () => { encodeKey(testCase.schema as ERC725JSONSchema, testCase.decodedValue), testCase.encodedValue, ); + assert.deepStrictEqual( decodeKey(testCase.schema as ERC725JSONSchema, testCase.encodedValue), testCase.decodedValue, ); }); }); + + it('should encode the array length only if passing a number', async () => { + const schema: ERC725JSONSchema = { + name: 'LSP3IssuedAssets[]', + key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + keyType: 'Array', + valueContent: 'Address', + valueType: 'address', + }; + + const decodedValue = 3; + const encodedValue = '0x00000000000000000000000000000003'; + + assert.equal(encodeKey(schema, decodedValue), encodedValue); + }); }); describe('encodeKeyValue/decodeKeyValue', () => { @@ -391,19 +407,19 @@ describe('utils', () => { }); describe('encodeArrayKey', () => { - it('encodes array key correctly', () => { - const key = - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0'; - - const expectedValues = [ - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000002', - ]; + it('should encode the array length only if passing a number', async () => { + const schema: ERC725JSONSchema = { + name: 'LSP3IssuedAssets[]', + key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + keyType: 'Array', + valueContent: 'Address', + valueType: 'address', + }; - expectedValues.forEach((expectedValue, index) => { - assert.strictEqual(encodeArrayKey(key, index), expectedValue); - }); + const decodedValue = 3; + const encodedValue = '0x00000000000000000000000000000003'; + + assert.equal(encodeKey(schema, decodedValue), encodedValue); }); }); @@ -567,6 +583,27 @@ describe('utils', () => { }); }); + it('encodes array length only if giving a number', () => { + const length = 5; + + const encodedArrayLengthKey = encodeData( + [ + { + keyName: 'LSP3IssuedAssets[]', + value: length, + }, + ], + schemas, + ); + + assert.deepStrictEqual(encodedArrayLengthKey, { + keys: [ + '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + ], + values: ['0x00000000000000000000000000000005'], + }); + }); + it('encodes multiple keys', () => { const encodedMultipleKeys = encodeData( [ diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a29a9a72..0e5555c9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -249,6 +249,11 @@ export function encodeKey( switch (lowerCaseKeyType) { case 'array': { + // if we are encoding only the Array length + if (typeof value === 'number') { + return encodeValueType('uint128', value); + } + if (!Array.isArray(value)) { console.error("Can't encode a non array for key of type array"); return null; diff --git a/src/types/encodeData/JSONURL.ts b/src/types/encodeData/JSONURL.ts index 1c00ac90..4948cdc9 100644 --- a/src/types/encodeData/JSONURL.ts +++ b/src/types/encodeData/JSONURL.ts @@ -27,7 +27,12 @@ export interface URLDataWithJson extends URLData { export type JSONURLDataToEncode = URLDataWithHash | URLDataWithJson; -export type EncodeDataType = string | string[] | JSONURLDataToEncode | boolean; +export type EncodeDataType = + | string + | string[] + | JSONURLDataToEncode + | boolean + | number; export interface EncodeDataReturn { keys: string[];