Skip to content

Commit

Permalink
Merge pull request #294 from YamenMerhi/supportGetDataBatch
Browse files Browse the repository at this point in the history
refactor: support `getDataBatch` of 5.0.0 ERC725 release
  • Loading branch information
Hugoo authored Jul 14, 2023
2 parents 6a8345b + 2fd0a8c commit 1ee2700
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 17 deletions.
26 changes: 23 additions & 3 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,24 @@ export const ERC725Y_INTERFACE_IDS = {
// - getData(bytes32[])
// - setData(bytes32[],bytes[])
'3.0': '0x714df77c',
// InterfaceId of version 3 == interfaceId of version 4
// version 5.0.0 removed function overloading
// interface functions:
// - getData(bytes32)
// - setData(bytes32,bytes)
// - getDataBatch(bytes32[])
// - setDataBatch(bytes32[],bytes[])
'5.0': '0x629aa694',
};

export enum ERC725_VERSION {
ERC725 = 'ERC725', // https://github.com/ERC725Alliance/ERC725/commit/cca7f98cdf243f1ebf1c0a3ae89b1e46931481b0
ERC725_LEGACY = 'ERC725_LEGACY',
NOT_ERC725 = 'NOT_ERC725',
// The ERC725Y_LEGACY version uses getData(bytes32) function
ERC725_LEGACY = 'ERC725_LEGACY',
// The ERC725_v2 version uses getData(bytes32[]) function, as well as v3 and v4
ERC725_v2 = 'ERC725_v2', // https://github.com/ERC725Alliance/ERC725/releases/tag/v2.2.0
// The ERC725_v5 version uses getDataBatch(bytes32[]) function
ERC725_v5 = 'ERC725_v5', // https://github.com/ERC725Alliance/ERC725/releases/tag/v5.0.0
}

export const METHODS: Record<Method, MethodData> = {
Expand All @@ -52,13 +64,21 @@ export const METHODS: Record<Method, MethodData> = {
returnEncoding: Encoding.BYTES,
},
[Method.GET_DATA]: {
// https://github.com/ERC725Alliance/erc725/blob/main/docs/ERC-725.md#erc725y
// https://github.com/ERC725Alliance/ERC725/blob/v4.0.0/docs/ERC-725.md#erc725y
sig: '0x4e3e6e9c',
gas: numberToHex(2000000),
gasPrice: numberToHex(100000000),
value: numberToHex(0),
returnEncoding: Encoding.BYTES_ARRAY,
},
[Method.GET_DATA_BATCH]: {
// https://github.com/ERC725Alliance/ERC725/blob/v5.1.0/docs/ERC-725.md#erc725y
sig: '0xdedff9c6',
gas: numberToHex(2000000),
gasPrice: numberToHex(100000000),
value: numberToHex(0),
returnEncoding: Encoding.BYTES_ARRAY,
},
[Method.OWNER]: {
sig: '0x8da5cb5b',
gas: numberToHex(2000000),
Expand Down
92 changes: 91 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe('Running @erc725/erc725.js tests...', () => {
]);
});

