Get value for nested array, mapping with address keys and struct value (#64)

* Add test for mapping with address keys and struct value.

* Implement getting value for nested array.

Co-authored-by: nikugogoi <95nikass@gmail.com>
This commit is contained in:
Ashwin Phatak 2021-06-15 15:46:39 +05:30 committed by GitHub
parent 3815853f7b
commit 3439dd4041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 12 deletions

View File

@ -49,7 +49,7 @@ $ yarn test
* [ ] Struct Type * [ ] Struct Type
* [ ] Mapping Type * [ ] Mapping Type
* [ ] Nested Arrays * [ ] Nested Arrays
* [ ] Fixed size arrays * [x] Fixed size arrays
* [ ] Dynamically-sized arrays * [ ] Dynamically-sized arrays
* [ ] Dynamically-sized byte array * [ ] Dynamically-sized byte array
* [ ] Bytes * [ ] Bytes

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Contract } from '@ethersproject/contracts'; import { Contract } from '@ethersproject/contracts';
import { expect } from 'chai'; import { expect } from 'chai';
import { ethers } from 'hardhat'; import { ethers } from 'hardhat';
@ -416,9 +417,63 @@ describe('Get value from storage', () => {
}); });
}); });
describe('nested arrays', () => {
let testNestedArrays: Contract, storageLayout: StorageLayout;
const nestedStructArray: Array<Array<{[key: string]: any}>> = [];
before(async () => {
const TestNestedArrays = await ethers.getContractFactory('TestNestedArrays');
testNestedArrays = await TestNestedArrays.deploy();
await testNestedArrays.deployed();
storageLayout = await getStorageLayout('TestNestedArrays');
const signers = await ethers.getSigners();
// Set value for nestedStructArray.
for (let i = 0; i < 5; i++) {
nestedStructArray[i] = [];
for (let j = 0; j < 3; j++) {
const value = {
uint1: BigInt((i + j) * 100),
address1: signers[(i + j) % 5].address.toLowerCase()
};
nestedStructArray[i][j] = value;
// Set value in contract.
await testNestedArrays.setNestedStructArray(i, j, value);
}
}
});
// Get all elements of array.
it('get value for fixed size nested array of struct type', async () => {
const blockHash = await getBlockHash();
const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedArrays.address, 'nestedStructArray');
expect(value).to.eql(nestedStructArray);
const proofData = JSON.parse(proof.data);
expect(proofData.length).to.equal(nestedStructArray.length);
expect(proofData[0].length).to.equal(nestedStructArray[0].length);
expect(proofData[0]).to.have.all.keys(Object.keys(nestedStructArray[0]));
});
// Get element of array by index.
it('get value of fixed size struct type nested array by index', async () => {
const arrayIndex = 2;
const nestedArrayIndex = 1;
const blockHash = await getBlockHash();
let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedArrays.address, 'nestedStructArray', arrayIndex, nestedArrayIndex);
expect(value).to.eql(nestedStructArray[arrayIndex][nestedArrayIndex]);
const structMember = 'address1';
({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedArrays.address, 'nestedStructArray', arrayIndex, nestedArrayIndex, structMember));
expect(value).to.equal(nestedStructArray[arrayIndex][nestedArrayIndex][structMember]);
});
});
describe('structs with value type members', () => { describe('structs with value type members', () => {
let testValueStructs: Contract, storageLayout: StorageLayout; let testValueStructs: Contract, storageLayout: StorageLayout;
/* eslint-disable @typescript-eslint/no-explicit-any */
let addressStruct: { [key: string]: any }, contractStruct: { [key: string]: any }; let addressStruct: { [key: string]: any }, contractStruct: { [key: string]: any };
const singleSlotStruct = { const singleSlotStruct = {
@ -691,7 +746,7 @@ describe('Get value from storage', () => {
expect(value).to.eql(stringStruct[member]); expect(value).to.eql(stringStruct[member]);
}); });
it.skip('get value of mapping type member in a struct', async () => { it('get value of mapping type member in a struct', async () => {
const [signer1, signer2] = await ethers.getSigners(); const [signer1, signer2] = await ethers.getSigners();
const valueMappingStruct: { [key: string]: any } = { const valueMappingStruct: { [key: string]: any } = {
@ -710,6 +765,7 @@ describe('Get value from storage', () => {
let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'valueMappingStruct', member, mappingKey); let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'valueMappingStruct', member, mappingKey);
expect(value).to.equal(valueMappingStruct[member].get(mappingKey)); expect(value).to.equal(valueMappingStruct[member].get(mappingKey));
// Get value for structs with mapping of reference type keys.
const referenceMappingStruct: { [key: string]: any } = { const referenceMappingStruct: { [key: string]: any } = {
bytesAddressMap: new Map(), bytesAddressMap: new Map(),
stringUintMap: new Map() stringUintMap: new Map()
@ -718,8 +774,8 @@ describe('Get value from storage', () => {
const bytesKey = ethers.utils.hexlify(ethers.utils.randomBytes(40)); const bytesKey = ethers.utils.hexlify(ethers.utils.randomBytes(40));
const stringKey = 'abc'; const stringKey = 'abc';
referenceMappingStruct.bytesAddressMap.set(bytesKey, signer1.address.toLowerCase()); referenceMappingStruct.bytesAddressMap.set(bytesKey, signer1.address.toLowerCase());
referenceMappingStruct.stringUintMap.set(stringKey, 123); referenceMappingStruct.stringUintMap.set(stringKey, BigInt(123));
member = 'stringAddressMap'; member = 'stringUintMap';
await testReferenceStructs.setReferenceMappingStruct(bytesKey, referenceMappingStruct.bytesAddressMap.get(bytesKey), stringKey, referenceMappingStruct.stringUintMap.get(stringKey)); await testReferenceStructs.setReferenceMappingStruct(bytesKey, referenceMappingStruct.bytesAddressMap.get(bytesKey), stringKey, referenceMappingStruct.stringUintMap.get(stringKey));
blockHash = await getBlockHash(); blockHash = await getBlockHash();
@ -877,6 +933,23 @@ describe('Get value from storage', () => {
({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'fixedBytesStructMap', mapKey, structMember)); ({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'fixedBytesStructMap', mapKey, structMember));
expect(value).to.equal(expectedValue[structMember]); expect(value).to.equal(expectedValue[structMember]);
}); });
it('get value for mapping of address type keys and struct type values', async () => {
const [signer1, signer2] = await ethers.getSigners();
const expectedValue = {
uint1: BigInt(123),
int1: BigInt(456),
bool1: true,
address1: signer1.address.toLowerCase()
};
const mapKey = signer2.address;
await testMappingTypes.setAddressStructMap(mapKey, expectedValue);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'addressStructMap', mapKey);
expect(value).to.eql(expectedValue);
});
}); });
describe('nested mapping type', () => { describe('nested mapping type', () => {

View File

@ -105,11 +105,13 @@ const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, ad
const { slot, offset, type } = storageInfo; const { slot, offset, type } = storageInfo;
const { encoding, numberOfBytes, label: typeLabel, base, value: mappingValueType, key: mappingKeyType, members } = types[type]; const { encoding, numberOfBytes, label: typeLabel, base, value: mappingValueType, key: mappingKeyType, members } = types[type];
const [isArray, arraySize] = typeLabel.match(/\[([0-9]*)\]/) || [false];
let value: string, proof: { data: string }; let value: string, proof: { data: string };
const arrayMatch = [...typeLabel.matchAll(/\[([0-9]*)\]/g)];
// If variable is array type. // If variable is array type.
if (isArray && base) { if (arrayMatch.length && base) {
const arraySize = arrayMatch[arrayMatch.length - 1][1];
return getArrayValue(getStorageAt, blockHash, address, types, mappingKeys, slot, base, Number(arraySize)); return getArrayValue(getStorageAt, blockHash, address, types, mappingKeys, slot, base, Number(arraySize));
} }

View File

@ -44,6 +44,9 @@ contract TestBasicMapping {
// Mapping with signed integer as keys and struct type values. // Mapping with signed integer as keys and struct type values.
mapping(bytes32 => TestStruct) public fixedBytesStructMap; mapping(bytes32 => TestStruct) public fixedBytesStructMap;
// Mapping with address as keys and struct type values.
mapping(address => TestStruct) public addressStructMap;
// Set variable addressUintMap. // Set variable addressUintMap.
function setAddressUintMap(uint value) external { function setAddressUintMap(uint value) external {
addressUintMap[msg.sender] = value; addressUintMap[msg.sender] = value;
@ -93,4 +96,9 @@ contract TestBasicMapping {
function setFixedBytesStructMap(bytes32 key, TestStruct calldata value) external { function setFixedBytesStructMap(bytes32 key, TestStruct calldata value) external {
fixedBytesStructMap[key] = value; fixedBytesStructMap[key] = value;
} }
// Set variable fixedBytesStructMap.
function setAddressStructMap(address key, TestStruct calldata value) external {
addressStructMap[key] = value;
}
} }

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma abicoder v2;
contract TestNestedArrays {
struct TestStruct {
uint256 uint1;
address address1;
}
TestStruct[3][5] nestedStructArray;
// Set variable nestedStructArray.
function setNestedStructArray(uint index, uint nestedIndex, TestStruct calldata value) external {
nestedStructArray[index][nestedIndex] = value;
}
}

View File

@ -6,7 +6,7 @@
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */ "lib": [ "ES2020" ], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
@ -21,7 +21,7 @@
// "removeComments": true, /* Do not emit comments to output. */ // "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */ // "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */ /* Strict Type-Checking Options */

View File

@ -6,7 +6,7 @@
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": [ "ES5", "ES6" ], /* Specify library files to be included in the compilation. */ "lib": [ "ES5", "ES6", "ES2020" ], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
@ -21,7 +21,7 @@
// "removeComments": true, /* Do not emit comments to output. */ // "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */ // "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */ /* Strict Type-Checking Options */