Skip to content

Commit

Permalink
feat: add support for new getData([])
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugoo committed Oct 29, 2021
1 parent daadd0d commit 0ef7f27
Show file tree
Hide file tree
Showing 8 changed files with 832 additions and 261 deletions.
352 changes: 224 additions & 128 deletions src/index.test.ts

Large diffs are not rendered by default.

22 changes: 21 additions & 1 deletion src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@ import { numberToHex, keccak256 } from 'web3-utils';

import { MethodData, Encoding, Method } from '../types/Method';

// https://github.com/ERC725Alliance/ERC725/blob/develop/docs/ERC-725.md#specification
export const INTERFACE_IDS = {
ERC725Y_LEGACY: '0x2bd57b73',
ERC725Y: '0x5a988c0f',
};

export const METHODS: Record<Method, MethodData> = {
[Method.GET_DATA]: {
[Method.GET_DATA_LEGACY]: {
sig: '0x54f6127f',
gas: numberToHex(2000000),
gasPrice: numberToHex(100000000),
value: numberToHex(0),
returnEncoding: Encoding.BYTES,
},
[Method.GET_DATA]: {
sig: '0x4e3e6e9c',
gas: numberToHex(2000000),
gasPrice: numberToHex(100000000),
value: numberToHex(0),
returnEncoding: Encoding.BYTES_ARRAY,
},
[Method.DATA_COUNT]: {
sig: '0x5da40c47',
gas: numberToHex(2000000),
Expand All @@ -32,6 +45,13 @@ export const METHODS: Record<Method, MethodData> = {
value: numberToHex(0),
returnEncoding: Encoding.ADDRESS,
},
[Method.SUPPORTS_INTERFACE]: {
sig: '0x01ffc9a7',
gas: numberToHex(2000000),
gasPrice: numberToHex(100000000),
value: numberToHex(0),
returnEncoding: Encoding.BOOL,
},
};

export enum SUPPORTED_HASH_FUNCTION_STRINGS {
Expand Down
64 changes: 58 additions & 6 deletions src/providers/ethereumProviderWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

import * as abi from 'web3-eth-abi';

import { METHODS } from '../lib/constants';
import { INTERFACE_IDS, METHODS } from '../lib/constants';
import { Method } from '../types/Method';
import { ProviderTypes } from '../types/provider';

Expand All @@ -49,11 +49,59 @@ export class EthereumProviderWrapper {
return this.decodeResult(Method.OWNER, result);
}

/**
* https://eips.ethereum.org/EIPS/eip-165
*
* @param address the smart contract address
* @param interfaceId ERC-165 identifier as described here: https://github.com/ERC725Alliance/ERC725/blob/develop/docs/ERC-725.md#specification
*/
async supportsInterface(address: string, interfaceId: string) {
return this.decodeResult(
Method.SUPPORTS_INTERFACE,
await this.callContract([
this.constructJSONRPC(
address,
Method.SUPPORTS_INTERFACE,
`${interfaceId}${'00000000000000000000000000000000000000000000000000000000'}`,
),
]),
);
}

async getData(address: string, keyHash: string) {
const result = await this.callContract([
this.constructJSONRPC(address, Method.GET_DATA, keyHash),
]);
return this.decodeResult(Method.GET_DATA, result);
let isErc725Y = false;
let isErc725YLegacy = false;

isErc725Y = await this.supportsInterface(address, INTERFACE_IDS.ERC725Y);

if (!isErc725Y) {
isErc725YLegacy = await this.supportsInterface(
address,
INTERFACE_IDS.ERC725Y_LEGACY,
);
}

if (!isErc725Y && !isErc725YLegacy) {
throw new Error(
`Contract: ${address} does not support ERC725Y interface.`,
);
}

if (isErc725Y) {
return this.decodeResult(
Method.GET_DATA,
await this.callContract([
this.constructJSONRPC(address, Method.GET_DATA, keyHash),
]),
)[0];
}

return this.decodeResult(
Method.GET_DATA_LEGACY,
await this.callContract([
this.constructJSONRPC(address, Method.GET_DATA_LEGACY, keyHash),
]),
);
}

async getAllData(address: string, keys: string[]) {
Expand All @@ -63,9 +111,13 @@ export class EthereumProviderWrapper {
}[] = [];

for (let index = 0; index < keys.length; index++) {
// TODO: call getData with array instead of multiple calls with 1 element
const value = await this.getData(address, keys[index]);

results.push({
key: keys[index],
value: await this.getData(address, keys[index]),
// TODO: get the interface id here to prevent multiple calls in getData
value,
});
}

Expand Down
101 changes: 94 additions & 7 deletions src/providers/web3ProviderWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@
in accordance with implementation of smart contract interfaces of ERC725
*/

import AbiCoder from 'web3-eth-abi';

import { JsonRpc } from '../types/JsonRpc';
import { Method } from '../types/Method';
import { constructJSONRPC, decodeResult } from '../lib/provider-wrapper-utils';
import { ProviderTypes } from '../types/provider';
import { INTERFACE_IDS } from '../lib/constants';

// TS can't get the types from the import...
// @ts-ignore
const abiCoder: AbiCoder.AbiCoder = AbiCoder;

export class Web3ProviderWrapper {
type: ProviderTypes;
Expand All @@ -46,19 +53,97 @@ export class Web3ProviderWrapper {
return decodeResult(Method.OWNER, result);
}

/**
* https://eips.ethereum.org/EIPS/eip-165
*
* @param address the smart contract address
* @param interfaceId ERC-165 identifier as described here: https://github.com/ERC725Alliance/ERC725/blob/develop/docs/ERC-725.md#specification
*/
async supportsInterface(address: string, interfaceId: string) {
return decodeResult(
Method.SUPPORTS_INTERFACE,
await this.callContract(
constructJSONRPC(
address,
Method.SUPPORTS_INTERFACE,
`${interfaceId}${'00000000000000000000000000000000000000000000000000000000'}`,
),
),
);
}

async getData(address: string, keyHash: string) {
let isErc725Y = false;
let isErc725YLegacy = false;

isErc725Y = await this.supportsInterface(address, INTERFACE_IDS.ERC725Y);

if (!isErc725Y) {
isErc725YLegacy = await this.supportsInterface(
address,
INTERFACE_IDS.ERC725Y_LEGACY,
);
}

if (!isErc725Y && !isErc725YLegacy) {
throw new Error(
`Contract: ${address} does not support ERC725Y interface.`,
);
}

if (isErc725Y) {
return decodeResult(
Method.GET_DATA,
await this.callContract(
constructJSONRPC(
address,
Method.GET_DATA,
abiCoder.encodeParameter('bytes32[]', [keyHash]),
),
),
)[0];
}

return decodeResult(
Method.GET_DATA,
Method.GET_DATA_LEGACY,
await this.callContract(
constructJSONRPC(address, Method.GET_DATA, keyHash),
constructJSONRPC(address, Method.GET_DATA_LEGACY, keyHash),
),
);
}

async getAllData(address: string, keys: string[]) {
let isErc725Y = false;
let isErc725YLegacy = false;

isErc725Y = await this.supportsInterface(address, INTERFACE_IDS.ERC725Y);

if (!isErc725Y) {
isErc725YLegacy = await this.supportsInterface(
address,
INTERFACE_IDS.ERC725Y_LEGACY,
);
}

if (!isErc725Y && !isErc725YLegacy) {
throw new Error(
`Contract: ${address} does not support ERC725Y interface.`,
);
}

const method = isErc725Y ? Method.GET_DATA : Method.GET_DATA_LEGACY;

const payload: JsonRpc[] = [];
for (let index = 0; index < keys.length; index++) {
payload.push(constructJSONRPC(address, Method.GET_DATA, keys[index]));
payload.push(
constructJSONRPC(
address,
method,
isErc725Y
? abiCoder.encodeParameter('bytes32[]', [keys[index]])
: keys[index],
),
);
}

const results: any = await this.callContract(payload);
Expand All @@ -69,12 +154,14 @@ export class Web3ProviderWrapper {
value: Record<string, any> | null;
}[] = [];
for (let index = 0; index < payload.length; index++) {
const decodedValue = decodeResult(
method,
results.find((element) => payload[index].id === element.id),
);

returnValues.push({
key: keys[index],
value: decodeResult(
Method.GET_DATA,
results.find((element) => payload[index].id === element.id),
),
value: isErc725Y ? decodedValue[0] : decodedValue,
});
}

Expand Down
6 changes: 5 additions & 1 deletion src/types/Method.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
export enum Method {
GET_DATA = 'getData',
GET_DATA_LEGACY = 'getDataLegacy', // For legacy ERC725 with interface id: 0x2bd57b73 NOTE: I had to add Legacy at the end so the map keys stays unique
GET_DATA = 'getData', // For latest ERC725 with interface id: 0x5a988c0f
DATA_COUNT = 'dataCount',
ALL_DATA = 'allData',
OWNER = 'owner',
SUPPORTS_INTERFACE = 'supportsInterface',
}

export enum Encoding {
BYTES = 'bytes',
BOOL = 'bool',
UINT256 = 'uint256',
BYTES32_ARRAY = 'bytes32[]',
BYTES_ARRAY = 'bytes[]',
ADDRESS = 'address',
}

Expand Down
Loading

0 comments on commit 0ef7f27

Please sign in to comment.