From aabf9f8e154e525a67d26cee8f3d5661a5f0580b Mon Sep 17 00:00:00 2001 From: nikugogoi Date: Thu, 6 Jan 2022 18:32:08 +0530 Subject: [PATCH] Implement data source templates (#106) * Implement data source templates * Remove optional param from wasm instantiate * Add all types for solidity to typescript mapping * Set contract address in context for data source templates * Implement block handlers for data source templates --- packages/codegen/src/generate-code.ts | 26 +- packages/codegen/src/schema.ts | 4 +- .../src/templates/indexer-template.handlebars | 4 + .../templates/reset-state-template.handlebars | 29 +- packages/codegen/src/utils/solToTs.ts | 112 ++ packages/codegen/src/utils/type-mappings.ts | 20 +- .../eden-watcher/src/cli/reset-cmds/state.ts | 29 +- packages/graph-node/package.json | 2 +- packages/graph-node/src/call-handler.test.ts | 5 +- packages/graph-node/src/crypto.test.ts | 8 +- packages/graph-node/src/eden.test.ts | 15 +- packages/graph-node/src/eth-abi.test.ts | 8 +- packages/graph-node/src/eth-call.test.ts | 5 +- packages/graph-node/src/json.test.ts | 8 +- packages/graph-node/src/loader.test.ts | 11 +- packages/graph-node/src/loader.ts | 28 +- packages/graph-node/src/numbers.test.ts | 7 +- .../graph-node/src/type-conversion.test.ts | 7 +- packages/graph-node/src/watcher.ts | 92 +- .../test/subgraph/example1/.gitignore | 5 + .../example1/abis/uniswap-v3/factory.json | 148 +++ .../example1/abis/uniswap-v3/pool.json | 988 +++++++++++++++++ .../example1/generated/Example1/Example1.ts | 184 ---- .../subgraph/example1/generated/export.ts | 101 -- .../subgraph/example1/generated/schema.ts | 254 ----- .../example1/src/uniswap-v3/factory.ts | 10 + .../subgraph/example1/src/uniswap-v3/pool.ts | 14 + .../test/subgraph/example1/subgraph.yaml | 41 + .../src/artifacts/Factory.json | 238 +++++ .../src/artifacts/Pool.json | 990 ++++++++++++++++++ .../src/cli/reset-cmds/state.ts | 28 +- packages/graph-test-watcher/src/indexer.ts | 150 ++- packages/graph-test-watcher/src/schema.gql | 15 +- packages/util/src/indexer.ts | 7 + packages/util/src/types.ts | 1 + 35 files changed, 2906 insertions(+), 688 deletions(-) create mode 100644 packages/codegen/src/utils/solToTs.ts create mode 100644 packages/graph-node/test/subgraph/example1/.gitignore create mode 100644 packages/graph-node/test/subgraph/example1/abis/uniswap-v3/factory.json create mode 100644 packages/graph-node/test/subgraph/example1/abis/uniswap-v3/pool.json delete mode 100644 packages/graph-node/test/subgraph/example1/generated/Example1/Example1.ts delete mode 100644 packages/graph-node/test/subgraph/example1/generated/export.ts delete mode 100644 packages/graph-node/test/subgraph/example1/generated/schema.ts create mode 100644 packages/graph-node/test/subgraph/example1/src/uniswap-v3/factory.ts create mode 100644 packages/graph-node/test/subgraph/example1/src/uniswap-v3/pool.ts create mode 100644 packages/graph-test-watcher/src/artifacts/Factory.json create mode 100644 packages/graph-test-watcher/src/artifacts/Pool.json diff --git a/packages/codegen/src/generate-code.ts b/packages/codegen/src/generate-code.ts index 3c5dbddd..59284445 100644 --- a/packages/codegen/src/generate-code.ts +++ b/packages/codegen/src/generate-code.ts @@ -335,19 +335,21 @@ function getConfig (configFile: string): any { subgraphPath = inputConfig.subgraphPath.replace(/^~/, os.homedir()); subgraphConfig = getSubgraphConfig(subgraphPath); - // Add contracts missing for dataSources in subgraph config. - subgraphConfig.dataSources.forEach((dataSource: any) => { - if (!contracts.some((contract: any) => contract.kind === dataSource.name)) { - const abi = dataSource.mapping.abis.find((abi: any) => abi.name === dataSource.source.abi); - const abiPath = path.resolve(subgraphPath, abi.file); + // Add contracts missing for dataSources and templates in subgraph config. + subgraphConfig.dataSources + .concat(subgraphConfig.templates ?? []) + .forEach((dataSource: any) => { + if (!contracts.some((contract: any) => contract.kind === dataSource.name)) { + const abi = dataSource.mapping.abis.find((abi: any) => abi.name === dataSource.source.abi); + const abiPath = path.resolve(subgraphPath, abi.file); - contracts.push({ - name: dataSource.name, - kind: dataSource.name, - abiPath - }); - } - }); + contracts.push({ + name: dataSource.name, + kind: dataSource.name, + abiPath + }); + } + }); } const inputFlatten = inputConfig.flatten; diff --git a/packages/codegen/src/schema.ts b/packages/codegen/src/schema.ts index b5b5c635..a9d7492b 100644 --- a/packages/codegen/src/schema.ts +++ b/packages/codegen/src/schema.ts @@ -6,7 +6,6 @@ import assert from 'assert'; import { GraphQLSchema, parse, printSchema, print } from 'graphql'; import { SchemaComposer } from 'graphql-compose'; import { Writable } from 'stream'; -import _ from 'lodash'; import { getTsForSol, getGqlForTs } from './utils/type-mappings'; import { Param } from './utils/types'; @@ -156,7 +155,8 @@ export class Schema { }); // Re-assigning the typeDefs. - const modifiedSchemaDocument = _.cloneDeep(subgraphSchemaDocument); + // Using JSON stringify and parse as lodash cloneDeep throws error. + const modifiedSchemaDocument = JSON.parse(JSON.stringify(subgraphSchemaDocument)); modifiedSchemaDocument.definitions = subgraphTypeDefs; // Adding subgraph-schema types to the schema composer. diff --git a/packages/codegen/src/templates/indexer-template.handlebars b/packages/codegen/src/templates/indexer-template.handlebars index a89992a5..12a9e649 100644 --- a/packages/codegen/src/templates/indexer-template.handlebars +++ b/packages/codegen/src/templates/indexer-template.handlebars @@ -584,6 +584,10 @@ export class Indexer implements IndexerInterface { return this._baseIndexer.isWatchedContract(address); } + getContractsByKind (kind: string): Contract[] { + return this._baseIndexer.getContractsByKind(kind); + } + async getProcessedBlockCountForRange (fromBlockNumber: number, toBlockNumber: number): Promise<{ expected: number, actual: number }> { return this._baseIndexer.getProcessedBlockCountForRange(fromBlockNumber, toBlockNumber); } diff --git a/packages/codegen/src/templates/reset-state-template.handlebars b/packages/codegen/src/templates/reset-state-template.handlebars index a866c815..1705cf85 100644 --- a/packages/codegen/src/templates/reset-state-template.handlebars +++ b/packages/codegen/src/templates/reset-state-template.handlebars @@ -59,12 +59,6 @@ export const handler = async (argv: any): Promise => { graphWatcher.setIndexer(indexer); await graphWatcher.init(); - const syncStatus = await indexer.getSyncStatus(); - assert(syncStatus, 'Missing syncStatus'); - - const ipldStatus = await indexer.getIPLDStatus(); - assert(ipldStatus, 'Missing ipldStatus'); - const blockProgresses = await indexer.getBlocksAtHeight(argv.blockNumber, false); assert(blockProgresses.length, `No blocks at specified block number ${argv.blockNumber}`); assert(!blockProgresses.some(block => !block.isComplete), `Incomplete block at block number ${argv.blockNumber} with unprocessed events`); @@ -85,6 +79,9 @@ export const handler = async (argv: any): Promise => { await Promise.all(removeEntitiesPromise); + const syncStatus = await indexer.getSyncStatus(); + assert(syncStatus, 'Missing syncStatus'); + if (syncStatus.latestIndexedBlockNumber > blockProgress.blockNumber) { await indexer.updateSyncStatusIndexedBlock(blockProgress.blockHash, blockProgress.blockNumber, true); } @@ -93,16 +90,20 @@ export const handler = async (argv: any): Promise => { await indexer.updateSyncStatusCanonicalBlock(blockProgress.blockHash, blockProgress.blockNumber, true); } - if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true); - } + const ipldStatus = await indexer.getIPLDStatus(); - if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true); - } + if(ipldStatus) { + if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true); + } - if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true); + if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true); + } + + if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true); + } } await indexer.updateSyncStatusChainHead(blockProgress.blockHash, blockProgress.blockNumber, true); diff --git a/packages/codegen/src/utils/solToTs.ts b/packages/codegen/src/utils/solToTs.ts new file mode 100644 index 00000000..8170ee70 --- /dev/null +++ b/packages/codegen/src/utils/solToTs.ts @@ -0,0 +1,112 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +const solToTs: Map = new Map(); + +// Solidity to Typescript type-mapping. +solToTs.set('string', 'string'); +solToTs.set('address', 'string'); +solToTs.set('bool', 'boolean'); + +solToTs.set('int8', 'number'); +solToTs.set('int16', 'number'); +solToTs.set('int24', 'number'); +solToTs.set('int32', 'number'); +solToTs.set('int48', 'number'); +solToTs.set('int56', 'bigint'); +solToTs.set('int64', 'bigint'); +solToTs.set('int72', 'bigint'); +solToTs.set('int80', 'bigint'); +solToTs.set('int88', 'bigint'); +solToTs.set('int96', 'bigint'); +solToTs.set('int104', 'bigint'); +solToTs.set('int112', 'bigint'); +solToTs.set('int120', 'bigint'); +solToTs.set('int128', 'bigint'); +solToTs.set('int136', 'bigint'); +solToTs.set('int144', 'bigint'); +solToTs.set('int152', 'bigint'); +solToTs.set('int160', 'bigint'); +solToTs.set('int168', 'bigint'); +solToTs.set('int176', 'bigint'); +solToTs.set('int184', 'bigint'); +solToTs.set('int192', 'bigint'); +solToTs.set('int200', 'bigint'); +solToTs.set('int208', 'bigint'); +solToTs.set('int216', 'bigint'); +solToTs.set('int224', 'bigint'); +solToTs.set('int232', 'bigint'); +solToTs.set('int240', 'bigint'); +solToTs.set('int248', 'bigint'); +solToTs.set('int256', 'bigint'); +solToTs.set('int', 'bigint'); + +solToTs.set('uint8', 'number'); +solToTs.set('uint16', 'number'); +solToTs.set('uint24', 'number'); +solToTs.set('uint32', 'number'); +solToTs.set('uint48', 'number'); +solToTs.set('uint56', 'bigint'); +solToTs.set('uint64', 'bigint'); +solToTs.set('uint72', 'bigint'); +solToTs.set('uint80', 'bigint'); +solToTs.set('uint88', 'bigint'); +solToTs.set('uint96', 'bigint'); +solToTs.set('uint104', 'bigint'); +solToTs.set('uint112', 'bigint'); +solToTs.set('uint120', 'bigint'); +solToTs.set('uint128', 'bigint'); +solToTs.set('uint136', 'bigint'); +solToTs.set('uint144', 'bigint'); +solToTs.set('uint152', 'bigint'); +solToTs.set('uint160', 'bigint'); +solToTs.set('uint168', 'bigint'); +solToTs.set('uint176', 'bigint'); +solToTs.set('uint184', 'bigint'); +solToTs.set('uint192', 'bigint'); +solToTs.set('uint200', 'bigint'); +solToTs.set('uint208', 'bigint'); +solToTs.set('uint216', 'bigint'); +solToTs.set('uint224', 'bigint'); +solToTs.set('uint232', 'bigint'); +solToTs.set('uint240', 'bigint'); +solToTs.set('uint248', 'bigint'); +solToTs.set('uint256', 'bigint'); +solToTs.set('uint', 'bigint'); + +solToTs.set('bytes', 'string'); +solToTs.set('bytes1', 'string'); +solToTs.set('bytes2', 'string'); +solToTs.set('bytes3', 'string'); +solToTs.set('bytes4', 'string'); +solToTs.set('bytes5', 'string'); +solToTs.set('bytes6', 'string'); +solToTs.set('bytes7', 'string'); +solToTs.set('bytes8', 'string'); +solToTs.set('bytes9', 'string'); +solToTs.set('bytes10', 'string'); +solToTs.set('bytes11', 'string'); +solToTs.set('bytes12', 'string'); +solToTs.set('bytes13', 'string'); +solToTs.set('bytes14', 'string'); +solToTs.set('bytes15', 'string'); +solToTs.set('bytes16', 'string'); +solToTs.set('bytes17', 'string'); +solToTs.set('bytes18', 'string'); +solToTs.set('bytes19', 'string'); +solToTs.set('bytes20', 'string'); +solToTs.set('bytes21', 'string'); +solToTs.set('bytes22', 'string'); +solToTs.set('bytes23', 'string'); +solToTs.set('bytes24', 'string'); +solToTs.set('bytes25', 'string'); +solToTs.set('bytes26', 'string'); +solToTs.set('bytes27', 'string'); +solToTs.set('bytes28', 'string'); +solToTs.set('bytes29', 'string'); +solToTs.set('bytes30', 'string'); +solToTs.set('bytes31', 'string'); +solToTs.set('bytes32', 'string'); + +export { solToTs }; diff --git a/packages/codegen/src/utils/type-mappings.ts b/packages/codegen/src/utils/type-mappings.ts index 2922b3c5..f35a1556 100644 --- a/packages/codegen/src/utils/type-mappings.ts +++ b/packages/codegen/src/utils/type-mappings.ts @@ -2,26 +2,12 @@ // Copyright 2021 Vulcanize, Inc. // -const _solToTs: Map = new Map(); +import { solToTs } from './solToTs'; + const _tsToGql: Map = new Map(); const _tsToPg: Map = new Map(); const _gqlToTs: Map = new Map(); -// TODO Get typemapping from ethersjs. -// Solidity to Typescript type-mapping. -_solToTs.set('string', 'string'); -_solToTs.set('uint8', 'number'); -_solToTs.set('uint16', 'number'); -_solToTs.set('uint64', 'bigint'); -_solToTs.set('uint128', 'bigint'); -_solToTs.set('uint256', 'bigint'); -_solToTs.set('uint', 'bigint'); -_solToTs.set('address', 'string'); -_solToTs.set('bool', 'boolean'); -_solToTs.set('bytes', 'string'); -_solToTs.set('bytes4', 'string'); -_solToTs.set('bytes32', 'string'); - // Typescript to Graphql type-mapping. _tsToGql.set('string', 'String'); _tsToGql.set('number', 'Int'); @@ -44,7 +30,7 @@ _gqlToTs.set('BigDecimal', 'Decimal'); _gqlToTs.set('Bytes', 'string'); function getTsForSol (solType: string): string | undefined { - return _solToTs.get(solType); + return solToTs.get(solType); } function getGqlForTs (tsType: string): string | undefined { diff --git a/packages/eden-watcher/src/cli/reset-cmds/state.ts b/packages/eden-watcher/src/cli/reset-cmds/state.ts index ecc78a39..4af26348 100644 --- a/packages/eden-watcher/src/cli/reset-cmds/state.ts +++ b/packages/eden-watcher/src/cli/reset-cmds/state.ts @@ -73,12 +73,6 @@ export const handler = async (argv: any): Promise => { graphWatcher.setIndexer(indexer); await graphWatcher.init(); - const syncStatus = await indexer.getSyncStatus(); - assert(syncStatus, 'Missing syncStatus'); - - const ipldStatus = await indexer.getIPLDStatus(); - assert(ipldStatus, 'Missing ipldStatus'); - const blockProgresses = await indexer.getBlocksAtHeight(argv.blockNumber, false); assert(blockProgresses.length, `No blocks at specified block number ${argv.blockNumber}`); assert(!blockProgresses.some(block => !block.isComplete), `Incomplete block at block number ${argv.blockNumber} with unprocessed events`); @@ -95,6 +89,9 @@ export const handler = async (argv: any): Promise => { await Promise.all(removeEntitiesPromise); + const syncStatus = await indexer.getSyncStatus(); + assert(syncStatus, 'Missing syncStatus'); + if (syncStatus.latestIndexedBlockNumber > blockProgress.blockNumber) { await indexer.updateSyncStatusIndexedBlock(blockProgress.blockHash, blockProgress.blockNumber, true); } @@ -103,16 +100,20 @@ export const handler = async (argv: any): Promise => { await indexer.updateSyncStatusCanonicalBlock(blockProgress.blockHash, blockProgress.blockNumber, true); } - if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true); - } + const ipldStatus = await indexer.getIPLDStatus(); - if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true); - } + if (ipldStatus) { + if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true); + } - if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true); + if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true); + } + + if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true); + } } await indexer.updateSyncStatusChainHead(blockProgress.blockHash, blockProgress.blockNumber, true); diff --git a/packages/graph-node/package.json b/packages/graph-node/package.json index 2932ff26..81b4606b 100644 --- a/packages/graph-node/package.json +++ b/packages/graph-node/package.json @@ -35,7 +35,7 @@ "asbuild:release": "asc assembly/index.ts --lib ./node_modules --exportRuntime --target release --runPasses asyncify", "asbuild": "yarn asbuild:debug && yarn asbuild:release", "test": "yarn asbuild:debug && DEBUG=vulcanize:* node --experimental-wasm-bigint node_modules/.bin/_mocha src/**/*.test.ts", - "build:example": "cd test/subgraph/example1 && yarn && yarn build", + "build:example": "cd test/subgraph/example1 && yarn && yarn codegen && yarn build", "watch": "DEBUG=vulcanize:* nodemon --watch src src/watcher.ts", "compare-entity": "node bin/compare-entity" }, diff --git a/packages/graph-node/src/call-handler.test.ts b/packages/graph-node/src/call-handler.test.ts index f2f4b106..6116b10e 100644 --- a/packages/graph-node/src/call-handler.test.ts +++ b/packages/graph-node/src/call-handler.test.ts @@ -71,7 +71,10 @@ describe('call handler in mapping code', () => { db, indexer, provider, - { block: dummyEventData.block }, + { + block: dummyEventData.block, + contractAddress: dummyGraphData.dataSource.address + }, filePath, dummyGraphData ); diff --git a/packages/graph-node/src/crypto.test.ts b/packages/graph-node/src/crypto.test.ts index 1630f6e1..6d8334ca 100644 --- a/packages/graph-node/src/crypto.test.ts +++ b/packages/graph-node/src/crypto.test.ts @@ -9,7 +9,7 @@ import { utils } from 'ethers'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -26,14 +26,18 @@ describe('crypto host api', () => { }); it('should load the subgraph example wasm', async () => { + const dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm'); + const instance = await instantiate( db, indexer, provider, {}, - filePath + filePath, + dummyGraphData ); + exports = instance.exports; const { _start } = exports; diff --git a/packages/graph-node/src/eden.test.ts b/packages/graph-node/src/eden.test.ts index b677937a..88a269be 100644 --- a/packages/graph-node/src/eden.test.ts +++ b/packages/graph-node/src/eden.test.ts @@ -89,7 +89,10 @@ describe('eden wasm loader tests', async () => { db, indexer, provider, - { block: dummyEventData.block }, + { + block: dummyEventData.block, + contractAddress + }, filePath, data )); @@ -205,7 +208,10 @@ describe('eden wasm loader tests', async () => { ({ exports } = await instantiate(db, indexer, provider, - { block: dummyEventData.block }, + { + block: dummyEventData.block, + contractAddress + }, filePath, data )); @@ -319,7 +325,10 @@ describe('eden wasm loader tests', async () => { db, indexer, provider, - { block: dummyEventData.block }, + { + block: dummyEventData.block, + contractAddress + }, filePath, data )); diff --git a/packages/graph-node/src/eth-abi.test.ts b/packages/graph-node/src/eth-abi.test.ts index aa7f4ad3..4078bb4c 100644 --- a/packages/graph-node/src/eth-abi.test.ts +++ b/packages/graph-node/src/eth-abi.test.ts @@ -8,7 +8,7 @@ import { expect } from 'chai'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -26,14 +26,18 @@ describe('ethereum ABI encode decode', () => { }); it('should load the subgraph example wasm', async () => { + const dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm'); + const instance = await instantiate( db, indexer, provider, {}, - filePath + filePath, + dummyGraphData ); + exports = instance.exports; const { _start } = exports; diff --git a/packages/graph-node/src/eth-call.test.ts b/packages/graph-node/src/eth-call.test.ts index 36c014bc..6afe75db 100644 --- a/packages/graph-node/src/eth-call.test.ts +++ b/packages/graph-node/src/eth-call.test.ts @@ -50,7 +50,10 @@ describe('eth-call wasm tests', () => { db, indexer, provider, - { block: dummyEventData.block }, + { + block: dummyEventData.block, + contractAddress + }, filePath, data ); diff --git a/packages/graph-node/src/json.test.ts b/packages/graph-node/src/json.test.ts index 9a37078d..515002f3 100644 --- a/packages/graph-node/src/json.test.ts +++ b/packages/graph-node/src/json.test.ts @@ -7,7 +7,7 @@ import path from 'path'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -24,14 +24,18 @@ describe('json host api', () => { }); it('should load the subgraph example wasm', async () => { + const dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm'); + const instance = await instantiate( db, indexer, provider, {}, - filePath + filePath, + dummyGraphData ); + exports = instance.exports; const { _start } = exports; diff --git a/packages/graph-node/src/loader.test.ts b/packages/graph-node/src/loader.test.ts index 1a7e2132..a12ada90 100644 --- a/packages/graph-node/src/loader.test.ts +++ b/packages/graph-node/src/loader.test.ts @@ -9,7 +9,7 @@ import { utils } from 'ethers'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -21,19 +21,23 @@ describe('wasm loader tests', () => { let indexer: Indexer; let provider: BaseProvider; let module: WebAssembly.Module; + let dummyGraphData: any; before(async () => { db = getTestDatabase(); indexer = getTestIndexer(); provider = getTestProvider(); + dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, WASM_FILE_PATH); + const instance = await instantiate( db, indexer, provider, {}, - filePath + filePath, + dummyGraphData ); exports = instance.exports; @@ -110,7 +114,8 @@ describe('wasm loader tests', () => { indexer, provider, {}, - module + module, + dummyGraphData ); exports = instance.exports; diff --git a/packages/graph-node/src/loader.ts b/packages/graph-node/src/loader.ts index f7513ab7..f1f4b4e1 100644 --- a/packages/graph-node/src/loader.ts +++ b/packages/graph-node/src/loader.ts @@ -38,14 +38,14 @@ type idOfType = (TypeId: number) => number export interface GraphData { abis?: {[key: string]: ContractInterface}; - dataSource?: { - address: string, + dataSource: { network: string; }; } export interface Context { block?: Block + contractAddress?: string } const log = debug('vulcanize:graph-node'); @@ -56,7 +56,7 @@ export const instantiate = async ( provider: BaseProvider, context: Context, filePathOrModule: string | WebAssembly.Module, - data: GraphData = {} + data: GraphData ): Promise => { const { abis = {}, dataSource } = data; @@ -108,8 +108,8 @@ export const instantiate = async ( // Create an auto-diff. assert(indexer.createDiffStaged); - assert(dataSource?.address); - await indexer.createDiffStaged(dataSource.address, context.block.blockHash, diffData); + assert(context.contractAddress); + await indexer.createDiffStaged(context.contractAddress, context.block.blockHash, diffData); }, 'log.log': (level: number, msg: number) => { @@ -203,8 +203,9 @@ export const instantiate = async ( const res = await __newArray(arrayEthereumValueId, resultPtrArray); return res; - } catch (err) { - console.log('eth_call error', err); + } catch (err: any) { + log('eth_call error', err.message); + return null; } }, @@ -608,8 +609,8 @@ export const instantiate = async ( }, datasource: { 'dataSource.address': async () => { - assert(dataSource); - const addressStringPtr = await __newString(dataSource.address); + assert(context.contractAddress); + const addressStringPtr = await __newString(context.contractAddress); return Address.fromString(addressStringPtr); }, 'dataSource.context': async () => { @@ -621,6 +622,15 @@ export const instantiate = async ( 'dataSource.network': async () => { assert(dataSource); return __newString(dataSource.network); + }, + 'dataSource.create': async (name: number, params: number) => { + const [addressStringPtr] = __getArray(params); + const addressString = __getString(addressStringPtr); + const contractKind = __getString(name); + + assert(indexer.watchContract); + assert(context.block); + await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber)); } }, json: { diff --git a/packages/graph-node/src/numbers.test.ts b/packages/graph-node/src/numbers.test.ts index e5611668..db6d674e 100644 --- a/packages/graph-node/src/numbers.test.ts +++ b/packages/graph-node/src/numbers.test.ts @@ -10,7 +10,7 @@ import { GraphDecimal } from '@vulcanize/util'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; import { @@ -37,13 +37,16 @@ describe('numbers wasm tests', () => { indexer = getTestIndexer(); provider = getTestProvider(); + const dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH); + const instance = await instantiate( db, indexer, provider, {}, - filePath + filePath, + dummyGraphData ); exports = instance.exports; const { _start } = exports; diff --git a/packages/graph-node/src/type-conversion.test.ts b/packages/graph-node/src/type-conversion.test.ts index eac8e085..03e6e618 100644 --- a/packages/graph-node/src/type-conversion.test.ts +++ b/packages/graph-node/src/type-conversion.test.ts @@ -9,7 +9,7 @@ import { utils, BigNumber } from 'ethers'; import { BaseProvider } from '@ethersproject/providers'; import { instantiate } from './loader'; -import { getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; +import { getDummyGraphData, getTestDatabase, getTestIndexer, getTestProvider } from '../test/utils'; import { Database } from './database'; import { Indexer } from '../test/utils/indexer'; @@ -26,13 +26,16 @@ describe('typeConversion wasm tests', () => { indexer = getTestIndexer(); provider = getTestProvider(); + const dummyGraphData = getDummyGraphData(); const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH); + const instance = await instantiate( db, indexer, provider, {}, - filePath + filePath, + dummyGraphData ); exports = instance.exports; const { _start } = exports; diff --git a/packages/graph-node/src/watcher.ts b/packages/graph-node/src/watcher.ts index b382d368..71fdda3a 100644 --- a/packages/graph-node/src/watcher.ts +++ b/packages/graph-node/src/watcher.ts @@ -47,12 +47,12 @@ export class GraphWatcher { } async init () { - const { dataSources } = await getSubgraphConfig(this._subgraphPath); - this._dataSources = dataSources; + const { dataSources, templates = [] } = await getSubgraphConfig(this._subgraphPath); + this._dataSources = dataSources.concat(templates); - // Create wasm instance and contract interface for each dataSource in subgraph yaml. + // Create wasm instance and contract interface for each dataSource and template in subgraph yaml. const dataPromises = this._dataSources.map(async (dataSource: any) => { - const { source: { address, abi }, mapping, network } = dataSource; + const { source: { abi }, mapping, network } = dataSource; const { abis, file } = mapping; const abisMap = abis.reduce((acc: {[key: string]: ContractInterface}, abi: any) => { @@ -68,7 +68,6 @@ export class GraphWatcher { const data = { abis: abisMap, dataSource: { - address, network } }; @@ -94,8 +93,8 @@ export class GraphWatcher { // TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533 instance.exports._start(); - const { source: { address } } = dataSource; - acc[address] = data[index]; + const { name } = dataSource; + acc[name] = data[index]; return acc; }, {}); @@ -110,10 +109,13 @@ export class GraphWatcher { for (const dataSource of this._dataSources) { const { source: { address, startBlock }, name } = dataSource; - const watchedContract = await this._indexer.isWatchedContract(address); + // Skip for templates as they are added dynamically. + if (address) { + const watchedContract = await this._indexer.isWatchedContract(address); - if (!watchedContract) { - await this._indexer.watchContract(address, name, true, startBlock); + if (!watchedContract) { + await this._indexer.watchContract(address, name, true, startBlock); + } } } } @@ -128,15 +130,21 @@ export class GraphWatcher { const blockData = this._context.block; assert(blockData); + assert(this._indexer && this._indexer.isWatchedContract); + const watchedContract = await this._indexer.isWatchedContract(contract); + assert(watchedContract); + // Get dataSource in subgraph yaml based on contract address. - const dataSource = this._dataSources.find(dataSource => dataSource.source.address === contract); + const dataSource = this._dataSources.find(dataSource => dataSource.name === watchedContract.kind); if (!dataSource) { - log(`Subgraph doesnt have configuration for contract ${contract}`); + log(`Subgraph doesn't have configuration for contract ${contract}`); return; } - const { instance, contractInterface } = this._dataSourceMap[contract]; + this._context.contractAddress = contract; + + const { instance, contractInterface } = this._dataSourceMap[watchedContract.kind]; assert(instance); const { exports: instanceExports } = instance; @@ -172,7 +180,7 @@ export class GraphWatcher { // Create ethereum event to be passed to the wasm event handler. const ethereumEvent = await createEvent(instanceExports, contract, data); - await this._handleMemoryError(instanceExports[eventHandler.handler](ethereumEvent), dataSource.source.address); + await this._handleMemoryError(instanceExports[eventHandler.handler](ethereumEvent), dataSource.name); } async handleBlock (blockHash: string) { @@ -191,27 +199,47 @@ export class GraphWatcher { // https://stackoverflow.com/a/40453962 // https://github.com/AssemblyScript/assemblyscript/pull/1268#issue-618411291 // https://github.com/WebAssembly/memory64/blob/main/proposals/memory64/Overview.md#motivation - await this._reInitWasm(dataSource.source.address); + await this._reInitWasm(dataSource.name); } - // Check if block handler(s) are configured and start block has been reached. - if (!dataSource.mapping.blockHandlers || blockData.blockNumber < dataSource.source.startBlock) { + // Check if block handler(s) are configured. + if (!dataSource.mapping.blockHandlers) { continue; } - const { instance } = this._dataSourceMap[dataSource.source.address]; + const { instance } = this._dataSourceMap[dataSource.name]; assert(instance); const { exports: instanceExports } = instance; // Create ethereum block to be passed to a wasm block handler. const ethereumBlock = await createBlock(instanceExports, blockData); - // Call all the block handlers one after the another for a contract. - const blockHandlerPromises = dataSource.mapping.blockHandlers.map(async (blockHandler: any): Promise => { - await instanceExports[blockHandler.handler](ethereumBlock); - }); + let contractAddressList: string[] = []; - await this._handleMemoryError(Promise.all(blockHandlerPromises), dataSource.source.address); + if (dataSource.source.address) { + // Check if start block has been reached. + if (blockData.blockNumber >= dataSource.source.startBlock) { + contractAddressList.push(dataSource.source.address); + } + } else { + // Data source templates will have multiple watched contracts. + assert(this._indexer?.getContractsByKind); + const watchedContracts = this._indexer.getContractsByKind(dataSource.name); + + contractAddressList = watchedContracts.filter(contract => blockData.blockNumber >= contract.startingBlock) + .map(contract => contract.address); + } + + for (const contractAddress of contractAddressList) { + this._context.contractAddress = contractAddress; + + // Call all the block handlers one after another for a contract. + const blockHandlerPromises = dataSource.mapping.blockHandlers.map(async (blockHandler: any): Promise => { + await instanceExports[blockHandler.handler](ethereumBlock); + }); + + await this._handleMemoryError(Promise.all(blockHandlerPromises), dataSource.name); + } } } @@ -228,20 +256,20 @@ export class GraphWatcher { } /** - * Method to reinstantiate WASM instance for specified contract address. - * @param contractAddress + * Method to reinstantiate WASM instance for specified dataSource. + * @param dataSourceName */ - async _reInitWasm (contractAddress: string): Promise { - const { data, instance } = this._dataSourceMap[contractAddress]; + async _reInitWasm (dataSourceName: string): Promise { + const { data, instance } = this._dataSourceMap[dataSourceName]; assert(instance); const { module } = instance; - delete this._dataSourceMap[contractAddress].instance; + delete this._dataSourceMap[dataSourceName].instance; assert(this._indexer); // Reinstantiate with existing module. - this._dataSourceMap[contractAddress].instance = await instantiate( + this._dataSourceMap[dataSourceName].instance = await instantiate( this._database, this._indexer, this._ethProvider, @@ -252,17 +280,17 @@ export class GraphWatcher { // 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 - this._dataSourceMap[contractAddress].instance!.exports._start(); + this._dataSourceMap[dataSourceName].instance!.exports._start(); } - async _handleMemoryError (handlerPromise: Promise, contractAddress: string): Promise { + async _handleMemoryError (handlerPromise: Promise, dataSourceName: string): Promise { try { await handlerPromise; } catch (error) { if (error instanceof WebAssembly.RuntimeError && error instanceof Error) { if (error.message === 'unreachable') { // Reintantiate WASM for out of memory error. - this._reInitWasm(contractAddress); + this._reInitWasm(dataSourceName); } } diff --git a/packages/graph-node/test/subgraph/example1/.gitignore b/packages/graph-node/test/subgraph/example1/.gitignore new file mode 100644 index 00000000..40601278 --- /dev/null +++ b/packages/graph-node/test/subgraph/example1/.gitignore @@ -0,0 +1,5 @@ +build/ +node_modules/ +generated/ + +yarn-error.log diff --git a/packages/graph-node/test/subgraph/example1/abis/uniswap-v3/factory.json b/packages/graph-node/test/subgraph/example1/abis/uniswap-v3/factory.json new file mode 100644 index 00000000..1ab193fc --- /dev/null +++ b/packages/graph-node/test/subgraph/example1/abis/uniswap-v3/factory.json @@ -0,0 +1,148 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "FeeAmountEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolCreated", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "tokenA", "type": "address" }, + { "internalType": "address", "name": "tokenB", "type": "address" }, + { "internalType": "uint24", "name": "fee", "type": "uint24" } + ], + "name": "createPool", + "outputs": [ + { "internalType": "address", "name": "pool", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint24", "name": "fee", "type": "uint24" }, + { "internalType": "int24", "name": "tickSpacing", "type": "int24" } + ], + "name": "enableFeeAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint24", "name": "", "type": "uint24" }], + "name": "feeAmountTickSpacing", + "outputs": [{ "internalType": "int24", "name": "", "type": "int24" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "uint24", "name": "", "type": "uint24" } + ], + "name": "getPool", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parameters", + "outputs": [ + { "internalType": "address", "name": "factory", "type": "address" }, + { "internalType": "address", "name": "token0", "type": "address" }, + { "internalType": "address", "name": "token1", "type": "address" }, + { "internalType": "uint24", "name": "fee", "type": "uint24" }, + { "internalType": "int24", "name": "tickSpacing", "type": "int24" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_owner", "type": "address" } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/graph-node/test/subgraph/example1/abis/uniswap-v3/pool.json b/packages/graph-node/test/subgraph/example1/abis/uniswap-v3/pool.json new file mode 100644 index 00000000..c87d64dd --- /dev/null +++ b/packages/graph-node/test/subgraph/example1/abis/uniswap-v3/pool.json @@ -0,0 +1,988 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "CollectProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid1", + "type": "uint256" + } + ], + "name": "Flash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextOld", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextNew", + "type": "uint16" + } + ], + "name": "IncreaseObservationCardinalityNext", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0New", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1New", + "type": "uint8" + } + ], + "name": "SetFeeProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount1", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collectProtocol", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal0X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal1X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "flash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + } + ], + "name": "increaseObservationCardinalityNext", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidityPerTick", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "observations", + "outputs": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32[]", + "name": "secondsAgos", + "type": "uint32[]" + } + ], + "name": "observe", + "outputs": [ + { + "internalType": "int56[]", + "name": "tickCumulatives", + "type": "int56[]" + }, + { + "internalType": "uint160[]", + "name": "secondsPerLiquidityCumulativeX128s", + "type": "uint160[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint128", + "name": "_liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFees", + "outputs": [ + { + "internalType": "uint128", + "name": "token0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "token1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feeProtocol0", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "feeProtocol1", + "type": "uint8" + } + ], + "name": "setFeeProtocol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "snapshotCumulativesInside", + "outputs": [ + { + "internalType": "int56", + "name": "tickCumulativeInside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityInsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsInside", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "internalType": "int256", + "name": "amount1", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int16", + "name": "wordPosition", + "type": "int16" + } + ], + "name": "tickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "ticks", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/packages/graph-node/test/subgraph/example1/generated/Example1/Example1.ts b/packages/graph-node/test/subgraph/example1/generated/Example1/Example1.ts deleted file mode 100644 index 12555d96..00000000 --- a/packages/graph-node/test/subgraph/example1/generated/Example1/Example1.ts +++ /dev/null @@ -1,184 +0,0 @@ -// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - -import { - ethereum, - JSONValue, - TypedMap, - Entity, - Bytes, - Address, - BigInt -} from "@graphprotocol/graph-ts"; - -export class Test extends ethereum.Event { - get params(): Test__Params { - return new Test__Params(this); - } -} - -export class Test__Params { - _event: Test; - - constructor(event: Test) { - this._event = event; - } - - get param1(): string { - return this._event.parameters[0].value.toString(); - } - - get param2(): i32 { - return this._event.parameters[1].value.toI32(); - } - - get param3(): BigInt { - return this._event.parameters[2].value.toBigInt(); - } -} - -export class Example1__structMethodResultValue0Struct extends ethereum.Tuple { - get bidAmount1(): BigInt { - return this[0].toBigInt(); - } - - get bidAmount2(): BigInt { - return this[1].toBigInt(); - } -} - -export class Example1 extends ethereum.SmartContract { - static bind(address: Address): Example1 { - return new Example1("Example1", address); - } - - addMethod(bidAmount1: BigInt, bidAmount2: BigInt): BigInt { - let result = super.call( - "addMethod", - "addMethod(uint128,uint128):(uint256)", - [ - ethereum.Value.fromUnsignedBigInt(bidAmount1), - ethereum.Value.fromUnsignedBigInt(bidAmount2) - ] - ); - - return result[0].toBigInt(); - } - - try_addMethod( - bidAmount1: BigInt, - bidAmount2: BigInt - ): ethereum.CallResult { - let result = super.tryCall( - "addMethod", - "addMethod(uint128,uint128):(uint256)", - [ - ethereum.Value.fromUnsignedBigInt(bidAmount1), - ethereum.Value.fromUnsignedBigInt(bidAmount2) - ] - ); - if (result.reverted) { - return new ethereum.CallResult(); - } - let value = result.value; - return ethereum.CallResult.fromValue(value[0].toBigInt()); - } - - emitEvent(): boolean { - let result = super.call("emitEvent", "emitEvent():(bool)", []); - - return result[0].toBoolean(); - } - - try_emitEvent(): ethereum.CallResult { - let result = super.tryCall("emitEvent", "emitEvent():(bool)", []); - if (result.reverted) { - return new ethereum.CallResult(); - } - let value = result.value; - return ethereum.CallResult.fromValue(value[0].toBoolean()); - } - - getMethod(): string { - let result = super.call("getMethod", "getMethod():(string)", []); - - return result[0].toString(); - } - - try_getMethod(): ethereum.CallResult { - let result = super.tryCall("getMethod", "getMethod():(string)", []); - if (result.reverted) { - return new ethereum.CallResult(); - } - let value = result.value; - return ethereum.CallResult.fromValue(value[0].toString()); - } - - structMethod( - bidAmount1: BigInt, - bidAmount2: BigInt - ): Example1__structMethodResultValue0Struct { - let result = super.call( - "structMethod", - "structMethod(uint128,uint128):((uint128,uint128))", - [ - ethereum.Value.fromUnsignedBigInt(bidAmount1), - ethereum.Value.fromUnsignedBigInt(bidAmount2) - ] - ); - - return changetype( - result[0].toTuple() - ); - } - - try_structMethod( - bidAmount1: BigInt, - bidAmount2: BigInt - ): ethereum.CallResult { - let result = super.tryCall( - "structMethod", - "structMethod(uint128,uint128):((uint128,uint128))", - [ - ethereum.Value.fromUnsignedBigInt(bidAmount1), - ethereum.Value.fromUnsignedBigInt(bidAmount2) - ] - ); - if (result.reverted) { - return new ethereum.CallResult(); - } - let value = result.value; - return ethereum.CallResult.fromValue( - changetype(value[0].toTuple()) - ); - } -} - -export class EmitEventCall extends ethereum.Call { - get inputs(): EmitEventCall__Inputs { - return new EmitEventCall__Inputs(this); - } - - get outputs(): EmitEventCall__Outputs { - return new EmitEventCall__Outputs(this); - } -} - -export class EmitEventCall__Inputs { - _call: EmitEventCall; - - constructor(call: EmitEventCall) { - this._call = call; - } -} - -export class EmitEventCall__Outputs { - _call: EmitEventCall; - - constructor(call: EmitEventCall) { - this._call = call; - } - - get value0(): boolean { - return this._call.outputValues[0].value.toBoolean(); - } -} diff --git a/packages/graph-node/test/subgraph/example1/generated/export.ts b/packages/graph-node/test/subgraph/example1/generated/export.ts deleted file mode 100644 index be6ab08e..00000000 --- a/packages/graph-node/test/subgraph/example1/generated/export.ts +++ /dev/null @@ -1,101 +0,0 @@ -// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - -// Re-exports -import { - BigDecimal, - BigInt, - ethereum, - Address, - ByteArray, - Bytes, - Entity, - Value, - JSONValue, - TypedMap, - JSONValueKind, - Result, - Wrapped -} from '@graphprotocol/graph-ts'; - -// All exports are used in JS host API implementations. - -/** - * Class used to create TypedMap instance in json fromBytes host API. - */ -export class JSONValueTypedMap extends TypedMap {} - -/** - * Class used to create JSONValue instances from different value types. - * Implementation is based on Value class in graph-ts. https://github.com/graphprotocol/graph-ts/blob/master/common/value.ts#L188 - */ -export class CustomJSONValue extends JSONValue { - static fromArray(input: Array): JSONValue { - const jsonValue = new JSONValue(); - jsonValue.kind = JSONValueKind.ARRAY; - jsonValue.data = changetype(input); - return jsonValue; - } - - static fromObject(object: TypedMap): JSONValue { - const jsonValue = new JSONValue(); - jsonValue.kind = JSONValueKind.OBJECT; - jsonValue.data = changetype(object); - return jsonValue; - } - - static fromNumber(n: string): JSONValue { - const jsonValue = new JSONValue(); - jsonValue.kind = JSONValueKind.NUMBER; - jsonValue.data = changetype(n); - return jsonValue; - } - - static fromBoolean(b: boolean): JSONValue { - const jsonValue = new JSONValue(); - jsonValue.kind = JSONValueKind.BOOL; - jsonValue.data = b ? 1 : 0; - return jsonValue; - } - - static fromString(s: string): JSONValue { - const jsonValue = new JSONValue(); - jsonValue.kind = JSONValueKind.STRING; - jsonValue.data = changetype(s); - return jsonValue; - } - - static fromNull(): JSONValue { - const jsonValue = new JSONValue(); - jsonValue.kind = JSONValueKind.NULL; - return jsonValue; - } -} - -/** - * Class used to create Result instance in json try_fromBytes host API. - */ -export class JSONResult extends Result { - constructor (value: JSONValue | null) { - super(); - - if (value) { - this._value = new Wrapped(value); - } else { - this._error = new Wrapped(true); - } - } -} - -export { - BigDecimal, - BigInt, - - ethereum, - Entity, - - Address, - ByteArray, - Bytes, - Value, - JSONValue -} diff --git a/packages/graph-node/test/subgraph/example1/generated/schema.ts b/packages/graph-node/test/subgraph/example1/generated/schema.ts deleted file mode 100644 index 2c186824..00000000 --- a/packages/graph-node/test/subgraph/example1/generated/schema.ts +++ /dev/null @@ -1,254 +0,0 @@ -// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - -import { - TypedMap, - Entity, - Value, - ValueKind, - store, - Address, - Bytes, - BigInt, - BigDecimal -} from "@graphprotocol/graph-ts"; - -export class Blog extends Entity { - constructor(id: string) { - super(); - this.set("id", Value.fromString(id)); - - this.set("kind", Value.fromString("")); - this.set("isActive", Value.fromBoolean(false)); - this.set("reviews", Value.fromBigIntArray(new Array(0))); - this.set("author", Value.fromString("")); - this.set("categories", Value.fromStringArray(new Array(0))); - } - - save(): void { - let id = this.get("id"); - assert(id != null, "Cannot save Blog entity without an ID"); - if (id) { - assert( - id.kind == ValueKind.STRING, - "Cannot save Blog entity with non-string ID. " + - 'Considering using .toHex() to convert the "id" to a string.' - ); - store.set("Blog", id.toString(), this); - } - } - - static load(id: string): Blog | null { - return changetype(store.get("Blog", id)); - } - - get id(): string { - let value = this.get("id"); - return value!.toString(); - } - - set id(value: string) { - this.set("id", Value.fromString(value)); - } - - get kind(): string { - let value = this.get("kind"); - return value!.toString(); - } - - set kind(value: string) { - this.set("kind", Value.fromString(value)); - } - - get isActive(): boolean { - let value = this.get("isActive"); - return value!.toBoolean(); - } - - set isActive(value: boolean) { - this.set("isActive", Value.fromBoolean(value)); - } - - get reviews(): Array { - let value = this.get("reviews"); - return value!.toBigIntArray(); - } - - set reviews(value: Array) { - this.set("reviews", Value.fromBigIntArray(value)); - } - - get author(): string { - let value = this.get("author"); - return value!.toString(); - } - - set author(value: string) { - this.set("author", Value.fromString(value)); - } - - get categories(): Array { - let value = this.get("categories"); - return value!.toStringArray(); - } - - set categories(value: Array) { - this.set("categories", Value.fromStringArray(value)); - } -} - -export class Author extends Entity { - constructor(id: string) { - super(); - this.set("id", Value.fromString(id)); - - this.set("blogCount", Value.fromBigInt(BigInt.zero())); - this.set("name", Value.fromString("")); - this.set("rating", Value.fromBigDecimal(BigDecimal.zero())); - this.set("paramInt", Value.fromI32(0)); - this.set("paramBigInt", Value.fromBigInt(BigInt.zero())); - this.set("paramBytes", Value.fromBytes(Bytes.empty())); - } - - save(): void { - let id = this.get("id"); - assert(id != null, "Cannot save Author entity without an ID"); - if (id) { - assert( - id.kind == ValueKind.STRING, - "Cannot save Author entity with non-string ID. " + - 'Considering using .toHex() to convert the "id" to a string.' - ); - store.set("Author", id.toString(), this); - } - } - - static load(id: string): Author | null { - return changetype(store.get("Author", id)); - } - - get id(): string { - let value = this.get("id"); - return value!.toString(); - } - - set id(value: string) { - this.set("id", Value.fromString(value)); - } - - get blogCount(): BigInt { - let value = this.get("blogCount"); - return value!.toBigInt(); - } - - set blogCount(value: BigInt) { - this.set("blogCount", Value.fromBigInt(value)); - } - - get name(): string { - let value = this.get("name"); - return value!.toString(); - } - - set name(value: string) { - this.set("name", Value.fromString(value)); - } - - get rating(): BigDecimal { - let value = this.get("rating"); - return value!.toBigDecimal(); - } - - set rating(value: BigDecimal) { - this.set("rating", Value.fromBigDecimal(value)); - } - - get paramInt(): i32 { - let value = this.get("paramInt"); - return value!.toI32(); - } - - set paramInt(value: i32) { - this.set("paramInt", Value.fromI32(value)); - } - - get paramBigInt(): BigInt { - let value = this.get("paramBigInt"); - return value!.toBigInt(); - } - - set paramBigInt(value: BigInt) { - this.set("paramBigInt", Value.fromBigInt(value)); - } - - get paramBytes(): Bytes { - let value = this.get("paramBytes"); - return value!.toBytes(); - } - - set paramBytes(value: Bytes) { - this.set("paramBytes", Value.fromBytes(value)); - } - - get blogs(): Array { - let value = this.get("blogs"); - return value!.toStringArray(); - } - - set blogs(value: Array) { - this.set("blogs", Value.fromStringArray(value)); - } -} - -export class Category extends Entity { - constructor(id: string) { - super(); - this.set("id", Value.fromString(id)); - - this.set("name", Value.fromString("")); - this.set("count", Value.fromBigInt(BigInt.zero())); - } - - save(): void { - let id = this.get("id"); - assert(id != null, "Cannot save Category entity without an ID"); - if (id) { - assert( - id.kind == ValueKind.STRING, - "Cannot save Category entity with non-string ID. " + - 'Considering using .toHex() to convert the "id" to a string.' - ); - store.set("Category", id.toString(), this); - } - } - - static load(id: string): Category | null { - return changetype(store.get("Category", id)); - } - - get id(): string { - let value = this.get("id"); - return value!.toString(); - } - - set id(value: string) { - this.set("id", Value.fromString(value)); - } - - get name(): string { - let value = this.get("name"); - return value!.toString(); - } - - set name(value: string) { - this.set("name", Value.fromString(value)); - } - - get count(): BigInt { - let value = this.get("count"); - return value!.toBigInt(); - } - - set count(value: BigInt) { - this.set("count", Value.fromBigInt(value)); - } -} diff --git a/packages/graph-node/test/subgraph/example1/src/uniswap-v3/factory.ts b/packages/graph-node/test/subgraph/example1/src/uniswap-v3/factory.ts new file mode 100644 index 00000000..493ec6db --- /dev/null +++ b/packages/graph-node/test/subgraph/example1/src/uniswap-v3/factory.ts @@ -0,0 +1,10 @@ +import { PoolCreated } from '../../generated/Factory/Factory'; +import { Pool as PoolTemplate } from '../../generated/templates'; +import { log } from '@graphprotocol/graph-ts'; + +export function handlePoolCreated (event: PoolCreated): void { + log.debug('PoolCreated event', []); + + // create the tracked contract based on the template + PoolTemplate.create(event.params.pool); +} diff --git a/packages/graph-node/test/subgraph/example1/src/uniswap-v3/pool.ts b/packages/graph-node/test/subgraph/example1/src/uniswap-v3/pool.ts new file mode 100644 index 00000000..b1c05966 --- /dev/null +++ b/packages/graph-node/test/subgraph/example1/src/uniswap-v3/pool.ts @@ -0,0 +1,14 @@ +import { dataSource, ethereum, log } from '@graphprotocol/graph-ts'; + +import { Initialize } from '../../generated/templates/Pool/Pool'; + +export function handleInitialize (event: Initialize): void { + log.debug('event.address: {}', [event.address.toHexString()]); + log.debug('event.params.sqrtPriceX96: {}', [event.params.sqrtPriceX96.toString()]); + log.debug('event.params.tick: {}', [event.params.tick.toString()]); +} + +export function handleBlock (block: ethereum.Block): void { + log.debug('block info: {}', [block.number.toString()]); + log.debug('dataSource address: {}', [dataSource.address().toHex()]); +} diff --git a/packages/graph-node/test/subgraph/example1/subgraph.yaml b/packages/graph-node/test/subgraph/example1/subgraph.yaml index 67ec9c72..856144a1 100644 --- a/packages/graph-node/test/subgraph/example1/subgraph.yaml +++ b/packages/graph-node/test/subgraph/example1/subgraph.yaml @@ -24,3 +24,44 @@ dataSources: blockHandlers: - handler: handleBlock file: ./src/mapping.ts + - kind: ethereum/contract + name: Factory + network: mainnet + source: + address: "" + abi: Factory + startBlock: 100 + mapping: + kind: ethereum/events + apiVersion: 0.0.5 + language: wasm/assemblyscript + entities: [] + abis: + - name: Factory + file: ./abis/factory.json + - name: Pool + file: ./abis/pool.json + eventHandlers: + - event: PoolCreated(indexed address,indexed address,indexed uint24,int24,address) + handler: handlePoolCreated + file: ./src/factory.ts +templates: + - kind: ethereum/contract + name: Pool + network: mainnet + source: + abi: Pool + mapping: + kind: ethereum/events + apiVersion: 0.0.5 + language: wasm/assemblyscript + entities: [] + file: ./src/pool.ts + abis: + - name: Pool + file: ./abis/pool.json + blockHandlers: + - handler: handleBlock + eventHandlers: + - event: Initialize(uint160,int24) + handler: handleInitialize diff --git a/packages/graph-test-watcher/src/artifacts/Factory.json b/packages/graph-test-watcher/src/artifacts/Factory.json new file mode 100644 index 00000000..e8e051be --- /dev/null +++ b/packages/graph-test-watcher/src/artifacts/Factory.json @@ -0,0 +1,238 @@ +{ + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "FeeAmountEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "createPool", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "enableFeeAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "feeAmountTickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "getPool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parameters", + "outputs": [ + { + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} \ No newline at end of file diff --git a/packages/graph-test-watcher/src/artifacts/Pool.json b/packages/graph-test-watcher/src/artifacts/Pool.json new file mode 100644 index 00000000..cea0ad1b --- /dev/null +++ b/packages/graph-test-watcher/src/artifacts/Pool.json @@ -0,0 +1,990 @@ +{ + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "CollectProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid1", + "type": "uint256" + } + ], + "name": "Flash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextOld", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextNew", + "type": "uint16" + } + ], + "name": "IncreaseObservationCardinalityNext", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0New", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1New", + "type": "uint8" + } + ], + "name": "SetFeeProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount1", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collectProtocol", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal0X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal1X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "flash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + } + ], + "name": "increaseObservationCardinalityNext", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidityPerTick", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "observations", + "outputs": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32[]", + "name": "secondsAgos", + "type": "uint32[]" + } + ], + "name": "observe", + "outputs": [ + { + "internalType": "int56[]", + "name": "tickCumulatives", + "type": "int56[]" + }, + { + "internalType": "uint160[]", + "name": "secondsPerLiquidityCumulativeX128s", + "type": "uint160[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint128", + "name": "_liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFees", + "outputs": [ + { + "internalType": "uint128", + "name": "token0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "token1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feeProtocol0", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "feeProtocol1", + "type": "uint8" + } + ], + "name": "setFeeProtocol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "snapshotCumulativesInside", + "outputs": [ + { + "internalType": "int56", + "name": "tickCumulativeInside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityInsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsInside", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "internalType": "int256", + "name": "amount1", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int16", + "name": "wordPosition", + "type": "int16" + } + ], + "name": "tickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "ticks", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ] +} \ No newline at end of file diff --git a/packages/graph-test-watcher/src/cli/reset-cmds/state.ts b/packages/graph-test-watcher/src/cli/reset-cmds/state.ts index e532b8e7..415e2a65 100644 --- a/packages/graph-test-watcher/src/cli/reset-cmds/state.ts +++ b/packages/graph-test-watcher/src/cli/reset-cmds/state.ts @@ -61,12 +61,6 @@ export const handler = async (argv: any): Promise => { graphWatcher.setIndexer(indexer); await graphWatcher.init(); - const syncStatus = await indexer.getSyncStatus(); - assert(syncStatus, 'Missing syncStatus'); - - const ipldStatus = await indexer.getIPLDStatus(); - assert(ipldStatus, 'Missing ipldStatus'); - const blockProgresses = await indexer.getBlocksAtHeight(argv.blockNumber, false); assert(blockProgresses.length, `No blocks at specified block number ${argv.blockNumber}`); assert(!blockProgresses.some(block => !block.isComplete), `Incomplete block at block number ${argv.blockNumber} with unprocessed events`); @@ -83,6 +77,9 @@ export const handler = async (argv: any): Promise => { await Promise.all(removeEntitiesPromise); + const syncStatus = await indexer.getSyncStatus(); + assert(syncStatus, 'Missing syncStatus'); + if (syncStatus.latestIndexedBlockNumber > blockProgress.blockNumber) { await indexer.updateSyncStatusIndexedBlock(blockProgress.blockHash, blockProgress.blockNumber, true); } @@ -91,16 +88,19 @@ export const handler = async (argv: any): Promise => { await indexer.updateSyncStatusCanonicalBlock(blockProgress.blockHash, blockProgress.blockNumber, true); } - if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true); - } + const ipldStatus = await indexer.getIPLDStatus(); + if (ipldStatus) { + if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true); + } - if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true); - } + if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true); + } - if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) { - await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true); + if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) { + await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true); + } } await indexer.updateSyncStatusChainHead(blockProgress.blockHash, blockProgress.blockNumber, true); diff --git a/packages/graph-test-watcher/src/indexer.ts b/packages/graph-test-watcher/src/indexer.ts index c8e0f242..b682f122 100644 --- a/packages/graph-test-watcher/src/indexer.ts +++ b/packages/graph-test-watcher/src/indexer.ts @@ -36,7 +36,9 @@ import { SyncStatus } from './entity/SyncStatus'; import { IpldStatus } from './entity/IpldStatus'; import { BlockProgress } from './entity/BlockProgress'; import { IPLDBlock } from './entity/IPLDBlock'; -import artifacts from './artifacts/Example.json'; +import Example1Artifacts from './artifacts/Example.json'; +import FactoryArtifacts from './artifacts/Factory.json'; +import PoolArtifacts from './artifacts/Pool.json'; import { createInitialState, handleEvent, createStateDiff, createStateCheckpoint } from './hooks'; import { Author } from './entity/Author'; import { Blog } from './entity/Blog'; @@ -44,7 +46,13 @@ import { Category } from './entity/Category'; const log = debug('vulcanize:indexer'); +const KIND_EXAMPLE1 = 'Example1'; +const KIND_FACTORY = 'Factory'; +const KIND_POOL = 'Pool'; + const TEST_EVENT = 'Test'; +const POOLCREATED_EVENT = 'PoolCreated'; +const INITIALIZE_EVENT = 'Initialize'; export type ResultEvent = { block: { @@ -93,9 +101,9 @@ export class Indexer implements IndexerInterface { _serverConfig: ServerConfig _graphWatcher: GraphWatcher; - _abi: JsonFragment[] - _storageLayout: StorageLayout - _contract: ethers.utils.Interface + _abiMap: Map + _storageLayoutMap: Map + _contractMap: Map _ipfsClient: IPFSClient @@ -116,15 +124,36 @@ export class Indexer implements IndexerInterface { this._baseIndexer = new BaseIndexer(this._serverConfig, this._db, this._ethClient, this._postgraphileClient, this._ethProvider, jobQueue, this._ipfsClient); this._graphWatcher = graphWatcher; - const { abi, storageLayout } = artifacts; + this._abiMap = new Map(); + this._storageLayoutMap = new Map(); + this._contractMap = new Map(); - assert(abi); - assert(storageLayout); + const { + abi: Example1ABI, + storageLayout: Example1StorageLayout + } = Example1Artifacts; - this._abi = abi; - this._storageLayout = storageLayout; + const { + abi: FactoryABI + } = FactoryArtifacts; - this._contract = new ethers.utils.Interface(this._abi); + const { + abi: PoolABI + } = PoolArtifacts; + + assert(Example1ABI); + assert(Example1StorageLayout); + this._abiMap.set(KIND_EXAMPLE1, Example1ABI); + this._storageLayoutMap.set(KIND_EXAMPLE1, Example1StorageLayout); + this._contractMap.set(KIND_EXAMPLE1, new ethers.utils.Interface(Example1ABI)); + + assert(FactoryABI); + this._abiMap.set(KIND_FACTORY, FactoryABI); + this._contractMap.set(KIND_FACTORY, new ethers.utils.Interface(FactoryABI)); + + assert(PoolABI); + this._abiMap.set(KIND_POOL, PoolABI); + this._contractMap.set(KIND_POOL, new ethers.utils.Interface(PoolABI)); this._entityTypesMap = new Map(); this._populateEntityTypesMap(); @@ -209,7 +238,10 @@ export class Indexer implements IndexerInterface { const { block: { number } } = await this._ethClient.getBlockByHash(blockHash); const blockNumber = ethers.BigNumber.from(number).toNumber(); - const contract = new ethers.Contract(contractAddress, this._abi, this._ethProvider); + const abi = this._abiMap.get(KIND_EXAMPLE1); + assert(abi); + + const contract = new ethers.Contract(contractAddress, abi, this._ethProvider); const value = await contract.getMethod({ blockTag: blockHash }); const result: ValueResult = { value }; @@ -235,8 +267,11 @@ export class Indexer implements IndexerInterface { const { block: { number } } = await this._ethClient.getBlockByHash(blockHash); const blockNumber = ethers.BigNumber.from(number).toNumber(); + const storageLayout = this._storageLayoutMap.get(KIND_EXAMPLE1); + assert(storageLayout); + const result = await this._baseIndexer.getStorageValue( - this._storageLayout, + storageLayout, blockHash, contractAddress, '_test' @@ -383,7 +418,40 @@ export class Indexer implements IndexerInterface { let eventInfo = {}; const { topics, data } = logObj; - const logDescription = this._contract.parseLog({ data, topics }); + + const contract = this._contractMap.get(kind); + assert(contract); + + const logDescription = contract.parseLog({ data, topics }); + + switch (kind) { + case KIND_EXAMPLE1: { + ({ eventName, eventInfo } = this.parseExample1Event(logDescription)); + + break; + } + case KIND_FACTORY: { + ({ eventName, eventInfo } = this.parseFactoryEvent(logDescription)); + + break; + } + case KIND_POOL: { + ({ eventName, eventInfo } = this.parsePoolEvent(logDescription)); + + break; + } + } + + return { + eventName, + eventInfo, + eventSignature: logDescription.signature + }; + } + + parseExample1Event (logDescription: ethers.utils.LogDescription): { eventName: string, eventInfo: any } { + let eventName = UNKNOWN_EVENT_NAME; + let eventInfo = {}; switch (logDescription.name) { case TEST_EVENT: { @@ -401,8 +469,56 @@ export class Indexer implements IndexerInterface { return { eventName, - eventInfo, - eventSignature: logDescription.signature + eventInfo + }; + } + + parseFactoryEvent (logDescription: ethers.utils.LogDescription): { eventName: string, eventInfo: any } { + let eventName = UNKNOWN_EVENT_NAME; + let eventInfo = {}; + + switch (logDescription.name) { + case POOLCREATED_EVENT: { + eventName = logDescription.name; + const { token0, token1, fee, tickSpacing, pool } = logDescription.args; + eventInfo = { + token0, + token1, + fee, + tickSpacing, + pool + }; + + break; + } + } + + return { + eventName, + eventInfo + }; + } + + parsePoolEvent (logDescription: ethers.utils.LogDescription): { eventName: string, eventInfo: any } { + let eventName = UNKNOWN_EVENT_NAME; + let eventInfo = {}; + + switch (logDescription.name) { + case INITIALIZE_EVENT: { + eventName = logDescription.name; + const { sqrtPriceX96, tick } = logDescription.args; + eventInfo = { + sqrtPriceX96: BigInt(sqrtPriceX96.toString()), + tick + }; + + break; + } + } + + return { + eventName, + eventInfo }; } @@ -497,6 +613,10 @@ export class Indexer implements IndexerInterface { return this._baseIndexer.isWatchedContract(address); } + getContractsByKind (kind: string): Contract[] { + return this._baseIndexer.getContractsByKind(kind); + } + async getProcessedBlockCountForRange (fromBlockNumber: number, toBlockNumber: number): Promise<{ expected: number, actual: number }> { return this._baseIndexer.getProcessedBlockCountForRange(fromBlockNumber, toBlockNumber); } diff --git a/packages/graph-test-watcher/src/schema.gql b/packages/graph-test-watcher/src/schema.gql index e96a1eb5..b25d1c51 100644 --- a/packages/graph-test-watcher/src/schema.gql +++ b/packages/graph-test-watcher/src/schema.gql @@ -57,7 +57,7 @@ type ResultEvent { proof: Proof } -union Event = TestEvent +union Event = TestEvent | PoolCreatedEvent | InitializeEvent type TestEvent { param1: String! @@ -65,6 +65,19 @@ type TestEvent { param3: BigInt! } +type PoolCreatedEvent { + token0: String! + token1: String! + fee: Int! + tickSpacing: Int! + pool: String! +} + +type InitializeEvent { + sqrtPriceX96: BigInt! + tick: Int! +} + type ResultIPLDBlock { block: Block! contractAddress: String! diff --git a/packages/util/src/indexer.ts b/packages/util/src/indexer.ts index 59f2b46d..4c9c0b26 100644 --- a/packages/util/src/indexer.ts +++ b/packages/util/src/indexer.ts @@ -308,6 +308,13 @@ export class Indexer { return this._watchedContracts[address]; } + getContractsByKind (kind: string): ContractInterface[] { + const watchedContracts = Object.values(this._watchedContracts) + .filter(contract => contract.kind === kind); + + return watchedContracts; + } + async watchContract (address: string, kind: string, checkpoint: boolean, startingBlock: number): Promise { assert(this._db.saveContract); const dbTx = await this._db.createTransactionRunner(); diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index b5c15aa4..d31f6a06 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -94,6 +94,7 @@ export interface IndexerInterface { processEvent (event: EventInterface): Promise; parseEventNameAndArgs?: (kind: string, logObj: any) => any; isWatchedContract?: (address: string) => Promise; + getContractsByKind?: (kind: string) => ContractInterface[]; cacheContract?: (contract: ContractInterface) => void; watchContract?: (address: string, kind: string, checkpoint: boolean, startingBlock: number) => Promise getEntityTypesMap?: () => Map