From d5857d659014c50c1063da529d88fd0d212fd3f3 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 16 May 2024 14:25:12 +0100 Subject: [PATCH 01/25] docs: structure erc725.js docs function by functionalities --- docs/classes/_category_.yml | 3 - docs/{classes/ERC725.md => methods.md} | 2358 +++++++++++++++--------- 2 files changed, 1476 insertions(+), 885 deletions(-) delete mode 100644 docs/classes/_category_.yml rename docs/{classes/ERC725.md => methods.md} (70%) diff --git a/docs/classes/_category_.yml b/docs/classes/_category_.yml deleted file mode 100644 index cb44a08e..00000000 --- a/docs/classes/_category_.yml +++ /dev/null @@ -1,3 +0,0 @@ -label: 'Classes' -collapsed: true -position: 5 diff --git a/docs/classes/ERC725.md b/docs/methods.md similarity index 70% rename from docs/classes/ERC725.md rename to docs/methods.md index 13115ba5..a33d6efe 100644 --- a/docs/classes/ERC725.md +++ b/docs/methods.md @@ -1,984 +1,969 @@ --- sidebar_position: 1 +title: 'Methods' --- -# ERC725 +## Encoding -## checkPermissions +### encodeData ```js -myErc725.checkPermissions(requiredPermissions, grantedPermissions); +myErc725.encodeData(data [, schemas]); ``` ```js -ERC725.checkPermissions(requiredPermissions, grantedPermissions); +ERC725.encodeData(data, schemas); ``` -Check if the required permissions are included in the granted permissions as defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). +Encode the data of a smart contract according to your `ERC725JSONSchema` so that you can store the information in smart contracts. -:::info +:::tip -`checkPermissions` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. +When encoding JSON, it is possible to pass in the JSON object and the URL where it is available publicly. The JSON will be hashed with `keccak256`. ::: -#### Parameters - -##### 1. `requiredPermissions` - String[] | String - -An array of required permissions or a single required permission. (32bytes hex or the official name of the permission). - -##### 2. `grantedPermissions` - String +:::info -The granted permissions. (32bytes hex). +When encoding some values using specific `string` or `bytesN` as `valueType`, if the data passed is a non-hex value, _erc725.js_ will convert the value +to its utf8-hex representation for you. For instance: -#### Returns +- If `valueType` is `string` and you provide a `number` as input. -| Type | Description | -| :------ | :----------------------------------------------------------------------------------------------------------------------------------------------- | -| boolean | A boolean value indicating whether the required permissions are included in the granted permissions as defined by the [LSP6 KeyManager Standard] | +_Example: input `42` --> will encode as `0x3432` (utf-8 hex code for `4` = `0x34`, for `2` = `0x32`)._ -#### Permission-Name Example +- If `valueType` is `bytes32` or `bytes4`, it will convert as follow: -```javascript title="Checking permissions by name" -const requiredPermissions = 'CHANGEOWNER'; -const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; -ERC725.checkPermissions(requiredPermissions, grantedPermissions); -// true +_Example 1: input `week` encoded as `bytes4` --> will encode as `0x7765656b`._ -// This method is also available on the instance: +_Example 2: input `1122334455` encoded as `bytes4` --> will encode as `0x42e576f7`._ -const requiredPermissions = ['CHANGEOWNER', 'CALL']; -const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; -myErc725.checkPermissions(requiredPermissions, grantedPermissions); -// false -``` +::: -#### 32bytes hex Example +#### Parameters -```javascript title="Checking permissions by 32bytes hex" -const requiredPermissions = [ - '0x0000000000000000000000000000000000000000000000000000000000000001', - '0x0000000000000000000000000000000000000000000000000000000000000800', -]; -const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; +##### 1. `data` - Array of Objects -ERC725.checkPermissions(requiredPermissions, grantedPermissions); -// false +An array of objects containing the following properties: -// This method is also available on the instance: +| Name | Type | Description | +| :---------------------------- | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `keyName` | string | Can be either the named key (i.e. `LSP3Profile`, `LSP12IssuedAssetsMap:
`) or the hashed key (with or without `0x` prefix, i.e. `0x5ef...` or `5ef...`). | +| `dynamicKeyParts` (optional) | string or
string[ ] | The dynamic parts of the `keyName` that will be used for encoding the key. | +| `value` | string or
string[ ]
JSON | The value that should be encoded. Can be a string, an array of string or a JSON... | +| `startingIndex` (optional) | number | Starting index for `Array` types to encode a subset of elements. Defaults to `0`. | +| `totalArrayLength` (optional) | number | Parameter for `Array` types, specifying the total length when encoding a subset of elements. Defaults to the number of elements in the `value` field. | -const requiredPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000001'; -const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; +The `keyName` also supports dynamic keys for [`Mapping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping) and [`MappingWithGrouping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping). Therefore, you can use variables in the key name such as `LSP12IssuedAssetsMap:
`. In that case, the value should also set the `dynamicKeyParts` property: -myErc725.checkPermissions(requiredPermissions, grantedPermissions); -// true -``` +- `dynamicKeyParts`: string or string[ ] which holds the variables that needs to be encoded. ---- +:::info Handling keyType `Array`. -## decodeData +If the keyType is Array and you pass an integer as a value it will encode only the data key for the Array length. See the example below. -```js -myErc725.decodeData(data [, schemas]); -``` +Additionally, the `totalArrayLength` parameter must be explicitly provided to ensure integrity when encoding subsets or modifying existing array elements. Its value specifies the total length of the array **after the operation is completed**, not just the size of the encoded subset. -```js -ERC725.decodeData(data, schemas); -``` +**When to use `totalArrayLength`** -If you are reading the key-value store from an ERC725 smart contract you can use the `decodeData` function to do the decoding for you. +- **Adding Elements:** When adding new elements to an array, `totalArrayLength` should equal the sum of the current array's length plus the number of new elements added. +- **Modifying Elements:** If modifying elements within an existing array without changing the total number of elements, `totalArrayLength` should match the previous length of the array. +- **Removing Elements:** In cases where elements are removed, `totalArrayLength` should reflect the number of elements left. -:::tip -If you want total convenience, it is recommended to use the [`fetchData`](ERC725.md#fetchdata) function, which automatically `decodes` and `fetches` external references. ::: -#### Parameters - -##### 1. `data` - Object or array of Objects - -An object or array of objects containing the following properties: - -| Name | Type | Description | -| :--------------------------- | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `keyName` | string | Can be either the named key (i.e. `LSP3Profile`, `LSP12IssuedAssetsMap:
`) or the hashed key (with or without `0x` prefix, i.e. `0x5ef...` or `5ef...`). | -| `dynamicKeyParts` (optional) | string or
string[ ] | If `keyName` is a dynamic key, the dynamic parts of the `keyName` that will be used for encoding the key. | -| `value` | string or
string[ ] | The value that should be decoded. Can be a string, an array of string, or a JSON. | +:::caution Encoding array lengths -The `keyName` also supports dynamic keys for [`Mapping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping) and [`MappingWithGrouping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping). Therefore, you can use variables in the key name such as `LSP12IssuedAssetsMap:
`. In that case, the value should also set the `dynamicKeyParts` property: +Please be careful when updating existing contract data. Incorrect usage of `startingIndex` and `totalArrayLength` can lead to improperly encoded data that changes the intended structure of the data field. -- `dynamicKeyParts`: string or string[ ] which holds the variables that needs to be encoded. +::: ##### 2. `schemas` - Array of Objects (optional) -An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the schema. If called on an instance, the parameter is optional and will be concatenated with the schema provided on instantiation. +An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the schema. If called on an instance, it is optional and it will be concatenated with the schema provided on instantiation. #### Returns -| Name | Type | Description | -| :------------ | :-------------- | :----------------------------------------------------------------- | -| `decodedData` | Object or Array | The decoded data as defined and expected in the following schemas. | +| Name | Type | Description | +| :------------ | :----- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `encodedData` | Object | An object containing the encoded keys and values according to the [LSP2 ERC725Y JSON Schema](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md) of the data which was passed | -:::info +After the `data` is encoded, the object is ready to be stored in smart contracts. -- If the input is an array of objects, the values will be returned in an array. -- If the input is a single object, the output will be the decodedData object directly. +#### Examples -::: +
+ Encode a VerifiableURI with JSON and uploaded URL -### Single-Key Example +```javascript title="Encode a VerifiableURI with JSON and uploaded URL" +import ERC725 from '@erc725/erc725.js'; -```javascript title="Decoding an object with one key" -myErc725.decodeData([ +const schemas = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }, +]; + +const myErc725 = new ERC725(schemas); + +myErc725.encodeData([ { keyName: 'LSP3Profile', - value: - '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', + value: { + json: profileJson, + url: 'ipfs://QmQTqheBLZFnQUxu5RDs8tA9JtkxfZqMBcmGd9sukXxwRm', + }, }, ]); /** -[ +{ + keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], + values: ['0x6f357c6a2404a2866f05e53e141eb61382a045e53c2fc54831daca9d9e1e039a11f739e1696670733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'], +} +*/ + +// You can also use the hashed key (with or without 0x prefix) +myErc725.encodeData([ { - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyName: + '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: { - verification: { - method: 'keccak256(utf8)', - data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361' - }, - url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', + json: profileJson, + url: 'ipfs://QmQTqheBLZFnQUxu5RDs8tA9JtkxfZqMBcmGd9sukXxwRm', }, }, -] -*/ - -myErc725.decodeData({ - keyName: 'LSP3Profile', - value: - '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', -}); +]); /** { - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - value: { - verification: { - method: 'keccak256(utf8)', - data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361' + keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], + values: ['0x6f357c6a2404a2866f05e53e141eb61382a045e53c2fc54831daca9d9e1e039a11f739e1696670733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'], +} +*/ + +myErc725.encodeData([ + { + keyName: + '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + json: profileJson, + url: 'ipfs://QmQTqheBLZFnQUxu5RDs8tA9JtkxfZqMBcmGd9sukXxwRm', }, - url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', }, +]); +/** +{ + keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], + values: ['0x6f357c6a2404a2866f05e53e141eb61382a045e53c2fc54831daca9d9e1e039a11f739e1696670733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'], } */ ``` -### Multi-Key Example +```javascript title="encode a Singleton data key of valueContent Address" +import ERC725 from '@erc725/erc725.js'; -```javascript title="Decoding an object with multiple keys" -myErc725.decodeData([ +const schemas = [ { - keyName: 'LSP3Profile', - value: - '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', + name: 'LSP1UniversalReceiverDelegate', + key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', + keyType: 'Singleton', + valueType: 'address', + valueContent: 'Address', }, +]; + +const myErc725 = new ERC725(schemas); + +myErc725.encodeData([ { - keyName: 'LSP12IssuedAssets[]', - value: [ - { - key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - value: '0x00000000000000000000000000000002', - }, - { - key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', - value: '0xd94353d9b005b3c0a9da169b768a31c57844e490', - }, - { - key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', - value: '0xdaea594e385fc724449e3118b2db7e86dfba1826', - }, - ], + keyName: 'LSP1UniversalReceiverDelegate', + value: '0x1183790f29BE3cDfD0A102862fEA1a4a30b3AdAb', }, ]); /** -[ +{ + keys: ['0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47'], + values: ['0x1183790f29be3cdfd0a102862fea1a4a30b3adab'], +} +*/ +``` + +
+ +
+ Encode a VerifiableURI with hash function, hash and uploaded URL + +```javascript title="Encode a VerifiableURI with hash function, hash and uploaded URL" +import ERC725 from '@erc725/erc725.js'; + +const schemas = [ { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }, +]; + +const myErc725 = new ERC725(schemas); + +myErc725.encodeData([ + { + keyName: 'LSP3Profile', value: { verification: { + method: 'keccak256(utf8)', data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', - method: 'keccak256(utf8)' }, url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', }, }, - { - name: 'LSP12IssuedAssets[]', - key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - value: [ - '0xD94353D9B005B3c0A9Da169b768a31C57844e490', - '0xDaea594E385Fc724449E3118B2Db7E86dFBa1826', - ], - }, -]; +]); +/** +{ + keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], + values: ['0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178'], +} */ ``` -### Dynamic-Key Example +
+ +
+ Encode dynamic keys + +```javascript title="Encode dynamic keys" +import ERC725 from '@erc725/erc725.js'; -```javascript title="Decoding an object with dynamic key and a custom schema" const schemas = [ { - name: 'MyKeyName::', - key: '0x...', - keyType: 'Singleton', + name: 'DynamicKey:
', + key: '0x0fb367364e1852abc5f20000
', + keyType: 'Mapping', valueType: 'bytes', - valueContent: 'VerifiableURI', + valueContent: 'Address', }, ]; -myErc725.decodeData( +myErc725.encodeData( [ { - keyName: 'MyKeyName::', - dynamicKeyParts: [ - '0xaaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa', - 'true', - ], - value: - '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', + keyName: 'DynamicKey:
', + dynamicKeyParts: ['0xbedbedbedbedbedbedbedbedbedbedbedbedbedb'], + value: '0xcafecafecafecafecafecafecafecafecafecafe', }, ], schemas, ); /** -[ +{ + keys: ['0x0fb367364e1852abc5f20000bedbedbedbedbedbedbedbedbedbedbedbedbedb'], + values: ['0xcafecafecafecafecafecafecafecafecafecafe] +} +*/ + +const schemas = [ { - name: 'MyKeyName:aaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa:true', - key: '0x35e6950bc8d2aaaabbbb00000000000000000000000000000000000000000001', - value: { - verification: { - method: 'keccak256(utf8)', - data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361' - }, - url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', - }, + name: 'DynamicKey::', + key: '0xForDynamicKeysThisFieldIsIrrelevantAndWillBeOverwriten', + keyType: 'Mapping', + valueType: 'bytes', + valueContent: 'Address', }, ]; + +myErc725.encodeData( + [ + { + keyName: 'DynamicKey::', + dynamicKeyParts: ['0x11223344', 'Summer'], + value: '0xcafecafecafecafecafecafecafecafecafecafe', + }, + ], + schemas, +); +/** +{ + keys: ['0x0fb367364e1852abc5f2000078c964cd805233eb39f2db152340079088809725'], + values: ['0xcafecafecafecafecafecafecafecafecafecafe'] +} */ ``` ---- - -## decodePermissions - -```js -ERC725.decodePermissions(permission); -``` +
-Decodes permissions from hexadecimal defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). +
+ Encode multiple data keys at once -:::info +```javascript title="Encode multiple keys at once" +import ERC725 from '@erc725/erc725.js'; -`decodePermissions` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. +const schemas = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }, + { + name: 'LSP1UniversalReceiverDelegate', + key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', + keyType: 'Singleton', + valueType: 'address', + valueContent: 'Address', + }, + { + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + keyType: 'Array', + valueType: 'address', + valueContent: 'Address', + }, +]; -::: +const myErc725 = new ERC725(schemas); -#### Parameters +myErc725.encodeData([ + { + keyName: 'LSP3Profile', + value: { + verification: { + method: 'keccak256(utf8)', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + }, + url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', + }, + }, + { + keyName: 'LSP1UniversalReceiverDelegate', + value: '0x1183790f29BE3cDfD0A102862fEA1a4a30b3AdAb', + }, + { + keyName: 'LSP12IssuedAssets[]', + value: [ + '0xD94353D9B005B3c0A9Da169b768a31C57844e490', + '0xDaea594E385Fc724449E3118B2Db7E86dFBa1826', + ], + }, +]); +/** +{ + keys: [ + '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', // LSP3Profile -> data key + '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', // LSP1UniversalReceiverDelegate -> data key + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', // LSP12IssuedAssets[] -> data key for `LSP12IssuedAssets[].length` + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', // LSP12IssuedAssets[0] -> data key for index 0 + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', // LSP12IssuedAssets[1] -> data key for index 1 + ], + values: [ + '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455 a4c6a7452504466573834554178', // LSP3Profile -> value as VerifiableURI + '0x1183790f29be3cdfd0a102862fea1a4a30b3adab', // LSP1UniversalReceiverDelegate -> value as Address + '0x00000000000000000000000000000002', // LSP12IssuedAssets[].length = 2 + '0xd94353d9b005b3c0a9da169b768a31c57844e490', // LSP12IssuedAssets[0] -> Address stored at index 0 + '0xdaea594e385fc724449e3118b2db7e86dfba1826', // LSP12IssuedAssets[1] -> Address stored at index 1 + ], +} +*/ +``` -##### 1. `permission` - String +
-The encoded permission (32bytes hex). +
+ Encode array length only -#### Returns +```javascript title="Encode the length of an array" +import ERC725 from '@erc725/erc725.js'; -| Name | Type | Description | -| :------------------- | :----- | :------------------------------------------------------------------------------------------------- | -| `decodedPermissions` | Object | An object specifying whether default LSP6 permissions are included in provided hexademical string. | +const schemas = [ + { + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + keyType: 'Array', + valueType: 'address', + valueContent: 'Address', + }, +]; -#### Example +const myErc725 = new ERC725(schemas); -```javascript title="Decoding permissions" -ERC725.decodePermissions('0x0000000000000000000000000000000000000000000000000000000000000110'), +myErc725.encodeData([ + { + keyName: 'LSP12IssuedAssets[]', + value: 5, + }, +]); /** { - CHANGEOWNER: false, - EDITPERMISSIONS: false, - ADDCONTROLLER: false, - SETDATA: false, - CALL: true, - STATICCALL: false, - DELEGATECALL: false, - DEPLOY: false, - TRANSFERVALUE: true, - SIGN: false, + keys: [ + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + ], + values: ['0x00000000000000000000000000000005'], } */ +``` -ERC725.decodePermissions('0x000000000000000000000000000000000000000000000000000000000000000a'), +
+ +
+ Encode a subset of array elements + +```javascript title="Encode a subset of array elements" +import ERC725 from '@erc725/erc725.js'; + +const schemas = [ + { + name: 'AddressPermissions[]', + key: '0xdf30dba06db6a30e65354d9a64c609861f089545ca58c6b4dbe31a5f338cb0e3', + keyType: 'Array', + valueType: 'address', + valueContent: 'Address', + }, +]; + +const myErc725 = new ERC725(schemas); + +myErc725.encodeData( + [ + { + keyName: 'AddressPermissions[]', + value: [ + '0x983abc616f2442bab7a917e6bb8660df8b01f3bf', + '0x56ecbc104136d00eb37aa0dce60e075f10292d81', + ], + totalArrayLength: 23, + startingIndex: 21, + }, + ], + schemas, +); /** { - CHANGEOWNER: false, - EDITPERMISSIONS: true, - ADDCONTROLLER: false, - SETDATA: true, - CALL: false, - STATICCALL: false, - DELEGATECALL: false, - DEPLOY: false, - TRANSFERVALUE: false, - SIGN: false, + keys: [ + '0xdf30dba06db6a30e65354d9a64c609861f089545ca58c6b4dbe31a5f338cb0e3', // LSP12IssuedAssets[] -> data key for `LSP12IssuedAssets[].length` + '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000015', // LSP12IssuedAssets[21] -> data key for index 21 + '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000016', // LSP12IssuedAssets[22] -> data key for index 22 + ], + values: [ + '0x00000000000000000000000000000017', // LSP12IssuedAssets[].length = 23 + '0x983abc616f2442bab7a917e6bb8660df8b01f3bf', // LSP12IssuedAssets[21] -> Address stored at index 21 + '0x56ecbc104136d00eb37aa0dce60e075f10292d81', // LSP12IssuedAssets[22] -> Address stored at index 22 + ], } */ - -// This method is also available on the instance: -myErc725.decodePermissions('0x0000000000000000000000000000000000000000000000000000000000000110'), ``` +
+ --- -## decodeValueType +### encodeKeyName ```js -myErc725.decodeValueType(type, data); +ERC725.encodeKeyName(keyName [, dynamicKeyParts]); ``` -```js -ERC725.decodeValueType(type, data); -``` +Hashes a key name for use on an [ERC725Y contract](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-725.md#erc725y) according to the [LSP2 ERC725Y JSON Schema Standard](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md). -Decode some data according to a provided value type. +:::info -#### Parameters +`encodeKeyName` is available as either a static or non-static method so can be called without instantiating an ERC725 object. -| Name | Type | Description | -| :----- | :----- | :---------------------------------------------------------------------------- | -| `type` | string | The value type to decode the data (i.e. `uint256`, `bool`, `bytes4`, etc...). | -| `data` | string | A hex encoded string starting with `0x` to decode | +::: -#### Returns +#### Parameters -| Name | Type | Description | -| :------------- | :--------------------- | :----------------------------------- | -| `decodedValue` | string or
number | A value decoded according to `type`. | +##### 1. `keyName` - String -#### Examples +The key name you want to encode, for instance: `LSP3Profile`. -```javascript -myErc725.decodeValueType('uint128', '0x0000000000000000000000000000000a'); -// 10 +##### 2. `dynamicKeyParts` - String or array of Strings (optional) -myErc725.decodeValueType('bool', '0x01'); -// true +The variables used to encode the key name, if the key name is a dynamic (i.e.: `MyKey:
`...) -myErc725.decodeValueType('string', '0x48656c6c6f21'); -// 'Hello!'; +#### Returns -// also available for ABI encoded array + CompactBytesArray -myErc725.decodeValueType( - 'uint256[]', - '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001e', -); -// [ 10, 20, 30 ] +| Name | Type | Description | +| :--------------- | :----- | :------------------------------------------- | +| `encodedKeyName` | string | The keccak256 hash of the provided key name. | -myErc725.decodeValueType( - 'uint256[CompactBytesArray]'', - '0x0020000000000000000000000000000000000000000000000000000000000000000500200000000000000000000000000000000000000000000000000000000000000008' -) -// [ 5, 8 ] -``` +The hash must be retrievable from the ERC725Y contract via the [getData](#getdata) function. -This method is also available as a static method: +#### Example -```js -ERC725.decodeValueType( - 'uint256', - '0x000000000000000000000000000000000000000000000000000000000000002a', +```javascript title="Encode the key name" +import ERC725 from '@erc725/erc725.js'; + +ERC725.encodeKeyName('LSP3Profile'); +// '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5' + +ERC725.encodeKeyName('SupportedStandards:LSP3Profile'); +// '0xeafec4d89fa9619884b600005ef83ad9559033e6e941db7d7c495acdce616347' + +ERC725.encodeKeyName( + 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', ); -// 42 +// '0x4b80742de2bf82acb3630000cafecafecafecafecafecafecafecafecafecafe' + +ERC725.encodeKeyName('MyKeyName:', 'true'); +// '0x35e6950bc8d21a1699e500000000000000000000000000000000000000000001' + +ERC725.encodeKeyName('MyKeyName::', ['ffff', '4081242941']); +// 0x35e6950bc8d20000ffff000000000000000000000000000000000000f342d33d + +ERC725.encodeKeyName('MyKeyName:', ['4081242941']); +// 0x35e6950bc8d21a1699e5000000000000000000000000000000000000f342d33d + +// This method is also available on the instance: +myErc725.encodeKeyName('LSP3Profile'); ``` -## encodeData +--- + +### encodeArrayKey (TBD) + +### encodeValueType ```js -myErc725.encodeData(data [, schemas]); +myErc725.encodeValueType(type, value); ``` ```js -ERC725.encodeData(data, schemas); +ERC725.encodeValueType(type, value); ``` -Encode the data of a smart contract according to your `ERC725JSONSchema` so that you can store the information in smart contracts. +#### Parameters -:::tip +| Name | Type | Description | +| :------ | :--------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | +| `type` | string | The value type to encode the value (i.e. `uint256`, `bool`, `bytes4`, etc...). | +| `value` | string or
string[ ] or
number or
number[ ] or
boolean or
boolean[] | The value that should be encoded as `type` | -When encoding JSON, it is possible to pass in the JSON object and the URL where it is available publicly. The JSON will be hashed with `keccak256`. +#### Returns -::: +| Name | Type | Description | +| :----------------- | :----- | :------------------------------------------------------- | +| `encodedValueType` | string | A hex string representing the `value` encoded as `type`. | -:::info +After the `value` is encoded, the hex string can be used to be stored inside the ERC725Y smart contract. -When encoding some values using specific `string` or `bytesN` as `valueType`, if the data passed is a non-hex value, _erc725.js_ will convert the value -to its utf8-hex representation for you. For instance: +#### Examples -- If `valueType` is `string` and you provide a `number` as input. +```javascript +import ERC725 from '@erc725/erc725.js'; -_Example: input `42` --> will encode as `0x3432` (utf-8 hex code for `4` = `0x34`, for `2` = `0x32`)._ +const myErc725 = new ERC725(); -- If `valueType` is `bytes32` or `bytes4`, it will convert as follow: +myErc725.encodeValueType('uint256', 5); +// '0x0000000000000000000000000000000000000000000000000000000000000005' -_Example 1: input `week` encoded as `bytes4` --> will encode as `0x7765656b`._ +myErc725.encodeValueType('bool', true); +// '0x01' -_Example 2: input `1122334455` encoded as `bytes4` --> will encode as `0x42e576f7`._ +// the word `boolean` (Name of the Typescript type) is also available +myErc725.encodeValueType('boolean', true); +// '0x01' -::: +// `bytesN` type will pad on the right if the value contains less than N bytes +myErc725.encodeValueType('bytes4', '0xcafe'); +// '0xcafe0000' +myErc725.encodeValueType('bytes32', '0xcafe'); +// '0xcafe000000000000000000000000000000000000000000000000000000000000' -#### Parameters +// `bytesN` type will throw an error if the value contains more than N bytes +myERC725.encodeValueType('bytes4', '0xcafecafebeef'); +// Error: Can't convert 0xcafecafebeef to bytes4. Too many bytes, expected at most 4 bytes, received 6. -##### 1. `data` - Array of Objects +// Can also be used to encode arrays as `CompactBytesArray` +myERC725.encodeValueType('uint256[CompactBytesArray]', [1, 2, 3]); +// '0x002000000000000000000000000000000000000000000000000000000000000000010020000000000000000000000000000000000000000000000000000000000000000200200000000000000000000000000000000000000000000000000000000000000003' -An array of objects containing the following properties: +myERC725.encodeValueType('bytes[CompactBytesArray]', [ + '0xaaaaaaaa', + '0xbbbbbbbbbbbbbbbbbb', +]); +// '0x0004aaaaaaaa0009bbbbbbbbbbbbbbbbbb' +``` -| Name | Type | Description | -| :---------------------------- | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `keyName` | string | Can be either the named key (i.e. `LSP3Profile`, `LSP12IssuedAssetsMap:
`) or the hashed key (with or without `0x` prefix, i.e. `0x5ef...` or `5ef...`). | -| `dynamicKeyParts` (optional) | string or
string[ ] | The dynamic parts of the `keyName` that will be used for encoding the key. | -| `value` | string or
string[ ]
JSON | The value that should be encoded. Can be a string, an array of string, or a JSON. | -| `startingIndex` (optional) | number | Starting index for `Array` types to encode a subset of elements. Defaults to `0`. | -| `totalArrayLength` (optional) | number | Parameter for `Array` types, specifying the total length when encoding a subset of elements. Defaults to the number of elements in the `value` field. | +This method is also available as a static method. -The `keyName` also supports dynamic keys for [`Mapping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping) and [`MappingWithGrouping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping). Therefore, you can use variables in the key name such as `LSP12IssuedAssetsMap:
`. In that case, the value should also set the `dynamicKeyParts` property: +```javascript +import ERC725 from '@erc725/erc725.js'; -- `dynamicKeyParts`: string or string[ ] which holds the variables that needs to be encoded. +ERC725.encodeValueType('string', 'Hello'); +// '0x48656c6c6f' +``` -:::info Handling array subsets +--- -The `totalArrayLength` parameter must be explicitly provided to ensure integrity when encoding subsets or modifying existing array elements. Its value specifies the total length of the array **after the operation is completed**, not just the size of the encoded subset. +### encodeValueContent (TBD) -**When to Use `totalArrayLength`** +--- -- **Adding Elements:** When adding new elements to an array, `totalArrayLength` should equal the sum of the current array's length plus the number of new elements added. -- **Modifying Elements:** If modifying elements within an existing array without changing the total number of elements, `totalArrayLength` should match the previous length of the array. -- **Removing Elements:** In cases where elements are removed, `totalArrayLength` should reflect the number of elements left. +## Decoding -::: +### decodeData -:::caution Encoding array lengths +```js +myErc725.decodeData(data [, schemas]); +``` -Please be careful when updating existing contract data. Incorrect usage of `startingIndex` and `totalArrayLength` can lead to improperly encoded data that changes the intended structure of the data field. +```js +ERC725.decodeData(data, schemas); +``` +If you are reading the key-value store from an ERC725 smart contract you can use the `decodeData` function to do the decoding for you. + +:::tip +If you want total convenience, it is recommended to use the [`fetchData`](ERC725.md#fetchdata) function, which automatically `decodes` and `fetches` external references. ::: +#### Parameters + +##### 1. `data` - Object or array of Objects + +An object or array of objects containing the following properties: + +| Name | Type | Description | +| :--------------------------- | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `keyName` | string | Can be either the named key (i.e. `LSP3Profile`, `LSP12IssuedAssetsMap:
`) or the hashed key (with or without `0x` prefix, i.e. `0x5ef...` or `5ef...`). | +| `dynamicKeyParts` (optional) | string or
string[ ] | If `keyName` is a dynamic key, the dynamic parts of the `keyName` that will be used for encoding the key. | +| `value` | string or
string[ ] | The value that should be decoded. Can be a string, an array of string, or a JSON. | + +The `keyName` also supports dynamic keys for [`Mapping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping) and [`MappingWithGrouping`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping). Therefore, you can use variables in the key name such as `LSP12IssuedAssetsMap:
`. In that case, the value should also set the `dynamicKeyParts` property: + +- `dynamicKeyParts`: string or string[ ] which holds the variables that needs to be encoded. + ##### 2. `schemas` - Array of Objects (optional) -An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the schema. If called on an instance, it is optional and it will be concatenated with the schema provided on instantiation. +An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the schema. If called on an instance, the parameter is optional and will be concatenated with the schema provided on instantiation. #### Returns -| Name | Type | Description | -| :------------ | :----- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `encodedData` | Object | An object containing the encoded keys and values according to the [LSP2 ERC725Y JSON Schema](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md) of the data which was passed | +| Name | Type | Description | +| :------------ | :-------------- | :----------------------------------------------------------------- | +| `decodedData` | Object or Array | The decoded data as defined and expected in the following schemas. | -After the `data` is encoded, the object is ready to be stored in smart contracts. +:::info -#### Examples +- If the input is an array of objects, the values will be returned in an array. +- If the input is a single object, the output will be the decodedData object directly. -
- Encode a VerifiableURI with JSON and uploaded URL +::: -```javascript title="Encode a VerifiableURI with JSON and uploaded URL" -myErc725.encodeData([ +#### Single-Key Example + +```javascript title="Decoding an object with one key" +myErc725.decodeData([ { keyName: 'LSP3Profile', - value: { - json: profileJson, - url: 'ipfs://QmQTqheBLZFnQUxu5RDs8tA9JtkxfZqMBcmGd9sukXxwRm', - }, + value: + '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', }, ]); /** -{ - keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], - values: ['0x6f357c6a2404a2866f05e53e141eb61382a045e53c2fc54831daca9d9e1e039a11f739e1696670733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'], -} -*/ - -// You can also use the hashed key (with or without 0x prefix) -myErc725.encodeData([ +[ { - keyName: - '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: { - json: profileJson, - url: 'ipfs://QmQTqheBLZFnQUxu5RDs8tA9JtkxfZqMBcmGd9sukXxwRm', + verification: { + method: 'keccak256(utf8)', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361' + }, + url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', }, }, -]); -/** -{ - keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], - values: ['0x6f357c6a2404a2866f05e53e141eb61382a045e53c2fc54831daca9d9e1e039a11f739e1696670733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'], -} +] */ -myErc725.encodeData([ - { - keyName: '5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - value: { - json: profileJson, - url: 'ipfs://QmQTqheBLZFnQUxu5RDs8tA9JtkxfZqMBcmGd9sukXxwRm', - }, - }, -]); +myErc725.decodeData({ + keyName: 'LSP3Profile', + value: + '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', +}); /** { - keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], - values: ['0x6f357c6a2404a2866f05e53e141eb61382a045e53c2fc54831daca9d9e1e039a11f739e1696670733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'], + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + verification: { + method: 'keccak256(utf8)', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361' + }, + url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', + }, } */ ``` -```javascript -myErc725.encodeData([ +#### Multi-Key Example + +```javascript title="Decoding an object with multiple keys" +myErc725.decodeData([ { - keyName: 'LSP1UniversalReceiverDelegate', - value: '0x1183790f29BE3cDfD0A102862fEA1a4a30b3AdAb', + keyName: 'LSP3Profile', + value: + '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', + }, + { + keyName: 'LSP12IssuedAssets[]', + value: [ + { + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + value: '0x00000000000000000000000000000002', + }, + { + key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', + value: '0xd94353d9b005b3c0a9da169b768a31c57844e490', + }, + { + key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', + value: '0xdaea594e385fc724449e3118b2db7e86dfba1826', + }, + ], }, ]); /** -{ - keys: ['0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47'], - values: ['0x1183790f29be3cdfd0a102862fea1a4a30b3adab'], -} -*/ -``` - -
- -
- Encode a VerifiableURI with hash function, hash and uploaded URL - -```javascript title="Encode a VerifiableURI with hash function, hash and uploaded URL" -myErc725.encodeData([ +[ { - keyName: 'LSP3Profile', + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: { verification: { - method: 'keccak256(utf8)', data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + method: 'keccak256(utf8)' }, url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', }, }, -]); -/** -{ - keys: ['0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5'], - values: ['0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178'], -} -*/ -``` - -
- -
- Encode dynamic keys - -```javascript title="Encode dynamic keys" -const schemas = [ { - name: 'DynamicKey:
', - key: '0x0fb367364e1852abc5f20000
', - keyType: 'Mapping', - valueType: 'bytes', - valueContent: 'Address', + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + value: [ + '0xD94353D9B005B3c0A9Da169b768a31C57844e490', + '0xDaea594E385Fc724449E3118B2Db7E86dFBa1826', + ], }, ]; - -myErc725.encodeData( - [ - { - keyName: 'DynamicKey:
', - dynamicKeyParts: ['0xbedbedbedbedbedbedbedbedbedbedbedbedbedb'], - value: '0xcafecafecafecafecafecafecafecafecafecafe', - }, - ], - schemas, -); -/** -{ - keys: ['0x0fb367364e1852abc5f20000bedbedbedbedbedbedbedbedbedbedbedbedbedb'], - values: ['0xcafecafecafecafecafecafecafecafecafecafe] -} */ +``` + +#### Dynamic-Key Example +```javascript title="Decoding an object with dynamic key and a custom schema" const schemas = [ { - name: 'DynamicKey::', - key: '0xForDynamicKeysThisFieldIsIrrelevantAndWillBeOverwriten', - keyType: 'Mapping', + name: 'MyKeyName::', + key: '0x...', + keyType: 'Singleton', valueType: 'bytes', - valueContent: 'Address', + valueContent: 'VerifiableURI', }, ]; -myErc725.encodeData( +myErc725.decodeData( [ { - keyName: 'DynamicKey::', - dynamicKeyParts: ['0x11223344', 'Summer'], - value: '0xcafecafecafecafecafecafecafecafecafecafe', + keyName: 'MyKeyName::', + dynamicKeyParts: [ + '0xaaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa', + 'true', + ], + value: + '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', }, ], schemas, ); /** -{ - keys: ['0x0fb367364e1852abc5f2000078c964cd805233eb39f2db152340079088809725'], - values: ['0xcafecafecafecafecafecafecafecafecafecafe'] -} -*/ -``` - -
- -
- Encode multiple keys at once - -```javascript title="Encode multiple keys at once" -myErc725.encodeData([ +[ { - keyName: 'LSP3Profile', + name: 'MyKeyName:aaaabbbbccccddddeeeeffff111122223333444455556666777788889999aaaa:true', + key: '0x35e6950bc8d2aaaabbbb00000000000000000000000000000000000000000001', value: { verification: { method: 'keccak256(utf8)', - data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361' }, - url: 'ipfs://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', + url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', }, }, - { - keyName: 'LSP12IssuedAssets[]', - value: [ - '0xD94353D9B005B3c0A9Da169b768a31C57844e490', - '0xDaea594E385Fc724449E3118B2Db7E86dFBa1826', - ], - }, - { - keyName: 'LSP1UniversalReceiverDelegate', - value: '0x1183790f29BE3cDfD0A102862fEA1a4a30b3AdAb', - }, -]); -/** -{ - keys: [ - '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', - '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', - '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', - ], - values: [ - '0x6f357c6a820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', - '0x00000000000000000000000000000002', - '0xd94353d9b005b3c0a9da169b768a31c57844e490', - '0xdaea594e385fc724449e3118b2db7e86dfba1826', - '0x1183790f29be3cdfd0a102862fea1a4a30b3adab', - ], -} +]; */ ``` -
+--- -
- Encode array length +### decodeValueType -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. +```js +myErc725.decodeValueType(type, data); +``` -```javascript title="Encode the length of an array" -myErc725.encodeData([ - { - keyName: 'LSP3IssuedAssets[]', - value: 5, - }, -]); -/** -{ - keys: [ - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', - ], - values: ['0x00000000000000000000000000000005'], -} -*/ +```js +ERC725.decodeValueType(type, data); ``` -
+Decode some data according to a provided value type. -
- Encode a subset of array elements +#### Parameters -```javascript title="Encode a subset of array elements" -const schemas = [ - { - name: 'AddressPermissions[]', - key: '0xdf30dba06db6a30e65354d9a64c609861f089545ca58c6b4dbe31a5f338cb0e3', - keyType: 'Array', - valueType: 'address', - valueContent: 'Address', - }, -]; +| Name | Type | Description | +| :----- | :----- | :---------------------------------------------------------------------------- | +| `type` | string | The value type to decode the data (i.e. `uint256`, `bool`, `bytes4`, etc...). | +| `data` | string | A hex encoded string starting with `0x` to decode | -myErc725.encodeData( - [ - { - keyName: 'AddressPermissions[]', - value: [ - '0x983abc616f2442bab7a917e6bb8660df8b01f3bf', - '0x56ecbc104136d00eb37aa0dce60e075f10292d81', - ], - totalArrayLength: 23, - startingIndex: 21, - }, - ], - schemas, +#### Returns + +| Name | Type | Description | +| :------------- | :--------------------- | :----------------------------------- | +| `decodedValue` | string or
number | A value decoded according to `type`. | + +#### Examples + +```javascript +myErc725.decodeValueType('uint128', '0x0000000000000000000000000000000a'); +// 10 + +myErc725.decodeValueType('bool', '0x01'); +// true + +myErc725.decodeValueType('string', '0x48656c6c6f21'); +// 'Hello!'; + +// also available for ABI encoded array + CompactBytesArray +myErc725.decodeValueType( + 'uint256[]', + '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001e', ); -/** -{ - keys: [ - '0xdf30dba06db6a30e65354d9a64c609861f089545ca58c6b4dbe31a5f338cb0e3', - '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000015', - '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000016', - ], - values: [ - '0x00000000000000000000000000000017', - '0x983abc616f2442bab7a917e6bb8660df8b01f3bf', - '0x56ecbc104136d00eb37aa0dce60e075f10292d81', - ], -} -*/ +// [ 10, 20, 30 ] + +myErc725.decodeValueType( + 'uint256[CompactBytesArray]'', + '0x0020000000000000000000000000000000000000000000000000000000000000000500200000000000000000000000000000000000000000000000000000000000000008' +) +// [ 5, 8 ] ``` -
+This method is also available as a static method: + +```js +ERC725.decodeValueType( + 'uint256', + '0x000000000000000000000000000000000000000000000000000000000000002a', +); +// 42 +``` --- -## encodePermissions +### decodeValueContent ```js -ERC725.encodePermissions(permissions); +const erc725js = new ERC725(); + +erc725js.decodeValueContent(valueContent, value); ``` -Encodes permissions into a hexadecimal string as defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). +OR -:::info +```js +ERC725.decodeValueContent(valueContent, value); +``` -`encodePermissions` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. +OR -::: +```js +import { decodeValueContent } from '@erc725/erc725.js'; -#### Parameters - -##### 1. `permissions` - Object - -An object with [LSP6 KeyManager Permissions] as keys and a `boolean` as value. Any omitted permissions will default to `false`. - -#### Returns - -| Type | Description | -| :----- | :---------------------------------------------------------------------------------------- | -| string | The permissions encoded as a hexadecimal string defined by the [LSP6 KeyManager Standard] | - -#### Example - -```javascript title="Encoding permissions" -ERC725.encodePermissions({ - CHANGEOWNER: false, - ADDCONTROLLER: false, - EDITPERMISSIONS: false, - ADDEXTENSIONS: false, - CHANGEEXTENSIONS: true, - ADDUNIVERSALRECEIVERDELEGATE: false, - CHANGEUNIVERSALRECEIVERDELEGATE: false, - REENTRANCY: false, - SUPER_TRANSFERVALUE: true, - TRANSFERVALUE: true, - SUPER_CALL: false, - CALL: true, - SUPER_STATICCALL: false, - STATICCALL: false, - SUPER_DELEGATECALL: false, - DELEGATECALL: false, - DEPLOY: false, - SUPER_SETDATA: false, - SETDATA: false, - ENCRYPT: false, - DECRYPT: false, - SIGN: false, - EXECUTE_RELAY_CALL: false, - ERC4337_PERMISSION: false -}), -// '0x0000000000000000000000000000000000000000000000000000000000000110' - -// Any omitted Permissions will default to false -ERC725.encodePermissions({ - ADDCONTROLLER: true, - ADDEXTENSIONS: true, -}), -// '0x000000000000000000000000000000000000000000000000000000000000000a' -ERC725.encodePermissions({ - EDITPERMISSIONS: true, - CHANGEEXTENSIONS: true, - CHANGEUNIVERSALRECEIVERDELEGATE: true, - SETDATA: true, -}), -// '0x0000000000000000000000000000000000000000000000000000000000040054' - - -// This method is also available on the instance: -myErc725.encodePermissions({ - EDITPERMISSIONS: true, - SETDATA: true, -}), -``` - ---- - -## encodeValueType - -```js -myErc725.encodeValueType(type, value); -``` - -```js -ERC725.encodeValueType(type, value); -``` - -#### Parameters - -| Name | Type | Description | -| :------ | :--------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | -| `type` | string | The value type to encode the value (i.e. `uint256`, `bool`, `bytes4`, etc...). | -| `value` | string or
string[ ] or
number or
number[ ] or
boolean or
boolean[] | The value that should be encoded as `type` | - -#### Returns - -| Name | Type | Description | -| :----------------- | :----- | :------------------------------------------------------- | -| `encodedValueType` | string | A hex string representing the `value` encoded as `type`. | - -After the `value` is encoded, the hex string can be used to be stored inside the ERC725Y smart contract. - -#### Examples - -```javascript -myErc725.encodeValueType('uint256', 5); -// '0x0000000000000000000000000000000000000000000000000000000000000005' - -myErc725.encodeValueType('bool', true); -// '0x01' - -// the word `boolean` (Name of the Typescript type) is also available -myErc725.encodeValueType('boolean', true); -// '0x01' - -// `bytesN` type will pad on the right if the value contains less than N bytes -myErc725.encodeValueType('bytes4', '0xcafe'); -// '0xcafe0000' -myErc725.encodeValueType('bytes32', '0xcafe'); -// '0xcafe000000000000000000000000000000000000000000000000000000000000' - -// `bytesN` type will throw an error if the value contains more than N bytes -myERC725.encodeValueType('bytes4', '0xcafecafebeef'); -// Error: Can't convert 0xcafecafebeef to bytes4. Too many bytes, expected at most 4 bytes, received 6. - -// Can also be used to encode arrays as `CompactBytesArray` -myERC725.encodeValueType('uint256[CompactBytesArray]', [1, 2, 3]); -// '0x002000000000000000000000000000000000000000000000000000000000000000010020000000000000000000000000000000000000000000000000000000000000000200200000000000000000000000000000000000000000000000000000000000000003' - -myERC725.encodeValueType('bytes[CompactBytesArray]', [ - '0xaaaaaaaa', - '0xbbbbbbbbbbbbbbbbbb', -]); -// '0x0004aaaaaaaa0009bbbbbbbbbbbbbbbbbb' -``` - -This method is also available as a static method. - -```javascript -ERC725.encodeValueType('string', 'Hello'); -// '0x48656c6c6f' -``` - ---- - -## encodeKeyName - -```js -ERC725.encodeKeyName(keyName [, dynamicKeyParts]); +decodeValueContent(valueContent, value); ``` -Hashes a key name for use on an [ERC725Y contract](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-725.md#erc725y) according to the [LSP2 ERC725Y JSON Schema Standard](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md). - -:::info +Decode a hex encoded value and return it as if it was interpreted as a `valueContent`. -`encodeKeyName` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. +Available `valueContent` are: -::: +- `Address` (checksummed) +- `AssetURL` (deprecated) +- `Boolean` +- `BytesN` +- `JSONURL` (deprecated) +- `Keccak256` +- `Markdown` (similar to `String`) +- `Number` +- `String` +- `URL` +- `VerifiableURI` +- Any Hex `Literal` (e.g.: 0x1345ABCD...) #### Parameters -##### 1. `keyName` - String - -The key name you want to encode, for instance: `LSP3Profile`. - -##### 2. `dynamicKeyParts` - String or array of Strings (optional) - -The variables used to encode the key name, if the key name is a dynamic (i.e.: `MyKey:
`...) +| Name | Type | Description | +| :------------- | :------- | :----------------------------------------------------------------- | +| `valueContent` | `string` | One of the value content options defined by the LSP2 standard. | +| `value` | `string` | A hex encoded string starting with `0x` to decode as `valueContent | #### Returns -| Name | Type | Description | -| :--------------- | :----- | :------------------------------------------- | -| `encodedKeyName` | string | The keccak256 hash of the provided key name. | - -The hash must be retrievable from the ERC725Y contract via the [getData](#getdata) function. - -#### Example - -```javascript title="Encode the key name" -ERC725.encodeKeyName('LSP3Profile'); -// '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5' +| Name | Type | Description | +| :------------- | :--------------------------------------------------------------------------------------- | :------------------------------------------- | +| `decodedValue` | `string` or
`URLDataWithHash` or
`number` or
`boolean` or
`null` | A value decoded according to `valueContent`. | -ERC725.encodeKeyName('SupportedStandards:LSP3Profile'); -// '0xeafec4d89fa9619884b600005ef83ad9559033e6e941db7d7c495acdce616347' +### Examples -ERC725.encodeKeyName( - 'AddressPermissions:Permissions:cafecafecafecafecafecafecafecafecafecafe', +```javascript title="decodeValueContent example" +erc725js.decodeValueContent( + 'VerifiableURI', + '0x00006f357c6a0020027547537d35728a741470df1ccf65de10b454ca0def7c5c20b257b7b8d16168687474703a2f2f746573742e636f6d2f61737365742e676c62', ); -// '0x4b80742de2bf82acb3630000cafecafecafecafecafecafecafecafecafecafe' +// { +// verification: { +// method: SUPPORTED_VERIFICATION_METHOD_STRINGS.KECCAK256_UTF8, +// data: '0x027547537d35728a741470df1ccf65de10b454ca0def7c5c20b257b7b8d16168', +// }, +// url: 'http://test.com/asset.glb', +// } + +erc725js.decodeValueContent('String', '0x232068656c6c6f'); +// # hello + +erc725js.decodeValueContent('String', '0x68656c6c6f'); +// hello + +// `Address` will be checksummed +erc725js.decodeValueContent( + 'Address', + '0xa29afb8f3cce086b3992621324e9d7c104f03d1b', +); +// 0xa29Afb8F3ccE086B3992621324E9d7c104F03D1B -ERC725.encodeKeyName('MyKeyName:', 'true'); -// '0x35e6950bc8d21a1699e500000000000000000000000000000000000000000001' +erc725js.decodeValueContent( + 'Number', + '0x000000000000000000000000000000000000000000000000000000000000000a', +); +// 10 -ERC725.encodeKeyName('MyKeyName::', ['ffff', '4081242941']); -// 0x35e6950bc8d20000ffff000000000000000000000000000000000000f342d33d +erc725js.decodeValueContent( + 'Number', + '0x000000000000000000000000000000000000000000000000000000000000036c', +); +// 876 -ERC725.encodeKeyName('MyKeyName:', ['4081242941']); -// 0x35e6950bc8d21a1699e5000000000000000000000000000000000000f342d33d +erc725js.decodeValueContent('Boolean', '0x01'); +// true -// This method is also available on the instance: -myErc725.encodeKeyName('LSP3Profile'); +erc725js.decodeValueContent('Boolean', '0x00'); +// false ``` --- -## decodeMappingKey +### decodeMappingKey ```js ERC725.decodeMappingKey(keyNameOrSchema, keyHash); @@ -1066,7 +1051,9 @@ myErc725.decodeMappingKey( --- -## fetchData +## Fetching Data + +### fetchData ```js myErc725.fetchData([keys]); @@ -1113,6 +1100,11 @@ The name(s) (or the encoded name(s) as schema key) of the element(s) in the smar ### All-Keys Example ```javascript title="Receiving all keys from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const universalProfileAddress = '0x0F4180da178ed1C71398a57ca8Cb177F69591f1f'; + +const myErc725 = new ERC725(LSP3Schemas, universalProfileAddress); await myErc725.fetchData(); /** [ @@ -1151,9 +1143,15 @@ await myErc725.fetchData(); */ ``` -### Single-Key Example +#### Single-Key Example ```javascript title="Receiving one key from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const universalProfileAddress = '0x0F4180da178ed1C71398a57ca8Cb177F69591f1f'; + +const myErc725 = new ERC725(LSP3Schemas, universalProfileAddress); + await myErc725.fetchData('LSP3Profile'); /** { @@ -1174,9 +1172,15 @@ await myErc725.fetchData(['LSP1UniversalReceiverDelegate']); */ ``` -### Multi-Keys / Dynamic-Keys Example +#### Multi-Keys / Dynamic-Keys Example ```javascript title="Receiving multiple keys from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const universalProfileAddress = '0x0F4180da178ed1C71398a57ca8Cb177F69591f1f'; + +const myErc725 = new ERC725(LSP3Schemas, universalProfileAddress); + await myErc725.fetchData(['LSP3Profile', 'LSP1UniversalReceiverDelegate']); /** [ @@ -1218,7 +1222,7 @@ await myErc725.fetchData([ --- -## getData +### getData ```js myErc725.getData([keys]); @@ -1258,9 +1262,15 @@ The name(s) (or the encoded name(s) as schema key) of the element(s) in the smar ::: -### All-Keys Example +#### All-Keys Example ```javascript title="Receiving all keys from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const universalProfileAddress = '0x0F4180da178ed1C71398a57ca8Cb177F69591f1f'; + +const myErc725 = new ERC725(LSP3Schemas, universalProfileAddress); + await myErc725.getData(); /** [ @@ -1305,9 +1315,15 @@ await myErc725.getData(); */ ``` -### Single-Key Example +#### Single-Key Example ```javascript title="Receiving one key from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const universalProfileAddress = '0x0F4180da178ed1C71398a57ca8Cb177F69591f1f'; + +const myErc725 = new ERC725(LSP3Schemas, universalProfileAddress); + await myErc725.getData('LSP3Profile'); /** { @@ -1350,9 +1366,15 @@ await myErc725.getData('LSP1UniversalReceiverDelegate'); */ ``` -### Multi-Key Example +#### Multi-Key Example ```javascript title="Receiving multiple keys from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const universalProfileAddress = '0x0F4180da178ed1C71398a57ca8Cb177F69591f1f'; + +const myErc725 = new ERC725(LSP3Schemas, universalProfileAddress); + await myErc725.getData(['LSP3Profile', 'LSP1UniversalReceiverDelegate']); /** [ @@ -1376,17 +1398,23 @@ await myErc725.getData(['LSP3Profile', 'LSP1UniversalReceiverDelegate']); */ ``` -### Dynamic-Key Example +#### Dynamic-Key Example ```javascript title="Receiving dynamic keys from the schema" +import ERC725 from '@erc725/erc725.js'; +import LSP3Schemas from '@erc725/erc725.js/schemas/LSP3ProfileMetadata.sjon'; +const luksoUniversalProfile = '0x8363Cfe6c787218f0ADA0A4aBC289A8d9dfc2453'; + +const myErc725 = new ERC725(LSP3Schemas, luksoUniversalProfile); + await myErc725.getData({ keyName: 'LSP12IssuedAssetsMap:
', - dynamicKeyParts: '0xcafecafecafecafecafecafecafecafecafecafe', + dynamicKeyParts: '0x592dCACb0A0d4b85eea0975992E42Bc543207F74', // asset address (KidSuper World Arts Path) }); /** { - name: 'LSP12IssuedAssetsMap:cafecafecafecafecafecafecafecafecafecafe', - key: '0x74ac2555c10b9349e78f0000cafecafecafecafecafecafecafecafecafecafe', + name: 'LSP12IssuedAssetsMap:592dCACb0A0d4b85eea0975992E42Bc543207F74', + key: '0x74ac2555c10b9349e78f0000592dCACb0A0d4b85eea0975992E42Bc543207F74', value: '0x6b175474e89094c44da98b954eedeac495271d0f', } */ @@ -1440,7 +1468,7 @@ await myErc725.getData([ --- -## getOwner +### getOwner ```js myErc725.getOwner([address]); @@ -1466,256 +1494,822 @@ The address of the contract owner as stored in the contract. ::: -#### Example +#### Example + +```javascript title="Receiving the owner address" +// If no address is set, it will return the owner of the contract used to initialize the ERC725() class. +await myErc725.getOwner(); +// '0x94933413384997F9402cc07a650e8A34d60F437A' + +// You can also get the owner of a specific contract by setting the address parameter +await myErc725.getOwner('0x3000783905Cc7170cCCe49a4112Deda952DDBe24'); +// '0x7f1b797b2Ba023Da2482654b50724e92EB5a7091' +``` + +--- + +### isValidSignature + +```js +myErc725.isValidSignature(messageOrHash, signature); +``` + +Checks if a signature was signed by the `owner` of the ERC725 Account contract, according to [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271). If the `owner` is a contract itself, it will delegate the `isValidsignature()` call to the owner contract if it supports [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271). Otherwise, it will fail. + +#### Parameters + +##### 1. `messageOrHash` - String + +Value of a message or hash that needs to be verified. + +##### 2. `signature` - String + +The raw RLP encoded signature. + +:::info + +- The hash must be 66 chars long with the `0x` prefix. Otherwise, it will be interpreted as message. +- The message will be: enveloped as `"\x19Ethereum Signed Message:\n" + message.length + message` and hashed using `keccak256` function. + The signature can be generated with [`web3.eth.accounts.sign()`](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-accounts.html#sign). + +::: + +#### Returns + +| Name | Type | Description | +| :-------- | :------ | :------------------------------------------------------------- | +| `Promise` | boolean | `true` if signature is valid, `false` if signature is invalid. | + +:::info + +- A valid signature means that the smart contract response IS the MAGICVALUE: `0x1626ba7e`. +- If this function is called on a contract which does not support [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271), it will throw an error. + +::: + +#### Examples + +```javascript title="Checking the signature with a message" +await myErc725.isValidSignature( + 'hello', + '0xb91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c', +); +// true +``` + +```javascript title="Checking the signature with a hash" +await myErc725.isValidSignature( + '0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655', + '0xcafecafeb915466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c', +); +// false +``` + +[lsp6 keymanager permissions]: ../../../../../standards/universal-profile/lsp6-key-manager#permissions +[lsp6 keymanager standard]: https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager +[lsp-2 erc725yjsonschema]: https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md + +### supportsInterface + +```js +myERC725.supportsInterface(interfaceIdOrName); +``` + +```js +ERC725.supportsInterface(interfaceIdOrName, options); +``` + +You can use this function if you need to check if the ERC725 object or a smart contract supports a specific interface (by ID or name). When you use the function on your instantiated ERC725 class, it will use the contract address and provider provided at instantiation. On a non-instantiated class, you need to specify them in the `options` parameter. + +:::caution +The `interfaceId` is not the most secure way to check for a standard, as they could be set manually. +::: + +#### Parameters + +##### 1. `interfaceIdOrName` - String + +Either a string of the hexadecimal `interfaceID` as defined by [ERC165](https://eips.ethereum.org/EIPS/eip-165) or one of the predefined interface names: + +| interfaceName | Standard | +| :------------------------------ | :------------------------------------------------------------------------------------------------------------------------- | +| `ERC1271` | [EIP-1271: Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271) | +| `ERC725X` | [EIP-725: General execution standard](https://eips.ethereum.org/EIPS/eip-725) | +| `ERC725Y` | [EIP-725: General key-value store](https://eips.ethereum.org/EIPS/eip-725) | +| `LSP0ERC725Account` | [LSP-0: ERC725 Account](https://docs.lukso.tech/standards/universal-profile/lsp0-erc725account) | +| `LSP1UniversalReceiver` | [LSP-1: Universal Receiver](https://docs.lukso.tech/standards/generic-standards/lsp1-universal-receiver) | +| `LSP1UniversalReceiverDelegate` | [LSP-1: Universal Receiver Delegate](https://docs.lukso.tech/standards/universal-profile/lsp1-universal-receiver-delegate) | +| `LSP6KeyManager` | [LSP-6: Key Manager](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager) | +| `LSP7DigitalAsset` | [LSP-7: Digital Asset](https://docs.lukso.tech/standards/nft-2.0/LSP7-Digital-Asset) | +| `LSP8IdentifiableDigitalAsset` | [LSP-8: Identifiable Digital Asset](https://docs.lukso.tech/standards/nft-2.0/LSP8-Identifiable-Digital-Asset) | +| `LSP9Vault` | [LSP-9: Vault](https://docs.lukso.tech/standards/universal-profile/lsp9-vault) | + +:::info + +The `interfaceName` will only check for the latest version of the standard's `interfaceID`, which can be found in `src/constants/interfaces`. For LSPs, the `interfaceIDs` are taken from the latest release of the [@lukso/lsp-smart-contracts](https://github.com/lukso-network/lsp-smart-contracts) library. + +::: + +##### 2. `options` - Object (optional) + +On non instantiated class, you should provide an `options` object. + +| Name | Type | Description | +| :-------- | :----- | :------------------------------------------------------------------- | +| `address` | string | Address of the smart contract to check against a certain interface. | +| `rpcUrl` | string | RPC URL to connect to the network the smart contract is deployed to. | +| `gas` | number | Optional: gas parameter to use. Default: 1_000_000. | + +#### Returns + +| Type | Description | +| :----------------- | :------------------------------------------------------------ | +| `Promise` | Returns `true` if the interface was found, otherwise `false`. | + +#### Examples + +```javascript title="By using the interface ID" +myErc725.supportsInterface('0xfd4d5c50'); +// true + +ERC725.supportsInterface('0xfd4d5c50', { + address: '0xe408BDDbBAB1985006A2c481700DD473F932e5cB', + rpcUrl: 'https://rpc.testnet.lukso.network', +}); +// false +``` + +```javascript title="By using interface name" +myErc725.supportsInterface('LSP0ERC725Account'); +// false + +ERC725.supportsInterface('LSP0ERC725Account', { + address: '0x0Dc07C77985fE31996Ed612F568eb441afe5768D', + rpcUrl: 'https://rpc.testnet.lukso.network', + gas: 20_000_000, +}); +// true +``` + +--- + +## External Data Source utilities (`VerifiableURI` and `JSONURI`) + +### encodeDataSourceWithHash + +```js +const myErc725 = new ERC725(); +myErc725.encodeDataSourceWithHash(verification, dataSource); +``` + +OR + +```js +ERC725.encodeDataSourceWithHash(verification, dataSource); +``` + +Encode a verifiableURI providing the hashing function of the json file (method), the hash of the json file (data) and the url where the json file is stored. + +#### Parameters + +| Name | Type | Description | +| :------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- | +| `verification` | `undefined` or `Verification` | Verification is an object containing the hashing function of the json file (method) and the hash of the json file (data) | +| `dataSource` | `string` | The url where the json file is stored. | + +
+ Types details + +```js +interface Verification { + method: SUPPORTED_VERIFICATION_METHODS | string; + data: string; + source?: string; +} + +type SUPPORTED_VERIFICATION_METHODS = + | SUPPORTED_VERIFICATION_METHOD_STRINGS + | SUPPORTED_VERIFICATION_METHOD_HASHES; + +enum SUPPORTED_VERIFICATION_METHOD_STRINGS { + KECCAK256_UTF8 = 'keccak256(utf8)', + KECCAK256_BYTES = 'keccak256(bytes)', +} + +enum SUPPORTED_VERIFICATION_METHOD_HASHES { + HASH_KECCAK256_UTF8 = '0x6f357c6a', + HASH_KECCAK256_BYTES = '0x8019f9b1', +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :-------------- | :----- | :---------------- | +| `verifiableURI` | string | The verifiableURI | + +#### Examples + +
+ Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL + +```javascript title="Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL" +const verifiableURI = myErc725.encodeDataSourceWithHash( + { + method: 'keccak256(utf8)', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + }, + 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', +); +/** +0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178 +*/ +``` + +
+ +### decodeDataSourceWithHash + +```js +const myErc725 = new ERC725(); +myErc725.decodeDataSourceWithHash(verifiableURI); +``` + +```js +ERC725.decodeDataSourceWithHash(verifiableURI); +``` + +Decode a verifiableURI into the hash function of the json file, the hash of the json file and the url where the json file is stored. + +#### Parameters + +| Name | Type | Description | +| :-------------- | :------- | :---------------- | +| `verifiableURI` | `string` | The verifiableURI | + +#### Returns + +| Name | Type | Description | +| :--------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------- | +| `decodedVerifiableURI` | `URLDataWithHash` | Object containing the hash function, the hash of the JSON file and the link where the json file is stored. | + +
+ Types details + +```js +interface URLDataWithHash { + verification: Verification; + url: string +} + +interface Verification { +method: SUPPORTED_VERIFICATION_METHODS | string; +data: string; +source?: string; +} + +type SUPPORTED_VERIFICATION_METHODS = +| SUPPORTED_VERIFICATION_METHOD_STRINGS +| SUPPORTED_VERIFICATION_METHOD_HASHES; + +enum SUPPORTED_VERIFICATION_METHOD_STRINGS { +KECCAK256_UTF8 = 'keccak256(utf8)', +KECCAK256_BYTES = 'keccak256(bytes)', +} + +enum SUPPORTED_VERIFICATION_METHOD_HASHES { +HASH_KECCAK256_UTF8 = '0x6f357c6a', +HASH_KECCAK256_BYTES = '0x8019f9b1', +} + +``` + +
+ +#### Examples + +
+ Decode a VerifiableURI + +```javascript title="Decode a VerifiableURI" +const decodedVerifiableURI = myErc725.decodeDataSourceWithHash( + '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', +); +/** +verification: { + data: '820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + method: 'keccak256(utf8)', + } +url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx' +*/ +``` + +
+ +### getVerificationMethod + +```js +const myErc725 = new ERC725(); +myErc725.getVerificationMethod(nameOrSig); +``` + +```js +ERC725.getVerificationMethod(nameOrSig); +``` + +```js +import { getVerificationMethod } from '@erc725/erc725.js'; +getVerificationMethod(nameOrSig); +``` + +Get the verification method definition, including the name, signature and related method. + +method: (data: string | object | Uint8Array | null) => string; +name: SUPPORTED_VERIFICATION_METHOD_STRINGS; +sig: SUPPORTED_VERIFICATION_METHODS; + +#### Parameters + +| Name | Type | Description | +| :---------- | :----- | :------------------------------------------ | +| `nameOrSig` | string | The 4 bytes hex of the verification method. | + +#### Returns + +| Name | Type | Description | +| :--- | :----- | :-------------------------------------------------------------------------------------- | +| | object | An object containing the name, signature and method related to the verification method. | + +#### Example + +```javascript title="Example of the method" +getVerificationMethod('0x6f357c6a'); +/* +{ + method: [Function: keccak256Method], + name: 'keccak256(utf8)', + sig: '0x6f357c6a' +} +*/ +``` + +### isDataAuthentic + +```js +const myErc725 = new ERC725(); +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +import { isDataAuthentic } from '@erc725/erc725.js'; + +isDataAuthentic(data, verificationOptions); +``` + +Hashes the `data` passed as parameter using the specified hashing functions (available under `method` in the `verificationOption` object) and compares the result with the provided hash. + +:::info +This method will console an error if the hash provided as `data` and the expected hash obtained using the verification method do not match. +::: + +#### Parameters + +| Name | Type | Description | +| :-------------------- | :----------------------- | :---------------------------------- | +| `data` | `string` or `Uint8Array` | The data to be hashed and verified. | +| `verificationOptions` | `Verification` | An object as defined below | + +
+ Types details + +```js + KECCAK256_UTF8 = , + KECCAK256_BYTES = , + HASH_KECCAK256_UTF8 = , + HASH_KECCAK256_BYTES = , + +export interface Verification { + data: string; + method: 'keccak256(utf8)' | 'keccak256(bytes)' | '0x6f357c6a' | '0x8019f9b1' | string; + source?: string; +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :--- | :-------- | :-------------------------------------------------------------------------------------------- | +| | `boolean` | `true` if the data is authentic according to the verification method used, `false` otherwise. | + +#### Example + +
+ JSON data to verify from data.json + +```json +[ + { + "name": "LSP3Profile", + "key": "0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5", + "value": { + "LSP3Profile": { + "name": "test", + "description": "", + "tags": ["profile"], + "links": [], + "profileImage": [ + { + "width": 1024, + "height": 709, + "verification": { + "method": "keccak256(bytes)", + "data": "0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c" + }, + "url": "ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C" + }, + { + "width": 640, + "height": 443, + "verification": { + "method": "keccak256(bytes)", + "data": "0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82" + }, + "url": "ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW" + }, + { + "width": 320, + "height": 221, + "verification": { + "method": "keccak256(bytes)", + "data": "0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5" + }, + "url": "ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH" + }, + { + "width": 180, + "height": 124, + "verification": { + "method": "keccak256(bytes)", + "data": "0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810" + }, + "url": "ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ" + } + ], + "backgroundImage": [ + { + "width": 1800, + "height": 1012, + "verification": { + "method": "keccak256(bytes)", + "data": "0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8" + }, + "url": "ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n" + }, + { + "width": 1024, + "height": 576, + "verification": { + "method": "keccak256(bytes)", + "data": "0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f" + }, + "url": "ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9" + }, + { + "width": 640, + "height": 360, + "verification": { + "method": "keccak256(bytes)", + "data": "0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967" + }, + "url": "ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV" + }, + { + "width": 320, + "height": 180, + "verification": { + "method": "keccak256(bytes)", + "data": "0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509" + }, + "url": "ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz" + }, + { + "width": 180, + "height": 101, + "verification": { + "method": "keccak256(bytes)", + "data": "0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475" + }, + "url": "ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm" + } + ] + } + } + } +] +``` + +
+ +```typescript title="isDataAuthentic example" +import jsonData from './data.json'; + +isDataAuthentic(jsonData, { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', +}); +// true + +isDataAuthentic(jsonData, { + data: '0xdeadbeef00000000000000000000000000000000000000000000000000000000', + method: 'keccak256(utf8)', +}); +// false +``` + +--- + +## Permissions + +### checkPermissions + +```js +myErc725.checkPermissions(requiredPermissions, grantedPermissions); +``` + +```js +ERC725.checkPermissions(requiredPermissions, grantedPermissions); +``` + +Check if the required permissions are included in the granted permissions as defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). + +:::info + +`checkPermissions` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. + +::: + +#### Parameters + +##### 1. `requiredPermissions` - String[] | String + +An array of required permissions or a single required permission. (32bytes hex or the official name of the permission). + +##### 2. `grantedPermissions` - String + +The granted permissions. (32bytes hex). + +#### Returns + +| Type | Description | +| :------ | :----------------------------------------------------------------------------------------------------------------------------------------------- | +| boolean | A boolean value indicating whether the required permissions are included in the granted permissions as defined by the [LSP6 KeyManager Standard] | + +#### Permission-Name Example + +```javascript title="Checking permissions by name" +const requiredPermissions = 'CHANGEOWNER'; +const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; +ERC725.checkPermissions(requiredPermissions, grantedPermissions); +// true + +// This method is also available on the instance: + +const requiredPermissions = ['CHANGEOWNER', 'CALL']; +const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; +myErc725.checkPermissions(requiredPermissions, grantedPermissions); +// false +``` + +#### 32bytes hex Example + +```javascript title="Checking permissions by 32bytes hex" +const requiredPermissions = [ + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000000000000000000000000000800', +]; +const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + +ERC725.checkPermissions(requiredPermissions, grantedPermissions); +// false + +// This method is also available on the instance: -```javascript title="Receiving the owner address" -// If no address is set, it will return the owner of the contract used to initialize the ERC725() class. -await myErc725.getOwner(); -// '0x94933413384997F9402cc07a650e8A34d60F437A' +const requiredPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000001'; +const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; -// You can also get the owner of a specific contract by setting the address parameter -await myErc725.getOwner('0x3000783905Cc7170cCCe49a4112Deda952DDBe24'); -// '0x7f1b797b2Ba023Da2482654b50724e92EB5a7091' +myErc725.checkPermissions(requiredPermissions, grantedPermissions); +// true ``` --- -## getSchema +### encodePermissions ```js -myErc725.getSchema(keys [, providedSchemas]); +ERC725.encodePermissions(permissions); ``` -Parses a hashed key or a list of hashed keys and will attempt to return its corresponding [LSP2 ERC725YJSONSchema](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md) object. Additionally, it will look for a corresponding key within the schemas: +Encodes permissions into a hexadecimal string as defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). -- in the [`schemas`](https://github.com/ERC725Alliance/myErc725.js/tree/main/schemas) folder (which includes all [LSPs](https://github.com/lukso-network/LIPs/tree/main/LSPs)), -- that were provided at ERC725 initialization, and -- that were provided in the function call (`providedSchemas`). +:::info -#### Parameters +`encodePermissions` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. -##### 1. `keys` - String or array of Strings +::: -The key(s) you are trying to get the schema for. +#### Parameters -##### 2. `providedSchemas` - Object (optional) +##### 1. `permissions` - Object -An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the schema. +An object with [LSP6 KeyManager Permissions] as keys and a `boolean` as value. Any omitted permissions will default to `false`. #### Returns -| Name | Type | Description | -| :------- | :--------------- | :-------------------------------------------------------------------- | -| `result` | ERC725JSONSchema | If the parameter `keys` is a string and the schema was found. | -| `result` | Record string | If the parameter `keys` is a string[ ] and the schema was found. | -| `result` | null | If the schema was not found. | +| Type | Description | +| :----- | :---------------------------------------------------------------------------------------- | +| string | The permissions encoded as a hexadecimal string defined by the [LSP6 KeyManager Standard] | -### Example using a predefined LSP3 schema +#### Example -```javascript title="Parsing the hashed key from the LSP3 schema" -myErc725.getSchema( - '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', -); -/** -{ - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - keyType: 'Singleton', - valueContent: 'VerifiableURI', - valueType: 'bytes' -} -*/ -myErc725.getSchema([ - '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', -]); -/** -{ - '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5': { - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - keyType: 'Singleton', - valueContent: 'VerifiableURI', - valueType: 'bytes' - }, - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001': { - name: 'LSP12IssuedAssets[1]', - key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - keyType: 'Singleton', - valueContent: 'Address', - valueType: 'address' - } -} -*/ -``` +```javascript title="Encoding permissions" +ERC725.encodePermissions({ + CHANGEOWNER: false, + ADDCONTROLLER: false, + EDITPERMISSIONS: false, + ADDEXTENSIONS: false, + CHANGEEXTENSIONS: true, + ADDUNIVERSALRECEIVERDELEGATE: false, + CHANGEUNIVERSALRECEIVERDELEGATE: false, + REENTRANCY: false, + SUPER_TRANSFERVALUE: true, + TRANSFERVALUE: true, + SUPER_CALL: false, + CALL: true, + SUPER_STATICCALL: false, + STATICCALL: false, + SUPER_DELEGATECALL: false, + DELEGATECALL: false, + DEPLOY: false, + SUPER_SETDATA: false, + SETDATA: false, + ENCRYPT: false, + DECRYPT: false, + SIGN: false, + EXECUTE_RELAY_CALL: false, + ERC4337_PERMISSION: false +}), +// '0x0000000000000000000000000000000000000000000000000000000000000110' -### Example using a custom schema +// Any omitted Permissions will default to false +ERC725.encodePermissions({ + ADDCONTROLLER: true, + ADDEXTENSIONS: true, +}), +// '0x000000000000000000000000000000000000000000000000000000000000000a' +ERC725.encodePermissions({ + EDITPERMISSIONS: true, + CHANGEEXTENSIONS: true, + CHANGEUNIVERSALRECEIVERDELEGATE: true, + SETDATA: true, +}), +// '0x0000000000000000000000000000000000000000000000000000000000040054' -```javascript title="Parsing the hashed key from a custom schema" -myErc725.getSchema( - '0x777f55baf2e0c9f73d3bb456dfb8dbf6e609bf557969e3184c17ff925b3c402c', - [ - { - name: 'ParameterSchema', - key: '0x777f55baf2e0c9f73d3bb456dfb8dbf6e609bf557969e3184c17ff925b3c402c', - keyType: 'Singleton', - valueContent: 'VerifiableURI', - valueType: 'bytes', - }, - ], -); -/** -{ - name: 'ParameterSchema', - key: '0x777f55baf2e0c9f73d3bb456dfb8dbf6e609bf557969e3184c17ff925b3c402c', - keyType: 'Singleton', - valueContent: 'VerifiableURI', - valueType: 'bytes', -} -*/ + +// This method is also available on the instance: +myErc725.encodePermissions({ + EDITPERMISSIONS: true, + SETDATA: true, +}), ``` --- -## isValidSignature +### decodePermissions ```js -myErc725.isValidSignature(messageOrHash, signature); +ERC725.decodePermissions(permission); ``` -Checks if a signature was signed by the `owner` of the ERC725 Account contract, according to [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271). If the `owner` is a contract itself, it will delegate the `isValidsignature()` call to the owner contract if it supports [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271). Otherwise, it will fail. - -#### Parameters - -##### 1. `messageOrHash` - String - -Value of a message or hash that needs to be verified. - -##### 2. `signature` - String - -The raw RLP encoded signature. +Decodes permissions from hexadecimal defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). :::info -- The hash must be 66 chars long with the `0x` prefix. Otherwise, it will be interpreted as message. -- The message will be: enveloped as `"\x19Ethereum Signed Message:\n" + message.length + message` and hashed using `keccak256` function. - The signature can be generated with [`web3.eth.accounts.sign()`](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-accounts.html#sign). +`decodePermissions` is available as either a static or non-static method, so it can be called without instantiating an ERC725 object. ::: -#### Returns +#### Parameters -| Name | Type | Description | -| :-------- | :------ | :------------------------------------------------------------- | -| `Promise` | boolean | `true` if signature is valid, `false` if signature is invalid. | +##### 1. `permission` - String -:::info +The encoded permission (32bytes hex). -- A valid signature means that the smart contract response IS the MAGICVALUE: `0x1626ba7e`. -- If this function is called on a contract which does not support [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271), it will throw an error. +#### Returns -::: +| Name | Type | Description | +| :------------------- | :----- | :------------------------------------------------------------------------------------------------- | +| `decodedPermissions` | Object | An object specifying whether default LSP6 permissions are included in provided hexademical string. | -#### Examples +#### Example -```javascript title="Checking the signature with a message" -await myErc725.isValidSignature( - 'hello', - '0xb91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c', -); -// true -``` +```javascript title="Decoding permissions" +ERC725.decodePermissions('0x0000000000000000000000000000000000000000000000000000000000000110'), +/** +{ + CHANGEOWNER: false, + EDITPERMISSIONS: false, + ADDCONTROLLER: false, + SETDATA: false, + CALL: true, + STATICCALL: false, + DELEGATECALL: false, + DEPLOY: false, + TRANSFERVALUE: true, + SIGN: false, +} +*/ -```javascript title="Checking the signature with a hash" -await myErc725.isValidSignature( - '0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655', - '0xcafecafeb915466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c', -); -// false +ERC725.decodePermissions('0x000000000000000000000000000000000000000000000000000000000000000a'), +/** +{ + CHANGEOWNER: false, + EDITPERMISSIONS: true, + ADDCONTROLLER: false, + SETDATA: true, + CALL: false, + STATICCALL: false, + DELEGATECALL: false, + DEPLOY: false, + TRANSFERVALUE: false, + SIGN: false, +} +*/ + +// This method is also available on the instance: +myErc725.decodePermissions('0x0000000000000000000000000000000000000000000000000000000000000110'), ``` -[lsp6 keymanager permissions]: ../../../../../standards/universal-profile/lsp6-key-manager#permissions -[lsp6 keymanager standard]: https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager -[lsp-2 erc725yjsonschema]: https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md +--- -## supportsInterface +### mapPermission ```js -myERC725.supportsInterface(interfaceIdOrName); +const erc725 = new ERC725(); +erc725.mapPermission(permissionName); ``` ```js -ERC725.supportsInterface(interfaceIdOrName, options); +ERC725.mapPermission(permissionName); ``` -You can use this function if you need to check if the ERC725 object or a smart contract supports a specific interface (by ID or name). When you use the function on your instantiated ERC725 class, it will use the contract address and provider provided at instantiation. On a non-instantiated class, you need to specify them in the `options` parameter. - -:::caution -The `interfaceId` is not the most secure way to check for a standard, as they could be set manually. -::: - -#### Parameters - -##### 1. `interfaceIdOrName` - String - -Either a string of the hexadecimal `interfaceID` as defined by [ERC165](https://eips.ethereum.org/EIPS/eip-165) or one of the predefined interface names: +```js +import { mapPermission } from '@erc725/erc725.js'; -| interfaceName | Standard | -| :------------------------------ | :------------------------------------------------------------------------------------------------------------------------- | -| `ERC1271` | [EIP-1271: Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271) | -| `ERC725X` | [EIP-725: General execution standard](https://eips.ethereum.org/EIPS/eip-725) | -| `ERC725Y` | [EIP-725: General key-value store](https://eips.ethereum.org/EIPS/eip-725) | -| `LSP0ERC725Account` | [LSP-0: ERC725 Account](https://docs.lukso.tech/standards/universal-profile/lsp0-erc725account) | -| `LSP1UniversalReceiver` | [LSP-1: Universal Receiver](https://docs.lukso.tech/standards/generic-standards/lsp1-universal-receiver) | -| `LSP1UniversalReceiverDelegate` | [LSP-1: Universal Receiver Delegate](https://docs.lukso.tech/standards/universal-profile/lsp1-universal-receiver-delegate) | -| `LSP6KeyManager` | [LSP-6: Key Manager](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager) | -| `LSP7DigitalAsset` | [LSP-7: Digital Asset](https://docs.lukso.tech/standards/nft-2.0/LSP7-Digital-Asset) | -| `LSP8IdentifiableDigitalAsset` | [LSP-8: Identifiable Digital Asset](https://docs.lukso.tech/standards/nft-2.0/LSP8-Identifiable-Digital-Asset) | -| `LSP9Vault` | [LSP-9: Vault](https://docs.lukso.tech/standards/universal-profile/lsp9-vault) | +mapPermission(permissionName); +``` -:::info +/\*\* -The `interfaceName` will only check for the latest version of the standard's `interfaceID`, which can be found in `src/constants/interfaces`. For LSPs, the `interfaceIDs` are taken from the latest release of the [@lukso/lsp-smart-contracts](https://github.com/lukso-network/lsp-smart-contracts) library. +Return the bytes32 representation of an LSP6 permission from its name. +:::tip +When using the `mapPermission` method, if the permission is not known, the function will return `null`. ::: -##### 2. `options` - Object (optional) - -On non instantiated class, you should provide an `options` object. +#### Parameters -| Name | Type | Description | -| :-------- | :----- | :------------------------------------------------------------------- | -| `address` | string | Address of the smart contract to check against a certain interface. | -| `rpcUrl` | string | RPC URL to connect to the network the smart contract is deployed to. | -| `gas` | number | Optional: gas parameter to use. Default: 1_000_000. | +| Name | Type | Description | +| :--------------- | :------- | :------------------------------------------------------------------------------------------------ | +| `permissionName` | `string` | The name of the permission to return the associated `bytes32` value (\_e.g: `SETDATA`, `DEPLOY`). | #### Returns -| Type | Description | -| :----------------- | :------------------------------------------------------------ | -| `Promise` | Returns `true` if the interface was found, otherwise `false`. | - -#### Examples +| Name | Type | Description | +| :--- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | +| | `string` or `null` | A bytes32 hex string representing the `permissionName`. Or `null` if the input is not a known permission name or a valid 32-byte hex string. | -```javascript title="By using the interface ID" -myErc725.supportsInterface('0xfd4d5c50'); -// true +### Example -ERC725.supportsInterface('0xfd4d5c50', { - address: '0xe408BDDbBAB1985006A2c481700DD473F932e5cB', - rpcUrl: 'https://rpc.testnet.lukso.network', -}); -// false -``` +```javascript title="mapPermission example" +erc725js.mapPermissions('CHANGEOWNER'); +// 0x0000000000000000000000000000000000000000000000000000000000000001 -```javascript title="By using interface name" -myErc725.supportsInterface('LSP0ERC725Account'); -// false +erc725js.mapPermissions('SETDATA'); +// 0x0000000000000000000000000000000000000000000000000000000000040000 -ERC725.supportsInterface('LSP0ERC725Account', { - address: '0x0Dc07C77985fE31996Ed612F568eb441afe5768D', - rpcUrl: 'https://rpc.testnet.lukso.network', - gas: 20_000_000, -}); -// true +erc725js.mapPermissions('DEPLOY'); +// 0x0000000000000000000000000000000000000000000000000000000000010000 ``` From e3364f45c8e91aca2648f1f2cc8dbb11bd2afc35 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 16 May 2024 14:35:15 +0100 Subject: [PATCH 02/25] docs: fix broken links --- docs/methods.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/methods.md b/docs/methods.md index a33d6efe..69f7387d 100644 --- a/docs/methods.md +++ b/docs/methods.md @@ -619,7 +619,7 @@ ERC725.decodeData(data, schemas); If you are reading the key-value store from an ERC725 smart contract you can use the `decodeData` function to do the decoding for you. :::tip -If you want total convenience, it is recommended to use the [`fetchData`](ERC725.md#fetchdata) function, which automatically `decodes` and `fetches` external references. +If you want total convenience, it is recommended to use the [`fetchData`](#fetchdata) function, which automatically `decodes` and `fetches` external references. ::: #### Parameters @@ -1086,9 +1086,9 @@ The name(s) (or the encoded name(s) as schema key) of the element(s) in the smar #### Returns -| Name | Type | Description | -| :--- | :------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| data | `Promise` or
`Promise` | An array with same objects as for [`decodeData()`](./ERC725#decodedata) function but with the value being replaced by the actual file for `VerifiableURI` valueContent. If there is a hash mismatch, the value will be `null`. | +| Name | Type | Description | +| :--- | :------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| data | `Promise` or
`Promise` | An array with same objects as for [`decodeData()`](#decodedata) function but with the value being replaced by the actual file for `VerifiableURI` valueContent. If there is a hash mismatch, the value will be `null`. | :::info @@ -1234,7 +1234,7 @@ When omitting the `keys` parameter, it will give back every key (as per `ERC725J :::caution - Data returned by this function does not contain external data of [`VerifiableURI`](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#verifiableuri) schema elements. -- If you would like to receive everything in one go, you can use [`fetchData`](ERC725.md#fetchdata). +- If you would like to receive everything in one go, you can use [`fetchData`](#fetchdata). ::: @@ -1251,9 +1251,9 @@ The name(s) (or the encoded name(s) as schema key) of the element(s) in the smar #### Returns -| Name | Type | Description | -| :--- | :------------------------------------------ | :------------------------------------------------------------------------------------ | -| data | `Promise` or
`Promise` | An array with the same objects as for [`decodeData()`](./ERC725#decodedata) function. | +| Name | Type | Description | +| :--- | :------------------------------------------ | :---------------------------------------------------------------------------- | +| data | `Promise` or
`Promise` | An array with the same objects as for [`decodeData()`](#decodedata) function. | :::info From f6b4a9df803f1c63d53067ca3b67604219502cda Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 16 May 2024 16:37:28 +0100 Subject: [PATCH 03/25] feat: allow to pass `number` directly in array when encoding tuples --- src/index.test.ts | 28 ++++++++++++++++++++++++++++ src/types/encodeData/JSONURL.ts | 3 ++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/index.test.ts b/src/index.test.ts index 25e2c9ca..fb320fbd 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -937,6 +937,34 @@ describe('Running @erc725/erc725.js tests...', () => { }); }); }); + + describe('for tuples', () => { + it('encode `(bytes4,uint128)`', () => { + const schema = { + name: 'LSP4CreatorsMap:
', + key: '0x6de85eaf5d982b4e5da00000
', + keyType: 'Mapping', + valueType: '(bytes4,uint128)', + valueContent: '(Bytes4,Number)', + }; + + const expectedEncodedValue = + '0xb3c4928f00000000000000000000000000000005'; + + const result = ERC725.encodeData( + [ + { + keyName: 'LSP4CreatorsMap:
', + dynamicKeyParts: address, + value: ['0xb3c4928f', 5], + }, + ], + [schema], + ); + + assert.equal(result.values[0], expectedEncodedValue); + }); + }); }); describe('Testing utility encoding & decoding functions', () => { diff --git a/src/types/encodeData/JSONURL.ts b/src/types/encodeData/JSONURL.ts index 440e6f72..6680c39a 100644 --- a/src/types/encodeData/JSONURL.ts +++ b/src/types/encodeData/JSONURL.ts @@ -32,7 +32,8 @@ export type EncodeDataType = | string[] | URLDataToEncode | boolean - | number; + | number + | (string | number)[]; // for tuples such as `(bytes4,uint128)` export interface EncodeDataReturn { keys: string[]; From 8b860926f6a8e5a733c3d01321bf888a19b79f12 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 16 May 2024 16:47:18 +0100 Subject: [PATCH 04/25] feat: return `dynamicKeyName` and `dynamicKeyPart` for `getSchema` with Array --- src/lib/schemaParser.test.ts | 10 +++++++--- src/lib/schemaParser.ts | 11 +++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib/schemaParser.test.ts b/src/lib/schemaParser.test.ts index 58960c7a..65bdfd0e 100644 --- a/src/lib/schemaParser.test.ts +++ b/src/lib/schemaParser.test.ts @@ -34,7 +34,7 @@ describe('schemaParser getSchema', () => { }); }); - describe('Array', () => { + describe.only('Array', () => { it('finds initial key of type Array correctly', () => { const schema = getSchema( '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', @@ -56,15 +56,19 @@ describe('schemaParser getSchema', () => { assert.deepStrictEqual(schema, { '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001': { - name: 'LSP12IssuedAssets[1]', + name: 'LSP12IssuedAssets[]', key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', + dynamicName: 'LSP12IssuedAssets[1]', + dynamicKeyPart: '0x00000000000000000000000000000001', keyType: 'Singleton', valueContent: 'Address', valueType: 'address', }, '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000000': { - name: 'AddressPermissions[0]', + name: 'AddressPermissions[]', key: '0xdf30dba06db6a30e65354d9a64c6098600000000000000000000000000000000', + dynamicName: 'AddressPermissions[0]', + dynamicKeyPart: '0x00000000000000000000000000000000', keyType: 'Singleton', valueContent: 'Address', valueType: 'address', diff --git a/src/lib/schemaParser.ts b/src/lib/schemaParser.ts index fee9f607..ea49488d 100644 --- a/src/lib/schemaParser.ts +++ b/src/lib/schemaParser.ts @@ -102,7 +102,7 @@ const findSingletonSchemaForKey = ( const findArraySchemaForKey = ( key: string, schemas: ERC725JSONSchema[], -): ERC725JSONSchema | null => { +): ERC725JSONSchema | DynamicNameSchema | null => { // Should detect: // 1. Initial key @@ -132,7 +132,9 @@ const findArraySchemaForKey = ( return { ...arraySchema, key, - name: arraySchema.name.replace('[]', `[${elementIndex}]`), + dynamicName: arraySchema.name.replace('[]', `[${elementIndex}]`), + dynamicKeyPart: `0x${key.substring(34)}`, + name: arraySchema.name, keyType: 'Singleton', }; }; @@ -178,8 +180,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}`; } From 5217fba910295f3a66fb5a282054c38b47f4f669 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 16 May 2024 16:48:39 +0100 Subject: [PATCH 05/25] chore: replace incorrect reference `LSP3IssuedAssets[]` -> `LSP12IssuedAssets[]` --- src/lib/encodeKeyName.test.ts | 4 ++-- src/lib/utils.test.ts | 36 +++++++++++++++++------------------ test/mockSchema.ts | 4 ++-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lib/encodeKeyName.test.ts b/src/lib/encodeKeyName.test.ts index 31e44922..05dfa044 100644 --- a/src/lib/encodeKeyName.test.ts +++ b/src/lib/encodeKeyName.test.ts @@ -38,9 +38,9 @@ describe('encodeKeyName', () => { expectedKey: keccak256('MyKeyName'), }, { - keyName: 'LSP3IssuedAssets[]', + keyName: 'LSP12IssuedAssets[]', expectedKey: - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', }, { keyName: 'SupportedStandards:LSP3Profile', diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 3f994707..a84a7772 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -49,8 +49,8 @@ describe('utils', () => { // test encoding an array of address { schema: { - name: 'LSP3IssuedAssets[]', - key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', valueType: 'address', @@ -61,7 +61,7 @@ describe('utils', () => { ], encodedValue: [ { - key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', value: '0x00000000000000000000000000000002', }, { @@ -239,8 +239,8 @@ describe('utils', () => { it('should encode the array length only if passing a number', async () => { const schema: ERC725JSONSchema = { - name: 'LSP3IssuedAssets[]', - key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', valueType: 'address', @@ -438,8 +438,8 @@ describe('utils', () => { describe('encodeArrayKey', () => { it('should encode the array length only if passing a number', async () => { const schema: ERC725JSONSchema = { - name: 'LSP3IssuedAssets[]', - key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', valueType: 'address', @@ -455,8 +455,8 @@ describe('utils', () => { describe('encodeData', () => { const schemas: ERC725JSONSchema[] = [ { - name: 'LSP3IssuedAssets[]', - key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', valueType: 'address', @@ -550,7 +550,7 @@ describe('utils', () => { const encodedDataWithMultipleKeys = encodeData( [ { - keyName: 'LSP3IssuedAssets[]', + keyName: 'LSP12IssuedAssets[]', value: ['0xa3e6F38477D45727F6e6f853Cdb479b0D60c0aC9'], }, ], @@ -559,7 +559,7 @@ describe('utils', () => { assert.deepStrictEqual(encodedDataWithMultipleKeys, { keys: [ - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', ], values: [ @@ -586,7 +586,7 @@ describe('utils', () => { const encodedDataWithMultipleKeys = encodeData( [ { - keyName: 'LSP3IssuedAssets[]', + keyName: 'LSP12IssuedAssets[]', value: addressArray, }, ], @@ -595,7 +595,7 @@ describe('utils', () => { assert.deepStrictEqual(encodedDataWithMultipleKeys, { keys: [ - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000002', @@ -618,7 +618,7 @@ describe('utils', () => { const encodedArrayLengthKey = encodeData( [ { - keyName: 'LSP3IssuedAssets[]', + keyName: 'LSP12IssuedAssets[]', value: length, }, ], @@ -627,7 +627,7 @@ describe('utils', () => { assert.deepStrictEqual(encodedArrayLengthKey, { keys: [ - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', ], values: ['0x00000000000000000000000000000005'], }); @@ -647,7 +647,7 @@ describe('utils', () => { }, }, { - keyName: 'LSP3IssuedAssets[]', + keyName: 'LSP12IssuedAssets[]', value: [ '0xD94353D9B005B3c0A9Da169b768a31C57844e490', '0xDaea594E385Fc724449E3118B2Db7E86dFBa1826', @@ -664,7 +664,7 @@ describe('utils', () => { assert.deepStrictEqual(encodedMultipleKeys, { keys: [ '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', @@ -921,7 +921,7 @@ describe('utils', () => { }, { keyType: 'Array', - keyName: 'LSP3IssuedAssets[]', + keyName: 'LSP12IssuedAssets[]', }, { keyType: 'Mapping', diff --git a/test/mockSchema.ts b/test/mockSchema.ts index 485b778e..52174495 100644 --- a/test/mockSchema.ts +++ b/test/mockSchema.ts @@ -181,8 +181,8 @@ export const mockSchema: (ERC725JSONSchema & { // Case 9 { - name: 'LSP3IssuedAssets[]', - key: '0x3a47ab5bd3a594c3a8995f8fa58d0876c96819ca4516bd76100c92462f2f9dc0', + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', keyType: 'Array', valueContent: 'Address', valueType: 'address', From a36f69478d7131ad33c2aedce876c92c6cb12a67 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Thu, 16 May 2024 20:54:56 +0100 Subject: [PATCH 06/25] test: add tests for encoding array index --- src/index.test.ts | 11 +++++++++++ src/lib/encodeKeyName.ts | 16 +++++++++++++--- src/lib/schemaParser.test.ts | 2 +- src/lib/utils.test.ts | 32 ++++++++++++++++---------------- src/types/dynamicKeys.ts | 2 +- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 25e2c9ca..35e30eab 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1880,6 +1880,17 @@ describe('encodeKeyName', () => { '0x31145577efe228036af40000a4fbbfe353124e6fa6bb7f8e088a9269df552ea2', ); }); + + it('works for Array keys with index as `dynamicKeyParts', () => { + assert.deepStrictEqual( + encodeKeyName('MusicPlaylist[]', 2), + '0x03573598507fc76d82171baa336b7fd700000000000000000000000000000002', + ); + assert.deepStrictEqual( + erc725Instance.encodeKeyName('MusicPlaylist[]', 2), + '0x03573598507fc76d82171baa336b7fd700000000000000000000000000000002', + ); + }); }); describe('supportsInterface', () => { diff --git a/src/lib/encodeKeyName.ts b/src/lib/encodeKeyName.ts index 7cf4c0da..68c8543d 100644 --- a/src/lib/encodeKeyName.ts +++ b/src/lib/encodeKeyName.ts @@ -26,7 +26,7 @@ import { padLeft, } from 'web3-utils'; -import { guessKeyTypeFromKeyName } from './utils'; +import { encodeArrayKey, guessKeyTypeFromKeyName } from './utils'; import { DynamicKeyParts } from '../types/dynamicKeys'; // https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md#mapping @@ -248,9 +248,12 @@ function encodeDynamicKeyName( switch (keyType) { case 'Mapping': - return encodeDynamicMapping(name, dynamicKeyPartsArray); + return encodeDynamicMapping(name, dynamicKeyPartsArray as string[]); case 'MappingWithGrouping': - return encodeDynamicMappingWithGrouping(name, dynamicKeyPartsArray); + return encodeDynamicMappingWithGrouping( + name, + dynamicKeyPartsArray as string[], + ); default: throw new Error( `Could not encode dynamic key: ${name} of type: ${keyType}`, @@ -295,6 +298,13 @@ export function encodeKeyName(name: string, dynamicKeyParts?: DynamicKeyParts) { ]); } case 'Array': // Warning: this can not correctly encode subsequent keys of array, only the initial Array key will work + // encode for array index + if (dynamicKeyParts && typeof dynamicKeyParts == 'number') { + return encodeArrayKey(keccak256(name), dynamicKeyParts); + } + + // encode for array length + return keccak256(name); case 'Singleton': return keccak256(name); default: diff --git a/src/lib/schemaParser.test.ts b/src/lib/schemaParser.test.ts index 65bdfd0e..dcd8d60f 100644 --- a/src/lib/schemaParser.test.ts +++ b/src/lib/schemaParser.test.ts @@ -34,7 +34,7 @@ describe('schemaParser getSchema', () => { }); }); - describe.only('Array', () => { + describe('Array', () => { it('finds initial key of type Array correctly', () => { const schema = getSchema( '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index a84a7772..50693904 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -65,11 +65,11 @@ describe('utils', () => { value: '0x00000000000000000000000000000002', }, { - key: '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', + key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', value: '0xc444009d38d3046bb0cf81fa2cd295ce46a67c78', }, { - key: '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', + key: '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', value: '0x4febc3491230571f6e1829e46602e3b110215a2e', }, ], @@ -560,7 +560,7 @@ describe('utils', () => { assert.deepStrictEqual(encodedDataWithMultipleKeys, { keys: [ '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', ], values: [ '0x00000000000000000000000000000001', @@ -596,17 +596,17 @@ describe('utils', () => { assert.deepStrictEqual(encodedDataWithMultipleKeys, { keys: [ '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000002', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000003', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000004', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000005', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000006', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000007', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000008', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000009', - '0x3a47ab5bd3a594c3a8995f8fa58d08760000000000000000000000000000000a', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000002', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000003', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000004', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000005', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000006', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000007', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000008', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000009', + '0x7c8c3416d6cda87cd42c71ea1843df280000000000000000000000000000000a', ], values: ['0x0000000000000000000000000000000b', ...addressArray], }); @@ -665,8 +665,8 @@ describe('utils', () => { keys: [ '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000000', - '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000000', + '0x7c8c3416d6cda87cd42c71ea1843df2800000000000000000000000000000001', '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47', ], values: [ diff --git a/src/types/dynamicKeys.ts b/src/types/dynamicKeys.ts index 19db626e..a2ab4b9f 100644 --- a/src/types/dynamicKeys.ts +++ b/src/types/dynamicKeys.ts @@ -2,7 +2,7 @@ import { EncodeDataType } from './encodeData/JSONURL'; -export type DynamicKeyParts = string | string[]; +export type DynamicKeyParts = string | string[] | number; export interface DynamicKeyPartInput { dynamicKeyParts: DynamicKeyParts; From a85adaab7886b14e5e0ace5477a9a29d3ce2c34b Mon Sep 17 00:00:00 2001 From: CJ42 Date: Wed, 15 May 2024 14:56:03 +0100 Subject: [PATCH 07/25] chore: replace incorrect valueType and valueContent in tests --- src/lib/getSchemaElement.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/getSchemaElement.test.ts b/src/lib/getSchemaElement.test.ts index c038d8f3..0469391c 100644 --- a/src/lib/getSchemaElement.test.ts +++ b/src/lib/getSchemaElement.test.ts @@ -42,15 +42,15 @@ describe('getSchemaElement', () => { name: 'LSP12IssuedAssetsMap:
', key: '0x74ac2555c10b9349e78f0000
', keyType: 'Mapping', - valueType: 'bytes', - valueContent: 'Mixed', + valueType: '(bytes4,uint128)', + valueContent: '(Bytes4,Number)', }, { name: 'ARandomKey', key: '0x7cf0c8053453d0353fdbad6a48e68966b35dd13cb3a62e7b75009dc5035b80c0', keyType: 'Singleton', - valueContent: 'JSONURL', valueType: 'bytes', + valueContent: 'JSONURL', }, ]; @@ -71,8 +71,8 @@ describe('getSchemaElement', () => { name: 'LSP12IssuedAssetsMap:2ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', key: '0x74ac2555c10b9349e78f00002ab3903c6e5815f4bc2a95b7f3b22b6a289bacac', keyType: 'Mapping', - valueType: 'bytes', - valueContent: 'Mixed', + valueType: '(bytes4,uint128)', + valueContent: '(Bytes4,Number)', }, ); }); From ef30c6c7cbec192487708ea85e31e0ac3d88fd6a Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 17:25:22 +0100 Subject: [PATCH 08/25] refactor: remove custom method in favour of web3js lib function --- src/lib/detector.ts | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/lib/detector.ts b/src/lib/detector.ts index 1fae16a1..359772ad 100644 --- a/src/lib/detector.ts +++ b/src/lib/detector.ts @@ -21,6 +21,7 @@ * @date 2022 */ +import { isHexStrict } from 'web3-utils'; import { LSP6_DEFAULT_PERMISSIONS } from '../constants/constants'; import { @@ -57,22 +58,6 @@ export const internalSupportsInterface = async ( } }; -/** - * @notice Check if the given string is a valid 32-byte hex string. - * @param str The string to be checked. - * @return A boolean value indicating whether the string is a valid 32-byte hex string. - */ -function isValid32ByteHexString(str: string): boolean { - return ( - str.startsWith('0x') && - str.length === 66 && - str - .slice(2) - .split('') - .every((char) => '0123456789abcdefABCDEF'.includes(char)) - ); -} - /** * @notice Map a permission to its corresponding bytes32 representation. * @param permission The permission string to be mapped. @@ -80,10 +65,7 @@ function isValid32ByteHexString(str: string): boolean { * @dev Throws an error if the input is not a known permission name or a valid 32-byte hex string. */ function mapPermission(permission: string): string { - if ( - !LSP6_DEFAULT_PERMISSIONS[permission] && - !isValid32ByteHexString(permission) - ) { + if (!LSP6_DEFAULT_PERMISSIONS[permission] && !isHexStrict(permission)) { throw new Error( 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', ); @@ -103,7 +85,7 @@ export const checkPermissions = ( grantedPermissions: string, ): boolean => { // Validate the grantedPermissions string - if (!isValid32ByteHexString(grantedPermissions)) { + if (!isHexStrict(grantedPermissions)) { throw new Error( 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', ); From 63016d63997f3559b4b5f32199ad2af1dc4fde06 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 17:25:41 +0100 Subject: [PATCH 09/25] test: add extra test case for encoding number `0` as `uintN` --- src/lib/encoder.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/encoder.test.ts b/src/lib/encoder.test.ts index 4f8d02b5..aa6b62f0 100644 --- a/src/lib/encoder.test.ts +++ b/src/lib/encoder.test.ts @@ -409,6 +409,11 @@ describe('encoder', () => { decodedValue: 11, encodedValue: '0x0000000000000000000000000000000b', }, + { + valueType: 'uint128', + decodedValue: 0, + encodedValue: '0x00000000000000000000000000000000', + }, ]; validTestCases.forEach((testCase) => { From 08e72d61f37d8e3d8b70b5b0bf7cb355687f3546 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 17:47:47 +0100 Subject: [PATCH 10/25] feat: export `mapPermission` function and don't make it throw an error --- src/index.ts | 22 +++++++++++++++++++++- src/lib/detector.ts | 17 ++++++++++------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index d571f779..2f061268 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,7 +67,11 @@ import { encodeValueContent, decodeValueContent, } from './lib/encoder'; -import { internalSupportsInterface, checkPermissions } from './lib/detector'; +import { + internalSupportsInterface, + checkPermissions, + mapPermission, +} from './lib/detector'; import { decodeMappingKey } from './lib/decodeMappingKey'; import { encodePermissions, decodePermissions } from './lib/permissions'; import { AssetURLEncode } from './types/encodeData'; @@ -664,6 +668,22 @@ export class ERC725 { return checkPermissions(requiredPermissions, grantedPermissions); } + static mapPermission(permission: string): string | null { + return mapPermission(permission); + } + + mapPermission(permission: string): string | null { + return mapPermission(permission); + } + + static mapPermission(permission: string): string | null { + return mapPermission(permission); + } + + mapPermission(permission: string): string | null { + return mapPermission(permission); + } + encodeDataSourceWithHash( verification: undefined | Verification, dataSource: string, diff --git a/src/lib/detector.ts b/src/lib/detector.ts index 359772ad..35df9a67 100644 --- a/src/lib/detector.ts +++ b/src/lib/detector.ts @@ -62,13 +62,11 @@ export const internalSupportsInterface = async ( * @notice Map a permission to its corresponding bytes32 representation. * @param permission The permission string to be mapped. * @return The bytes32 representation of the permission. - * @dev Throws an error if the input is not a known permission name or a valid 32-byte hex string. + * @dev Return null if the input is not a known permission name or a valid 32-byte hex string. */ -function mapPermission(permission: string): string { +export function mapPermission(permission: string): string | null { if (!LSP6_DEFAULT_PERMISSIONS[permission] && !isHexStrict(permission)) { - throw new Error( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); + return null; } return LSP6_DEFAULT_PERMISSIONS[permission] || permission; } @@ -97,11 +95,16 @@ export const checkPermissions = ( : [requiredPermissions]; // Map the literal permissions to their bytes32 representation - const mappedPermissionArray: string[] = + const mappedPermissionArray: (string | null)[] = requiredPermissionArray.map(mapPermission); // Perform the AND operation check for each required permission - return mappedPermissionArray.every((requiredPermission: string) => { + return mappedPermissionArray.every((requiredPermission: string | null) => { + if (!requiredPermission) { + throw new Error( + `Invalid permission string: ${requiredPermission}. It must be a valid 32-byte hex string or a known permission name.`, + ); + } const requiredPermissionBigInt = BigInt(requiredPermission); const grantedPermissionsBigInt = BigInt(grantedPermissions); From 07e123a8670297f8e748a80dda6aefbf67076cdb Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 17:49:09 +0100 Subject: [PATCH 11/25] chore: group together permissions functions in the ERC725 class --- src/index.ts | 91 +++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2f061268..cd2ce112 100644 --- a/src/index.ts +++ b/src/index.ts @@ -506,50 +506,6 @@ export class ERC725 { ); } - /** - * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. - * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. - */ - static encodePermissions(permissions: Permissions): string { - return encodePermissions(permissions); - } - - /** - * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. - * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. - */ - encodePermissions(permissions: Permissions): string { - return encodePermissions(permissions); - } - - /** - * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissionHex The permission hexadecimal value to be decoded. - * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. - */ - static decodePermissions(permissionHex: string) { - return decodePermissions(permissionHex); - } - - /** - * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissionHex The permission hexadecimal value to be decoded. - * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. - */ - decodePermissions(permissionHex: string) { - return decodePermissions(permissionHex); - } - /** * Hashes a key name for use on an ERC725Y contract according to LSP2 ERC725Y JSONSchema standard. * @@ -638,6 +594,53 @@ export class ERC725 { return supportsInterface(interfaceIdOrName, options); } + // Permissions related functions + // ----------------------------- + + /** + * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. + * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. + */ + static encodePermissions(permissions: Permissions): string { + return encodePermissions(permissions); + } + + /** + * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. + * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. + */ + encodePermissions(permissions: Permissions): string { + return encodePermissions(permissions); + } + + /** + * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissionHex The permission hexadecimal value to be decoded. + * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. + */ + static decodePermissions(permissionHex: string) { + return decodePermissions(permissionHex); + } + + /** + * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissionHex The permission hexadecimal value to be decoded. + * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. + */ + decodePermissions(permissionHex: string) { + return decodePermissions(permissionHex); + } + /** * Check if the required permissions are included in the granted permissions as defined by the LSP6 KeyManager Standard. * From 8b2bb98025e7cb4d76e59a6960b6252b5a9c5d55 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 17:55:15 +0100 Subject: [PATCH 12/25] refactor: move all permissions functionalities under `permissions.ts` --- src/index.ts | 25 ++++++++---------- src/lib/detector.ts | 60 ------------------------------------------ src/lib/permissions.ts | 59 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 75 deletions(-) diff --git a/src/index.ts b/src/index.ts index cd2ce112..28de3ffe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,13 +67,14 @@ import { encodeValueContent, decodeValueContent, } from './lib/encoder'; +import { internalSupportsInterface } from './lib/detector'; +import { decodeMappingKey } from './lib/decodeMappingKey'; import { - internalSupportsInterface, + encodePermissions, + decodePermissions, checkPermissions, mapPermission, -} from './lib/detector'; -import { decodeMappingKey } from './lib/decodeMappingKey'; -import { encodePermissions, decodePermissions } from './lib/permissions'; +} from './lib/permissions'; import { AssetURLEncode } from './types/encodeData'; import { URLDataToEncode, URLDataWithHash, Verification } from './types'; @@ -99,8 +100,12 @@ export { decodeValueContent, } from './lib/encoder'; export { getDataFromExternalSources } from './lib/getDataFromExternalSources'; -export { encodePermissions, decodePermissions } from './lib/permissions'; -export { checkPermissions } from './lib/detector'; +export { + encodePermissions, + decodePermissions, + checkPermissions, + mapPermission, +} from './lib/permissions'; export { getSchema } from './lib/schemaParser'; // PRIVATE FUNCTION @@ -679,14 +684,6 @@ export class ERC725 { return mapPermission(permission); } - static mapPermission(permission: string): string | null { - return mapPermission(permission); - } - - mapPermission(permission: string): string | null { - return mapPermission(permission); - } - encodeDataSourceWithHash( verification: undefined | Verification, dataSource: string, diff --git a/src/lib/detector.ts b/src/lib/detector.ts index 35df9a67..e7a6e1b9 100644 --- a/src/lib/detector.ts +++ b/src/lib/detector.ts @@ -21,9 +21,6 @@ * @date 2022 */ -import { isHexStrict } from 'web3-utils'; -import { LSP6_DEFAULT_PERMISSIONS } from '../constants/constants'; - import { AddressProviderOptions, INTERFACE_IDS_0_12_0, @@ -57,60 +54,3 @@ export const internalSupportsInterface = async ( throw new Error(`Error checking the interface: ${error}`); } }; - -/** - * @notice Map a permission to its corresponding bytes32 representation. - * @param permission The permission string to be mapped. - * @return The bytes32 representation of the permission. - * @dev Return null if the input is not a known permission name or a valid 32-byte hex string. - */ -export function mapPermission(permission: string): string | null { - if (!LSP6_DEFAULT_PERMISSIONS[permission] && !isHexStrict(permission)) { - return null; - } - return LSP6_DEFAULT_PERMISSIONS[permission] || permission; -} - -/** - * @notice Check if the required permissions are included in the granted permissions. - * @param requiredPermissions An array of required permissions or a single required permission. - * @param grantedPermissions The granted permissions as a 32-byte hex string. - * @return A boolean value indicating whether the required permissions are included in the granted permissions. - * @dev Throws an error if the grantedPermissions input is not a valid 32-byte hex string. - */ -export const checkPermissions = ( - requiredPermissions: string[] | string, - grantedPermissions: string, -): boolean => { - // Validate the grantedPermissions string - if (!isHexStrict(grantedPermissions)) { - throw new Error( - 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', - ); - } - - // Convert requiredPermissions to an array if it's a single string - const requiredPermissionArray: string[] = Array.isArray(requiredPermissions) - ? requiredPermissions - : [requiredPermissions]; - - // Map the literal permissions to their bytes32 representation - const mappedPermissionArray: (string | null)[] = - requiredPermissionArray.map(mapPermission); - - // Perform the AND operation check for each required permission - return mappedPermissionArray.every((requiredPermission: string | null) => { - if (!requiredPermission) { - throw new Error( - `Invalid permission string: ${requiredPermission}. It must be a valid 32-byte hex string or a known permission name.`, - ); - } - const requiredPermissionBigInt = BigInt(requiredPermission); - const grantedPermissionsBigInt = BigInt(grantedPermissions); - - return ( - (requiredPermissionBigInt & grantedPermissionsBigInt) === - requiredPermissionBigInt - ); - }); -}; diff --git a/src/lib/permissions.ts b/src/lib/permissions.ts index b35fb55e..2d680119 100644 --- a/src/lib/permissions.ts +++ b/src/lib/permissions.ts @@ -1,4 +1,4 @@ -import { hexToNumber, leftPad, numberToHex } from 'web3-utils'; +import { hexToNumber, isHexStrict, leftPad, numberToHex } from 'web3-utils'; import { LSP6_DEFAULT_PERMISSIONS } from '../constants/constants'; import { Permissions } from '../types/Method'; @@ -80,3 +80,60 @@ export function decodePermissions(permissionHex: string) { return result; } + +/** + * @notice Map a permission to its corresponding bytes32 representation. + * @param permission The permission string to be mapped. + * @return The bytes32 representation of the permission. + * @dev Return null if the input is not a known permission name or a valid 32-byte hex string. + */ +export function mapPermission(permission: string): string | null { + if (!LSP6_DEFAULT_PERMISSIONS[permission] && !isHexStrict(permission)) { + return null; + } + return LSP6_DEFAULT_PERMISSIONS[permission] || permission; +} + +/** + * @notice Check if the required permissions are included in the granted permissions. + * @param requiredPermissions An array of required permissions or a single required permission. + * @param grantedPermissions The granted permissions as a 32-byte hex string. + * @return A boolean value indicating whether the required permissions are included in the granted permissions. + * @dev Throws an error if the grantedPermissions input is not a valid 32-byte hex string. + */ +export const checkPermissions = ( + requiredPermissions: string[] | string, + grantedPermissions: string, +): boolean => { + // Validate the grantedPermissions string + if (!isHexStrict(grantedPermissions)) { + throw new Error( + 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', + ); + } + + // Convert requiredPermissions to an array if it's a single string + const requiredPermissionArray: string[] = Array.isArray(requiredPermissions) + ? requiredPermissions + : [requiredPermissions]; + + // Map the literal permissions to their bytes32 representation + const mappedPermissionArray: (string | null)[] = + requiredPermissionArray.map(mapPermission); + + // Perform the AND operation check for each required permission + return mappedPermissionArray.every((requiredPermission: string | null) => { + if (!requiredPermission) { + throw new Error( + `Invalid permission string: ${requiredPermission}. It must be a valid 32-byte hex string or a known permission name.`, + ); + } + const requiredPermissionBigInt = BigInt(requiredPermission); + const grantedPermissionsBigInt = BigInt(grantedPermissions); + + return ( + (requiredPermissionBigInt & grantedPermissionsBigInt) === + requiredPermissionBigInt + ); + }); +}; From 5445222d2d235c67ffefabbcfbc5c02a40395edb Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 17 May 2024 09:27:55 +0200 Subject: [PATCH 13/25] chore: fix linter errors --- src/lib/encodeKeyName.ts | 2 +- src/lib/schemaParser.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/encodeKeyName.ts b/src/lib/encodeKeyName.ts index 68c8543d..ebdabdf5 100644 --- a/src/lib/encodeKeyName.ts +++ b/src/lib/encodeKeyName.ts @@ -299,7 +299,7 @@ export function encodeKeyName(name: string, dynamicKeyParts?: DynamicKeyParts) { } case 'Array': // Warning: this can not correctly encode subsequent keys of array, only the initial Array key will work // encode for array index - if (dynamicKeyParts && typeof dynamicKeyParts == 'number') { + if (dynamicKeyParts && typeof dynamicKeyParts === 'number') { return encodeArrayKey(keccak256(name), dynamicKeyParts); } diff --git a/src/lib/schemaParser.ts b/src/lib/schemaParser.ts index ea49488d..3cdde5ec 100644 --- a/src/lib/schemaParser.ts +++ b/src/lib/schemaParser.ts @@ -180,9 +180,8 @@ 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}`; } From fca2ea53af20f5d45975b238b161c1eec09fb0b9 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 17 May 2024 09:31:50 +0200 Subject: [PATCH 14/25] test: move `permissions.test.ts` file --- src/lib/detector.test.ts | 164 +----------------------------------- src/lib/permissions.test.ts | 164 ++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 163 deletions(-) create mode 100644 src/lib/permissions.test.ts diff --git a/src/lib/detector.test.ts b/src/lib/detector.test.ts index d397feae..ac4afd50 100644 --- a/src/lib/detector.test.ts +++ b/src/lib/detector.test.ts @@ -24,7 +24,7 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; import { INTERFACE_IDS_0_12_0 } from '../constants/interfaces'; -import { internalSupportsInterface, checkPermissions } from './detector'; +import { internalSupportsInterface } from './detector'; describe('supportsInterface', () => { it('it should return true if the contract supports the interface with name', async () => { @@ -66,165 +66,3 @@ describe('supportsInterface', () => { expect(doesSupportInterface).to.be.true; }); }); - -describe('checkPermissions', () => { - describe('test with single permission', () => { - it('should throw an error when given an invalid permission string', async () => { - const requiredPermissions = 'INVALIDPERMISSION'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should throw an error when given an invalid 32-byte hex string', async () => { - const requiredPermissions = '0xinvalidhexstring'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should throw an error when given an invalid grantedPermission 32-byte hex string', async () => { - const requiredPermissions = 'CHANGEOWNER'; - const grantedPermissions = '0xinvalidgrantedpermissionhexstring'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', - ); - }); - - it('should return true when single literal permission matches granted permissions', async () => { - const requiredPermissions = 'CHANGEOWNER'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return true when single bytes32 permission matches granted permissions', async () => { - const requiredPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000001'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return false when single bytes32 permission does not match granted permissions', async () => { - const requiredPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000001'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000fff2'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return false when single literal permission does not match granted permissions', async () => { - const requiredPermissions = 'CHANGEOWNER'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000fff2'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - }); - - describe('test with multiple permissions', () => { - it('should throw an error when given an array containing an invalid permission string', async () => { - const requiredPermissions = ['CHANGEOWNER', 'INVALIDPERMISSION']; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should throw an error when given an array containing an invalid 32-byte hex string', async () => { - const requiredPermissions = ['CHANGEOWNER', '0xinvalidhexstring']; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should return false when one of the literal permissions does not match granted permissions', async () => { - const requiredPermissions = ['EDITPERMISSIONS', 'CALL']; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return false when one of the bytes32 permissions does not match granted permissions', async () => { - const requiredPermissions = [ - '0x0000000000000000000000000000000000000000000000000000000000000004', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return true when all the mixed literal and bytes32 permissions match granted permissions', async () => { - const requiredPermissions = [ - 'EDITPERMISSIONS', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff54'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return false when not all multiple literal permissions match granted permissions', async () => { - const requiredPermissions = ['CHANGEOWNER', 'CALL']; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return true when all multiple literal permissions match granted permissions', async () => { - const requiredPermissions = ['CHANGEOWNER', 'CALL']; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000801'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return false when not all multiple bytes32 permissions match granted permissions', async () => { - const requiredPermissions = [ - '0x0000000000000000000000000000000000000000000000000000000000000001', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return false when not all mixed literal and bytes32 permissions match granted permissions', async () => { - const requiredPermissions = [ - 'CHANGEOWNER', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - }); -}); diff --git a/src/lib/permissions.test.ts b/src/lib/permissions.test.ts new file mode 100644 index 00000000..e7ac432b --- /dev/null +++ b/src/lib/permissions.test.ts @@ -0,0 +1,164 @@ +import { expect } from 'chai'; +import { checkPermissions } from './permissions'; + +describe('checkPermissions', () => { + describe('test with single permission', () => { + it('should throw an error when given an invalid permission string', async () => { + const requiredPermissions = 'INVALIDPERMISSION'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + ); + }); + + it('should throw an error when given an invalid 32-byte hex string', async () => { + const requiredPermissions = '0xinvalidhexstring'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + ); + }); + + it('should throw an error when given an invalid grantedPermission 32-byte hex string', async () => { + const requiredPermissions = 'CHANGEOWNER'; + const grantedPermissions = '0xinvalidgrantedpermissionhexstring'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', + ); + }); + + it('should return true when single literal permission matches granted permissions', async () => { + const requiredPermissions = 'CHANGEOWNER'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return true when single bytes32 permission matches granted permissions', async () => { + const requiredPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000001'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return false when single bytes32 permission does not match granted permissions', async () => { + const requiredPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000001'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000fff2'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return false when single literal permission does not match granted permissions', async () => { + const requiredPermissions = 'CHANGEOWNER'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000fff2'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + }); + + describe('test with multiple permissions', () => { + it('should throw an error when given an array containing an invalid permission string', async () => { + const requiredPermissions = ['CHANGEOWNER', 'INVALIDPERMISSION']; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + ); + }); + + it('should throw an error when given an array containing an invalid 32-byte hex string', async () => { + const requiredPermissions = ['CHANGEOWNER', '0xinvalidhexstring']; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + ); + }); + + it('should return false when one of the literal permissions does not match granted permissions', async () => { + const requiredPermissions = ['EDITPERMISSIONS', 'CALL']; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return false when one of the bytes32 permissions does not match granted permissions', async () => { + const requiredPermissions = [ + '0x0000000000000000000000000000000000000000000000000000000000000004', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return true when all the mixed literal and bytes32 permissions match granted permissions', async () => { + const requiredPermissions = [ + 'EDITPERMISSIONS', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff54'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return false when not all multiple literal permissions match granted permissions', async () => { + const requiredPermissions = ['CHANGEOWNER', 'CALL']; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return true when all multiple literal permissions match granted permissions', async () => { + const requiredPermissions = ['CHANGEOWNER', 'CALL']; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000801'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return false when not all multiple bytes32 permissions match granted permissions', async () => { + const requiredPermissions = [ + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return false when not all mixed literal and bytes32 permissions match granted permissions', async () => { + const requiredPermissions = [ + 'CHANGEOWNER', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + }); +}); From d0ee0f7f3804e35f5252ec5c12a908f39c8d4589 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Tue, 21 May 2024 15:41:36 +0200 Subject: [PATCH 15/25] test: fix failing tests --- src/lib/permissions.test.ts | 8 ++++---- src/lib/permissions.ts | 28 +++++++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/lib/permissions.test.ts b/src/lib/permissions.test.ts index e7ac432b..f3822f3a 100644 --- a/src/lib/permissions.test.ts +++ b/src/lib/permissions.test.ts @@ -10,7 +10,7 @@ describe('checkPermissions', () => { expect(() => checkPermissions(requiredPermissions, grantedPermissions), ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + `Invalid permission string: ${requiredPermissions}. It must be a valid 32-byte hex string or a known permission name.`, ); }); @@ -21,7 +21,7 @@ describe('checkPermissions', () => { expect(() => checkPermissions(requiredPermissions, grantedPermissions), ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + `Invalid permission string: ${requiredPermissions}. It must be a valid 32-byte hex string or a known permission name.`, ); }); @@ -78,7 +78,7 @@ describe('checkPermissions', () => { expect(() => checkPermissions(requiredPermissions, grantedPermissions), ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + `Invalid permission string: ${requiredPermissions[1]}. It must be a valid 32-byte hex string or a known permission name.`, ); }); @@ -89,7 +89,7 @@ describe('checkPermissions', () => { expect(() => checkPermissions(requiredPermissions, grantedPermissions), ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', + `Invalid permission string: ${requiredPermissions[1]}. It must be a valid 32-byte hex string or a known permission name.`, ); }); diff --git a/src/lib/permissions.ts b/src/lib/permissions.ts index 2d680119..917791d5 100644 --- a/src/lib/permissions.ts +++ b/src/lib/permissions.ts @@ -122,18 +122,20 @@ export const checkPermissions = ( requiredPermissionArray.map(mapPermission); // Perform the AND operation check for each required permission - return mappedPermissionArray.every((requiredPermission: string | null) => { - if (!requiredPermission) { - throw new Error( - `Invalid permission string: ${requiredPermission}. It must be a valid 32-byte hex string or a known permission name.`, - ); - } - const requiredPermissionBigInt = BigInt(requiredPermission); - const grantedPermissionsBigInt = BigInt(grantedPermissions); + return mappedPermissionArray.every( + (requiredPermission: string | null, index: number) => { + if (!requiredPermission) { + throw new Error( + `Invalid permission string: ${requiredPermissionArray[index]}. It must be a valid 32-byte hex string or a known permission name.`, + ); + } + const requiredPermissionBigInt = BigInt(requiredPermission); + const grantedPermissionsBigInt = BigInt(grantedPermissions); - return ( - (requiredPermissionBigInt & grantedPermissionsBigInt) === - requiredPermissionBigInt - ); - }); + return ( + (requiredPermissionBigInt & grantedPermissionsBigInt) === + requiredPermissionBigInt + ); + }, + ); }; From f426a31239f708775e5ab2829ccd0e206da43988 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 11:11:56 +0100 Subject: [PATCH 16/25] feat: export function to get verification method --- .../methods/external-data-source-utilities.md | 209 ++++++++++++++++++ src/index.ts | 78 +++++-- src/lib/utils.ts | 11 +- 3 files changed, 273 insertions(+), 25 deletions(-) create mode 100644 docs/methods/external-data-source-utilities.md diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md new file mode 100644 index 00000000..ef1c824b --- /dev/null +++ b/docs/methods/external-data-source-utilities.md @@ -0,0 +1,209 @@ +--- +sidebar_position: 1 +--- + +# External Data Source utilities (`VerifiableURI` and `JSONURI`) + +## encodeDataSourceWithHash + +```js +const myErc725 = new ERC725(); +myErc725.encodeDataSourceWithHash(verification, dataSource); +``` + +OR + +```js +ERC725.encodeDataSourceWithHash(verification, dataSource); +``` + +Encode a verifiableURI providing the hashing function of the json file (method), the hash of the json file (data) and the url where the json file is stored. + +#### Parameters + +| Name | Type | Description | +| :------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- | +| `verification` | `undefined` or `Verification` | Verification is an object containing the hashing function of the json file (method) and the hash of the json file (data) | +| `dataSource` | `string` | The url where the json file is stored. | + +
+ Types details + +```js +interface Verification { + method: SUPPORTED_VERIFICATION_METHODS | string; + data: string; + source?: string; +} + +type SUPPORTED_VERIFICATION_METHODS = + | SUPPORTED_VERIFICATION_METHOD_STRINGS + | SUPPORTED_VERIFICATION_METHOD_HASHES; + +enum SUPPORTED_VERIFICATION_METHOD_STRINGS { + KECCAK256_UTF8 = 'keccak256(utf8)', + KECCAK256_BYTES = 'keccak256(bytes)', +} + +enum SUPPORTED_VERIFICATION_METHOD_HASHES { + HASH_KECCAK256_UTF8 = '0x6f357c6a', + HASH_KECCAK256_BYTES = '0x8019f9b1', +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :-------------- | :----- | :---------------- | +| `verifiableURI` | string | The verifiableURI | + +#### Examples + +
+ Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL + +```javascript title="Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL" +const verifiableURI = myErc725.encodeDataSourceWithHash( + { + method: 'keccak256(utf8)', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + }, + 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', +); +/** +0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178 +*/ +``` + +
+ +## decodeDataSourceWithHash + +```js +const myErc725 = new ERC725(); +myErc725.decodeDataSourceWithHash(verifiableURI); +``` + +```js +ERC725.decodeDataSourceWithHash(verifiableURI); +``` + +Decode a verifiableURI into the hash function of the json file, the hash of the json file and the url where the json file is stored. + +#### Parameters + +| Name | Type | Description | +| :-------------- | :------- | :---------------- | +| `verifiableURI` | `string` | The verifiableURI | + +#### Returns + +| Name | Type | Description | +| :--------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------- | +| `decodedVerifiableURI` | `URLDataWithHash` | Object containing the hash function, the hash of the JSON file and the link where the json file is stored. | + +
+ Types details + +```js +interface URLDataWithHash { + verification: Verification; + url: string +} + +interface Verification { +method: SUPPORTED_VERIFICATION_METHODS | string; +data: string; +source?: string; +} + +type SUPPORTED_VERIFICATION_METHODS = +| SUPPORTED_VERIFICATION_METHOD_STRINGS +| SUPPORTED_VERIFICATION_METHOD_HASHES; + +enum SUPPORTED_VERIFICATION_METHOD_STRINGS { +KECCAK256_UTF8 = 'keccak256(utf8)', +KECCAK256_BYTES = 'keccak256(bytes)', +} + +enum SUPPORTED_VERIFICATION_METHOD_HASHES { +HASH_KECCAK256_UTF8 = '0x6f357c6a', +HASH_KECCAK256_BYTES = '0x8019f9b1', +} + +``` + +
+ +#### Examples + +
+ Decode a VerifiableURI + +```javascript title="Decode a VerifiableURI" +const decodedVerifiableURI = myErc725.decodeDataSourceWithHash( + '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', +); +/** +verification: { + data: '820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + method: 'keccak256(utf8)', + } +url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx' +*/ +``` + +
+ +## getVerificationMethod + +```js +const myErc725 = new ERC725(); +myErc725.getVerificationMethod(nameOrSig); +``` + +```js +ERC725.getVerificationMethod(nameOrSig); +``` + +```js +import { getVerificationMethod } from '@erc725/erc725.js'; +getVerificationMethod(nameOrSig); +``` + +Get the verification method definition, including the name, signature and related method. + +method: (data: string | object | Uint8Array | null) => string; +name: SUPPORTED_VERIFICATION_METHOD_STRINGS; +sig: SUPPORTED_VERIFICATION_METHODS; + +#### Parameters + +| Name | Type | Description | +| :---------- | :----- | :------------------------------------------ | +| `nameOrSig` | string | The 4 bytes hex of the verification method. | + +#### Returns + +| Name | Type | Description | +| :--- | :----- | :-------------------------------------------------------------------------------------- | +| | object | An object containing the name, signature and method related to the verification method. | + +### Example + +```javascript title="Example of the method" +getVerificationMethod('0x6f357c6a'); +/* +{ + method: [Function: keccak256Method], + name: 'keccak256(utf8)', + sig: '0x6f357c6a' +} +*/ +``` + +## hashData + +## isDataAuthentic diff --git a/src/index.ts b/src/index.ts index 28de3ffe..8b118d1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ * @author Robert McLeod <@robertdavid010> * @author Fabian Vogelsteller * @author Hugo Masclet <@Hugoo> + * @author Jean Cavallera <@CJ42> * @date 2020 */ @@ -30,12 +31,17 @@ import { convertIPFSGatewayUrl, generateSchemasFromDynamicKeys, duplicateMultiTypeERC725SchemaEntry, + getVerificationMethod, } from './lib/utils'; import { getSchema } from './lib/schemaParser'; import { isValidSignature } from './lib/isValidSignature'; -import { DEFAULT_GAS_VALUE } from './constants/constants'; +import { + DEFAULT_GAS_VALUE, + SUPPORTED_VERIFICATION_METHODS, + SUPPORTED_VERIFICATION_METHOD_STRINGS, +} from './constants/constants'; import { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; // Types @@ -87,7 +93,7 @@ export { }; export { ERC725Config, KeyValuePair, ProviderTypes } from './types'; -export { encodeData, encodeArrayKey } from './lib/utils'; +export { encodeData, encodeArrayKey, getVerificationMethod } from './lib/utils'; export { decodeData } from './lib/decodeData'; export { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; export { decodeMappingKey } from './lib/decodeMappingKey'; @@ -684,27 +690,8 @@ export class ERC725 { return mapPermission(permission); } - encodeDataSourceWithHash( - verification: undefined | Verification, - dataSource: string, - ): string { - return encodeDataSourceWithHash(verification, dataSource); - } - - static encodeDataSourceWithHash( - verification: undefined | Verification, - dataSource: string, - ): string { - return encodeDataSourceWithHash(verification, dataSource); - } - - decodeDataSourceWithHash(value: string): URLDataWithHash { - return decodeDataSourceWithHash(value); - } - - static decodeDataSourceWithHash(value: string): URLDataWithHash { - return decodeDataSourceWithHash(value); - } + // Encoding methods + // ---------------- /** * @param type The valueType to encode the value as @@ -765,6 +752,51 @@ export class ERC725 { ): string | URLDataWithHash | number | boolean | null { return decodeValueContent(valueContent, value); } + + // External Data Source utilities (`VerifiableURI` and `JSONURI`) + // ---------------------------------------------------------------- + + encodeDataSourceWithHash( + verification: undefined | Verification, + dataSource: string, + ): string { + return encodeDataSourceWithHash(verification, dataSource); + } + + static encodeDataSourceWithHash( + verification: undefined | Verification, + dataSource: string, + ): string { + return encodeDataSourceWithHash(verification, dataSource); + } + + decodeDataSourceWithHash(value: string): URLDataWithHash { + return decodeDataSourceWithHash(value); + } + + static decodeDataSourceWithHash(value: string): URLDataWithHash { + return decodeDataSourceWithHash(value); + } + + static getVerificationMethod(nameOrSig: string): + | { + method: (data: string | object | Uint8Array | null) => string; + name: SUPPORTED_VERIFICATION_METHOD_STRINGS; + sig: SUPPORTED_VERIFICATION_METHODS; + } + | undefined { + return getVerificationMethod(nameOrSig); + } + + getVerificationMethod(nameOrSig: string): + | { + method: (data: string | object | Uint8Array | null) => string; + name: SUPPORTED_VERIFICATION_METHOD_STRINGS; + sig: SUPPORTED_VERIFICATION_METHODS; + } + | undefined { + return getVerificationMethod(nameOrSig); + } } export default ERC725; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e8e49baf..4c4b6386 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -47,6 +47,7 @@ import { SUPPORTED_VERIFICATION_METHODS, SUPPORTED_VERIFICATION_METHODS_LIST, COMPACT_BYTES_ARRAY_STRING, + SUPPORTED_VERIFICATION_METHOD_STRINGS, } from '../constants/constants'; import { decodeValueContent, @@ -157,7 +158,7 @@ export function encodeKeyValue( /** * - * @param key The schema key of a schema with keyType = 'Array' + * @param key A data key either as a 32 bytes long value, or a data key name of keyType = 'Array' * @param index An integer representing the intended array index * @return The raw bytes key for the array element */ @@ -524,7 +525,13 @@ export function encodeData( ); } -export function getVerificationMethod(nameOrSig: string) { +export function getVerificationMethod(nameOrSig: string): + | { + method: (data: string | object | Uint8Array | null) => string; + name: SUPPORTED_VERIFICATION_METHOD_STRINGS; + sig: SUPPORTED_VERIFICATION_METHODS; + } + | undefined { const verificationMethod = Object.values(HASH_METHODS).find( ({ name, sig }) => name === nameOrSig || sig === nameOrSig, ); From 0c7ba987540e0684e803ca8d722aa4b0b5d04fde Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 16:09:34 +0100 Subject: [PATCH 17/25] feat: export method `isDataAuthentic` as static, single method or member of class --- .../methods/external-data-source-utilities.md | 179 +++++++++++++++++- src/index.ts | 22 ++- src/lib/getDataFromExternalSources.test.ts | 130 +++++++++++++ src/lib/getDataFromExternalSources.ts | 2 + src/lib/utils.ts | 2 +- 5 files changed, 331 insertions(+), 4 deletions(-) diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md index ef1c824b..503c1345 100644 --- a/docs/methods/external-data-source-utilities.md +++ b/docs/methods/external-data-source-utilities.md @@ -204,6 +204,181 @@ getVerificationMethod('0x6f357c6a'); */ ``` -## hashData - ## isDataAuthentic + +```js +const myErc725 = new ERC725(); +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +import { isDataAuthentic } from '@erc725/erc725.js'; + +isDataAuthentic(data, verificationOptions); +``` + +Hashes the `data` passed as parameter using the specified hashing functions (available under `method` in the `verificationOption` object) and compares the result with the provided hash. + +:::info +This method will console an error if the hash provided as `data` and the expected hash obtained using the verification method do not match. +::: + +#### Parameters + +| Name | Type | Description | +| :-------------------- | :----------------------- | :---------------------------------- | +| `data` | `string` or `Uint8Array` | The data to be hashed and verified. | +| `verificationOptions` | `Verification` | An object as defined below | + +
+ Types details + +```js + KECCAK256_UTF8 = , + KECCAK256_BYTES = , + HASH_KECCAK256_UTF8 = , + HASH_KECCAK256_BYTES = , + +export interface Verification { + data: string; + method: 'keccak256(utf8)' | 'keccak256(bytes)' | '0x6f357c6a' | '0x8019f9b1' | string; + source?: string; +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :--- | :-------- | :-------------------------------------------------------------------------------------------- | +| | `boolean` | `true` if the data is authentic according to the verification method used, `false` otherwise. | + +### Example + +
+ JSON data to verify from data.json + +```json +[ + { + "name": "LSP3Profile", + "key": "0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5", + "value": { + "LSP3Profile": { + "name": "test", + "description": "", + "tags": ["profile"], + "links": [], + "profileImage": [ + { + "width": 1024, + "height": 709, + "verification": { + "method": "keccak256(bytes)", + "data": "0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c" + }, + "url": "ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C" + }, + { + "width": 640, + "height": 443, + "verification": { + "method": "keccak256(bytes)", + "data": "0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82" + }, + "url": "ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW" + }, + { + "width": 320, + "height": 221, + "verification": { + "method": "keccak256(bytes)", + "data": "0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5" + }, + "url": "ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH" + }, + { + "width": 180, + "height": 124, + "verification": { + "method": "keccak256(bytes)", + "data": "0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810" + }, + "url": "ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ" + } + ], + "backgroundImage": [ + { + "width": 1800, + "height": 1012, + "verification": { + "method": "keccak256(bytes)", + "data": "0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8" + }, + "url": "ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n" + }, + { + "width": 1024, + "height": 576, + "verification": { + "method": "keccak256(bytes)", + "data": "0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f" + }, + "url": "ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9" + }, + { + "width": 640, + "height": 360, + "verification": { + "method": "keccak256(bytes)", + "data": "0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967" + }, + "url": "ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV" + }, + { + "width": 320, + "height": 180, + "verification": { + "method": "keccak256(bytes)", + "data": "0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509" + }, + "url": "ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz" + }, + { + "width": 180, + "height": 101, + "verification": { + "method": "keccak256(bytes)", + "data": "0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475" + }, + "url": "ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm" + } + ] + } + } + } +] +``` + +
+ +```typescript title="isDataAuthentic example" +import jsonData from './data.json'; + +isDataAuthentic(jsonData, { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', +}); +// true + +isDataAuthentic(jsonData, { + data: '0xdeadbeef00000000000000000000000000000000000000000000000000000000', + method: 'keccak256(utf8)', +}); +// false +``` diff --git a/src/index.ts b/src/index.ts index 8b118d1f..eef1967f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import { generateSchemasFromDynamicKeys, duplicateMultiTypeERC725SchemaEntry, getVerificationMethod, + isDataAuthentic, } from './lib/utils'; import { getSchema } from './lib/schemaParser'; @@ -93,7 +94,12 @@ export { }; export { ERC725Config, KeyValuePair, ProviderTypes } from './types'; -export { encodeData, encodeArrayKey, getVerificationMethod } from './lib/utils'; +export { + encodeData, + encodeArrayKey, + getVerificationMethod, + isDataAuthentic, +} from './lib/utils'; export { decodeData } from './lib/decodeData'; export { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; export { decodeMappingKey } from './lib/decodeMappingKey'; @@ -797,6 +803,20 @@ export class ERC725 { | undefined { return getVerificationMethod(nameOrSig); } + + static isDataAuthentic( + data: string | Uint8Array, + verificationOptions: Verification, + ): boolean { + return isDataAuthentic(data, verificationOptions); + } + + isDataAuthentic( + data: string | Uint8Array, + verificationOptions: Verification, + ): boolean { + return isDataAuthentic(data, verificationOptions); + } } export default ERC725; diff --git a/src/lib/getDataFromExternalSources.test.ts b/src/lib/getDataFromExternalSources.test.ts index c8cd7659..2c3992e9 100644 --- a/src/lib/getDataFromExternalSources.test.ts +++ b/src/lib/getDataFromExternalSources.test.ts @@ -52,4 +52,134 @@ describe('getDataFromExternalSources', () => { ); }).to.not.throw(); }); + + it("should return the right data when the schema's valueContent is VerifiableURI", async () => { + const schema: ERC725JSONSchema = { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }; + + const dataFromChain: DecodeDataOutput[] = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + verification: { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', + }, + url: 'ipfs://QmdMGUxuQsm1U9Qs8oJSn5PfY4B1apGG75YBRxQPybtRVm', + }, + }, + ]; + + const result = await getDataFromExternalSources( + [schema], + dataFromChain, + 'https://api.universalprofile.cloud/ipfs/', + ); + + expect(result).to.deep.equal([ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + LSP3Profile: { + name: 'test', + description: '', + tags: ['profile'], + links: [], + profileImage: [ + { + width: 1024, + height: 709, + verification: { + method: 'keccak256(bytes)', + data: '0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c', + }, + url: 'ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C', + }, + { + width: 640, + height: 443, + verification: { + method: 'keccak256(bytes)', + data: '0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82', + }, + url: 'ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW', + }, + { + width: 320, + height: 221, + verification: { + method: 'keccak256(bytes)', + data: '0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5', + }, + url: 'ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH', + }, + { + width: 180, + height: 124, + verification: { + method: 'keccak256(bytes)', + data: '0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810', + }, + url: 'ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ', + }, + ], + backgroundImage: [ + { + width: 1800, + height: 1012, + verification: { + method: 'keccak256(bytes)', + data: '0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8', + }, + url: 'ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n', + }, + { + width: 1024, + height: 576, + verification: { + method: 'keccak256(bytes)', + data: '0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f', + }, + url: 'ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9', + }, + { + width: 640, + height: 360, + verification: { + method: 'keccak256(bytes)', + data: '0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967', + }, + url: 'ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV', + }, + { + width: 320, + height: 180, + verification: { + method: 'keccak256(bytes)', + data: '0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509', + }, + url: 'ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz', + }, + { + width: 180, + height: 101, + verification: { + method: 'keccak256(bytes)', + data: '0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475', + }, + url: 'ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm', + }, + ], + }, + }, + }, + ]); + }); }); diff --git a/src/lib/getDataFromExternalSources.ts b/src/lib/getDataFromExternalSources.ts index 7c898417..f7bde427 100644 --- a/src/lib/getDataFromExternalSources.ts +++ b/src/lib/getDataFromExternalSources.ts @@ -129,12 +129,14 @@ export const getDataFromExternalSources = ( if (/^(\[.*\]|\{.*\})\s*$/s.test(key)) { const json = arrToBufArr(receivedData).toString(); const value = JSON.parse(json); + if (isDataAuthentic(value, urlDataWithHash.verification)) { return { ...dataEntry, value }; } if (isDataAuthentic(receivedData, urlDataWithHash.verification)) { return { ...dataEntry, value }; } + throw new Error('result did not correctly validate'); } } catch { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4c4b6386..bb1ad658 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -158,7 +158,7 @@ export function encodeKeyValue( /** * - * @param key A data key either as a 32 bytes long value, or a data key name of keyType = 'Array' + * @param key The schema key of a schema with keyType = 'Array' * @param index An integer representing the intended array index * @return The raw bytes key for the array element */ From 335608f2abb7e1a9f83b91cf5be636b63412b630 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 17 May 2024 09:10:11 +0200 Subject: [PATCH 18/25] docs: remove dedicated docs page for external data source utilities --- .../methods/external-data-source-utilities.md | 384 ------------------ 1 file changed, 384 deletions(-) delete mode 100644 docs/methods/external-data-source-utilities.md diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md deleted file mode 100644 index 503c1345..00000000 --- a/docs/methods/external-data-source-utilities.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -sidebar_position: 1 ---- - -# External Data Source utilities (`VerifiableURI` and `JSONURI`) - -## encodeDataSourceWithHash - -```js -const myErc725 = new ERC725(); -myErc725.encodeDataSourceWithHash(verification, dataSource); -``` - -OR - -```js -ERC725.encodeDataSourceWithHash(verification, dataSource); -``` - -Encode a verifiableURI providing the hashing function of the json file (method), the hash of the json file (data) and the url where the json file is stored. - -#### Parameters - -| Name | Type | Description | -| :------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- | -| `verification` | `undefined` or `Verification` | Verification is an object containing the hashing function of the json file (method) and the hash of the json file (data) | -| `dataSource` | `string` | The url where the json file is stored. | - -
- Types details - -```js -interface Verification { - method: SUPPORTED_VERIFICATION_METHODS | string; - data: string; - source?: string; -} - -type SUPPORTED_VERIFICATION_METHODS = - | SUPPORTED_VERIFICATION_METHOD_STRINGS - | SUPPORTED_VERIFICATION_METHOD_HASHES; - -enum SUPPORTED_VERIFICATION_METHOD_STRINGS { - KECCAK256_UTF8 = 'keccak256(utf8)', - KECCAK256_BYTES = 'keccak256(bytes)', -} - -enum SUPPORTED_VERIFICATION_METHOD_HASHES { - HASH_KECCAK256_UTF8 = '0x6f357c6a', - HASH_KECCAK256_BYTES = '0x8019f9b1', -} -``` - -
- -#### Returns - -| Name | Type | Description | -| :-------------- | :----- | :---------------- | -| `verifiableURI` | string | The verifiableURI | - -#### Examples - -
- Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL - -```javascript title="Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL" -const verifiableURI = myErc725.encodeDataSourceWithHash( - { - method: 'keccak256(utf8)', - data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', - }, - 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', -); -/** -0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178 -*/ -``` - -
- -## decodeDataSourceWithHash - -```js -const myErc725 = new ERC725(); -myErc725.decodeDataSourceWithHash(verifiableURI); -``` - -```js -ERC725.decodeDataSourceWithHash(verifiableURI); -``` - -Decode a verifiableURI into the hash function of the json file, the hash of the json file and the url where the json file is stored. - -#### Parameters - -| Name | Type | Description | -| :-------------- | :------- | :---------------- | -| `verifiableURI` | `string` | The verifiableURI | - -#### Returns - -| Name | Type | Description | -| :--------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------- | -| `decodedVerifiableURI` | `URLDataWithHash` | Object containing the hash function, the hash of the JSON file and the link where the json file is stored. | - -
- Types details - -```js -interface URLDataWithHash { - verification: Verification; - url: string -} - -interface Verification { -method: SUPPORTED_VERIFICATION_METHODS | string; -data: string; -source?: string; -} - -type SUPPORTED_VERIFICATION_METHODS = -| SUPPORTED_VERIFICATION_METHOD_STRINGS -| SUPPORTED_VERIFICATION_METHOD_HASHES; - -enum SUPPORTED_VERIFICATION_METHOD_STRINGS { -KECCAK256_UTF8 = 'keccak256(utf8)', -KECCAK256_BYTES = 'keccak256(bytes)', -} - -enum SUPPORTED_VERIFICATION_METHOD_HASHES { -HASH_KECCAK256_UTF8 = '0x6f357c6a', -HASH_KECCAK256_BYTES = '0x8019f9b1', -} - -``` - -
- -#### Examples - -
- Decode a VerifiableURI - -```javascript title="Decode a VerifiableURI" -const decodedVerifiableURI = myErc725.decodeDataSourceWithHash( - '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', -); -/** -verification: { - data: '820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', - method: 'keccak256(utf8)', - } -url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx' -*/ -``` - -
- -## getVerificationMethod - -```js -const myErc725 = new ERC725(); -myErc725.getVerificationMethod(nameOrSig); -``` - -```js -ERC725.getVerificationMethod(nameOrSig); -``` - -```js -import { getVerificationMethod } from '@erc725/erc725.js'; -getVerificationMethod(nameOrSig); -``` - -Get the verification method definition, including the name, signature and related method. - -method: (data: string | object | Uint8Array | null) => string; -name: SUPPORTED_VERIFICATION_METHOD_STRINGS; -sig: SUPPORTED_VERIFICATION_METHODS; - -#### Parameters - -| Name | Type | Description | -| :---------- | :----- | :------------------------------------------ | -| `nameOrSig` | string | The 4 bytes hex of the verification method. | - -#### Returns - -| Name | Type | Description | -| :--- | :----- | :-------------------------------------------------------------------------------------- | -| | object | An object containing the name, signature and method related to the verification method. | - -### Example - -```javascript title="Example of the method" -getVerificationMethod('0x6f357c6a'); -/* -{ - method: [Function: keccak256Method], - name: 'keccak256(utf8)', - sig: '0x6f357c6a' -} -*/ -``` - -## isDataAuthentic - -```js -const myErc725 = new ERC725(); -ERC725.isDataAuthentic(data, verificationOptions); -``` - -```js -ERC725.isDataAuthentic(data, verificationOptions); -``` - -```js -import { isDataAuthentic } from '@erc725/erc725.js'; - -isDataAuthentic(data, verificationOptions); -``` - -Hashes the `data` passed as parameter using the specified hashing functions (available under `method` in the `verificationOption` object) and compares the result with the provided hash. - -:::info -This method will console an error if the hash provided as `data` and the expected hash obtained using the verification method do not match. -::: - -#### Parameters - -| Name | Type | Description | -| :-------------------- | :----------------------- | :---------------------------------- | -| `data` | `string` or `Uint8Array` | The data to be hashed and verified. | -| `verificationOptions` | `Verification` | An object as defined below | - -
- Types details - -```js - KECCAK256_UTF8 = , - KECCAK256_BYTES = , - HASH_KECCAK256_UTF8 = , - HASH_KECCAK256_BYTES = , - -export interface Verification { - data: string; - method: 'keccak256(utf8)' | 'keccak256(bytes)' | '0x6f357c6a' | '0x8019f9b1' | string; - source?: string; -} -``` - -
- -#### Returns - -| Name | Type | Description | -| :--- | :-------- | :-------------------------------------------------------------------------------------------- | -| | `boolean` | `true` if the data is authentic according to the verification method used, `false` otherwise. | - -### Example - -
- JSON data to verify from data.json - -```json -[ - { - "name": "LSP3Profile", - "key": "0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5", - "value": { - "LSP3Profile": { - "name": "test", - "description": "", - "tags": ["profile"], - "links": [], - "profileImage": [ - { - "width": 1024, - "height": 709, - "verification": { - "method": "keccak256(bytes)", - "data": "0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c" - }, - "url": "ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C" - }, - { - "width": 640, - "height": 443, - "verification": { - "method": "keccak256(bytes)", - "data": "0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82" - }, - "url": "ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW" - }, - { - "width": 320, - "height": 221, - "verification": { - "method": "keccak256(bytes)", - "data": "0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5" - }, - "url": "ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH" - }, - { - "width": 180, - "height": 124, - "verification": { - "method": "keccak256(bytes)", - "data": "0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810" - }, - "url": "ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ" - } - ], - "backgroundImage": [ - { - "width": 1800, - "height": 1012, - "verification": { - "method": "keccak256(bytes)", - "data": "0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8" - }, - "url": "ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n" - }, - { - "width": 1024, - "height": 576, - "verification": { - "method": "keccak256(bytes)", - "data": "0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f" - }, - "url": "ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9" - }, - { - "width": 640, - "height": 360, - "verification": { - "method": "keccak256(bytes)", - "data": "0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967" - }, - "url": "ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV" - }, - { - "width": 320, - "height": 180, - "verification": { - "method": "keccak256(bytes)", - "data": "0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509" - }, - "url": "ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz" - }, - { - "width": 180, - "height": 101, - "verification": { - "method": "keccak256(bytes)", - "data": "0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475" - }, - "url": "ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm" - } - ] - } - } - } -] -``` - -
- -```typescript title="isDataAuthentic example" -import jsonData from './data.json'; - -isDataAuthentic(jsonData, { - data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', - method: 'keccak256(utf8)', -}); -// true - -isDataAuthentic(jsonData, { - data: '0xdeadbeef00000000000000000000000000000000000000000000000000000000', - method: 'keccak256(utf8)', -}); -// false -``` From 042095986057e7e1c7ebf363bc816936a39d472e Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 24 May 2024 10:01:41 +0200 Subject: [PATCH 19/25] docs: add method docs template --- docs/README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/README.md b/docs/README.md index a35e996d..c2e5c27d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,52 @@ # erc725.js docs The markdown files in this folder are published on the LUKSO docs portal: + +# Method docs template + +You can use the following example below as a template to document any new methods added the library. + +### methodName + +```js +myErc725.methodName(param1, param2, [optionalParams]); +``` + +```js +ERC725.methodName(param1, param2, [optionalParams]); +``` + +Describe your method and how it is intended to be used, the benefits it brings, and any specific behaviour that the functions has. + +#### Parameters + +| Name | Type | Description | +| :------- | :--------------------------------------------------------------------------------------------------------- | :---------- | +| `param1` | `string` | ... | +| `param2` | `string` or
`string[]` or
`number` or
`number[]` or
`boolean` or
`boolean[]` | ... | + +#### Returns + +| Name | Type | Description | +| :--- | :------- | :---------- | +| | `string` | ... | + +#### Examples + +```javascript +import ERC725 from '@erc725/erc725.js'; + +const myErc725 = new ERC725(); + +myErc725.methodName('hello', 5); +// show in comment the expected output of the function +``` + +This method is also available as a static method. + +```javascript +import ERC725 from '@erc725/erc725.js'; + +ERC725.methodName('hello', 5); +// show in comment the expected output of the function +``` From d8ce6acddf14d4bc3d378cd2ad6a56c71cca93c4 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 24 May 2024 11:09:11 +0200 Subject: [PATCH 20/25] docs: add documentation for `encodeValueContent` --- docs/README.md | 2 +- docs/methods.md | 91 +++++++++++++++++++++++++++++++++++++++-- src/lib/encoder.test.ts | 7 ++++ 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index c2e5c27d..c9ec26fb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ The markdown files in this folder are published on the LUKSO docs portal: -# Method docs template +## Method docs template You can use the following example below as a template to document any new methods added the library. diff --git a/docs/methods.md b/docs/methods.md index 69f7387d..71b52970 100644 --- a/docs/methods.md +++ b/docs/methods.md @@ -524,8 +524,6 @@ myErc725.encodeKeyName('LSP3Profile'); --- -### encodeArrayKey (TBD) - ### encodeValueType ```js @@ -600,7 +598,94 @@ ERC725.encodeValueType('string', 'Hello'); --- -### encodeValueContent (TBD) +### encodeValueContent + +```js +myErc725.encodeValueContent(valueContent, value); +``` + +or + +```js +ERC725.encodeValueContent(valueContent, value); +``` + +or + +```js +import { encodeValueContent } from '@erc725/erc725.js'; +encodeValueContent(valueContent, value); +``` + +Encode `value` into a hex bytestring that can be then fetched, decoded and interpreted as the specified `valueContent`. + +#### Parameters + +| Name | Type | Description | +| :------------- | :------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `valueContent` | `string` | Available options are:
  • `AssetURL`
  • `JSONURL`
  • `Boolean`
  • `Keccak256`
  • `Number`
  • `Address`
  • `String`
  • `Markdown`
  • `URL`
  • `VerifiableURI`
  • `BytesN`
  • `BitArray`
  • `Boolean`
  • Any Hex `Literal` (e.g.: 0x1345ABCD...)
| +| `value` | `string` or
`number` or
`boolean` or
`AssetURLEncode` or
`AssetURLEncode` | The value to encode. | + +#### Returns + +| Name | Type | Description | +| :--- | :------- | :--------------------------------------- | +| | `string` | The `value` encoded as a `valueContent`. | + +#### Examples + +```javascript +import { encodeValueContent } from '@erc725/erc725.js'; + +encodeValueContent('String', 'hello'); +// 0x68656c6c6f + +// BytesN will right pad +encodeValueContent('Bytes8', '0xaabbccdd'); +// 0xaabbccdd00000000 + +// Encoding some markdown +encodeValueContent( + 'Markdown', + `# Lorem Ipsum + dolor sit amet ebriscus lanfogern`, +); +// 0x23204c6f72656d20497073756d0a2020202020202020646f6c6f722073697420616d6574206562726973637573206c616e666f6765726 + +encodeValueContent('URL', 'http://test.com'); +// 0x687474703a2f2f746573742e636f6d + +encodeValueContent('VerifiableURI', { + verification: { + method: 'keccak256(utf8)', + data: '0x027547537d35728a741470df1ccf65de10b454ca0def7c5c20b257b7b8d16168', + }, + url: 'http://test.com/asset.glb', +}); +// 0x00006f357c6a0020027547537d35728a741470df1ccf65de10b454ca0def7c5c20b257b7b8d16168687474703a2f2f746573742e636f6d2f61737365742e676c62 + +encodeValueContent('VerifiableURI', { + verification: { + method: '0x00000000', + data: '0x', + }, + url: 'https://name.universal.page/', +}); +// 0x000000000000000068747470733a2f2f6e616d652e756e6976657273616c2e706167652f +``` + +This method is also available as a static method or as a method from the `ERC725` instance. + +```javascript +import ERC725 from '@erc725/erc725.js'; + +ERC725.encodeValueContent('String', 'hello'); +// 0x68656c6c6f + +const erc725 = new ERC725(); +erc725.encodeValueContent('String', 'hello'); +// 0x68656c6c6f +``` --- diff --git a/src/lib/encoder.test.ts b/src/lib/encoder.test.ts index aa6b62f0..be0f45d2 100644 --- a/src/lib/encoder.test.ts +++ b/src/lib/encoder.test.ts @@ -938,6 +938,13 @@ describe('encoder', () => { decodedValue: '# hello', encodedValue: '0x232068656c6c6f', }, + { + valueContent: 'Markdown', + decodedValue: `# Lorem Ipsum + dolor sit amet ebriscus lanfogern`, + encodedValue: + '0x23204c6f72656d20497073756d0a2020202020202020646f6c6f722073697420616d6574206562726973637573206c616e666f6765726e', + }, { valueContent: 'URL', decodedValue: 'http://test.com', From e4f33ea354884e5730ebc40ba61b611db7ed5eb3 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 24 May 2024 12:08:26 +0200 Subject: [PATCH 21/25] docs: add missing methods `getSchema` and `getDataFromExternalSources` --- docs/methods.md | 233 +++++++++++++++++++++++++++++++++- src/types/ERC725JSONSchema.ts | 2 +- 2 files changed, 232 insertions(+), 3 deletions(-) diff --git a/docs/methods.md b/docs/methods.md index 71b52970..ffbf5b76 100644 --- a/docs/methods.md +++ b/docs/methods.md @@ -999,7 +999,7 @@ Available `valueContent` are: | :------------- | :--------------------------------------------------------------------------------------- | :------------------------------------------- | | `decodedValue` | `string` or
`URLDataWithHash` or
`number` or
`boolean` or
`null` | A value decoded according to `valueContent`. | -### Examples +#### Examples ```javascript title="decodeValueContent example" erc725js.decodeValueContent( @@ -1136,6 +1136,103 @@ myErc725.decodeMappingKey( --- +### getSchema + +```js +myErc725.getSchema(keys [, providedSchemas]); +``` + +Parses a hashed key or a list of hashed keys and will attempt to return its corresponding [LSP2 ERC725YJSONSchema](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md) object. Additionally, it will look for a corresponding key within the schemas: + +- in the [`schemas`](https://github.com/ERC725Alliance/myErc725.js/tree/main/schemas) folder (which includes all [LSPs](https://github.com/lukso-network/LIPs/tree/main/LSPs)), +- that were provided at ERC725 initialization, and +- that were provided in the function call (`providedSchemas`). + +#### Parameters + +##### 1. `keys` - String or array of Strings + +The key(s) you are trying to get the schema for. + +##### 2. `providedSchemas` - Object (optional) + +An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the schema. + +#### Returns + +| Name | Type | Description | +| :------- | :--------------- | :-------------------------------------------------------------------- | +| `result` | ERC725JSONSchema | If the parameter `keys` is a string and the schema was found. | +| `result` | Record string | If the parameter `keys` is a string[ ] and the schema was found. | +| `result` | null | If the schema was not found. | + +#### Example using a predefined LSP3 schema + +```javascript title="Parsing the hashed key from the LSP3 schema" +myErc725.getSchema( + '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', +); +/** +{ + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueContent: 'VerifiableURI', + valueType: 'bytes' +} +*/ +myErc725.getSchema([ + '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001', +]); +/** +{ + '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5': { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueContent: 'VerifiableURI', + valueType: 'bytes' + }, + '0x3a47ab5bd3a594c3a8995f8fa58d087600000000000000000000000000000001': { + name: 'LSP12IssuedAssets[1]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + keyType: 'Singleton', + valueContent: 'Address', + valueType: 'address' + } +} +*/ +``` + +#### Example using a custom schema + +```javascript title="Parsing the hashed key from a custom schema" +myErc725.getSchema( + '0x777f55baf2e0c9f73d3bb456dfb8dbf6e609bf557969e3184c17ff925b3c402c', + [ + { + name: 'ParameterSchema', + key: '0x777f55baf2e0c9f73d3bb456dfb8dbf6e609bf557969e3184c17ff925b3c402c', + keyType: 'Singleton', + valueContent: 'VerifiableURI', + valueType: 'bytes', + }, + ], +); +/** +{ + name: 'ParameterSchema', + key: '0x777f55baf2e0c9f73d3bb456dfb8dbf6e609bf557969e3184c17ff925b3c402c', + keyType: 'Singleton', + valueContent: 'VerifiableURI', + valueType: 'bytes', +} +*/ +``` + +--- + ## Fetching Data ### fetchData @@ -1182,7 +1279,7 @@ The name(s) (or the encoded name(s) as schema key) of the element(s) in the smar ::: -### All-Keys Example +#### All-Keys Example ```javascript title="Receiving all keys from the schema" import ERC725 from '@erc725/erc725.js'; @@ -1893,6 +1990,138 @@ url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx' +### getDataFromExternalSources + +```js +getDataFromExternalSources(schema, dataFromChain, ipfsGateway, [ + (throwException = true), +]); +``` + +Retrieve the data(s) stored on IPFS using one (or multiple) IPFS CID(s) and link(s) under the `dataFromChain` parameter. + +#### Parameters + +| Name | Type | Description | +| :-------------------------- | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | +| `schema` | `ERC725JSONSchema[]` | A list of JSON schemas for some given data keys. | +| `dataFromChain` | `DecodeDataOutput[]` | ... | +| `ipfsGateway` | `string` | The URL to use to fetch data from the IPFS network. | +| `throwException` (optional) | `boolean` | Define if the function should throw an error when fetching data. Set to `true` by default. This is useful for handling exceptions differently. | + +
+ Types details + +```js +interface ERC725JSONSchema { + name: string; // Describes the name of the key, SHOULD be composed of the Standards name + sub type. e.g: LSP2Name + key: string; // The keccak256 hash of the name. This is the actual key that MUST be retrievable via ERC725Y.getData(bytes32 key) + keyType: ERC725JSONSchemaKeyType | string; // Types that determine how the values should be interpreted. + valueContent: ERC725JSONSchemaValueContent | string; // string holds '0x1345ABCD...' If the value content are specific bytes, than the returned value is expected to equal those bytes. + valueType: ERC725JSONSchemaValueType | string; // The type of the value. This is used to determine how the value should be encoded / decode (`string` for tuples and CompactBytesArray). +} + +interface DecodeDataOutput { + value: Data | Data[] | URLDataWithHash | null; + name: string; + key: string; +} + +type Data = string | number | boolean | null; + +interface URLDataWithHash extends URLData { + verification: Verification; // | string is to allow use of string directly without importing the enum + json?: never; +} + +interface URLData { + url: string; +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :--- | :------- | :---------- | +| | `object` | ... | + +#### Examples + +```javascript +import { getDataFromExternalSources } from '@erc725/erc725.js'; + +const schema = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }, +]; + +const dataFromChain = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + verification: { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', + }, + url: 'ipfs://QmdMGUxuQsm1U9Qs8oJSn5PfY4B1apGG75YBRxQPybtRVm', + }, + }, +]; +const ipfsGateway = 'https://my-ipfs-gateway.com/ipfs/'; + +await getDataFromExternalSources(schema, dataFromChain, ipfsGateway); +/** +{ + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + LSP3Profile: { + name: 'test', + description: '', + tags: ['profile'], + links: [], + profileImage: [ + { + width: 1024, + height: 709, + verification: { + method: 'keccak256(bytes)', + data: '0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c', + }, + url: 'ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C', + }, + // more images... + ], + backgroundImage: [ + { + width: 1800, + height: 1012, + verification: { + method: 'keccak256(bytes)', + data: '0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8', + }, + url: 'ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n', + }, + // more images... + ], + }, + }, + }, + ]); + }); + */ +``` + +--- + ### getVerificationMethod ```js diff --git a/src/types/ERC725JSONSchema.ts b/src/types/ERC725JSONSchema.ts index 1aa64f08..c2115c85 100644 --- a/src/types/ERC725JSONSchema.ts +++ b/src/types/ERC725JSONSchema.ts @@ -157,7 +157,7 @@ export function isValidValueType( * Detailed information available on [LSP-2-ERC725YJSONSchema](https://github.com/lukso-network/LIPs/blob/master/LSPs/LSP-2-ERC725YJSONSchema.md) */ export interface ERC725JSONSchema { - name: string; // Describes the name of the key, SHOULD compromise of the Standards name + sub type. e.g: LSP2Name + name: string; // Describes the name of the key, SHOULD be composed of the Standards name + sub type. e.g: LSP2Name key: string; // The keccak256 hash of the name. This is the actual key that MUST be retrievable via ERC725Y.getData(bytes32 key) keyType: ERC725JSONSchemaKeyType | string; // Types that determine how the values should be interpreted. valueContent: ERC725JSONSchemaValueContent | string; // string holds '0x1345ABCD...' If the value content are specific bytes, than the returned value is expected to equal those bytes. From fde9f5da07dfe87f1459266515b58bca58eacbcd Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 27 May 2024 12:25:24 +0200 Subject: [PATCH 22/25] fix: allow to decode `0x` as `0` for keyType `Array` --- src/lib/Untitled-1 | 23 +++++++++++++++++++++++ src/lib/decodeData.test.ts | 21 +++++++++++++++++++++ src/lib/decodeData.ts | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/lib/Untitled-1 diff --git a/src/lib/Untitled-1 b/src/lib/Untitled-1 new file mode 100644 index 00000000..4ec309a2 --- /dev/null +++ b/src/lib/Untitled-1 @@ -0,0 +1,23 @@ +import { ERC725 } from '@erc725/erc725.js'; + +const schema = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }, +]; + +const erc725 = new ERC725(schema); +const encodedVerifiableURI = + '0x00008019f9b10020ed9808e44f28535d4673424413c06ce26210077628eddf95da6acba96f0b9e25697066733a2f2f6261666b72656967706176746475357035796d6a637a62346a35696d3434326f6e76336735693767747a6b6c3234336e3763616e71796d646e6334'; + +const result = erc725.decodeData([ + { + keyName: 'LSP3Profile', + value: encodedVerifiableURI, + }, +]); +console.log(JSON.stringify(result, null, 4)); diff --git a/src/lib/decodeData.test.ts b/src/lib/decodeData.test.ts index 39545ebf..662e5e16 100644 --- a/src/lib/decodeData.test.ts +++ b/src/lib/decodeData.test.ts @@ -284,6 +284,27 @@ describe('decodeData', () => { expect(decodedData.value).to.eql(3); }); + it('parses type Array correctly, return empty array when value is 0x', () => { + const decodedData = decodeData( + { + keyName: 'LSP12IssuedAssets[]', + value: '0x', + }, + [ + { + name: 'LSP12IssuedAssets[]', + key: '0x7c8c3416d6cda87cd42c71ea1843df28ac4850354f988d55ee2eaa47b6dc05cd', + keyType: 'Array', + valueContent: 'Address', + valueType: 'address', + }, + ], + ); + + expect(decodedData.name).to.eql('LSP12IssuedAssets[]'); + expect(decodedData.value).to.eql([]); + }); + it('decodes dynamic keys', () => { const decodedData = decodeData( [ diff --git a/src/lib/decodeData.ts b/src/lib/decodeData.ts index 05050361..59604dc1 100644 --- a/src/lib/decodeData.ts +++ b/src/lib/decodeData.ts @@ -196,7 +196,7 @@ export function decodeKey(schema: ERC725JSONSchema, value) { switch (lowerCaseKeyType) { case 'array': { // If user has requested a key which does not exist in the contract, value will be: 0x and value.find() will fail. - if (!value) { + if (!value || value == '0x') { return []; } From 93b1c081bd90428360069b7887cd46cbd87934cc Mon Sep 17 00:00:00 2001 From: CJ42 Date: Tue, 28 May 2024 15:25:37 +0200 Subject: [PATCH 23/25] chore: fix linter and remove accidentally commited file --- src/lib/Untitled-1 | 23 ----------------------- src/lib/decodeData.ts | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 src/lib/Untitled-1 diff --git a/src/lib/Untitled-1 b/src/lib/Untitled-1 deleted file mode 100644 index 4ec309a2..00000000 --- a/src/lib/Untitled-1 +++ /dev/null @@ -1,23 +0,0 @@ -import { ERC725 } from '@erc725/erc725.js'; - -const schema = [ - { - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - keyType: 'Singleton', - valueType: 'bytes', - valueContent: 'VerifiableURI', - }, -]; - -const erc725 = new ERC725(schema); -const encodedVerifiableURI = - '0x00008019f9b10020ed9808e44f28535d4673424413c06ce26210077628eddf95da6acba96f0b9e25697066733a2f2f6261666b72656967706176746475357035796d6a637a62346a35696d3434326f6e76336735693767747a6b6c3234336e3763616e71796d646e6334'; - -const result = erc725.decodeData([ - { - keyName: 'LSP3Profile', - value: encodedVerifiableURI, - }, -]); -console.log(JSON.stringify(result, null, 4)); diff --git a/src/lib/decodeData.ts b/src/lib/decodeData.ts index 59604dc1..a6b7cefd 100644 --- a/src/lib/decodeData.ts +++ b/src/lib/decodeData.ts @@ -196,7 +196,7 @@ export function decodeKey(schema: ERC725JSONSchema, value) { switch (lowerCaseKeyType) { case 'array': { // If user has requested a key which does not exist in the contract, value will be: 0x and value.find() will fail. - if (!value || value == '0x') { + if (!value || value === '0x') { return []; } From f16e78d0bfddf50414a35c111c7d30814c8d7728 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Wed, 29 May 2024 15:12:32 +0200 Subject: [PATCH 24/25] test: add mock for external data fetching test --- src/lib/getDataFromExternalSources.test.ts | 129 ++++----------------- 1 file changed, 24 insertions(+), 105 deletions(-) diff --git a/src/lib/getDataFromExternalSources.test.ts b/src/lib/getDataFromExternalSources.test.ts index 2c3992e9..b2d710e2 100644 --- a/src/lib/getDataFromExternalSources.test.ts +++ b/src/lib/getDataFromExternalSources.test.ts @@ -15,11 +15,13 @@ /* eslint-disable no-unused-expressions */ import { expect } from 'chai'; +import * as sinon from 'sinon'; import { ERC725JSONSchema } from '../types/ERC725JSONSchema'; import { getDataFromExternalSources } from './getDataFromExternalSources'; import { DecodeDataOutput } from '../types/decodeData'; +import { keccak256 } from 'web3-utils'; const IPFS_GATEWAY_MOCK = 'https://mock-ipfs.mock/ipfs/'; @@ -62,13 +64,15 @@ describe('getDataFromExternalSources', () => { valueContent: 'VerifiableURI', }; + const jsonResult = { LSP3Profile: { name: 'Test', description: 'Cool' } }; + const dataFromChain: DecodeDataOutput[] = [ { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: { verification: { - data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + data: keccak256(JSON.stringify(jsonResult)), method: 'keccak256(utf8)', }, url: 'ipfs://QmdMGUxuQsm1U9Qs8oJSn5PfY4B1apGG75YBRxQPybtRVm', @@ -76,110 +80,25 @@ describe('getDataFromExternalSources', () => { }, ]; - const result = await getDataFromExternalSources( - [schema], - dataFromChain, - 'https://api.universalprofile.cloud/ipfs/', - ); + const fetchStub = sinon.stub(global, 'fetch'); + try { + fetchStub.onCall(0).returns( + Promise.resolve( + new Response(JSON.stringify(jsonResult), { + headers: { 'content-type': 'application/json' }, + }), + ), + ); - expect(result).to.deep.equal([ - { - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - value: { - LSP3Profile: { - name: 'test', - description: '', - tags: ['profile'], - links: [], - profileImage: [ - { - width: 1024, - height: 709, - verification: { - method: 'keccak256(bytes)', - data: '0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c', - }, - url: 'ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C', - }, - { - width: 640, - height: 443, - verification: { - method: 'keccak256(bytes)', - data: '0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82', - }, - url: 'ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW', - }, - { - width: 320, - height: 221, - verification: { - method: 'keccak256(bytes)', - data: '0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5', - }, - url: 'ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH', - }, - { - width: 180, - height: 124, - verification: { - method: 'keccak256(bytes)', - data: '0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810', - }, - url: 'ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ', - }, - ], - backgroundImage: [ - { - width: 1800, - height: 1012, - verification: { - method: 'keccak256(bytes)', - data: '0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8', - }, - url: 'ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n', - }, - { - width: 1024, - height: 576, - verification: { - method: 'keccak256(bytes)', - data: '0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f', - }, - url: 'ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9', - }, - { - width: 640, - height: 360, - verification: { - method: 'keccak256(bytes)', - data: '0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967', - }, - url: 'ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV', - }, - { - width: 320, - height: 180, - verification: { - method: 'keccak256(bytes)', - data: '0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509', - }, - url: 'ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz', - }, - { - width: 180, - height: 101, - verification: { - method: 'keccak256(bytes)', - data: '0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475', - }, - url: 'ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm', - }, - ], - }, - }, - }, - ]); + const [{ value: result }] = await getDataFromExternalSources( + [schema], + dataFromChain, + IPFS_GATEWAY_MOCK, + ); + + expect(result).to.deep.equal(jsonResult); + } finally { + fetchStub.restore(); + } }); }); From 2a67f2673ed0495054c5a85665b4d318fd066a91 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Wed, 29 May 2024 15:23:53 +0200 Subject: [PATCH 25/25] docs: add missing descriptions for parameters and return examples --- docs/methods.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/methods.md b/docs/methods.md index ffbf5b76..32cc3852 100644 --- a/docs/methods.md +++ b/docs/methods.md @@ -1160,11 +1160,11 @@ An array of extra [LSP-2 ERC725YJSONSchema] objects that can be used to find the #### Returns -| Name | Type | Description | -| :------- | :--------------- | :-------------------------------------------------------------------- | -| `result` | ERC725JSONSchema | If the parameter `keys` is a string and the schema was found. | -| `result` | Record string | If the parameter `keys` is a string[ ] and the schema was found. | -| `result` | null | If the schema was not found. | +| Name | Type | Description | +| :------- | :----------------- | :-------------------------------------------------------------------- | +| `result` | `ERC725JSONSchema` | If the parameter `keys` is a string and the schema was found. | +| `result` | `string[]` | If the parameter `keys` is a string[ ] and the schema was found. | +| `result` | `null` | If the schema was not found. | #### Example using a predefined LSP3 schema @@ -1998,14 +1998,14 @@ getDataFromExternalSources(schema, dataFromChain, ipfsGateway, [ ]); ``` -Retrieve the data(s) stored on IPFS using one (or multiple) IPFS CID(s) and link(s) under the `dataFromChain` parameter. +Retrieve the data stored on IPFS using one (or multiple) IPFS CID(s) and link(s) under the `dataFromChain` parameter. #### Parameters | Name | Type | Description | | :-------------------------- | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | | `schema` | `ERC725JSONSchema[]` | A list of JSON schemas for some given data keys. | -| `dataFromChain` | `DecodeDataOutput[]` | ... | +| `dataFromChain` | `DecodeDataOutput[]` | A list of data output previously fetched from the ERC725Y contract via [`getData(...)](#getdata) or [`fetchData`](#fetchdata) | | `ipfsGateway` | `string` | The URL to use to fetch data from the IPFS network. | | `throwException` (optional) | `boolean` | Define if the function should throw an error when fetching data. Set to `true` by default. This is useful for handling exceptions differently. | @@ -2037,15 +2037,19 @@ interface URLDataWithHash extends URLData { interface URLData { url: string; } + +interface GetDataExternalSourcesOutput extends DecodeDataOutput { + value: any; +} ``` #### Returns -| Name | Type | Description | -| :--- | :------- | :---------- | -| | `object` | ... | +| Name | Type | Description | +| :--- | :------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| | `GetDataExternalSourcesOutput[]` (see above) | An array of objects that contain the result of the data fetch inside the `.value` property. Can be anything from a `string` to a JSON object. | #### Examples @@ -2140,10 +2144,6 @@ getVerificationMethod(nameOrSig); Get the verification method definition, including the name, signature and related method. -method: (data: string | object | Uint8Array | null) => string; -name: SUPPORTED_VERIFICATION_METHOD_STRINGS; -sig: SUPPORTED_VERIFICATION_METHODS; - #### Parameters | Name | Type | Description |