From a6fe7c8470688f573426b59fc2023a08da0cbd36 Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:03:31 +0000 Subject: [PATCH] feat: add `encode/decodeValueType` as public callable methods (#325) * feat: add `encode/decodeValueType` as public callable methods * docs: improve parameter names + add examples in documentation --- docs/classes/ERC725.md | 289 +++++++++++++++++++++++++++++------------ src/index.ts | 62 +++++++-- src/lib/encoder.ts | 10 +- 3 files changed, 262 insertions(+), 99 deletions(-) diff --git a/docs/classes/ERC725.md b/docs/classes/ERC725.md index d50f7c76..269870ce 100644 --- a/docs/classes/ERC725.md +++ b/docs/classes/ERC725.md @@ -342,6 +342,67 @@ myErc725.decodePermissions('0x00000000000000000000000000000000000000000000000000 --- +## decodeValueType + +```js +myErc725.decodeValueType(type, data); +``` + +```js +ERC725.decodeValueType(type, data); +``` + +Decode some data according to a provided value type. + +#### Parameters + +| 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 + +| 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', +); +// [ 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 +``` + ## encodeData ```js @@ -617,6 +678,154 @@ myErc725.encodeData([ --- +## encodePermissions + +```js +ERC725.encodePermissions(permissions); +``` + +Encodes permissions into a hexadecimal string as defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). + +:::info + +`encodePermissions` is available as either a static or non-static method so can be called without instantiating an ERC725 object. + +::: + +#### Parameters + +##### 1. `permissions` - Object + +An object with [LSP6 KeyManager Permissions] as keys and a `boolean` as value. Any ommited 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 +}), +// '0x0000000000000000000000000000000000000000000000000000000000000110' + +// Any ommited 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 @@ -766,86 +975,6 @@ myErc725.decodeMappingKey( --- -## encodePermissions - -```js -ERC725.encodePermissions(permissions); -``` - -Encodes permissions into a hexadecimal string as defined by the [LSP6 KeyManager Standard](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager). - -:::info - -`encodePermissions` is available as either a static or non-static method so can be called without instantiating an ERC725 object. - -::: - -#### Parameters - -##### 1. `permissions` - Object - -An object with [LSP6 KeyManager Permissions] as keys and a `boolean` as value. Any ommited 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 -}), -// '0x0000000000000000000000000000000000000000000000000000000000000110' - -// Any ommited 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, -}), -``` - ---- - ## fetchData ```js diff --git a/src/index.ts b/src/index.ts index 6cf27f14..957d5c50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -62,6 +62,7 @@ import { getDataFromExternalSources } from './lib/getDataFromExternalSources'; import { DynamicKeyPart, DynamicKeyParts } from './types/dynamicKeys'; import { getData } from './lib/getData'; import { checkPermissions } from './lib/detector'; +import { decodeValueType, encodeValueType } from './lib/encoder'; import { decodeMappingKey } from './lib/decodeMappingKey'; export { @@ -174,6 +175,21 @@ export class ERC725 { throw new Error(`Incorrect or unsupported provider ${providerOrRpcUrl}`); } + + private getAddressAndProvider() { + if (!this.options.address || !isAddress(this.options.address)) { + throw new Error('Missing ERC725 contract address.'); + } + if (!this.options.provider) { + throw new Error('Missing provider.'); + } + + return { + address: this.options.address, + provider: this.options.provider, + }; + } + /** * Gets **decoded data** for one, many or all keys of the specified `ERC725` smart-contract. * When omitting the `keyOrKeys` parameter, it will get all the keys (as per {@link ERC725JSONSchema | ERC725JSONSchema} definition). @@ -410,20 +426,6 @@ export class ERC725 { ); } - private getAddressAndProvider() { - if (!this.options.address || !isAddress(this.options.address)) { - throw new Error('Missing ERC725 contract address.'); - } - if (!this.options.provider) { - throw new Error('Missing provider.'); - } - - return { - address: this.options.address, - provider: this.options.provider, - }; - } - /** * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. * @@ -607,6 +609,38 @@ export class ERC725 { ): boolean { return ERC725.checkPermissions(requiredPermissions, grantedPermissions); } + + /** + * @param type The valueType to encode the value as + * @param value The value to encode + * @returns The encoded value + */ + static encodeValueType( + type: string, + value: string | string[] | number | number[] | boolean | boolean[], + ): string { + return encodeValueType(type, value); + } + + encodeValueType( + type: string, + value: string | string[] | number | number[] | boolean | boolean[], + ): string { + return ERC725.encodeValueType(type, value); + } + + /** + * @param type The valueType to decode the value as + * @param data The data to decode + * @returns The decoded value + */ + static decodeValueType(type: string, data: string) { + return decodeValueType(type, data); + } + + decodeValueType(type: string, data: string) { + return ERC725.decodeValueType(type, data); + } } export default ERC725; diff --git a/src/lib/encoder.ts b/src/lib/encoder.ts index 6ff989b3..42f93890 100644 --- a/src/lib/encoder.ts +++ b/src/lib/encoder.ts @@ -740,18 +740,18 @@ export function encodeValueType( return valueTypeEncodingMap[type].encode(value); } -export function decodeValueType(type: string, value: string) { +export function decodeValueType(type: string, data: string) { if (!valueTypeEncodingMap[type]) { throw new Error('Could not decode valueType: "' + type + '".'); } - if (value === '0x') return null; + if (data === '0x') return null; - if (typeof value === 'undefined' || value === null) { - return value; + if (typeof data === 'undefined' || data === null) { + return data; } - return valueTypeEncodingMap[type].decode(value); + return valueTypeEncodingMap[type].decode(data); } export function encodeValueContent(