Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: support getDataBatch of 5.0.0 ERC725 release #294

Merged
merged 11 commits into from
Jul 14, 2023
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
YamenMerhi marked this conversation as resolved.
Show resolved Hide resolved
// 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
Hugoo marked this conversation as resolved.
Show resolved Hide resolved
}

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
YamenMerhi marked this conversation as resolved.
Show resolved Hide resolved
sig: '0xdedff9c6',
gas: numberToHex(2000000),
gasPrice: numberToHex(100000000),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will need to make it dynamic at some point no? cc @CallumGrindle @magalimorin18

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats this being used for? The lib doesn't send any transactions does it?

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,
Hugoo marked this conversation as resolved.
Show resolved Hide resolved
): 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
Loading