From 717c12041c2658df316ff55ddbcf11f652972730 Mon Sep 17 00:00:00 2001 From: DC Date: Sat, 31 Aug 2024 22:22:41 -0700 Subject: [PATCH] api: delete remaining uses of Shovel --- .../src/contract/foreignCoinIndexer.ts | 3 +- .../daimo-api/src/contract/homeCoinIndexer.ts | 3 +- packages/daimo-api/src/contract/indexer.ts | 2 - .../daimo-api/src/contract/keyRegistry.ts | 109 +++---- .../daimo-api/src/contract/nameRegistry.ts | 33 ++- .../daimo-api/src/contract/noteIndexer.ts | 88 ++---- packages/daimo-api/src/contract/opIndexer.ts | 3 +- .../daimo-api/src/contract/requestIndexer.ts | 270 +++++++----------- packages/daimo-api/src/db/indexWatcher.ts | 2 +- packages/daimo-api/test/pushNotifier.test.ts | 1 - 10 files changed, 195 insertions(+), 319 deletions(-) diff --git a/packages/daimo-api/src/contract/foreignCoinIndexer.ts b/packages/daimo-api/src/contract/foreignCoinIndexer.ts index 25b87a4c0..248cee04c 100644 --- a/packages/daimo-api/src/contract/foreignCoinIndexer.ts +++ b/packages/daimo-api/src/contract/foreignCoinIndexer.ts @@ -12,7 +12,6 @@ import { } from "@daimo/common"; import { zeroAddr } from "@daimo/contract"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Address, Hex, bytesToHex, getAddress } from "viem"; import { Transfer } from "./homeCoinIndexer"; @@ -60,7 +59,7 @@ export class ForeignCoinIndexer extends Indexer { super("FOREIGN-COIN"); } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { + async load(kdb: Kysely, from: number, to: number) { const startMs = performance.now(); const result = await retryBackoff( diff --git a/packages/daimo-api/src/contract/homeCoinIndexer.ts b/packages/daimo-api/src/contract/homeCoinIndexer.ts index 85c9f7478..ad8148c1b 100644 --- a/packages/daimo-api/src/contract/homeCoinIndexer.ts +++ b/packages/daimo-api/src/contract/homeCoinIndexer.ts @@ -11,7 +11,6 @@ import { } from "@daimo/common"; import { DaimoNonce } from "@daimo/userop"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Address, Hex, bytesToHex, getAddress, numberToHex } from "viem"; import { ForeignCoinIndexer } from "./foreignCoinIndexer"; @@ -62,7 +61,7 @@ export class HomeCoinIndexer extends Indexer { return { numTransfers: this.allTransfers.length }; } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { + async load(kdb: Kysely, from: number, to: number) { const startTime = Date.now(); const result = await retryBackoff( diff --git a/packages/daimo-api/src/contract/indexer.ts b/packages/daimo-api/src/contract/indexer.ts index 126ac5e93..5b5103e45 100644 --- a/packages/daimo-api/src/contract/indexer.ts +++ b/packages/daimo-api/src/contract/indexer.ts @@ -1,5 +1,4 @@ import { Kysely } from "kysely"; -import { Pool } from "pg"; import { DB as IndexDB } from "../codegen/dbIndex"; @@ -14,7 +13,6 @@ export abstract class Indexer { // Loads a batch of blocks from the database. public abstract load( - pg: Pool, kdb: Kysely, from: number, to: number diff --git a/packages/daimo-api/src/contract/keyRegistry.ts b/packages/daimo-api/src/contract/keyRegistry.ts index a70047204..7c29b620a 100644 --- a/packages/daimo-api/src/contract/keyRegistry.ts +++ b/packages/daimo-api/src/contract/keyRegistry.ts @@ -1,11 +1,12 @@ import { KeyData, assert, + assertNotNull, contractFriendlyKeyToDER, + debugJson, retryBackoff, } from "@daimo/common"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Address, Hex, bytesToHex, getAddress } from "viem"; import { Indexer } from "./indexer"; @@ -19,7 +20,6 @@ export interface KeyChange { transactionHash: Hex; logIndex: number; address: Address; - account: Address; keySlot: number; key: [Hex, Hex]; } @@ -41,27 +41,49 @@ export class KeyRegistry extends Indexer { this.listeners.push(listener); } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { + async load(kdb: Kysely, from: number, to: number) { const startTime = Date.now(); - const changes: KeyChange[] = []; - changes.push(...(await this.loadKeyChange(pg, from, to, "added"))); - changes.push(...(await this.loadKeyChange(pg, from, to, "removed"))); + + const rows = await retryBackoff(`keyRegistry-${from}-${to}`, () => + kdb + .selectFrom("index.daimo_acct_update") + .selectAll() + .where("chain_id", "=", "" + chainConfig.chainL2.id) + .where((eb) => eb.between("block_num", "" + from, "" + to)) + .orderBy("block_num") + .orderBy("tx_idx") + .orderBy("log_idx") + .execute() + ); if (this.updateLastProcessedCheckStale(from, to)) return; - changes!.sort((a, b) => { - const bdiff = a.blockNumber - b.blockNumber; - if (bdiff !== 0n) return Number(bdiff); - const tdiff = a.transactionIndex - b.transactionIndex; - if (tdiff !== 0) return tdiff; - return a.logIndex - b.logIndex; - }); + const changes: KeyChange[] = rows + .filter((row) => row.key_slot != null) + .map((row) => ({ + change: (function () { + if (row.log_name === "SigningKeyAdded") return "added"; + else if (row.log_name === "SigningKeyRemoved") return "removed"; + else throw new Error(`Unexpected key log: ${debugJson(row)}`); + })(), + address: getAddress(bytesToHex(row.log_addr)), + blockNumber: BigInt(row.block_num), + key: (function () { + const k = assertNotNull(row.key); + if (k.length !== 64) throw new Error(`Bad key: ${debugJson(row)}`); + return [bytesToHex(k.subarray(0, 32)), bytesToHex(k.subarray(32))]; + })(), + keySlot: assertNotNull(row.key_slot), + logIndex: Number(row.log_idx), + transactionHash: bytesToHex(row.tx_hash), + transactionIndex: Number(row.tx_idx), + })); + for (const change of changes) { - const addr = getAddress(change.address); - if (this.addrToLogs.get(addr) === undefined) { - this.addrToLogs.set(addr, []); - } - this.addrToLogs.get(addr)!.push(change); + const addr = change.address; + const addrLogs = this.addrToLogs.get(addr) || []; + addrLogs.push(change); + this.addrToLogs.set(addr, addrLogs); if (!change.key) throw new Error("[KEY-REG] Invalid event, no key"); const slot = change.keySlot; @@ -95,6 +117,9 @@ export class KeyRegistry extends Indexer { this.keyToAddr.delete(derKey); break; } + default: { + throw new Error(`Invalid KeyChange: ${debugJson(change)}`); + } } } if (changes.length === 0) return; @@ -107,54 +132,6 @@ export class KeyRegistry extends Indexer { this.listeners.forEach((l) => l(changes)); } - private async loadKeyChange( - pg: Pool, - from: number, - to: number, - change: "added" | "removed" - ): Promise { - let table: string = ""; - if (change === "added") { - table = "key_added"; - } else if (change === "removed") { - table = "key_removed"; - } else { - throw new Error(`Invalid key change ${change}`); - } - const result = await retryBackoff(`keyRegistry-logs-query-${table}`, () => - pg.query( - ` - select - block_num, - tx_idx, - tx_hash, - log_idx, - log_addr, - account, - key_slot, - array_agg(key order by abi_idx asc) as key - from ${table} - where block_num >= $1 and block_num <= $2 - and chain_id = $3 - group by block_num, tx_idx, tx_hash, log_idx, log_addr, account, key_slot - `, - [from, to, chainConfig.chainL2.id] - ) - ); - - return result.rows.map((row: any) => ({ - change, - blockNumber: BigInt(row.block_num), - transactionIndex: row.tx_idx, - transactionHash: bytesToHex(row.tx_hash, { size: 32 }), - logIndex: row.log_idx, - address: getAddress(bytesToHex(row.log_addr, { size: 20 })), - account: getAddress(bytesToHex(row.account, { size: 20 })), - keySlot: row.key_slot, - key: row.key.map((k: Buffer) => bytesToHex(k)) as [Hex, Hex], - })); - } - /** Find address by DER key */ async resolveKey(key: Hex): Promise
{ return this.keyToAddr.get(key) || null; diff --git a/packages/daimo-api/src/contract/nameRegistry.ts b/packages/daimo-api/src/contract/nameRegistry.ts index dbb657a35..65f222f8f 100644 --- a/packages/daimo-api/src/contract/nameRegistry.ts +++ b/packages/daimo-api/src/contract/nameRegistry.ts @@ -5,15 +5,13 @@ import { EAccount, assertEqual, assertNotNull, - guessTimestampFromNum, isValidName, now, - validateName, retryBackoff, + validateName, } from "@daimo/common"; import { nameRegistryProxyConfig, teamDaimoFaucetAddr } from "@daimo/contract"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Address, bytesToHex, @@ -109,26 +107,27 @@ export class NameRegistry extends Indexer { return { numAccounts: this.accounts.length, numLogs: this.logs.length }; } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { - const startTime = Date.now(); - const result = await retryBackoff( + async load(kdb: Kysely, from: number, to: number) { + const startMs = performance.now(); + const rows = await retryBackoff( `nameRegistry-logs-query-${from}-${to}`, () => - pg.query( - `select block_num, addr, name - from names - where block_num >= $1 - and block_num <= $2 - and chain_id = $3`, - [from, to, chainConfig.chainL2.id] - ) + kdb + .selectFrom("index.daimo_name") + .selectAll() + .where("chain_id", "=", "" + chainConfig.chainL2.id) + .where((eb) => eb.between("block_num", "" + from, "" + to)) + .orderBy("block_num") + .orderBy("tx_idx") + .orderBy("log_idx") + .execute() ); if (this.updateLastProcessedCheckStale(from, to)) return; - const names = result.rows.map((r: any) => { + const names = rows.map((r) => { return { - timestamp: guessTimestampFromNum(r.block_num, chainConfig.daimoChain), + timestamp: Number(r.block_ts), name: bytesToString(r.name, { size: 32 }), addr: getAddress(bytesToHex(r.addr, { size: 20 })), }; @@ -136,7 +135,7 @@ export class NameRegistry extends Indexer { this.logs.push(...names); names.forEach(this.cacheAccount); - const elapsedMs = (Date.now() - startTime) | 0; + const elapsedMs = (performance.now() - startMs) | 0; console.log(`[NAME-REG] loaded ${names.length} names in ${elapsedMs}ms`); } diff --git a/packages/daimo-api/src/contract/noteIndexer.ts b/packages/daimo-api/src/contract/noteIndexer.ts index 874977044..478b6fb35 100644 --- a/packages/daimo-api/src/contract/noteIndexer.ts +++ b/packages/daimo-api/src/contract/noteIndexer.ts @@ -9,7 +9,6 @@ import { retryBackoff, } from "@daimo/common"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Address, Hex, bytesToHex, getAddress } from "viem"; import { Indexer } from "./indexer"; @@ -18,7 +17,7 @@ import { OpIndexer } from "./opIndexer"; import { DB as IndexDB } from "../codegen/dbIndex"; import { chainConfig } from "../env"; import { PaymentMemoTracker } from "../offchain/paymentMemoTracker"; -import { senderIdKey, logCoordinateKey } from "../utils/indexing"; +import { logCoordinateKey, senderIdKey } from "../utils/indexing"; interface NoteLog { blockNum: number; @@ -55,13 +54,13 @@ export class NoteIndexer extends Indexer { super("NOTE"); } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { + async load(kdb: Kysely, from: number, to: number) { // Load notes contract event logs - const startMs = Date.now(); - const logs = await this.loadNoteLogs(pg, from, to); + const startMs = performance.now(); + const logs = await this.loadNoteLogs(kdb, from, to); if (logs.length === 0) return; - const elapsedMs = (Date.now() - startMs) | 0; + const elapsedMs = (performance.now() - startMs) | 0; console.log(`[NOTE] ${elapsedMs}ms: loaded ${logs.length} logs`); if (this.updateLastProcessedCheckStale(from, to)) return; @@ -78,52 +77,35 @@ export class NoteIndexer extends Indexer { } private async loadNoteLogs( - pg: Pool, + kdb: Kysely, from: number, to: number ): Promise { - const result = await retryBackoff( + const rows = await retryBackoff( `noteIndexer-logs-query-${from}-${to}`, () => - pg.query( - ` - select * from ( - select - block_num, - tx_idx, - log_idx, - tx_hash, - f, - null as redeemer, - ephemeral_owner, - amount, - log_addr - from note_created - where block_num >= $1 - and block_num <= $2 - and chain_id = $3 - union - select - block_num, - tx_idx, - log_idx, - tx_hash, - f, - redeemer, - ephemeral_owner, - amount, - log_addr - from note_redeemed - where block_num >= $1 - and block_num <= $2 - and chain_id = $3 - ) as notelogs - order by block_num asc, tx_idx asc, log_idx asc - `, - [from, to, chainConfig.chainL2.id] - ) + kdb + .selectFrom("index.daimo_note") + .selectAll() + .where("chain_id", "=", "" + chainConfig.chainL2.id) + .where((eb) => eb.between("block_num", "" + from, "" + to)) + .orderBy("block_num") + .orderBy("tx_idx") + .orderBy("log_idx") + .execute() ); - return result.rows.map(rowToNoteLog); + + return rows.map((r) => ({ + blockNum: Number(r.block_num), + transactionIndex: Number(r.tx_idx), + logIndex: Number(r.log_idx), + transactionHash: bytesToHex(r.tx_hash, { size: 32 }), + from: getAddress(bytesToHex(r.creator, { size: 20 })), + redeemer: r.redeemer && getAddress(bytesToHex(r.redeemer, { size: 20 })), + ephemeralOwner: getAddress(bytesToHex(r.ephemeral_owner, { size: 20 })), + amount: BigInt(r.amount), + logAddr: getAddress(bytesToHex(r.log_addr, { size: 20 })), + })); } async handleNoteLogs(logs: NoteLog[]): Promise { @@ -258,17 +240,3 @@ export class NoteIndexer extends Indexer { return this.noteLogs.get(ephemeralOwner)?.claim; } } - -function rowToNoteLog(r: any): NoteLog { - return { - blockNum: r.block_num, - transactionIndex: r.tx_idx, - logIndex: r.log_idx, - transactionHash: bytesToHex(r.tx_hash, { size: 32 }), - from: getAddress(bytesToHex(r.f, { size: 20 })), - redeemer: r.redeemer && getAddress(bytesToHex(r.redeemer, { size: 20 })), - ephemeralOwner: getAddress(bytesToHex(r.ephemeral_owner, { size: 20 })), - amount: BigInt(r.amount), - logAddr: getAddress(bytesToHex(r.log_addr, { size: 20 })), - }; -} diff --git a/packages/daimo-api/src/contract/opIndexer.ts b/packages/daimo-api/src/contract/opIndexer.ts index aecbf891d..1e15aa8f6 100644 --- a/packages/daimo-api/src/contract/opIndexer.ts +++ b/packages/daimo-api/src/contract/opIndexer.ts @@ -1,7 +1,6 @@ import { assertNotNull, retryBackoff } from "@daimo/common"; import { DaimoNonce } from "@daimo/userop"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Hex, bytesToHex, numberToHex } from "viem"; import { Indexer } from "./indexer"; @@ -44,7 +43,7 @@ export class OpIndexer extends Indexer { this.callbacks.delete(userOp.hash); } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { + async load(kdb: Kysely, from: number, to: number) { const startTime = Date.now(); const result = await retryBackoff( diff --git a/packages/daimo-api/src/contract/requestIndexer.ts b/packages/daimo-api/src/contract/requestIndexer.ts index 8da86341a..54e22653a 100644 --- a/packages/daimo-api/src/contract/requestIndexer.ts +++ b/packages/daimo-api/src/contract/requestIndexer.ts @@ -1,18 +1,19 @@ import { + AddrLabel, DaimoRequestState, DaimoRequestV2Status, amountToDollars, - getEAccountStr, + assertNotNull, + debugJson, + decodeRequestIdString, encodeRequestId, - parseRequestMetadata, + getEAccountStr, guessTimestampFromNum, now, - AddrLabel, - decodeRequestIdString, + parseRequestMetadata, retryBackoff, } from "@daimo/common"; import { Kysely } from "kysely"; -import { Pool } from "pg"; import { Address, Hex, bytesToHex, getAddress } from "viem"; import { Indexer } from "./indexer"; @@ -33,6 +34,7 @@ interface RequestCreatedLog { metadata: Hex; logAddr: Address; blockNumber: bigint; + blockTime: number; } interface RequestFulfilledLog { @@ -41,11 +43,13 @@ interface RequestFulfilledLog { id: bigint; fulfiller: Address; blockNumber: bigint; + blockTime: number; } interface RequestCancelledLog { id: bigint; blockNumber: bigint; + blockTime: number; } /* Request contract. Tracks request creation and fulfillment. */ @@ -66,21 +70,71 @@ export class RequestIndexer extends Indexer { super("REQUEST"); } - async load(pg: Pool, kdb: Kysely, from: number, to: number) { - const startTime = Date.now(); + async load(kdb: Kysely, from: number, to: number) { + const startTime = performance.now(); + + // Load logs + const rows = await retryBackoff(`requestIndexer-${from}-${to}`, () => + kdb + .selectFrom("index.daimo_request") + .selectAll() + .where("chain_id", "=", "" + chainConfig.chainL2.id) + .where((eb) => eb.between("block_num", "" + from, "" + to)) + .orderBy("block_num") + .orderBy("tx_idx") + .orderBy("log_idx") + .execute() + ); + + // Load context + update in-memory request index + const reqsCreated: RequestCreatedLog[] = rows + .filter((r) => r.log_name === "RequestCreated") + .map((r) => ({ + transactionHash: bytesToHex(r.tx_hash, { size: 32 }), + logIndex: Number(r.log_idx), + id: BigInt(r.id), + recipient: getAddress( + bytesToHex(assertNotNull(r.recipient), { size: 20 }) + ), + creator: getAddress(bytesToHex(assertNotNull(r.creator), { size: 20 })), + amount: BigInt(assertNotNull(r.amount)), + metadata: bytesToHex(r.metadata || Buffer.from([])), + logAddr: getAddress(bytesToHex(r.log_addr, { size: 20 })), + blockNumber: BigInt(r.block_num), + blockTime: Number(r.block_ts), + })); + const reqsFulfilled: RequestFulfilledLog[] = rows + .filter((r) => r.log_name === "RequestFulfilled") + .map((r) => ({ + transactionHash: bytesToHex(r.tx_hash, { size: 32 }), + logIndex: Number(r.log_idx), + id: BigInt(r.id), + fulfiller: getAddress( + bytesToHex(assertNotNull(r.fulfiller), { size: 20 }) + ), + blockNumber: BigInt(r.block_num), + blockTime: Number(r.block_ts), + })); + const reqsCancelled: RequestCancelledLog[] = rows + .filter((r) => r.log_name === "RequestCancelled") + .map((r) => ({ + id: BigInt(r.id), + blockNumber: BigInt(r.block_num), + blockTime: Number(r.block_ts), + })); + const statuses: DaimoRequestV2Status[] = []; - statuses.push(...(await this.loadCreated(pg, from, to))); - statuses.push(...(await this.loadCancelled(pg, from, to))); - statuses.push(...(await this.loadFulfilled(pg, from, to))); + statuses.push(...(await this.handleRequestsCreated(reqsCreated))); + statuses.push(...(await this.handleRequestsFulfilled(reqsFulfilled))); + statuses.push(...(await this.handleRequestsCancelled(reqsCancelled))); if (statuses.length === 0) return; - const elapsedMs = (Date.now() - startTime) | 0; + // Log + const elapsedMs = (performance.now() - startTime) | 0; console.log( - `[REQUEST] loaded ${statuses.length} statuses in ${elapsedMs}ms` + `[REQUEST] handled ${rows.length} request updates in ${elapsedMs}ms` ); - if (this.updateLastProcessedCheckStale(from, to)) return; - // Finally, invoke listeners to send notifications etc. const ls = this.listeners; ls.forEach((l) => l(statuses)); @@ -104,45 +158,11 @@ export class RequestIndexer extends Indexer { addListener(listener: (statuses: DaimoRequestV2Status[]) => void) { this.listeners.push(listener); } - - private async loadCreated( - pg: Pool, - from: number, - to: number + private async handleRequestsCreated( + logs: RequestCreatedLog[] ): Promise { - const result = await retryBackoff(`requestLoadCreated-${from}-${to}`, () => - pg.query( - `select - tx_hash, - log_idx, - id, - recipient, - creator, - amount, - metadata, - log_addr, - block_num - from request_created - where block_num >= $1 - and block_num <= $2 - and chain_id = $3 - `, - [from, to, chainConfig.chainL2.id] - ) - ); - const logs = result.rows.map(rowToRequestCreatedLog); - // todo: ignore requests not made by the API - - const promises = logs.map(async (l) => { - try { - return this.handleRequestCreated(l); - } catch (e) { - console.error(`[REQUEST] error handling RequestCreated: ${e}`); - return null; - } - }); - const statuses = (await Promise.all(promises)).filter((n) => n != null); - return statuses as DaimoRequestV2Status[]; + const promises = logs.map((log) => this.handleRequestCreated(log)); + return Promise.all(promises); } private async handleRequestCreated( @@ -153,17 +173,14 @@ export class RequestIndexer extends Indexer { throw new Error(`bad RequestCreated: ${log.id} exists`); } - const recipient = await this.nameReg.getEAccount(log.recipient); - // TODO: Anyone is allowed to create a request for any recipient on-chain. // In future, this could lead to potential spam attacks, so we can use // the creator field to filter whitelisted creators. const creator = await this.nameReg.getEAccount(log.creator); + const recipient = await this.nameReg.getEAccount(log.recipient); if (creator.label !== AddrLabel.Faucet) { - console.warn( - `[REQUEST] ${log.id} creator ${JSON.stringify(creator)} is not API` - ); + console.warn(`[REQUEST] ${log.id} creator ${debugJson(creator)} not API`); } const { fulfiller } = parseRequestMetadata(log.metadata); @@ -171,11 +188,6 @@ export class RequestIndexer extends Indexer { ? await this.nameReg.getEAccount(fulfiller) : undefined; - const createdAt = guessTimestampFromNum( - log.blockNumber, - chainConfig.daimoChain - ); - // Get optional memo, offchain // TODO: index memos by {noteID, requestID, or transferLogCoord}, not hash const memo = this.paymentMemoTracker.getMemo(log.transactionHash); @@ -191,8 +203,8 @@ export class RequestIndexer extends Indexer { creator, status: DaimoRequestState.Created, metadata: log.metadata, - createdAt, - updatedAt: createdAt, + createdAt: log.blockTime, + updatedAt: log.blockTime, expectedFulfiller, memo, }; @@ -208,94 +220,52 @@ export class RequestIndexer extends Indexer { return requestStatus; } - private async loadCancelled( - pg: Pool, - from: number, - to: number + private async handleRequestsFulfilled( + reqs: RequestFulfilledLog[] ): Promise { - const result = await retryBackoff( - `requestLoadCancelled-${from}-${to}`, - () => - pg.query( - ` - select - id, - block_num - from request_cancelled - where block_num >= $1 - and block_num <= $2 - and chain_id = $3 - `, - [from, to, chainConfig.chainL2.id] - ) - ); - const cancelledRequests = result.rows.map(rowToRequestCancelledLog); - const statuses = cancelledRequests - .map((req) => { + const promises = reqs + .map(async (req) => { const request = this.requests.get(req.id); if (request == null) { - console.error(`[REQUEST] error handling RequestCancelled: ${req.id}`); + console.error(`[REQUEST] RequestFulfilled missing ID: ${req.id}`); return null; } - request.status = DaimoRequestState.Cancelled; - request.updatedAt = guessTimestampFromNum( - req.blockNumber, - chainConfig.daimoChain - ); + const fulfilledBy = await this.nameReg.getEAccount(req.fulfiller); + request.fulfilledBy = fulfilledBy; + request.status = DaimoRequestState.Fulfilled; + request.updatedAt = req.blockTime; this.requests.set(req.id, request); + this.logCoordinateToRequestFulfill.set( + logCoordinateKey(req.transactionHash, req.logIndex), + req.id + ); return request; }) - .filter((n) => n != null); - - return statuses as DaimoRequestV2Status[]; + .filter((p) => p != null); + const results = await Promise.all(promises); + return results.filter((n) => n != null) as DaimoRequestV2Status[]; } - private async loadFulfilled( - pg: Pool, - from: number, - to: number + private async handleRequestsCancelled( + reqs: RequestCancelledLog[] ): Promise { - const result = await retryBackoff( - `requestLoadFulfilled-${from}-${to}`, - () => - pg.query( - `select - tx_hash, - log_idx, - id, - fulfiller, - block_num - from request_fulfilled - where block_num >= $1 - and block_num <= $2 - and chain_id = $3`, - [from, to, chainConfig.chainL2.id] - ) - ); - const fulfilledRequests = result.rows.map(rowToRequestFulfilledLog); - const promises = fulfilledRequests - .map(async (req) => { + const statuses = reqs + .map((req) => { const request = this.requests.get(req.id); if (request == null) { - console.error(`[REQUEST] error handling RequestFulfilled: ${req.id}`); + console.error(`[REQUEST] error handling RequestCancelled: ${req.id}`); return null; } - const fulfilledBy = await this.nameReg.getEAccount(req.fulfiller); - request.fulfilledBy = fulfilledBy; - request.status = DaimoRequestState.Fulfilled; + request.status = DaimoRequestState.Cancelled; request.updatedAt = guessTimestampFromNum( req.blockNumber, chainConfig.daimoChain ); this.requests.set(req.id, request); - this.logCoordinateToRequestFulfill.set( - logCoordinateKey(req.transactionHash, req.logIndex), - req.id - ); return request; }) .filter((n) => n != null); - const statuses = (await Promise.all(promises)).filter((n) => n != null); + return statuses as DaimoRequestV2Status[]; } @@ -305,12 +275,11 @@ export class RequestIndexer extends Indexer { } // Fetch requests sent from/to an address: addr = creator or expectedFulfiller - getAddrRequests(addr: Address) { - const requests = (this.requestsByAddress.get(addr) || []).map( - (id) => this.requests.get(id)! - ); - - return requests; + getAddrRequests(addr: Address): DaimoRequestV2Status[] { + const requests = (this.requestsByAddress.get(addr) || []) + .map((id) => this.requests.get(id)) + .filter((r) => r != null); + return requests as DaimoRequestV2Status[]; } // TODO: gate creates and declines by account API key @@ -366,34 +335,3 @@ export class RequestIndexer extends Indexer { return this.getRequestStatusById(id); } } - -function rowToRequestCreatedLog(r: any): RequestCreatedLog { - return { - transactionHash: bytesToHex(r.tx_hash, { size: 32 }), - logIndex: r.log_idx, - id: BigInt(r.id), - recipient: getAddress(bytesToHex(r.recipient, { size: 20 })), - creator: getAddress(bytesToHex(r.creator, { size: 20 })), - amount: BigInt(r.amount), - metadata: bytesToHex(r.metadata || []), - logAddr: getAddress(bytesToHex(r.log_addr, { size: 20 })), - blockNumber: BigInt(r.block_num), - }; -} - -function rowToRequestFulfilledLog(r: any): RequestFulfilledLog { - return { - transactionHash: bytesToHex(r.tx_hash, { size: 32 }), - logIndex: r.log_idx, - id: BigInt(r.id), - fulfiller: getAddress(bytesToHex(r.fulfiller, { size: 20 })), - blockNumber: BigInt(r.block_num), - }; -} - -function rowToRequestCancelledLog(r: any): RequestCancelledLog { - return { - id: BigInt(r.id), - blockNumber: BigInt(r.block_num), - }; -} diff --git a/packages/daimo-api/src/db/indexWatcher.ts b/packages/daimo-api/src/db/indexWatcher.ts index 91cc142c1..39e82594f 100644 --- a/packages/daimo-api/src/db/indexWatcher.ts +++ b/packages/daimo-api/src/db/indexWatcher.ts @@ -166,7 +166,7 @@ export class IndexWatcher { console.log(`[WATCHER] loading ${start} to ${start + limit}`); for (const [, layer] of this.indexerLayers.entries()) { await Promise.all( - layer.map((i) => i.load(this.pg, this.kdb, start, start + limit)) + layer.map((i) => i.load(this.kdb, start, start + limit)) ); } console.log( diff --git a/packages/daimo-api/test/pushNotifier.test.ts b/packages/daimo-api/test/pushNotifier.test.ts index 98931957b..dbdd1f7c0 100644 --- a/packages/daimo-api/test/pushNotifier.test.ts +++ b/packages/daimo-api/test/pushNotifier.test.ts @@ -546,7 +546,6 @@ function createKeyRotation(args: { transactionIndex: 0, transactionHash: args.isDeploymentLog ? "0x42" : "0x0", logIndex: 0, - account: args.from, keySlot: args.keySlot, key: ["0x0", "0x0"], };