From c633d2788bdef245c0e6e541dfe3f2c9ad0dbb03 Mon Sep 17 00:00:00 2001 From: nabarun Date: Mon, 13 Jun 2022 17:47:12 +0530 Subject: [PATCH] Implement custom update of transfer counter in state diff --- packages/erc721-watcher/README.md | 8 ++++- packages/erc721-watcher/src/database.ts | 32 ++++++++++++++++- .../src/entity/TransferCount.ts | 20 +++++++++++ packages/erc721-watcher/src/hooks.ts | 26 +++++++++----- packages/erc721-watcher/src/indexer.ts | 34 +++++++++++++++++++ 5 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 packages/erc721-watcher/src/entity/TransferCount.ts diff --git a/packages/erc721-watcher/README.md b/packages/erc721-watcher/README.md index e841d918d..05fb20ede 100644 --- a/packages/erc721-watcher/README.md +++ b/packages/erc721-watcher/README.md @@ -143,7 +143,7 @@ } ``` -* Run the following GQL query (`storage`) in generated watcher graphql endpoint http://127.0.0.1:3006/graphql +* Run the following GQL query (`storage`) in generated watcher GraphQL endpoint http://127.0.0.1:3006/graphql ```graphql query { @@ -214,6 +214,8 @@ * An auto-generated diff_staged IPLDBlock should be added with parent cid pointing to the initial checkpoint IPLDBlock. + * Custom property `transferCount` should be 1 initially. + * Run the getState query at the endpoint to get the latest IPLDBlock for NFT_ADDRESS: ```graphql @@ -290,6 +292,8 @@ * An auto-generated diff_staged IPLDBlock should be added with parent cid pointing to the previous IPLDBlock. + * Custom property `transferCount` should be incremented after transfer. + * Get the latest blockHash and replace the blockHash in the above query. The result should be different and the token should be transferred to the recipient. * Run the getState query again at the endpoint with the event blockHash. @@ -310,6 +314,8 @@ * Open IPFS WebUI http://127.0.0.1:5001/webui and search for IPLDBlocks using their CIDs. +* The state should have auto indexed data and also custom property `transferCount` according to code in hooks file handleEvent method. + ## Customize * Indexing on an event: diff --git a/packages/erc721-watcher/src/database.ts b/packages/erc721-watcher/src/database.ts index 877e021a4..b63511271 100644 --- a/packages/erc721-watcher/src/database.ts +++ b/packages/erc721-watcher/src/database.ts @@ -3,7 +3,7 @@ // import assert from 'assert'; -import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner, FindManyOptions } from 'typeorm'; +import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner, FindManyOptions, FindOneOptions } from 'typeorm'; import path from 'path'; import { IPLDDatabase as BaseDatabase, IPLDDatabaseInterface, QueryOptions, StateKind, Where } from '@vulcanize/util'; @@ -28,6 +28,7 @@ import { _Owners } from './entity/_Owners'; import { _Balances } from './entity/_Balances'; import { _TokenApprovals } from './entity/_TokenApprovals'; import { _OperatorApprovals } from './entity/_OperatorApprovals'; +import { TransferCount } from './entity/TransferCount'; export class Database implements IPLDDatabaseInterface { _config: ConnectionOptions; @@ -128,6 +129,35 @@ export class Database implements IPLDDatabaseInterface { }); } + async getTransferCount (queryRunner: QueryRunner, { id, blockHash }: DeepPartial): Promise { + const repo = queryRunner.manager.getRepository(TransferCount); + const whereOptions: FindConditions = { id }; + + if (blockHash) { + whereOptions.blockHash = blockHash; + } + + const findOptions = { + where: whereOptions, + order: { + blockNumber: 'DESC' + } + }; + + let entity = await repo.findOne(findOptions as FindOneOptions); + + if (!entity && findOptions.where.blockHash) { + entity = await this._baseDatabase.getPrevEntityVersion(queryRunner, repo, findOptions); + } + + return entity; + } + + async saveTransferCount (queryRunner: QueryRunner, transferCount: TransferCount): Promise { + const repo = queryRunner.manager.getRepository(TransferCount); + return repo.save(transferCount); + } + async _getName ({ blockHash, contractAddress }: { blockHash: string, contractAddress: string }): Promise<_Name | undefined> { return this._conn.getRepository(_Name) .findOne({ diff --git a/packages/erc721-watcher/src/entity/TransferCount.ts b/packages/erc721-watcher/src/entity/TransferCount.ts new file mode 100644 index 000000000..681ee91af --- /dev/null +++ b/packages/erc721-watcher/src/entity/TransferCount.ts @@ -0,0 +1,20 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; + +@Entity() +export class TransferCount { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('integer') + count!: number; +} diff --git a/packages/erc721-watcher/src/hooks.ts b/packages/erc721-watcher/src/hooks.ts index 771715f8c..567d19295 100644 --- a/packages/erc721-watcher/src/hooks.ts +++ b/packages/erc721-watcher/src/hooks.ts @@ -7,6 +7,7 @@ import assert from 'assert'; import { updateStateForMappingType, updateStateForElementaryType } from '@vulcanize/util'; import { Indexer, ResultEvent } from './indexer'; +import { TransferCount } from './entity/TransferCount'; /** * Hook function to store an initial state. @@ -94,18 +95,25 @@ export async function handleEvent (indexer: Indexer, eventData: ResultEvent): Pr // Update owner for the tokenId in database. await indexer._owners(eventData.block.hash, eventData.contract, tokenId, true); - // Update custom state diffs with properties name and symbol. + // Update custom state diffs with transferCount. // { - // "name": "TestNFT", - // "symbol": "TNFT" + // "transferCount": "1" // } - const { value: name } = await indexer.name(eventData.block.hash, eventData.contract); - const nameUpdate = updateStateForElementaryType({}, 'name', name); - await indexer.createDiffStaged(eventData.contract, eventData.block.hash, nameUpdate); + let transferCount = await indexer.transferCount(eventData.block.hash, eventData.contract); + + if (!transferCount) { + transferCount = new TransferCount(); + transferCount.blockHash = eventData.block.hash; + transferCount.blockNumber = eventData.block.number; + transferCount.id = eventData.contract; + transferCount.count = 0; + } + + transferCount.count++; - const { value: symbol } = await indexer.symbol(eventData.block.hash, eventData.contract); - const symbolUpdate = updateStateForElementaryType({}, 'symbol', symbol); - await indexer.createDiffStaged(eventData.contract, eventData.block.hash, symbolUpdate); + const stateUpdate = updateStateForElementaryType({}, 'transferCount', String(transferCount.count)); + await indexer.createDiffStaged(eventData.contract, eventData.block.hash, stateUpdate); + await indexer.saveOrUpdateTransferCount(transferCount); break; } diff --git a/packages/erc721-watcher/src/indexer.ts b/packages/erc721-watcher/src/indexer.ts index 38f671ea4..14b07499a 100644 --- a/packages/erc721-watcher/src/indexer.ts +++ b/packages/erc721-watcher/src/indexer.ts @@ -40,6 +40,7 @@ import { SyncStatus } from './entity/SyncStatus'; import { IpldStatus } from './entity/IpldStatus'; import { BlockProgress } from './entity/BlockProgress'; import { IPLDBlock } from './entity/IPLDBlock'; +import { TransferCount } from './entity/TransferCount'; const log = debug('vulcanize:indexer'); @@ -430,6 +431,39 @@ export class Indexer implements IPLDIndexerInterface { return result; } + async transferCount (blockHash: string, contractAddress: string): Promise { + const dbTx = await this._db.createTransactionRunner(); + let res; + + try { + res = await this._db.getTransferCount(dbTx, { id: contractAddress, blockHash }); + await dbTx.commitTransaction(); + } catch (error) { + await dbTx.rollbackTransaction(); + throw error; + } finally { + await dbTx.release(); + } + + return res; + } + + async saveOrUpdateTransferCount (transferCount: TransferCount) { + const dbTx = await this._db.createTransactionRunner(); + let res; + + try { + await this._db.saveTransferCount(dbTx, transferCount); + } catch (error) { + await dbTx.rollbackTransaction(); + throw error; + } finally { + await dbTx.release(); + } + + return res; + } + async _name (blockHash: string, contractAddress: string, diff = false): Promise { const entity = await this._db._getName({ blockHash, contractAddress }); if (entity) {