mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-23 11:39:05 +00:00
Implement getting value of struct by member name.
This commit is contained in:
parent
b33ce0b640
commit
2fcfadecc2
@ -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
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user