Implement getting value of struct by member name.

This commit is contained in:
nikugogoi 2021-06-11 15:57:09 +05:30 committed by Ashwin Phatak
parent b33ce0b640
commit 2fcfadecc2
3 changed files with 126 additions and 54 deletions

View File

@ -55,9 +55,9 @@ $ yarn test
* [ ] Bytes * [ ] Bytes
* [x] String * [x] String
* [ ] Structs * [ ] Structs
* [ ] Get struct value with all members * [x] Get struct value with all members
* [ ] Value Types * [x] Value Types
* [ ] Get value of a single member in struct * [x] Get value of a single member in struct
* [ ] Reference Types * [ ] Reference Types
* [ ] Mapping Types * [ ] Mapping Types
* [x] Value Type keys * [x] Value Type keys

View File

@ -378,14 +378,53 @@ describe('Get value from storage', () => {
describe('structs type', () => { describe('structs type', () => {
let testStructs: Contract, storageLayout: StorageLayout; let testStructs: Contract, storageLayout: StorageLayout;
/* eslint-disable @typescript-eslint/no-explicit-any */
let addressStruct: { [key: string]: any }, contractStruct: { [key: string]: any };
const multipleSlotStruct = {
uint1: BigInt(123),
bool1: false,
int1: BigInt(456)
};
const fixedBytesStruct = {
uint1: BigInt(123),
bytesTen: ethers.utils.hexlify(ethers.utils.randomBytes(10)),
bytesTwenty: ethers.utils.hexlify(ethers.utils.randomBytes(20))
};
const enumStruct = {
uint1: BigInt(123),
choice1: BigInt(2),
choice2: BigInt(3)
};
before(async () => { before(async () => {
const TestStructs = await ethers.getContractFactory('TestStructs'); const TestStructs = await ethers.getContractFactory('TestStructs');
testStructs = await TestStructs.deploy(); testStructs = await TestStructs.deploy();
await testStructs.deployed(); await testStructs.deployed();
storageLayout = await getStorageLayout('TestStructs'); storageLayout = await getStorageLayout('TestStructs');
const [signer1, signer2] = await ethers.getSigners();
addressStruct = {
int1: BigInt(123),
address1: signer1.address.toLowerCase(),
address2: signer2.address.toLowerCase(),
uint1: BigInt(456)
};
const Contract = await ethers.getContractFactory('TestContractTypes');
const contract = await Contract.deploy();
await contract.deployed();
contractStruct = {
uint1: BigInt(123),
testContract: contract.address.toLowerCase()
};
}); });
// Get all members of a struct.
it('get value for struct using a single slot', async () => { it('get value for struct using a single slot', async () => {
const expectedValue = { const expectedValue = {
int1: BigInt(123), int1: BigInt(123),
@ -401,84 +440,104 @@ describe('Get value from storage', () => {
}); });
it('get value for struct using multiple slots', async () => { it('get value for struct using multiple slots', async () => {
const expectedValue = { await testStructs.setMultipleSlotStruct(multipleSlotStruct.uint1, multipleSlotStruct.bool1, multipleSlotStruct.int1);
uint1: BigInt(123),
bool1: false,
int1: BigInt(456)
};
await testStructs.setMultipleSlotStruct(expectedValue.uint1, expectedValue.bool1, expectedValue.int1);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'multipleSlotStruct'); const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'multipleSlotStruct');
expect(value).to.eql(expectedValue); expect(value).to.eql(multipleSlotStruct);
const proofData = JSON.parse(proof.data); const proofData = JSON.parse(proof.data);
expect(proofData).to.have.all.keys('uint1', 'bool1', 'int1'); expect(proofData).to.have.all.keys(Object.keys(multipleSlotStruct));
}); });
it('get value for struct with address type members', async () => { it('get value for struct with address type members', async () => {
const [signer1, signer2] = await ethers.getSigners(); await testStructs.setAddressStruct(addressStruct.int1, addressStruct.address1, addressStruct.address2, addressStruct.uint1);
const expectedValue = {
int1: BigInt(123),
address1: signer1.address.toLowerCase(),
address2: signer2.address.toLowerCase(),
uint1: BigInt(456)
};
await testStructs.setAddressStruct(expectedValue.int1, expectedValue.address1, expectedValue.address2, expectedValue.uint1);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'addressStruct'); const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'addressStruct');
expect(value).to.eql(expectedValue); expect(value).to.eql(addressStruct);
const proofData = JSON.parse(proof.data); const proofData = JSON.parse(proof.data);
expect(proofData).to.have.all.keys('int1', 'address1', 'address2', 'uint1'); expect(proofData).to.have.all.keys(Object.keys(addressStruct));
}); });
it('get value for struct with contract type members', async () => { it('get value for struct with contract type members', async () => {
const Contract = await ethers.getContractFactory('TestContractTypes'); await testStructs.setContractStruct(contractStruct.uint1, contractStruct.testContract);
const contract = await Contract.deploy();
await contract.deployed();
const expectedValue = {
uint1: BigInt(123),
testContract: contract.address.toLowerCase()
};
await testStructs.setContractStruct(expectedValue.uint1, expectedValue.testContract);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'contractStruct'); const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'contractStruct');
expect(value).to.eql(expectedValue); expect(value).to.eql(contractStruct);
const proofData = JSON.parse(proof.data); const proofData = JSON.parse(proof.data);
expect(proofData).to.have.all.keys('uint1', 'testContract'); expect(proofData).to.have.all.keys(Object.keys(contractStruct));
}); });
it('get value for struct with fixed-sized byte array members', async () => { it('get value for struct with fixed-sized byte array members', async () => {
const expectedValue = { await testStructs.setFixedBytesStruct(fixedBytesStruct.uint1, fixedBytesStruct.bytesTen, fixedBytesStruct.bytesTwenty);
uint1: BigInt(123),
bytesTen: ethers.utils.hexlify(ethers.utils.randomBytes(10)),
bytesTwenty: ethers.utils.hexlify(ethers.utils.randomBytes(20))
};
await testStructs.setFixedBytesStruct(expectedValue.uint1, expectedValue.bytesTen, expectedValue.bytesTwenty);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'fixedBytesStruct'); const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'fixedBytesStruct');
expect(value).to.eql(expectedValue); expect(value).to.eql(fixedBytesStruct);
const proofData = JSON.parse(proof.data); const proofData = JSON.parse(proof.data);
expect(proofData).to.have.all.keys('uint1', 'bytesTen', 'bytesTwenty'); expect(proofData).to.have.all.keys('uint1', 'bytesTen', 'bytesTwenty');
}); });
it('get value for struct with enum type members', async () => { it('get value for struct with enum type members', async () => {
const expectedValue = { await testStructs.setEnumStruct(enumStruct.uint1, enumStruct.choice1, enumStruct.choice2);
uint1: BigInt(123),
choice1: BigInt(2),
choice2: BigInt(3)
};
await testStructs.setEnumStruct(expectedValue.uint1, expectedValue.choice1, expectedValue.choice2);
const blockHash = await getBlockHash(); const blockHash = await getBlockHash();
const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'enumStruct'); const { value, proof } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'enumStruct');
expect(value).to.eql(expectedValue); expect(value).to.eql(enumStruct);
const proofData = JSON.parse(proof.data); const proofData = JSON.parse(proof.data);
expect(proofData).to.have.all.keys('uint1', 'choice1', 'choice2'); expect(proofData).to.have.all.keys('uint1', 'choice1', 'choice2');
}); });
// Get value of a member in a struct
it('get value of signed integer type member in a struct', async () => {
const member = 'int1';
await testStructs.setMultipleSlotStruct(multipleSlotStruct.uint1, multipleSlotStruct.bool1, multipleSlotStruct.int1);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'multipleSlotStruct', member);
expect(value).to.equal(multipleSlotStruct[member]);
});
it('get value of unsigned integer type member in a struct', async () => {
const member = 'uint1';
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'multipleSlotStruct', member);
expect(value).to.equal(multipleSlotStruct[member]);
});
it('get value of boolean type member in a struct', async () => {
const member = 'bool1';
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'multipleSlotStruct', member);
expect(value).to.equal(multipleSlotStruct[member]);
});
it('get value of address type member in a struct', async () => {
const member = 'address1';
await testStructs.setAddressStruct(addressStruct.int1, addressStruct.address1, addressStruct.address2, addressStruct.uint1);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'addressStruct', member);
expect(value).to.equal(addressStruct[member]);
});
it('get value of contract type member in a struct', async () => {
const member = 'testContract';
await testStructs.setContractStruct(contractStruct.uint1, contractStruct.testContract);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'contractStruct', member);
expect(value).to.equal(contractStruct[member]);
});
it('get value of fixed byte array member in a struct', async () => {
const member = 'bytesTen';
await testStructs.setFixedBytesStruct(fixedBytesStruct.uint1, fixedBytesStruct.bytesTen, fixedBytesStruct.bytesTwenty);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'fixedBytesStruct', member);
expect(value).to.equal(fixedBytesStruct[member]);
});
it('get value of enum type member in a struct', async () => {
const member = 'choice2';
await testStructs.setEnumStruct(enumStruct.uint1, enumStruct.choice1, enumStruct.choice2);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testStructs.address, 'enumStruct', member);
expect(value).to.equal(enumStruct[member]);
});
}); });
describe('basic mapping type', () => { describe('basic mapping type', () => {

View File

@ -155,12 +155,25 @@ const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, ad
// If variable is struct type. // If variable is struct type.
if (isStruct && members) { if (isStruct && members) {
// Get value of specified member in struct.
const getStructMember = async (member: Storage, mappingKeys: MappingKey[]) => {
const structSlot = BigNumber.from(slot).add(member.slot).toHexString();
return getDecodedValue(getStorageAt, blockHash, address, types, { slot: structSlot, offset: member.offset, type: member.type }, mappingKeys);
};
const [memberName, ...remainingKeys] = mappingKeys;
const member = members.find(member => member.label === memberName);
// If member name passed in argument is present.
if (member) {
return getStructMember(member, remainingKeys);
}
// TODO: Get values in single call and parse according to type. // TODO: Get values in single call and parse according to type.
// Get member values specified for the struct in storage layout. // Get member values specified for the struct in storage layout.
const resultPromises = members.map(async member => { const resultPromises = members.map(async member => {
const structSlot = BigNumber.from(slot).add(member.slot).toHexString(); return getStructMember(member, mappingKeys);
return getDecodedValue(getStorageAt, blockHash, address, types, { slot: structSlot, offset: member.offset, type: member.type }, []);
}); });
const results = await Promise.all(resultPromises); const results = await Promise.all(resultPromises);