From f186e6410d7f565cdc86c36c86407b87d81b31bc Mon Sep 17 00:00:00 2001 From: nabarun Date: Fri, 2 Sep 2022 15:01:08 +0530 Subject: [PATCH] Get bytes length in storageValue host API --- packages/graph-node/src/call-handler.test.ts | 6 +- packages/graph-node/src/crypto.test.ts | 6 +- packages/graph-node/src/eden.test.ts | 8 +- packages/graph-node/src/eth-abi.test.ts | 6 +- packages/graph-node/src/eth-call.test.ts | 6 +- packages/graph-node/src/json.test.ts | 6 +- packages/graph-node/src/loader.test.ts | 7 +- packages/graph-node/src/loader.ts | 39 ++++-- packages/graph-node/src/numbers.test.ts | 3 + packages/graph-node/src/storage-call.test.ts | 6 +- .../graph-node/src/type-conversion.test.ts | 6 +- packages/graph-node/src/watcher.ts | 3 +- packages/graph-node/test/utils/index.ts | 12 +- packages/graph-node/test/utils/indexer.ts | 22 +--- packages/solidity-mapper/src/index.ts | 2 +- packages/solidity-mapper/src/storage.ts | 118 ++++++++++-------- packages/util/src/types.ts | 1 - 17 files changed, 157 insertions(+), 100 deletions(-) diff --git a/packages/graph-node/src/call-handler.test.ts b/packages/graph-node/src/call-handler.test.ts index 089ec16d..14873344 100644 --- a/packages/graph-node/src/call-handler.test.ts +++ b/packages/graph-node/src/call-handler.test.ts @@ -6,10 +6,11 @@ import path from 'path'; import chai, { assert, expect } from 'chai'; import spies from 'chai-spies'; import { utils } from 'ethers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { BaseProvider } from '@ethersproject/providers'; -import { getDummyEventData, getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyEventData, getDummyGraphData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import abi from '../test/subgraph/example1/build/Example1/abis/Example1.json'; import { instantiate } from './loader'; import { createEvent, createBlock, Block, EventData } from './utils'; @@ -25,6 +26,7 @@ xdescribe('call handler in mapping code', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; let dummyEventData: EventData; let dummyGraphData: any; @@ -33,6 +35,7 @@ xdescribe('call handler in mapping code', () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); // Create dummy test data. dummyEventData = await getDummyEventData(); @@ -71,6 +74,7 @@ xdescribe('call handler in mapping code', () => { db, indexer, provider, + ethClient, { block: dummyEventData.block, contractAddress: dummyGraphData.dataSource.address diff --git a/packages/graph-node/src/crypto.test.ts b/packages/graph-node/src/crypto.test.ts index 6d8334ca..bdb27ad2 100644 --- a/packages/graph-node/src/crypto.test.ts +++ b/packages/graph-node/src/crypto.test.ts @@ -7,9 +7,10 @@ import { expect } from 'chai'; import { utils } from 'ethers'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; -import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -18,11 +19,13 @@ describe('crypto host api', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; before(async () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); }); it('should load the subgraph example wasm', async () => { @@ -33,6 +36,7 @@ describe('crypto host api', () => { db, indexer, provider, + ethClient, {}, filePath, dummyGraphData diff --git a/packages/graph-node/src/eden.test.ts b/packages/graph-node/src/eden.test.ts index 9348b718..6a9a28d3 100644 --- a/packages/graph-node/src/eden.test.ts +++ b/packages/graph-node/src/eden.test.ts @@ -9,13 +9,14 @@ import chai from 'chai'; import spies from 'chai-spies'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; import { createEvent, Block, createBlock, EventData } from './utils'; import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json'; import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json'; import distributorGovernanceAbi from '../test/subgraph/eden/EdenNetworkGovernance/abis/DistributorGovernance.json'; -import { getDummyEventData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyEventData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -29,6 +30,7 @@ xdescribe('eden wasm loader tests', async () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; let dummyEventData: EventData; @@ -36,6 +38,7 @@ xdescribe('eden wasm loader tests', async () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); // Create dummy test data. dummyEventData = await getDummyEventData(); @@ -90,6 +93,7 @@ xdescribe('eden wasm loader tests', async () => { db, indexer, provider, + ethClient, { block: dummyEventData.block, contractAddress @@ -210,6 +214,7 @@ xdescribe('eden wasm loader tests', async () => { ({ exports } = await instantiate(db, indexer, provider, + ethClient, { block: dummyEventData.block, contractAddress @@ -328,6 +333,7 @@ xdescribe('eden wasm loader tests', async () => { db, indexer, provider, + ethClient, { block: dummyEventData.block, contractAddress diff --git a/packages/graph-node/src/eth-abi.test.ts b/packages/graph-node/src/eth-abi.test.ts index 4078bb4c..d46d330b 100644 --- a/packages/graph-node/src/eth-abi.test.ts +++ b/packages/graph-node/src/eth-abi.test.ts @@ -6,9 +6,10 @@ import path from 'path'; import { expect } from 'chai'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; -import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -17,12 +18,14 @@ describe('ethereum ABI encode decode', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; let encoded: string; before(async () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); }); it('should load the subgraph example wasm', async () => { @@ -33,6 +36,7 @@ describe('ethereum ABI encode decode', () => { db, indexer, provider, + ethClient, {}, filePath, dummyGraphData diff --git a/packages/graph-node/src/eth-call.test.ts b/packages/graph-node/src/eth-call.test.ts index 2151dee6..8f6872c5 100644 --- a/packages/graph-node/src/eth-call.test.ts +++ b/packages/graph-node/src/eth-call.test.ts @@ -6,10 +6,11 @@ import assert from 'assert'; import path from 'path'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json'; -import { getTestDatabase, getTestIndexer, getTestProvider, getDummyEventData } from '../test/utils'; +import { getTestDatabase, getTestIndexer, getTestProvider, getDummyEventData, getTestEthClient } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; import { EventData } from './utils'; @@ -19,6 +20,7 @@ xdescribe('eth-call wasm tests', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS; assert(contractAddress); @@ -40,6 +42,7 @@ xdescribe('eth-call wasm tests', () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); // Create dummy test data. dummyEventData = await getDummyEventData(); @@ -51,6 +54,7 @@ xdescribe('eth-call wasm tests', () => { db, indexer, provider, + ethClient, { block: dummyEventData.block, contractAddress diff --git a/packages/graph-node/src/json.test.ts b/packages/graph-node/src/json.test.ts index 515002f3..413feb64 100644 --- a/packages/graph-node/src/json.test.ts +++ b/packages/graph-node/src/json.test.ts @@ -5,9 +5,10 @@ import path from 'path'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; -import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -16,11 +17,13 @@ describe('json host api', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; before(async () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); }); it('should load the subgraph example wasm', async () => { @@ -31,6 +34,7 @@ describe('json host api', () => { db, indexer, provider, + ethClient, {}, filePath, dummyGraphData diff --git a/packages/graph-node/src/loader.test.ts b/packages/graph-node/src/loader.test.ts index a12ada90..67e472fc 100644 --- a/packages/graph-node/src/loader.test.ts +++ b/packages/graph-node/src/loader.test.ts @@ -9,9 +9,10 @@ import { utils } from 'ethers'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; +import { EthClient } from '@vulcanize/ipld-eth-client'; const WASM_FILE_PATH = '../build/debug.wasm'; @@ -20,6 +21,7 @@ describe('wasm loader tests', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; let module: WebAssembly.Module; let dummyGraphData: any; @@ -27,6 +29,7 @@ describe('wasm loader tests', () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, WASM_FILE_PATH); @@ -35,6 +38,7 @@ describe('wasm loader tests', () => { db, indexer, provider, + ethClient, {}, filePath, dummyGraphData @@ -113,6 +117,7 @@ describe('wasm loader tests', () => { db, indexer, provider, + ethClient, {}, module, dummyGraphData diff --git a/packages/graph-node/src/loader.ts b/packages/graph-node/src/loader.ts index f1ba826e..518f3c58 100644 --- a/packages/graph-node/src/loader.ts +++ b/packages/graph-node/src/loader.ts @@ -16,6 +16,8 @@ import debug from 'debug'; import { BaseProvider } from '@ethersproject/providers'; import loader from '@vulcanize/assemblyscript/lib/loader'; import { IndexerInterface, GraphDecimal, getGraphDigitsAndExp, jsonBigIntStringReplacer } from '@vulcanize/util'; +import { EthClient } from '@vulcanize/ipld-eth-client'; +import { getBytesLength, getStorageValue } from '@vulcanize/solidity-mapper'; import { TypeId, Level } from './types'; import { @@ -55,6 +57,7 @@ export const instantiate = async ( database: Database, indexer: IndexerInterface, provider: BaseProvider, + ethClient: EthClient, context: Context, filePathOrModule: string | WebAssembly.Module, data: GraphData @@ -267,7 +270,7 @@ export const instantiate = async ( return toEthereumValue(instanceExports, utils.ParamType.from(typesString), decoded); }, - 'ethereum.storageValue': async (variable: number, mappingKeys: number) => { + 'ethereum.storageValue': async (variable: number, mappingKeys: number, bytesLength: number) => { assert(context.contractAddress); const addressStringPtr = await __newString(context.contractAddress); const addressString = __getString(addressStringPtr); @@ -284,23 +287,39 @@ export const instantiate = async ( const storageLayout = indexer.storageLayoutMap.get(dataSource.name); assert(storageLayout); assert(context.block); + let value: any; console.time(`time:loader#ethereum.storageValue-${variableString}`); - const result = await indexer.getStorageValue( - storageLayout, - context.block.blockHash, - addressString, - variableString, - ...mappingKeyValues - ); + if (bytesLength) { + value = await getBytesLength( + storageLayout, + ethClient.getStorageAt.bind(ethClient), + context.block.blockHash, + addressString, + variableString + ); + } else { + ({ value } = await getStorageValue( + storageLayout, + ethClient.getStorageAt.bind(ethClient), + context.block.blockHash, + addressString, + variableString, + ...mappingKeyValues + )); + } console.timeEnd(`time:loader#ethereum.storageValue-${variableString}`); - const storageValueType = getStorageValueType(storageLayout, variableString, mappingKeyValues); + let storageValueType = getStorageValueType(storageLayout, variableString, mappingKeyValues); + + if (bytesLength) { + storageValueType = utils.ParamType.from('uint'); + } return toEthereumValue( instanceExports, storageValueType, - result.value + value ); } }, diff --git a/packages/graph-node/src/numbers.test.ts b/packages/graph-node/src/numbers.test.ts index db6d674e..dc2c6aaa 100644 --- a/packages/graph-node/src/numbers.test.ts +++ b/packages/graph-node/src/numbers.test.ts @@ -8,6 +8,7 @@ import BN from 'bn.js'; import { GraphDecimal } from '@vulcanize/util'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; @@ -31,6 +32,7 @@ describe('numbers wasm tests', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; before(async () => { db = getTestDatabase(); @@ -44,6 +46,7 @@ describe('numbers wasm tests', () => { db, indexer, provider, + ethClient, {}, filePath, dummyGraphData diff --git a/packages/graph-node/src/storage-call.test.ts b/packages/graph-node/src/storage-call.test.ts index 91c0199e..a54ad756 100644 --- a/packages/graph-node/src/storage-call.test.ts +++ b/packages/graph-node/src/storage-call.test.ts @@ -6,11 +6,12 @@ import assert from 'assert'; import path from 'path'; import { BaseProvider } from '@ethersproject/providers'; +import { EthClient } from '@vulcanize/ipld-eth-client'; import { instantiate } from './loader'; import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json'; import { storageLayout } from '../test/artifacts/Example1.json'; -import { getTestDatabase, getTestIndexer, getTestProvider, getDummyEventData } from '../test/utils'; +import { getTestDatabase, getTestIndexer, getTestProvider, getDummyEventData, getTestEthClient } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; import { EventData } from './utils'; @@ -20,6 +21,7 @@ xdescribe('storage-call wasm tests', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS; assert(contractAddress); @@ -41,6 +43,7 @@ xdescribe('storage-call wasm tests', () => { db = getTestDatabase(); indexer = getTestIndexer(new Map([['Example1', storageLayout]])); provider = getTestProvider(); + ethClient = getTestEthClient(); // Create dummy test data. dummyEventData = await getDummyEventData(); @@ -52,6 +55,7 @@ xdescribe('storage-call wasm tests', () => { db, indexer, provider, + ethClient, { block: dummyEventData.block, contractAddress diff --git a/packages/graph-node/src/type-conversion.test.ts b/packages/graph-node/src/type-conversion.test.ts index 03e6e618..ab6cf442 100644 --- a/packages/graph-node/src/type-conversion.test.ts +++ b/packages/graph-node/src/type-conversion.test.ts @@ -9,9 +9,10 @@ import { utils, BigNumber } from 'ethers'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestEthClient, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; +import { EthClient } from '@vulcanize/ipld-eth-client'; const EXAMPLE_WASM_FILE_PATH = '../test/subgraph/example1/build/Example1/Example1.wasm'; @@ -20,11 +21,13 @@ describe('typeConversion wasm tests', () => { let db: Database; let indexer: Indexer; let provider: BaseProvider; + let ethClient: EthClient; before(async () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + ethClient = getTestEthClient(); const dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH); @@ -33,6 +36,7 @@ describe('typeConversion wasm tests', () => { db, indexer, provider, + ethClient, {}, filePath, dummyGraphData diff --git a/packages/graph-node/src/watcher.ts b/packages/graph-node/src/watcher.ts index 2f1088c1..9c629148 100644 --- a/packages/graph-node/src/watcher.ts +++ b/packages/graph-node/src/watcher.ts @@ -78,7 +78,7 @@ export class GraphWatcher { assert(this._indexer); return { - instance: await instantiate(this._database, this._indexer, this._ethProvider, this._context, filePath, data), + instance: await instantiate(this._database, this._indexer, this._ethProvider, this._ethClient, this._context, filePath, data), contractInterface, data }; @@ -315,6 +315,7 @@ export class GraphWatcher { this._database, this._indexer, this._ethProvider, + this._ethClient, this._context, module, data diff --git a/packages/graph-node/test/utils/index.ts b/packages/graph-node/test/utils/index.ts index 9e94c792..8cb3c13b 100644 --- a/packages/graph-node/test/utils/index.ts +++ b/packages/graph-node/test/utils/index.ts @@ -75,12 +75,7 @@ export const getTestDatabase = (): Database => { }; export const getTestIndexer = (storageLayout?: Map): Indexer => { - const ethClient = new EthClient({ - gqlEndpoint: IPLD_ETH_SERVER_GQL_URL, - cache: undefined - }); - - return new Indexer(ethClient, storageLayout); + return new Indexer(storageLayout); }; export const getTestProvider = (): BaseProvider => { @@ -88,3 +83,8 @@ export const getTestProvider = (): BaseProvider => { return provider; }; + +export const getTestEthClient = (): EthClient => new EthClient({ + gqlEndpoint: IPLD_ETH_SERVER_GQL_URL, + cache: undefined +}); diff --git a/packages/graph-node/test/utils/indexer.ts b/packages/graph-node/test/utils/indexer.ts index c813cdcb..ae3096bd 100644 --- a/packages/graph-node/test/utils/indexer.ts +++ b/packages/graph-node/test/utils/indexer.ts @@ -6,19 +6,14 @@ import { BlockProgressInterface, EventInterface, SyncStatusInterface, - ServerConfig as ServerConfigInterface, - ValueResult + ServerConfig as ServerConfigInterface } from '@vulcanize/util'; -import { EthClient } from '@vulcanize/ipld-eth-client'; -import { GetStorageAt, getStorageValue, MappingKey, StorageLayout } from '@vulcanize/solidity-mapper'; +import { StorageLayout } from '@vulcanize/solidity-mapper'; export class Indexer implements IndexerInterface { - _getStorageAt: GetStorageAt; _storageLayoutMap: Map = new Map() - constructor (ethClient: EthClient, storageLayoutMap?: Map) { - this._getStorageAt = ethClient.getStorageAt.bind(ethClient); - + constructor (storageLayoutMap?: Map) { if (storageLayoutMap) { this._storageLayoutMap = storageLayoutMap; } @@ -32,17 +27,6 @@ export class Indexer implements IndexerInterface { return this._storageLayoutMap; } - async getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise { - return getStorageValue( - storageLayout, - this._getStorageAt, - blockHash, - contractAddress, - variable, - ...mappingKeys - ); - } - async getBlockProgress (blockHash: string): Promise { assert(blockHash); diff --git a/packages/solidity-mapper/src/index.ts b/packages/solidity-mapper/src/index.ts index c887503b..c9b98b88 100644 --- a/packages/solidity-mapper/src/index.ts +++ b/packages/solidity-mapper/src/index.ts @@ -2,6 +2,6 @@ // Copyright 2021 Vulcanize, Inc. // -export { getStorageValue, getStorageInfo, getValueByType, StorageLayout, GetStorageAt, MappingKey } from './storage'; +export { getStorageValue, getStorageInfo, getValueByType, getBytesLength, StorageLayout, GetStorageAt, MappingKey } from './storage'; export { getEventNameTopics } from './logs'; diff --git a/packages/solidity-mapper/src/storage.ts b/packages/solidity-mapper/src/storage.ts index 376218a8..f1c9328a 100644 --- a/packages/solidity-mapper/src/storage.ts +++ b/packages/solidity-mapper/src/storage.ts @@ -2,6 +2,7 @@ // Copyright 2021 Vulcanize, Inc. // +import assert from 'assert'; import { utils, BigNumber } from 'ethers'; interface Storage { @@ -108,6 +109,55 @@ export const getValueByType = (storageValue: string, typeLabel: string): bigint return storageValue; }; +/** + * Function to get slot for mapping types. + * @param mappingSlot + * @param key + */ +export const getMappingSlot = (types: Types, mappingSlot: string, keyType: string, key: MappingKey): string => { + const { encoding, label: typeLabel } = types[keyType]; + + // If key is boolean type convert to 1 or 0 which is the way value is stored in memory. + if (typeLabel === 'bool') { + key = key ? 1 : 0; + } + + // If key is string convert to hex string representation. + if (typeLabel === 'string' && typeof key === 'string') { + key = utils.hexlify(utils.toUtf8Bytes(key)); + } + + // If key is still boolean type the argument passed as key is invalid. + if (typeof key === 'boolean') { + throw new Error('Invalid key.'); + } + + // https://github.com/ethers-io/ethers.js/issues/1079#issuecomment-703056242 + const mappingSlotPadded = utils.hexZeroPad(mappingSlot, 32); + + const keyPadded = encoding === 'bytes' + ? utils.hexlify(key) + : utils.hexZeroPad(utils.hexlify(key), 32); + + // https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays + const fullKey = utils.concat([ + keyPadded, + mappingSlotPadded + ]); + + const slot = utils.keccak256(fullKey); + return slot; +}; + +export const getBytesLength = async (storageLayout: StorageLayout, getStorageAt: GetStorageAt, blockHash: string, address: string, variableName: string): Promise => { + const { slot, type, types } = getStorageInfo(storageLayout, variableName); + const { encoding } = types[type]; + assert(encoding === 'bytes'); + const { value } = await getStorageAt({ blockHash, contract: address, slot }); + + return getBytesLengthFromValue(value); +}; + /** * Function to get decoded value according to type and encoding. * @param getStorageAt @@ -183,46 +233,6 @@ const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, ad }; }; -/** - * Function to get slot for mapping types. - * @param mappingSlot - * @param key - */ -export const getMappingSlot = (types: Types, mappingSlot: string, keyType: string, key: MappingKey): string => { - const { encoding, label: typeLabel } = types[keyType]; - - // If key is boolean type convert to 1 or 0 which is the way value is stored in memory. - if (typeLabel === 'bool') { - key = key ? 1 : 0; - } - - // If key is string convert to hex string representation. - if (typeLabel === 'string' && typeof key === 'string') { - key = utils.hexlify(utils.toUtf8Bytes(key)); - } - - // If key is still boolean type the argument passed as key is invalid. - if (typeof key === 'boolean') { - throw new Error('Invalid key.'); - } - - // https://github.com/ethers-io/ethers.js/issues/1079#issuecomment-703056242 - const mappingSlotPadded = utils.hexZeroPad(mappingSlot, 32); - - const keyPadded = encoding === 'bytes' - ? utils.hexlify(key) - : utils.hexZeroPad(utils.hexlify(key), 32); - - // https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays - const fullKey = utils.concat([ - keyPadded, - mappingSlotPadded - ]); - - const slot = utils.keccak256(fullKey); - return slot; -}; - const getDynamicArrayInfo = async (getStorageAt: GetStorageAt, blockHash: string, address: string, slot: string, offset: number, numberOfBytes: string) => { const { value } = await getInplaceValue(getStorageAt, blockHash, address, slot, offset, numberOfBytes); const size = Number(getValueByType(value, 'uint')); @@ -351,21 +361,9 @@ const getInplaceValue = async (getStorageAt: GetStorageAt, blockHash: string, ad */ const getBytesValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, slot: string) => { const { value, proof } = await getStorageAt({ blockHash, contract: address, slot }); - let length = 0; + const length = getBytesLengthFromValue(value); const proofs = [JSON.parse(proof.data)]; - // Get length of bytes stored. - if (BigNumber.from(utils.hexDataSlice(value, 0, 1)).isZero()) { - // If first byte is not set, get length directly from the zero padded byte array. - const slotValue = BigNumber.from(value); - length = slotValue.sub(1).div(2).toNumber(); - } else { - // If first byte is set the length is lesser than 32 bytes. - // Length of the value can be computed from the last byte. - 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. if (length < 32) { return { @@ -404,3 +402,17 @@ const getBytesValue = async (getStorageAt: GetStorageAt, blockHash: string, addr } }; }; + +const getBytesLengthFromValue = (value: string): number => { + // Get length of bytes stored. + if (BigNumber.from(utils.hexDataSlice(value, 0, 1)).isZero()) { + // If first byte is not set, get length directly from the zero padded byte array. + const slotValue = BigNumber.from(value); + return slotValue.sub(1).div(2).toNumber(); + } else { + // If first byte is set the length is lesser than 32 bytes. + // Length of the value can be computed from the last byte. + const lastByteHex = utils.hexDataSlice(value, 31, 32); + return BigNumber.from(lastByteHex).div(2).toNumber(); + } +}; diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index bf8f0d52..0b19b9b5 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -112,7 +112,6 @@ export interface IndexerInterface { processStateCheckpoint?: (contractAddress: string, blockHash: string) => Promise processBlock?: (blockHash: string, blockNumber: number) => Promise processBlockAfterEvents?: (blockHash: string) => Promise - getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise } export interface IPLDIndexerInterface extends IndexerInterface {