From cc1a5ddc664aa485d4546dd70b549bb0fed56036 Mon Sep 17 00:00:00 2001 From: Gustavo Inacio Date: Mon, 7 Oct 2024 11:31:15 +0200 Subject: [PATCH] feat: check escrow balance before rav redeem Signed-off-by: Gustavo Inacio --- .../src/allocations/escrow-accounts.ts | 71 +++++++++++++++++++ .../indexer-common/src/allocations/monitor.ts | 1 + .../src/allocations/tap-collector.ts | 23 ++++++ 3 files changed, 95 insertions(+) create mode 100644 packages/indexer-common/src/allocations/escrow-accounts.ts diff --git a/packages/indexer-common/src/allocations/escrow-accounts.ts b/packages/indexer-common/src/allocations/escrow-accounts.ts new file mode 100644 index 000000000..20c96fb20 --- /dev/null +++ b/packages/indexer-common/src/allocations/escrow-accounts.ts @@ -0,0 +1,71 @@ +import { Address, Logger } from '@graphprotocol/common-ts' +import { TAPSubgraph } from '../tap-subgraph' +import gql from 'graphql-tag' + +export interface MonitorEscrowAccountsOptions { + indexer: Address + logger: Logger + tapSubgraph: TAPSubgraph + interval: number +} + +type U256 = bigint + +type EscrowAccountResponse = { + escrowAccounts: { + balance: U256 + sender: { + id: Address + } + }[] +} + +export class EscrowAccounts { + constructor(private sendersBalances: Map) {} + + getBalanceForSender(sender: Address): U256 { + const balance = this.sendersBalances.get(sender) + if (balance === undefined) { + throw new Error(`No balance found for sender: ${sender}`) + } + return balance + } + + subtractSenderBalance(sender: Address, ravValue: U256) { + const balance = this.getBalanceForSender(sender) + const newBalance = balance - ravValue + this.sendersBalances.set(sender, newBalance) + } + + static fromResponse(response: EscrowAccountResponse): EscrowAccounts { + const sendersBalances = new Map() + response.escrowAccounts.forEach((account) => { + sendersBalances.set(account.sender.id, account.balance) + }) + + return new EscrowAccounts(sendersBalances) + } +} + +export const getEscrowAccounts = async ( + tapSubgraph: TAPSubgraph, + indexer: Address, +): Promise => { + const result = await tapSubgraph.query( + gql` + query EscrowAccountQuery($indexer: ID!) { + escrowAccounts(where: { receiver_: { id: $indexer } }) { + balance + sender { + id + } + } + } + `, + { indexer }, + ) + if (!result.data) { + throw `There was an error while querying Tap Subgraph. Errors: ${result.error}` + } + return EscrowAccounts.fromResponse(result.data) +} diff --git a/packages/indexer-common/src/allocations/monitor.ts b/packages/indexer-common/src/allocations/monitor.ts index 5ef5c9555..59ce80490 100644 --- a/packages/indexer-common/src/allocations/monitor.ts +++ b/packages/indexer-common/src/allocations/monitor.ts @@ -182,3 +182,4 @@ export const monitorEligibleAllocations = ({ return allocations } + diff --git a/packages/indexer-common/src/allocations/tap-collector.ts b/packages/indexer-common/src/allocations/tap-collector.ts index 95a51564e..797adb57b 100644 --- a/packages/indexer-common/src/allocations/tap-collector.ts +++ b/packages/indexer-common/src/allocations/tap-collector.ts @@ -29,6 +29,7 @@ import pReduce from 'p-reduce' import { TAPSubgraph } from '../tap-subgraph' import { NetworkSubgraph } from '../network-subgraph' import gql from 'graphql-tag' +import { getEscrowAccounts } from './escrow-accounts' const RAV_CHECK_INTERVAL_MS = 30_000 @@ -452,10 +453,28 @@ export class TapCollector { logger.info(`Redeem last RAVs on chain individually`, { signedRavs, }) + const escrowAccounts = await getEscrowAccounts(this.tapSubgraph, toAddress('')) // Redeem RAV one-by-one as no plual version available for (const { rav: signedRav, allocation, sender } of signedRavs) { const { rav } = signedRav + + // verify escrow balances + const ravValue = BigInt(rav.valueAggregate.toString()) + const senderBalance = escrowAccounts.getBalanceForSender(sender) + if (senderBalance < ravValue) { + this.logger.warn( + 'RAV was not sent to the blockchain \ + because its value aggregate is lower than escrow balance.', + { + rav, + sender, + senderBalance, + }, + ) + continue + } + const stopTimer = this.metrics.ravsRedeemDuration.startTimer({ allocation: rav.allocationId, }) @@ -486,6 +505,10 @@ export class TapCollector { this.metrics.ravRedeemsInvalid.inc({ allocation: rav.allocationId }) return } + // subtract from the escrow account + // THIS IS A MUT OPERATION + escrowAccounts.subtractSenderBalance(sender, ravValue) + this.metrics.ravCollectedFees.set( { allocation: rav.allocationId }, parseFloat(rav.valueAggregate.toString()),