Skip to content

Commit

Permalink
feat: allow to encode LSP2 Array length only (#326)
Browse files Browse the repository at this point in the history
* 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 <hugo@lukso.io>
  • Loading branch information
CJ42 and Hugoo authored Nov 24, 2023
1 parent a6fe7c8 commit 3a6be55
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 41 deletions.
24 changes: 24 additions & 0 deletions docs/classes/ERC725.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,30 @@ myErc725.encodeData([

</details>

<details>
<summary>Encode array length</summary>

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'],
}
*/
```

</details>

---

## encodePermissions
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 11 additions & 26 deletions src/lib/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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(),
Expand Down
63 changes: 50 additions & 13 deletions src/lib/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { SUPPORTED_VERIFICATION_METHOD_STRINGS } from '../constants/constants';
import {
guessKeyTypeFromKeyName,
isDataAuthentic,
encodeArrayKey,
encodeKeyValue,
decodeKeyValue,
encodeKey,
Expand All @@ -44,6 +43,7 @@ import { decodeKey } from './decodeData';
describe('utils', () => {
describe('encodeKey/decodeKey', () => {
const testCases = [
// test encoding an array of address
{
schema: {
name: 'LSP3IssuedAssets[]',
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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(
[
Expand Down
5 changes: 5 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion src/types/encodeData/JSONURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down

0 comments on commit 3a6be55

Please sign in to comment.