diff --git a/indexer/db/migrations/1733751857295-Data.js b/indexer/db/migrations/1733751857295-Data.js new file mode 100644 index 00000000..6ec0aeac --- /dev/null +++ b/indexer/db/migrations/1733751857295-Data.js @@ -0,0 +1,13 @@ +module.exports = class Data1733751857295 { + name = 'Data1733751857295' + + async up(db) { + await db.query(`ALTER TABLE "transfer" ADD "completed_at" TIMESTAMP WITH TIME ZONE`) + await db.query(`ALTER TABLE "completed_transfer" ADD "timestamp" TIMESTAMP WITH TIME ZONE`) + } + + async down(db) { + await db.query(`ALTER TABLE "transfer" DROP COLUMN "completed_at"`) + await db.query(`ALTER TABLE "completed_transfer" DROP COLUMN "timestamp"`) + } +} diff --git a/indexer/package.json b/indexer/package.json index d434d1b5..20f26f1a 100644 --- a/indexer/package.json +++ b/indexer/package.json @@ -2,7 +2,8 @@ "name": "squid-evm-template", "private": true, "scripts": { - "build": "rm -rf lib && tsc" + "build": "rm -rf lib && tsc", + "migration:generate": "sqd migration generate" }, "dependencies": { "@subsquid/evm-abi": "^0.3.0", diff --git a/indexer/schema.graphql b/indexer/schema.graphql index 55fddd60..a8d3732c 100644 --- a/indexer/schema.graphql +++ b/indexer/schema.graphql @@ -15,6 +15,7 @@ type Transfer @entity { txHash: String! blockNumber: String! timestamp: DateTime! @index + completedAt: DateTime nonce: String! @index sourceNetwork: Network! source: String! @index @@ -36,4 +37,5 @@ type CompletedTransfer @entity { id: ID! nonce: String! @index @unique destNetwork: Network! + timestamp: DateTime } diff --git a/indexer/src/common/tempState.ts b/indexer/src/common/tempState.ts index f8a48036..f5970fa2 100644 --- a/indexer/src/common/tempState.ts +++ b/indexer/src/common/tempState.ts @@ -45,8 +45,10 @@ export class TempState { if (transfers.length > 0) { for (const t of transfers) { + const completed = this._completed.get(t.nonce)!; t.status = Status.Completed; - completedToDelete.push(this._completed.get(t.nonce)!); + t.completedAt = completed.timestamp; + completedToDelete.push(completed); this._completed.delete(t.nonce); } if (completedToDelete.length > 0) { @@ -129,8 +131,11 @@ export class TempState { this._ctx.log.info(`Transfer requested: ${transfer.nonce}`); } - public transferCompleted(nonce: string) { - this._completed.set(nonce, new CompletedTransfer({ id: randomUUID(), nonce, destNetwork: this._network })); + public transferCompleted(nonce: string, ts: Date) { + this._completed.set( + nonce, + new CompletedTransfer({ id: randomUUID(), nonce, destNetwork: this._network, timestamp: ts }), + ); this._ctx.log.info(`Transfer completed: ${nonce}`); } diff --git a/indexer/src/eth/main.ts b/indexer/src/eth/main.ts index 5f4d5b69..f5f4d503 100644 --- a/indexer/src/eth/main.ts +++ b/indexer/src/eth/main.ts @@ -21,6 +21,7 @@ const handler = async (ctx: Context) => { const promises = []; for (let block of ctx.blocks) { + const timestamp = new Date(block.header.timestamp); for (let log of block.logs) { const address = log.address.toLowerCase(); const topic = log.topics[0].toLowerCase(); @@ -32,7 +33,7 @@ const handler = async (ctx: Context) => { id: randomUUID(), txHash: log.transactionHash, blockNumber: block.header.height.toString(), - timestamp: new Date(block.header.timestamp), + timestamp, nonce: ethNonce(`${block.header.height}${log.transactionIndex}`), sourceNetwork: Network.Ethereum, source: token, @@ -46,7 +47,7 @@ const handler = async (ctx: Context) => { ); } else if (address === MSGQ && topic === MSGQ_MESSAGE_PROCESSED) { const [_, __, nonce] = messageQueueAbi.events.MessageProcessed.decode(log); - promises.push(tempState.transferCompleted(gearNonce(nonce, false))); + promises.push(tempState.transferCompleted(gearNonce(nonce, false), timestamp)); } } } diff --git a/indexer/src/gear/main.ts b/indexer/src/gear/main.ts index 05c8073e..fba61896 100644 --- a/indexer/src/gear/main.ts +++ b/indexer/src/gear/main.ts @@ -91,7 +91,7 @@ const handler = async (ctx: ProcessorContext) => { ); const nonce = ethNonce(`${block_number}${transaction_index}`); - promises.push(tempState.transferCompleted(nonce)); + promises.push(tempState.transferCompleted(nonce, timestamp)); break; } } diff --git a/indexer/src/model/generated/completedTransfer.model.ts b/indexer/src/model/generated/completedTransfer.model.ts index 76e82c54..fec4e95c 100644 --- a/indexer/src/model/generated/completedTransfer.model.ts +++ b/indexer/src/model/generated/completedTransfer.model.ts @@ -1,4 +1,4 @@ -import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, StringColumn as StringColumn_, Index as Index_} from "@subsquid/typeorm-store" +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, StringColumn as StringColumn_, Index as Index_, DateTimeColumn as DateTimeColumn_} from "@subsquid/typeorm-store" import {Network} from "./_network" @Entity_() @@ -16,4 +16,7 @@ export class CompletedTransfer { @Column_("varchar", {length: 8, nullable: false}) destNetwork!: Network + + @DateTimeColumn_({nullable: true}) + timestamp!: Date | undefined | null } diff --git a/indexer/src/model/generated/transfer.model.ts b/indexer/src/model/generated/transfer.model.ts index 6edffa51..75643f7e 100644 --- a/indexer/src/model/generated/transfer.model.ts +++ b/indexer/src/model/generated/transfer.model.ts @@ -21,6 +21,9 @@ export class Transfer { @DateTimeColumn_({nullable: false}) timestamp!: Date + @DateTimeColumn_({nullable: true}) + completedAt!: Date | undefined | null + @Index_() @StringColumn_({nullable: false}) nonce!: string