Get bytes length in storageValue host API

This commit is contained in:
nabarun 2022-09-02 15:01:08 +05:30
parent 8af7417df6
commit f186e6410d
17 changed files with 157 additions and 100 deletions

View File

@ -6,10 +6,11 @@ import path from 'path';
import chai, { assert, expect } from 'chai'; import chai, { assert, expect } from 'chai';
import spies from 'chai-spies'; import spies from 'chai-spies';
import { utils } from 'ethers'; import { utils } from 'ethers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { BaseProvider } from '@ethersproject/providers'; 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 abi from '../test/subgraph/example1/build/Example1/abis/Example1.json';
import { instantiate } from './loader'; import { instantiate } from './loader';
import { createEvent, createBlock, Block, EventData } from './utils'; import { createEvent, createBlock, Block, EventData } from './utils';
@ -25,6 +26,7 @@ xdescribe('call handler in mapping code', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
let dummyEventData: EventData; let dummyEventData: EventData;
let dummyGraphData: any; let dummyGraphData: any;
@ -33,6 +35,7 @@ xdescribe('call handler in mapping code', () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
// Create dummy test data. // Create dummy test data.
dummyEventData = await getDummyEventData(); dummyEventData = await getDummyEventData();
@ -71,6 +74,7 @@ xdescribe('call handler in mapping code', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{ {
block: dummyEventData.block, block: dummyEventData.block,
contractAddress: dummyGraphData.dataSource.address contractAddress: dummyGraphData.dataSource.address

View File

@ -7,9 +7,10 @@ import { expect } from 'chai';
import { utils } from 'ethers'; import { utils } from 'ethers';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; 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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
@ -18,11 +19,13 @@ describe('crypto host api', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
before(async () => { before(async () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
}); });
it('should load the subgraph example wasm', async () => { it('should load the subgraph example wasm', async () => {
@ -33,6 +36,7 @@ describe('crypto host api', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
filePath, filePath,
dummyGraphData dummyGraphData

View File

@ -9,13 +9,14 @@ import chai from 'chai';
import spies from 'chai-spies'; import spies from 'chai-spies';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; import { instantiate } from './loader';
import { createEvent, Block, createBlock, EventData } from './utils'; import { createEvent, Block, createBlock, EventData } from './utils';
import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json'; import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json';
import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json'; import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json';
import distributorGovernanceAbi from '../test/subgraph/eden/EdenNetworkGovernance/abis/DistributorGovernance.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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
@ -29,6 +30,7 @@ xdescribe('eden wasm loader tests', async () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
let dummyEventData: EventData; let dummyEventData: EventData;
@ -36,6 +38,7 @@ xdescribe('eden wasm loader tests', async () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
// Create dummy test data. // Create dummy test data.
dummyEventData = await getDummyEventData(); dummyEventData = await getDummyEventData();
@ -90,6 +93,7 @@ xdescribe('eden wasm loader tests', async () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{ {
block: dummyEventData.block, block: dummyEventData.block,
contractAddress contractAddress
@ -210,6 +214,7 @@ xdescribe('eden wasm loader tests', async () => {
({ exports } = await instantiate(db, ({ exports } = await instantiate(db,
indexer, indexer,
provider, provider,
ethClient,
{ {
block: dummyEventData.block, block: dummyEventData.block,
contractAddress contractAddress
@ -328,6 +333,7 @@ xdescribe('eden wasm loader tests', async () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{ {
block: dummyEventData.block, block: dummyEventData.block,
contractAddress contractAddress

View File

@ -6,9 +6,10 @@ import path from 'path';
import { expect } from 'chai'; import { expect } from 'chai';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; 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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
@ -17,12 +18,14 @@ describe('ethereum ABI encode decode', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
let encoded: string; let encoded: string;
before(async () => { before(async () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
}); });
it('should load the subgraph example wasm', async () => { it('should load the subgraph example wasm', async () => {
@ -33,6 +36,7 @@ describe('ethereum ABI encode decode', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
filePath, filePath,
dummyGraphData dummyGraphData

View File

@ -6,10 +6,11 @@ import assert from 'assert';
import path from 'path'; import path from 'path';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; import { instantiate } from './loader';
import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json'; 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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
import { EventData } from './utils'; import { EventData } from './utils';
@ -19,6 +20,7 @@ xdescribe('eth-call wasm tests', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS; const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS;
assert(contractAddress); assert(contractAddress);
@ -40,6 +42,7 @@ xdescribe('eth-call wasm tests', () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
// Create dummy test data. // Create dummy test data.
dummyEventData = await getDummyEventData(); dummyEventData = await getDummyEventData();
@ -51,6 +54,7 @@ xdescribe('eth-call wasm tests', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{ {
block: dummyEventData.block, block: dummyEventData.block,
contractAddress contractAddress

View File

@ -5,9 +5,10 @@
import path from 'path'; import path from 'path';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; 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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
@ -16,11 +17,13 @@ describe('json host api', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
before(async () => { before(async () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
}); });
it('should load the subgraph example wasm', async () => { it('should load the subgraph example wasm', async () => {
@ -31,6 +34,7 @@ describe('json host api', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
filePath, filePath,
dummyGraphData dummyGraphData

View File

@ -9,9 +9,10 @@ import { utils } from 'ethers';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { instantiate } from './loader'; 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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
import { EthClient } from '@vulcanize/ipld-eth-client';
const WASM_FILE_PATH = '../build/debug.wasm'; const WASM_FILE_PATH = '../build/debug.wasm';
@ -20,6 +21,7 @@ describe('wasm loader tests', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
let module: WebAssembly.Module; let module: WebAssembly.Module;
let dummyGraphData: any; let dummyGraphData: any;
@ -27,6 +29,7 @@ describe('wasm loader tests', () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
dummyGraphData = getDummyGraphData(); dummyGraphData = getDummyGraphData();
const filePath = path.resolve(__dirname, WASM_FILE_PATH); const filePath = path.resolve(__dirname, WASM_FILE_PATH);
@ -35,6 +38,7 @@ describe('wasm loader tests', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
filePath, filePath,
dummyGraphData dummyGraphData
@ -113,6 +117,7 @@ describe('wasm loader tests', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
module, module,
dummyGraphData dummyGraphData

View File

@ -16,6 +16,8 @@ import debug from 'debug';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import loader from '@vulcanize/assemblyscript/lib/loader'; import loader from '@vulcanize/assemblyscript/lib/loader';
import { IndexerInterface, GraphDecimal, getGraphDigitsAndExp, jsonBigIntStringReplacer } from '@vulcanize/util'; 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 { TypeId, Level } from './types';
import { import {
@ -55,6 +57,7 @@ export const instantiate = async (
database: Database, database: Database,
indexer: IndexerInterface, indexer: IndexerInterface,
provider: BaseProvider, provider: BaseProvider,
ethClient: EthClient,
context: Context, context: Context,
filePathOrModule: string | WebAssembly.Module, filePathOrModule: string | WebAssembly.Module,
data: GraphData data: GraphData
@ -267,7 +270,7 @@ export const instantiate = async (
return toEthereumValue(instanceExports, utils.ParamType.from(typesString), decoded); 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); assert(context.contractAddress);
const addressStringPtr = await __newString(context.contractAddress); const addressStringPtr = await __newString(context.contractAddress);
const addressString = __getString(addressStringPtr); const addressString = __getString(addressStringPtr);
@ -284,23 +287,39 @@ export const instantiate = async (
const storageLayout = indexer.storageLayoutMap.get(dataSource.name); const storageLayout = indexer.storageLayoutMap.get(dataSource.name);
assert(storageLayout); assert(storageLayout);
assert(context.block); assert(context.block);
let value: any;
console.time(`time:loader#ethereum.storageValue-${variableString}`); console.time(`time:loader#ethereum.storageValue-${variableString}`);
const result = await indexer.getStorageValue( if (bytesLength) {
value = await getBytesLength(
storageLayout, storageLayout,
ethClient.getStorageAt.bind(ethClient),
context.block.blockHash,
addressString,
variableString
);
} else {
({ value } = await getStorageValue(
storageLayout,
ethClient.getStorageAt.bind(ethClient),
context.block.blockHash, context.block.blockHash,
addressString, addressString,
variableString, variableString,
...mappingKeyValues ...mappingKeyValues
); ));
}
console.timeEnd(`time:loader#ethereum.storageValue-${variableString}`); 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( return toEthereumValue(
instanceExports, instanceExports,
storageValueType, storageValueType,
result.value value
); );
} }
}, },

View File

@ -8,6 +8,7 @@ import BN from 'bn.js';
import { GraphDecimal } from '@vulcanize/util'; import { GraphDecimal } from '@vulcanize/util';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; import { instantiate } from './loader';
import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils';
@ -31,6 +32,7 @@ describe('numbers wasm tests', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
before(async () => { before(async () => {
db = getTestDatabase(); db = getTestDatabase();
@ -44,6 +46,7 @@ describe('numbers wasm tests', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
filePath, filePath,
dummyGraphData dummyGraphData

View File

@ -6,11 +6,12 @@ import assert from 'assert';
import path from 'path'; import path from 'path';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { instantiate } from './loader'; import { instantiate } from './loader';
import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json'; import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json';
import { storageLayout } from '../test/artifacts/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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; import { Indexer } from '../test/utils/indexer';
import { EventData } from './utils'; import { EventData } from './utils';
@ -20,6 +21,7 @@ xdescribe('storage-call wasm tests', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS; const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS;
assert(contractAddress); assert(contractAddress);
@ -41,6 +43,7 @@ xdescribe('storage-call wasm tests', () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(new Map([['Example1', storageLayout]])); indexer = getTestIndexer(new Map([['Example1', storageLayout]]));
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
// Create dummy test data. // Create dummy test data.
dummyEventData = await getDummyEventData(); dummyEventData = await getDummyEventData();
@ -52,6 +55,7 @@ xdescribe('storage-call wasm tests', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{ {
block: dummyEventData.block, block: dummyEventData.block,
contractAddress contractAddress

View File

@ -9,9 +9,10 @@ import { utils, BigNumber } from 'ethers';
import { BaseProvider } from '@ethersproject/providers'; import { BaseProvider } from '@ethersproject/providers';
import { instantiate } from './loader'; 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 { Database } from './database';
import { Indexer } from '../test/utils/indexer'; 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'; const EXAMPLE_WASM_FILE_PATH = '../test/subgraph/example1/build/Example1/Example1.wasm';
@ -20,11 +21,13 @@ describe('typeConversion wasm tests', () => {
let db: Database; let db: Database;
let indexer: Indexer; let indexer: Indexer;
let provider: BaseProvider; let provider: BaseProvider;
let ethClient: EthClient;
before(async () => { before(async () => {
db = getTestDatabase(); db = getTestDatabase();
indexer = getTestIndexer(); indexer = getTestIndexer();
provider = getTestProvider(); provider = getTestProvider();
ethClient = getTestEthClient();
const dummyGraphData = getDummyGraphData(); const dummyGraphData = getDummyGraphData();
const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH); const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH);
@ -33,6 +36,7 @@ describe('typeConversion wasm tests', () => {
db, db,
indexer, indexer,
provider, provider,
ethClient,
{}, {},
filePath, filePath,
dummyGraphData dummyGraphData

View File

@ -78,7 +78,7 @@ export class GraphWatcher {
assert(this._indexer); assert(this._indexer);
return { 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, contractInterface,
data data
}; };
@ -315,6 +315,7 @@ export class GraphWatcher {
this._database, this._database,
this._indexer, this._indexer,
this._ethProvider, this._ethProvider,
this._ethClient,
this._context, this._context,
module, module,
data data

View File

@ -75,12 +75,7 @@ export const getTestDatabase = (): Database => {
}; };
export const getTestIndexer = (storageLayout?: Map<string, StorageLayout>): Indexer => { export const getTestIndexer = (storageLayout?: Map<string, StorageLayout>): Indexer => {
const ethClient = new EthClient({ return new Indexer(storageLayout);
gqlEndpoint: IPLD_ETH_SERVER_GQL_URL,
cache: undefined
});
return new Indexer(ethClient, storageLayout);
}; };
export const getTestProvider = (): BaseProvider => { export const getTestProvider = (): BaseProvider => {
@ -88,3 +83,8 @@ export const getTestProvider = (): BaseProvider => {
return provider; return provider;
}; };
export const getTestEthClient = (): EthClient => new EthClient({
gqlEndpoint: IPLD_ETH_SERVER_GQL_URL,
cache: undefined
});

View File

@ -6,19 +6,14 @@ import {
BlockProgressInterface, BlockProgressInterface,
EventInterface, EventInterface,
SyncStatusInterface, SyncStatusInterface,
ServerConfig as ServerConfigInterface, ServerConfig as ServerConfigInterface
ValueResult
} from '@vulcanize/util'; } from '@vulcanize/util';
import { EthClient } from '@vulcanize/ipld-eth-client'; import { StorageLayout } from '@vulcanize/solidity-mapper';
import { GetStorageAt, getStorageValue, MappingKey, StorageLayout } from '@vulcanize/solidity-mapper';
export class Indexer implements IndexerInterface { export class Indexer implements IndexerInterface {
_getStorageAt: GetStorageAt;
_storageLayoutMap: Map<string, StorageLayout> = new Map() _storageLayoutMap: Map<string, StorageLayout> = new Map()
constructor (ethClient: EthClient, storageLayoutMap?: Map<string, StorageLayout>) { constructor (storageLayoutMap?: Map<string, StorageLayout>) {
this._getStorageAt = ethClient.getStorageAt.bind(ethClient);
if (storageLayoutMap) { if (storageLayoutMap) {
this._storageLayoutMap = storageLayoutMap; this._storageLayoutMap = storageLayoutMap;
} }
@ -32,17 +27,6 @@ export class Indexer implements IndexerInterface {
return this._storageLayoutMap; return this._storageLayoutMap;
} }
async getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise<ValueResult> {
return getStorageValue(
storageLayout,
this._getStorageAt,
blockHash,
contractAddress,
variable,
...mappingKeys
);
}
async getBlockProgress (blockHash: string): Promise<BlockProgressInterface | undefined> { async getBlockProgress (blockHash: string): Promise<BlockProgressInterface | undefined> {
assert(blockHash); assert(blockHash);

View File

@ -2,6 +2,6 @@
// Copyright 2021 Vulcanize, Inc. // 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'; export { getEventNameTopics } from './logs';

View File

@ -2,6 +2,7 @@
// Copyright 2021 Vulcanize, Inc. // Copyright 2021 Vulcanize, Inc.
// //
import assert from 'assert';
import { utils, BigNumber } from 'ethers'; import { utils, BigNumber } from 'ethers';
interface Storage { interface Storage {
@ -108,6 +109,55 @@ export const getValueByType = (storageValue: string, typeLabel: string): bigint
return storageValue; 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<number> => {
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. * Function to get decoded value according to type and encoding.
* @param getStorageAt * @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 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 { value } = await getInplaceValue(getStorageAt, blockHash, address, slot, offset, numberOfBytes);
const size = Number(getValueByType(value, 'uint')); 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 getBytesValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, slot: string) => {
const { value, proof } = await getStorageAt({ blockHash, contract: address, slot }); const { value, proof } = await getStorageAt({ blockHash, contract: address, slot });
let length = 0; const length = getBytesLengthFromValue(value);
const proofs = [JSON.parse(proof.data)]; 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. // Get value from the byte array directly if length is less than 32.
if (length < 32) { if (length < 32) {
return { 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();
}
};

View File

@ -112,7 +112,6 @@ export interface IndexerInterface {
processStateCheckpoint?: (contractAddress: string, blockHash: string) => Promise<boolean> processStateCheckpoint?: (contractAddress: string, blockHash: string) => Promise<boolean>
processBlock?: (blockHash: string, blockNumber: number) => Promise<void> processBlock?: (blockHash: string, blockNumber: number) => Promise<void>
processBlockAfterEvents?: (blockHash: string) => Promise<void> processBlockAfterEvents?: (blockHash: string) => Promise<void>
getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise<ValueResult>
} }
export interface IPLDIndexerInterface extends IndexerInterface { export interface IPLDIndexerInterface extends IndexerInterface {