Implement method for storage based access in subgraph mapping code (#162)

* Implement storage call in subgraph mapping code

* Add test for mapping type variable storage call

* Use vulcanize graph-ts

* Revert to graph-ts version 0.22.1
This commit is contained in:
2022-08-17 16:25:49 +05:30
committed by GitHub
parent 80682e2755
commit ec56de057f
21 changed files with 503 additions and 17 deletions
+37 -1
View File
@@ -25,7 +25,8 @@ import {
toEthereumValue,
resolveEntityFieldConflicts,
getEthereumTypes,
jsonFromBytes
jsonFromBytes,
getStorageValueType
} from './utils';
import { Database } from './database';
@@ -236,6 +237,41 @@ export const instantiate = async (
const [decoded] = utils.defaultAbiCoder.decode([typesString], dataString);
return toEthereumValue(instanceExports, utils.ParamType.from(typesString), decoded);
},
'ethereum.storageValue': async (contractName: number, contractAddress: number, variable: number, mappingKeys: number) => {
const contractNameString = __getString(contractName);
const address = await Address.wrap(contractAddress);
const addressStringPtr = await address.toHexString();
const addressString = __getString(addressStringPtr);
const variableString = __getString(variable);
const mappingKeyPtrs = __getArray(mappingKeys);
const mappingKeyPromises = mappingKeyPtrs.map(async mappingKeyPtr => {
const ethereumValue = await ethereum.Value.wrap(mappingKeyPtr);
return fromEthereumValue(instanceExports, ethereumValue);
});
const mappingKeyValues = await Promise.all(mappingKeyPromises);
const storageLayout = indexer.storageLayoutMap.get(contractNameString);
assert(storageLayout);
assert(context.block);
const result = await indexer.getStorageValue(
storageLayout,
context.block.blockHash,
addressString,
variableString,
...mappingKeyValues
);
const storageValueType = getStorageValueType(storageLayout, variableString, mappingKeyValues);
return toEthereumValue(
instanceExports,
storageValueType,
result.value
);
}
},
conversion: {
@@ -0,0 +1,80 @@
//
// Copyright 2022 Vulcanize, Inc.
//
import assert from 'assert';
import path from 'path';
import { BaseProvider } from '@ethersproject/providers';
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 { Database } from './database';
import { Indexer } from '../test/utils/indexer';
import { EventData } from './utils';
xdescribe('storage-call wasm tests', () => {
let exports: any;
let db: Database;
let indexer: Indexer;
let provider: BaseProvider;
const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS;
assert(contractAddress);
const data = {
abis: {
Example1: exampleAbi
},
dataSource: {
address: contractAddress,
network: 'mainnet'
}
};
let dummyEventData: EventData;
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer(new Map([['Example1', storageLayout]]));
provider = getTestProvider();
// Create dummy test data.
dummyEventData = await getDummyEventData();
});
it('should load the subgraph example wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm');
const instance = await instantiate(
db,
indexer,
provider,
{
block: dummyEventData.block,
contractAddress
},
filePath,
data
);
exports = instance.exports;
const { _start } = exports;
// Important to call _start for built subgraphs on instantiation!
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
_start();
});
it('should execute contract getStorageValue function', async () => {
const { testGetStorageValue } = exports;
await testGetStorageValue();
});
it('should execute getStorageValue function for mapping type variable', async () => {
const { testMapStorageValue } = exports;
await testMapStorageValue();
});
});
+31
View File
@@ -4,10 +4,12 @@ import fs from 'fs-extra';
import debug from 'debug';
import yaml from 'js-yaml';
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
import assert from 'assert';
import { GraphDecimal } from '@vulcanize/util';
import { TypeId, EthereumValueKind, ValueKind } from './types';
import { MappingKey, StorageLayout } from '@vulcanize/solidity-mapper';
const log = debug('vulcanize:utils');
@@ -767,3 +769,32 @@ export const jsonFromBytes = async (instanceExports: any, bytesPtr: number): Pro
return jsonValue;
};
export const getStorageValueType = (storageLayout: StorageLayout, variableString: string, mappingKeys: MappingKey[]): utils.ParamType => {
const storage = storageLayout.storage.find(({ label }) => label === variableString);
assert(storage);
return getEthereumType(storageLayout.types, storage.type, mappingKeys);
};
const getEthereumType = (storageTypes: StorageLayout['types'], type: string, mappingKeys: MappingKey[]): utils.ParamType => {
const { label, encoding, members, value } = storageTypes[type];
if (encoding === 'mapping') {
assert(value);
return getEthereumType(storageTypes, value, mappingKeys.slice(1));
}
// Struct type contains members field.
if (members) {
const mappingKey = mappingKeys.shift();
const member = members.find(({ label }) => label === mappingKey);
assert(member);
const { type } = member;
return getEthereumType(storageTypes, type, mappingKeys);
}
return utils.ParamType.from(label);
};