const e2eSchema: any = [
const e2eSchema: ERC725JSONSchema[] = [
{
name: 'LSP3Profile',
key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5',
Expand Down Expand Up @@ -304,6 +304,96 @@ describe('Running @erc725/erc725.js tests...', () => {
});
});

describe('Getting data (using new getDataBatch) in schema', () => {
const ERC725_V5_CONTRACT_ADDRESS =
'0x4b30900F119E11D2A8CAe18176c4f9840E586Cc4';
const web3 = new Web3('https://rpc.testnet.lukso.network');

describe('By HttpProvider', () => {
const provider = new HttpProvider(
{
returnData: [
{
key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47',
value:
'0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001436e4Eb6Ee168EF54B1E8e850ACBE51045214B313000000000000000000000000',
},
],
},
[ERC725Y_INTERFACE_IDS['5.0']],
);

it('with http provider [ERC725Y_BATCH]', async () => {
const erc725 = new ERC725(
[
{
name: 'LSP1UniversalReceiverDelegate',
key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47',
keyType: 'Singleton',
valueContent: 'Address',
valueType: 'address',
},
],
'0x24464DbA7e7781a21eD86133Ebe88Eb9C0762620', // result is mocked so we can use any address
provider,
);

const [result] = await erc725.getData();
assert.deepStrictEqual(result, {
name: 'LSP1UniversalReceiverDelegate',
key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47',
value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313',
});
});
});

describe('By provider [e2e] - luksoTestnet', () => {
const e2eSchema: ERC725JSONSchema[] = [
{
name: 'LSP3Profile',
key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5',
keyType: 'Singleton',
valueContent: 'JSONURL',
valueType: 'bytes',
},
{
name: 'LSP1UniversalReceiverDelegate',
key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47',
keyType: 'Singleton',
valueContent: 'Address',
valueType: 'address',
},
];

const e2eResults = [
{
name: 'LSP3Profile',
key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5',
value: {
hashFunction: 'keccak256(utf8)',
hash: '0x70546a2accab18748420b63c63b5af4cf710848ae83afc0c51dd8ad17fb5e8b3',
url: 'ipfs://QmecrGejUQVXpW4zS948pNvcnQrJ1KiAoM6bdfrVcWZsn5',
},
},
{
name: 'LSP1UniversalReceiverDelegate',
key: '0x0cfc51aec37c55a4d0b1a65c6255c4bf2fbdf6277f3cc0730c45b828b6db8b47',
value: '0x36e4Eb6Ee168EF54B1E8e850ACBE51045214B313',
},
];

it('with web3.currentProvider [ERC725Y_BATCH]', async () => {
const erc725 = new ERC725(
e2eSchema,
ERC725_V5_CONTRACT_ADDRESS,
web3.currentProvider,
);
const result = await erc725.getData();
assert.deepStrictEqual(result, e2eResults);
});
});
});

describe('Get/fetch edge cases [mock]', () => {
it('should return null if the JSONURL is not set [fetchData]', async () => {
const provider = new HttpProvider(
Expand Down
43 changes: 30 additions & 13 deletions src/provider/providerWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,32 @@ export class ProviderWrapper {
}

async getErc725YVersion(address: string): Promise<ERC725_VERSION> {
const isErc725Y = await this.supportsInterface(
const isErc725Yv5 = await this.supportsInterface(
address,
ERC725Y_INTERFACE_IDS['5.0'],
);

if (isErc725Yv5) {
return ERC725_VERSION.ERC725_v5;
}

const isErc725Yv3 = await this.supportsInterface(
address,
ERC725Y_INTERFACE_IDS['3.0'],
);

if (isErc725Y) {
return ERC725_VERSION.ERC725;
// The version 3 of the package can use the getData function from v2, still compatible
if (isErc725Yv3) {
return ERC725_VERSION.ERC725_v2;
}

const isErc725Yv200 = await this.supportsInterface(
const isErc725Yv2 = await this.supportsInterface(
address,
ERC725Y_INTERFACE_IDS['2.0'],
);

if (isErc725Yv200) {
return ERC725_VERSION.ERC725;
if (isErc725Yv2) {
return ERC725_VERSION.ERC725_v2;
}

// v0.2.0 and v0.6.0 have the same function signatures for getData, only versions before v0.2.0 requires a different call
Expand Down Expand Up @@ -197,29 +207,36 @@ export class ProviderWrapper {
}

switch (erc725Version) {
case ERC725_VERSION.ERC725:
return this._getAllData(address, keyHashes);
case ERC725_VERSION.ERC725_v5:
return this._getAllDataGeneric(
address,
keyHashes,
Method.GET_DATA_BATCH,
);
case ERC725_VERSION.ERC725_v2:
return this._getAllDataGeneric(address, keyHashes, Method.GET_DATA);
case ERC725_VERSION.ERC725_LEGACY:
return this._getAllDataLegacy(address, keyHashes);
default:
return [];
}
}

private async _getAllData(
private async _getAllDataGeneric(
address: string,
keyHashes: string[],
method: Method.GET_DATA | Method.GET_DATA_BATCH,
): Promise<GetDataReturn[]> {
if (this.type === ProviderTypes.ETHEREUM) {
const encodedResults = await this.callContract(
constructJSONRPC(
address,
Method.GET_DATA,
method,
abiCoder.encodeParameter('bytes32[]', keyHashes),
),
);

const decodedValues = decodeResult(Method.GET_DATA, encodedResults);
const decodedValues = decodeResult(method, encodedResults);

return keyHashes.map<GetDataReturn>((keyHash, index) => ({
key: keyHash,
Expand All @@ -230,13 +247,13 @@ export class ProviderWrapper {
const payload: JsonRpc[] = [
constructJSONRPC(
address,
Method.GET_DATA,
method,
abiCoder.encodeParameter('bytes32[]', keyHashes),
),
];

const results: any = await this.callContract(payload);
const decodedValues = decodeResult(Method.GET_DATA, results[0].result);
const decodedValues = decodeResult(method, results[0].result);

return keyHashes.map<GetDataReturn>((key, index) => ({
key,
Expand Down
1 change: 1 addition & 0 deletions src/types/Method.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum Method {
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
GET_DATA_BATCH = 'getDataBatch',
OWNER = 'owner',
SUPPORTS_INTERFACE = 'supportsInterface', // https://eips.ethereum.org/EIPS/eip-165
IS_VALID_SIGNATURE = 'isValidSignature', // https://eips.ethereum.org/EIPS/eip-1271
Expand Down
2 changes: 2 additions & 0 deletions test/mockProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export class HttpProvider {
});
break;
}
case METHODS[Method.GET_DATA_BATCH].sig:
case METHODS[Method.GET_DATA].sig:
// The new ERC725Y allows requesting multiple items in one call
// getData([A]), getData([A, B, C])...
Expand Down Expand Up @@ -147,6 +148,7 @@ export class HttpProvider {
result = foundResult ? foundResult.value : '0x';
}
break;
case METHODS[Method.GET_DATA_BATCH].sig:
case METHODS[Method.GET_DATA].sig:
{
const keyParam = '0x' + payload.params[0].data.slice(138);
Expand Down

0 comments on commit 1ee2700

Please sign in to comment.