Fix getting values for arrays. (#73)

Co-authored-by: nikugogoi <95nikass@gmail.com>
This commit is contained in:
Ashwin Phatak 2021-06-17 11:59:53 +05:30 committed by GitHub
parent 7d609f9a2b
commit e3ef17d547
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 24 deletions

View File

@ -26,24 +26,24 @@ $ yarn test
* [x] Fixed-size byte arrays
* [x] Enums
* [ ] Function Types
* [ ] Reference Types
* [ ] Arrays
* [x] Reference Types
* [x] Arrays
* [x] Get all elements in array
* [x] Get element in array by index
* [ ] Fixed size arrays
* [x] Fixed size arrays
* [x] Integer Type
* [x] Boolean Type
* [x] Address Type
* [ ] Fixed-size byte arrays
* [x] Fixed-size byte arrays
* [x] Enum type
* [x] Dynamically-sized byte array
* [x] Struct Type
* [x] Mapping Type
* [ ] Dynamically-sized arrays
* [x] Dynamically-sized arrays
* [x] Integer Type
* [x] Boolean Type
* [x] Address Type
* [ ] Fixed-size byte arrays
* [x] Fixed-size byte arrays
* [x] Enum Type
* [x] Dynamically-sized byte array
* [x] Struct Type
@ -54,14 +54,14 @@ $ yarn test
* [x] Dynamically-sized byte array
* [x] Bytes
* [x] String
* [ ] Structs
* [x] Structs
* [x] Get struct value with all members
* [x] Value Types
* [x] Get value of a single member in struct
* [ ] Reference Types
* [x] Reference Types
* [x] Struct type members (nested)
* [x] Fixed size Array members
* [ ] Dynamically sized Array members
* [x] Dynamically sized Array members
* [x] Bytes and string type members
* [x] Mapping type members
* [ ] Mapping Types

View File

@ -367,7 +367,7 @@ describe('Get value from storage', () => {
expect(proofData.length).to.equal(addressArray.length);
});
it.skip('get value for fixed size arrays of fixed size bytes type', async () => {
it('get value for fixed size arrays of fixed size bytes type', async () => {
const expectedValue = Array.from({ length: 5 }, () => ethers.utils.hexlify(ethers.utils.randomBytes(10)));
await testFixedArrays.setFixedBytesArray(expectedValue);
@ -592,7 +592,7 @@ describe('Get value from storage', () => {
expect(value).to.equal(addressArray[arrayIndex]);
});
it.skip('get value for dynamic sized array of fixed size byte array', async () => {
it('get value for dynamic sized array of fixed size byte array', async () => {
const fixedBytesArray = Array.from({ length: 4 }, () => ethers.utils.hexlify(ethers.utils.randomBytes(10)));
await testDynamicArrays.setFixedBytesArray(fixedBytesArray);
const blockHash = await getBlockHash();
@ -1072,7 +1072,7 @@ describe('Get value from storage', () => {
};
dynamicArrayStruct = {
address1: signers[4].address,
address1: signers[4].address.toLowerCase(),
uintArray: [1, 2, 3, 4, 5].map(BigInt)
};
});
@ -1099,7 +1099,7 @@ describe('Get value from storage', () => {
expect(value).to.eql(stringStruct);
});
it.skip('get value for struct with dynamic array members', async () => {
it('get value for struct with dynamic array members', async () => {
await testReferenceStructs.setDynamicArrayStruct(dynamicArrayStruct);
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'dynamicArrayStruct');
@ -1138,7 +1138,7 @@ describe('Get value from storage', () => {
expect(value).to.eql(stringStruct[member]);
});
it.skip('get value of dynamic array member in a struct', async () => {
it('get value of dynamic array member in a struct', async () => {
const member = 'uintArray';
const blockHash = await getBlockHash();
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testReferenceStructs.address, 'dynamicArrayStruct', member);
@ -1247,6 +1247,9 @@ describe('Get value from storage', () => {
expect(value).to.equal(expectedValue);
});
// TODO: Fix getting value for mapping with keys as fixed-size byte array
// Zero value is returned if using fixed-sized byte array keys of length less than 32 bytes
// Type Bytes32 works whereas types like bytes16, bytes24 do not work.
it.skip('get value for mapping with fixed-size byte array keys', async () => {
const mapKey = ethers.utils.hexlify(ethers.utils.randomBytes(8));
const [, signer1] = await ethers.getSigners();

View File

@ -219,17 +219,22 @@ const getDynamicArrayInfo = async (getStorageAt: GetStorageAt, blockHash: string
const getArrayValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, types: Types, mappingKeys: MappingKey[], slot: string, base: string, arraySize: number) => {
const resultArray = [];
const proofs = [];
let { numberOfBytes: baseNumberOfBytes, label: baseTypeLabel } = types[base];
// Address type elements use an entire single slot i.e. 32 bytes.
if (baseTypeLabel === 'address' || baseTypeLabel.includes('contract')) {
baseNumberOfBytes = '32';
}
const { numberOfBytes: baseNumberOfBytes } = types[base];
const getArrayElement = async (mappingKeys: MappingKey[], index: number) => {
const arrayOffset = index * Number(baseNumberOfBytes);
const arraySlot = BigNumber.from(slot).add(Math.floor(arrayOffset / 32)).toHexString();
const arraySlotOffset = arrayOffset % 32;
let arraySlotOffset = 0;
let slotIndex;
if (Number(baseNumberOfBytes) <= 32) {
const elementsInSlot = Math.floor(32 / Number(baseNumberOfBytes));
slotIndex = Math.floor(index / elementsInSlot);
arraySlotOffset = (index % elementsInSlot) * Number(baseNumberOfBytes);
} else {
const slotsUsedByElement = Math.ceil(Number(baseNumberOfBytes) / 32);
slotIndex = slotsUsedByElement * index;
}
const arraySlot = BigNumber.from(slot).add(slotIndex).toHexString();
return getDecodedValue(getStorageAt, blockHash, address, types, { slot: arraySlot, offset: arraySlotOffset, type: base }, mappingKeys);
};
@ -240,7 +245,6 @@ const getArrayValue = async (getStorageAt: GetStorageAt, blockHash: string, addr
return getArrayElement(remainingKeys, arrayIndex);
}
// TODO: Get values in single call and parse according to type.
// Loop over elements of array and get value.
for (let i = 0; i < arraySize; i++) {
const { value, proof } = await getArrayElement(mappingKeys, i);
@ -309,6 +313,7 @@ const getStructureValue = async (getStorageAt: GetStorageAt, blockHash: string,
* @param getStorageAt
*/
const getInplaceValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, slot: string, offset: number, numberOfBytes: string) => {
// TODO: Memoize getStorageAt function for duplicate multiple calls.
const { value, proof } = await getStorageAt({ blockHash, contract: address, slot });
const valueLength = utils.hexDataLength(value);