mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-07-29 03:12:10 +00:00
Use getStorageValue to parse value for string type variable (#29)
* Get string value of variable name. * Use getStorageValue to parse value for string type variable. Co-authored-by: nikugogoi <95nikass@gmail.com>
This commit is contained in:
parent
b243025ca8
commit
00eb129536
@ -1,3 +1,3 @@
|
|||||||
export { getStorageValue, getStorageInfo, StorageLayout, GetStorageAt } from './storage';
|
export { getStorageValue, getStorageInfo, getValueByType, StorageLayout, GetStorageAt } from './storage';
|
||||||
|
|
||||||
export { getEventNameTopics } from './logs';
|
export { getEventNameTopics } from './logs';
|
||||||
|
@ -74,21 +74,29 @@ it('get storage information', async function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Get value from storage', function () {
|
describe('Get value from storage', function () {
|
||||||
|
const getBlockHash = async () => {
|
||||||
|
const blockNumber = await ethers.provider.getBlockNumber();
|
||||||
|
const { hash } = await ethers.provider.getBlock(blockNumber);
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
it('get value for integer type variables packed together', async function () {
|
it('get value for integer type variables packed together', async function () {
|
||||||
const Integers = await ethers.getContractFactory('TestIntegers');
|
const Integers = await ethers.getContractFactory('TestIntegers');
|
||||||
const integers = await Integers.deploy();
|
const integers = await Integers.deploy();
|
||||||
await integers.deployed();
|
await integers.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestIntegers');
|
const storageLayout = await getStorageLayout('TestIntegers');
|
||||||
|
|
||||||
let value = 12;
|
let expectedValue = 12;
|
||||||
await integers.setInt1(value);
|
await integers.setInt1(expectedValue);
|
||||||
let storageValue = await getStorageValue(integers.address, storageLayout, getStorageAt, 'int1');
|
let blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, integers.address, 'int1');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
|
|
||||||
value = 34;
|
expectedValue = 34;
|
||||||
await integers.setInt2(value);
|
await integers.setInt2(expectedValue);
|
||||||
storageValue = await getStorageValue(integers.address, storageLayout, getStorageAt, 'int2');
|
blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, integers.address, 'int2'));
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for integer type variables using single slot', async function () {
|
it('get value for integer type variables using single slot', async function () {
|
||||||
@ -97,10 +105,11 @@ describe('Get value from storage', function () {
|
|||||||
await integers.deployed();
|
await integers.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestIntegers');
|
const storageLayout = await getStorageLayout('TestIntegers');
|
||||||
|
|
||||||
const value = 123;
|
const expectedValue = 123;
|
||||||
await integers.setInt3(value);
|
await integers.setInt3(expectedValue);
|
||||||
const storageValue = await getStorageValue(integers.address, storageLayout, getStorageAt, 'int3');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, integers.address, 'int3');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for unsigned integer type variables packed together', async function () {
|
it('get value for unsigned integer type variables packed together', async function () {
|
||||||
@ -109,15 +118,17 @@ describe('Get value from storage', function () {
|
|||||||
await unsignedIntegers.deployed();
|
await unsignedIntegers.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestUnsignedIntegers');
|
const storageLayout = await getStorageLayout('TestUnsignedIntegers');
|
||||||
|
|
||||||
let value = 12;
|
let expectedValue = 12;
|
||||||
await unsignedIntegers.setUint1(value);
|
await unsignedIntegers.setUint1(expectedValue);
|
||||||
let storageValue = await getStorageValue(unsignedIntegers.address, storageLayout, getStorageAt, 'uint1');
|
let blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, unsignedIntegers.address, 'uint1');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
|
|
||||||
value = 34;
|
expectedValue = 34;
|
||||||
await unsignedIntegers.setUint2(value);
|
await unsignedIntegers.setUint2(expectedValue);
|
||||||
storageValue = await getStorageValue(unsignedIntegers.address, storageLayout, getStorageAt, 'uint2');
|
blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, unsignedIntegers.address, 'uint2'));
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for unsigned integer type variables using single slot', async function () {
|
it('get value for unsigned integer type variables using single slot', async function () {
|
||||||
@ -126,10 +137,11 @@ describe('Get value from storage', function () {
|
|||||||
await unsignedIntegers.deployed();
|
await unsignedIntegers.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestUnsignedIntegers');
|
const storageLayout = await getStorageLayout('TestUnsignedIntegers');
|
||||||
|
|
||||||
const value = 123;
|
const expectedValue = 123;
|
||||||
await unsignedIntegers.setUint3(value);
|
await unsignedIntegers.setUint3(expectedValue);
|
||||||
const storageValue = await getStorageValue(unsignedIntegers.address, storageLayout, getStorageAt, 'uint3');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, unsignedIntegers.address, 'uint3');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for boolean type', async function () {
|
it('get value for boolean type', async function () {
|
||||||
@ -138,15 +150,17 @@ describe('Get value from storage', function () {
|
|||||||
await booleans.deployed();
|
await booleans.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestBooleans');
|
const storageLayout = await getStorageLayout('TestBooleans');
|
||||||
|
|
||||||
let value = true;
|
let expectedValue = true;
|
||||||
await booleans.setBool1(value);
|
await booleans.setBool1(expectedValue);
|
||||||
let storageValue = await getStorageValue(booleans.address, storageLayout, getStorageAt, 'bool1');
|
let blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, booleans.address, 'bool1');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
|
|
||||||
value = false;
|
expectedValue = false;
|
||||||
await booleans.setBool2(value);
|
await booleans.setBool2(expectedValue);
|
||||||
storageValue = await getStorageValue(booleans.address, storageLayout, getStorageAt, 'bool2');
|
blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, booleans.address, 'bool2'));
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for address type', async function () {
|
it('get value for address type', async function () {
|
||||||
@ -157,9 +171,10 @@ describe('Get value from storage', function () {
|
|||||||
|
|
||||||
const [signer] = await ethers.getSigners();
|
const [signer] = await ethers.getSigners();
|
||||||
await address.setAddress1(signer.address);
|
await address.setAddress1(signer.address);
|
||||||
const storageValue = await getStorageValue(address.address, storageLayout, getStorageAt, 'address1');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.be.a('string');
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, address.address, 'address1');
|
||||||
expect(String(storageValue).toLowerCase()).to.equal(signer.address.toLowerCase());
|
expect(value).to.be.a('string');
|
||||||
|
expect(String(value).toLowerCase()).to.equal(signer.address.toLowerCase());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for contract type', async function () {
|
it('get value for contract type', async function () {
|
||||||
@ -175,8 +190,9 @@ describe('Get value from storage', function () {
|
|||||||
const storageLayout = await getStorageLayout('TestContractTypes');
|
const storageLayout = await getStorageLayout('TestContractTypes');
|
||||||
|
|
||||||
await testContractTypes.setAddressContract1(testAddress.address);
|
await testContractTypes.setAddressContract1(testAddress.address);
|
||||||
const storageValue = await getStorageValue(testContractTypes.address, storageLayout, getStorageAt, 'addressContract1');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(testAddress.address.toLowerCase());
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testContractTypes.address, 'addressContract1');
|
||||||
|
expect(value).to.equal(testAddress.address.toLowerCase());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for fixed size byte arrays packed together', async function () {
|
it('get value for fixed size byte arrays packed together', async function () {
|
||||||
@ -185,15 +201,17 @@ describe('Get value from storage', function () {
|
|||||||
await testBytes.deployed();
|
await testBytes.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestBytes');
|
const storageLayout = await getStorageLayout('TestBytes');
|
||||||
|
|
||||||
let value = ethers.utils.hexlify(ethers.utils.randomBytes(10));
|
let expectedValue = ethers.utils.hexlify(ethers.utils.randomBytes(10));
|
||||||
await testBytes.setBytesTen(value);
|
await testBytes.setBytesTen(expectedValue);
|
||||||
let storageValue = await getStorageValue(testBytes.address, storageLayout, getStorageAt, 'bytesTen');
|
let blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
let { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testBytes.address, 'bytesTen');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
|
|
||||||
value = ethers.utils.hexlify(ethers.utils.randomBytes(20));
|
expectedValue = ethers.utils.hexlify(ethers.utils.randomBytes(20));
|
||||||
await testBytes.setBytesTwenty(value);
|
await testBytes.setBytesTwenty(expectedValue);
|
||||||
storageValue = await getStorageValue(testBytes.address, storageLayout, getStorageAt, 'bytesTwenty');
|
blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
({ value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testBytes.address, 'bytesTwenty'));
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for fixed size byte arrays using single slot', async function () {
|
it('get value for fixed size byte arrays using single slot', async function () {
|
||||||
@ -202,10 +220,11 @@ describe('Get value from storage', function () {
|
|||||||
await testBytes.deployed();
|
await testBytes.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestBytes');
|
const storageLayout = await getStorageLayout('TestBytes');
|
||||||
|
|
||||||
const value = ethers.utils.hexlify(ethers.utils.randomBytes(30));
|
const expectedValue = ethers.utils.hexlify(ethers.utils.randomBytes(30));
|
||||||
await testBytes.setBytesThirty(value);
|
await testBytes.setBytesThirty(expectedValue);
|
||||||
const storageValue = await getStorageValue(testBytes.address, storageLayout, getStorageAt, 'bytesThirty');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testBytes.address, 'bytesThirty');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for enum types', async function () {
|
it('get value for enum types', async function () {
|
||||||
@ -214,10 +233,11 @@ describe('Get value from storage', function () {
|
|||||||
await testEnums.deployed();
|
await testEnums.deployed();
|
||||||
const storageLayout = await getStorageLayout('TestEnums');
|
const storageLayout = await getStorageLayout('TestEnums');
|
||||||
|
|
||||||
const value = 1;
|
const expectedValue = 1;
|
||||||
await testEnums.setChoicesEnum1(value);
|
await testEnums.setChoicesEnum1(expectedValue);
|
||||||
const storageValue = await getStorageValue(testEnums.address, storageLayout, getStorageAt, 'choicesEnum1');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testEnums.address, 'choicesEnum1');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('string type', function () {
|
describe('string type', function () {
|
||||||
@ -231,17 +251,19 @@ describe('Get value from storage', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('get value for string length less than 32 bytes', async function () {
|
it('get value for string length less than 32 bytes', async function () {
|
||||||
const value = 'Hello world.';
|
const expectedValue = 'Hello world.';
|
||||||
await strings.setString1(value);
|
await strings.setString1(expectedValue);
|
||||||
const storageValue = await getStorageValue(strings.address, storageLayout, getStorageAt, 'string1');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, strings.address, 'string1');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for string length more than 32 bytes', async function () {
|
it('get value for string length more than 32 bytes', async function () {
|
||||||
const value = 'This sentence is more than 32 bytes long.';
|
const expectedValue = 'This sentence is more than 32 bytes long.';
|
||||||
await strings.setString2(value);
|
await strings.setString2(expectedValue);
|
||||||
const storageValue = await getStorageValue(strings.address, storageLayout, getStorageAt, 'string2');
|
const blockHash = await getBlockHash();
|
||||||
expect(storageValue).to.equal(value);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, strings.address, 'string2');
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ export interface StorageInfo extends Storage {
|
|||||||
types: { [type: string]: Type; }
|
types: { [type: string]: Type; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetStorageAt = (address: string, position: string) => Promise<string>
|
export type GetStorageAt = (param: { blockHash: string, contract: string, slot: string }) => Promise<{ value: string, proof: { data: string } }>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to get storage information of variable from storage layout.
|
* Function to get storage information of variable from storage layout.
|
||||||
@ -47,96 +47,123 @@ export const getStorageInfo = (storageLayout: StorageLayout, variableName: strin
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to get the value from storage for a contract variable.
|
* Function to get the value from storage for a contract variable.
|
||||||
* @param address
|
|
||||||
* @param storageLayout
|
* @param storageLayout
|
||||||
* @param getStorageAt
|
* @param getStorageAt
|
||||||
|
* @param blockHash
|
||||||
|
* @param address
|
||||||
* @param variableName
|
* @param variableName
|
||||||
*/
|
*/
|
||||||
export const getStorageValue = async (address: string, storageLayout: StorageLayout, getStorageAt: GetStorageAt, variableName: string): Promise<number | string | boolean | undefined> => {
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
export const getStorageValue = async (storageLayout: StorageLayout, getStorageAt: GetStorageAt, blockHash: string, address: string, variableName: string): Promise<{ value: any, proof: { data: string } }> => {
|
||||||
const { slot, offset, type, types } = getStorageInfo(storageLayout, variableName);
|
const { slot, offset, type, types } = getStorageInfo(storageLayout, variableName);
|
||||||
const { encoding, numberOfBytes, label } = types[type];
|
const { encoding, numberOfBytes, label: typeLabel } = types[type];
|
||||||
|
let value: string, proof: { data: string };
|
||||||
|
|
||||||
// Get value according to encoding i.e. how the data is encoded in storage.
|
// Get value according to encoding i.e. how the data is encoded in storage.
|
||||||
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#json-output
|
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#json-output
|
||||||
switch (encoding) {
|
switch (encoding) {
|
||||||
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#layout-of-state-variables-in-storage
|
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#layout-of-state-variables-in-storage
|
||||||
case 'inplace': {
|
case 'inplace':
|
||||||
const valueArray = await getInplaceArray(address, slot, offset, numberOfBytes, getStorageAt);
|
({ value, proof } = await getInplaceValue(blockHash, address, slot, offset, numberOfBytes, getStorageAt));
|
||||||
|
break;
|
||||||
// Parse value for boolean type.
|
|
||||||
if (label === 'bool') {
|
|
||||||
return !BigNumber.from(valueArray).isZero();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse value for uint/int type.
|
|
||||||
if (label.match(/^enum|u?int[0-9]+/)) {
|
|
||||||
return BigNumber.from(valueArray).toNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.hexlify(valueArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#bytes-and-string
|
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#bytes-and-string
|
||||||
case 'bytes': {
|
case 'bytes':
|
||||||
const valueArray = await getBytesArray(address, slot, getStorageAt);
|
({ value, proof } = await getBytesValue(blockHash, address, slot, getStorageAt));
|
||||||
|
break;
|
||||||
return utils.toUtf8String(valueArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
throw new Error(`Encoding ${encoding} not implemented.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: getValueByType(value, typeLabel),
|
||||||
|
proof
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to get array value for inplace encoding.
|
* Get value according to type described by the label.
|
||||||
|
* @param storageValue
|
||||||
|
* @param typeLabel
|
||||||
|
*/
|
||||||
|
// getStorageByType
|
||||||
|
export const getValueByType = (storageValue: string, typeLabel: string): number | string | boolean => {
|
||||||
|
// Parse value for boolean type.
|
||||||
|
if (typeLabel === 'bool') {
|
||||||
|
return !BigNumber.from(storageValue).isZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse value for uint/int type or enum type.
|
||||||
|
if (typeLabel.match(/^enum|u?int[0-9]+/)) {
|
||||||
|
return BigNumber.from(storageValue).toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse value for string type.
|
||||||
|
if (typeLabel.includes('string')) {
|
||||||
|
return utils.toUtf8String(storageValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return storageValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to get value for inplace encoding.
|
||||||
* @param address
|
* @param address
|
||||||
* @param slot
|
* @param slot
|
||||||
* @param offset
|
* @param offset
|
||||||
* @param numberOfBytes
|
* @param numberOfBytes
|
||||||
* @param getStorageAt
|
* @param getStorageAt
|
||||||
*/
|
*/
|
||||||
const getInplaceArray = async (address: string, slot: string, offset: number, numberOfBytes: string, getStorageAt: GetStorageAt) => {
|
const getInplaceValue = async (blockHash: string, address: string, slot: string, offset: number, numberOfBytes: string, getStorageAt: GetStorageAt) => {
|
||||||
const value = await getStorageAt(address, slot);
|
const { value, proof } = await getStorageAt({ blockHash, contract: address, slot });
|
||||||
const uintArray = utils.arrayify(value);
|
const valueLength = utils.hexDataLength(value);
|
||||||
|
|
||||||
// Get value according to offset.
|
// Get value according to offset.
|
||||||
const start = uintArray.length - (offset + Number(numberOfBytes));
|
const start = valueLength - (offset + Number(numberOfBytes));
|
||||||
const end = uintArray.length - offset;
|
const end = valueLength - offset;
|
||||||
const offsetArray = uintArray.slice(start, end);
|
|
||||||
|
|
||||||
return offsetArray;
|
return {
|
||||||
|
value: utils.hexDataSlice(value, start, end),
|
||||||
|
proof
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to get array value for bytes encoding.
|
* Function to get value for bytes encoding.
|
||||||
* @param address
|
* @param address
|
||||||
* @param slot
|
* @param slot
|
||||||
* @param getStorageAt
|
* @param getStorageAt
|
||||||
*/
|
*/
|
||||||
const getBytesArray = async (address: string, slot: string, getStorageAt: GetStorageAt) => {
|
const getBytesValue = async (blockHash: string, address: string, slot: string, getStorageAt: GetStorageAt) => {
|
||||||
const value = await getStorageAt(address, slot);
|
const { value, proof } = await getStorageAt({ blockHash, contract: address, slot });
|
||||||
const uintArray = utils.arrayify(value);
|
|
||||||
let length = 0;
|
let length = 0;
|
||||||
|
|
||||||
// Get length of bytes stored.
|
// Get length of bytes stored.
|
||||||
if (BigNumber.from(uintArray[0]).isZero()) {
|
if (BigNumber.from(utils.hexDataSlice(value, 0, 1)).isZero()) {
|
||||||
// If first byte is not set, get length directly from the zero padded byte array.
|
// If first byte is not set, get length directly from the zero padded byte array.
|
||||||
const slotValue = BigNumber.from(value);
|
const slotValue = BigNumber.from(value);
|
||||||
length = slotValue.sub(1).div(2).toNumber();
|
length = slotValue.sub(1).div(2).toNumber();
|
||||||
} else {
|
} else {
|
||||||
// If first byte is set the length is lesser than 32 bytes.
|
// If first byte is set the length is lesser than 32 bytes.
|
||||||
// Length of the value can be computed from the last byte.
|
// Length of the value can be computed from the last byte.
|
||||||
length = BigNumber.from(uintArray[uintArray.length - 1]).div(2).toNumber();
|
const lastByteHex = utils.hexDataSlice(value, 31, 32);
|
||||||
|
length = BigNumber.from(lastByteHex).div(2).toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get value from the byte array directly if length is less than 32.
|
// Get value from the byte array directly if length is less than 32.
|
||||||
if (length < 32) {
|
if (length < 32) {
|
||||||
return uintArray.slice(0, length);
|
return {
|
||||||
|
value: utils.hexDataSlice(value, 0, length),
|
||||||
|
proof
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array to hold multiple bytes32 data.
|
// Array to hold multiple bytes32 data.
|
||||||
const values = [];
|
const values = [];
|
||||||
|
const proofs = [
|
||||||
|
JSON.parse(proof.data)
|
||||||
|
];
|
||||||
|
|
||||||
// Compute zero padded hexstring to calculate hashed position of storage.
|
// Compute zero padded hexstring to calculate hashed position of storage.
|
||||||
// https://github.com/ethers-io/ethers.js/issues/1079#issuecomment-703056242
|
// https://github.com/ethers-io/ethers.js/issues/1079#issuecomment-703056242
|
||||||
@ -145,10 +172,21 @@ const getBytesArray = async (address: string, slot: string, getStorageAt: GetSto
|
|||||||
|
|
||||||
// Get value from consecutive storage slots for longer data.
|
// Get value from consecutive storage slots for longer data.
|
||||||
for (let i = 0; i < length / 32; i++) {
|
for (let i = 0; i < length / 32; i++) {
|
||||||
const value = await getStorageAt(address, BigNumber.from(position).add(i).toHexString());
|
const { value, proof } = await getStorageAt({
|
||||||
|
blockHash,
|
||||||
|
contract: address,
|
||||||
|
slot: BigNumber.from(position).add(i).toHexString()
|
||||||
|
});
|
||||||
|
|
||||||
values.push(value);
|
values.push(value);
|
||||||
|
proofs.push(JSON.parse(proof.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slice trailing bytes according to length of value.
|
// Slice trailing bytes according to length of value.
|
||||||
return utils.concat(values).slice(0, length);
|
return {
|
||||||
|
value: utils.hexDataSlice(utils.hexConcat(values), 0, length),
|
||||||
|
proof: {
|
||||||
|
data: JSON.stringify(proofs)
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -51,8 +51,18 @@ export const getStorageLayout = async (contractName: string): Promise<StorageLay
|
|||||||
* @param address
|
* @param address
|
||||||
* @param position
|
* @param position
|
||||||
*/
|
*/
|
||||||
export const getStorageAt: GetStorageAt = async (address, position) => {
|
export const getStorageAt: GetStorageAt = async ({ blockHash, contract, slot }) => {
|
||||||
const value = await ethers.provider.getStorageAt(address, position);
|
// TODO: Fix use of blockHash as hex string in getStorageAt.
|
||||||
|
// Using blockHash in getStorageAt throws error.
|
||||||
|
// https://github.com/ethers-io/ethers.js/pull/1550#issuecomment-841746994
|
||||||
|
// Using latest tag for temporary fix.
|
||||||
|
blockHash = 'latest';
|
||||||
|
const value = await ethers.provider.getStorageAt(contract, slot, blockHash);
|
||||||
|
|
||||||
return value;
|
return {
|
||||||
|
value,
|
||||||
|
proof: {
|
||||||
|
data: JSON.stringify(null)
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import debug from 'debug';
|
|||||||
import { Connection } from "typeorm";
|
import { Connection } from "typeorm";
|
||||||
import { invert } from "lodash";
|
import { invert } from "lodash";
|
||||||
import { EthClient, getMappingSlot, topictoAddress } from "@vulcanize/ipld-eth-client";
|
import { EthClient, getMappingSlot, topictoAddress } from "@vulcanize/ipld-eth-client";
|
||||||
import { getStorageInfo, getEventNameTopics } from '@vulcanize/solidity-mapper';
|
import { getStorageInfo, getEventNameTopics, getStorageValue } from '@vulcanize/solidity-mapper';
|
||||||
|
|
||||||
import { storageLayout, abi } from './artifacts/ERC20.json';
|
import { storageLayout, abi } from './artifacts/ERC20.json';
|
||||||
|
|
||||||
@ -70,32 +70,28 @@ export class Indexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async name(blockHash, token) {
|
async name(blockHash, token) {
|
||||||
const { slot } = getStorageInfo(storageLayout, '_name');
|
const result = await getStorageValue(
|
||||||
|
storageLayout,
|
||||||
const vars = {
|
this._ethClient.getStorageAt.bind(this._ethClient),
|
||||||
blockHash,
|
blockHash,
|
||||||
contract: token,
|
token,
|
||||||
slot
|
'_name'
|
||||||
};
|
)
|
||||||
|
|
||||||
// TODO: Integrate with storage-mapper to get string value (currently hex encoded).
|
|
||||||
const result = await this._ethClient.getStorageAt(vars);
|
|
||||||
log(JSON.stringify(result, null, 2));
|
log(JSON.stringify(result, null, 2));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async symbol(blockHash, token) {
|
async symbol(blockHash, token) {
|
||||||
const { slot } = getStorageInfo(storageLayout, '_symbol');
|
const result = await getStorageValue(
|
||||||
|
storageLayout,
|
||||||
const vars = {
|
this._ethClient.getStorageAt.bind(this._ethClient),
|
||||||
blockHash,
|
blockHash,
|
||||||
contract: token,
|
token,
|
||||||
slot
|
'_symbol'
|
||||||
};
|
)
|
||||||
|
|
||||||
// TODO: Integrate with storage-mapper to get string value (currently hex encoded).
|
|
||||||
const result = await this._ethClient.getStorageAt(vars);
|
|
||||||
log(JSON.stringify(result, null, 2));
|
log(JSON.stringify(result, null, 2));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
Loading…
Reference in New Issue
Block a user