/** @module-tag localnet */ import * as NodeBuffer from 'node:buffer' import { Hash, Hex } from 'ox' import { parseEventLogs } from 'viem' import { waitForTransactionReceipt } from 'viem/actions' import { Abis, Actions, Addresses } from 'viem/tempo' import * as Organizations from '../../db/tables/organizations.js' import * as RoutesCatalog from '../../db/tables/routesCatalog.js' import * as RoutesDeposits from '../../db/tables/routesDeposits.js' import * as RoutesSubsidies from '../../db/tables/routesSubsidies.js' import * as StripeCustomers from '../../db/tables/stripeCustomers.js' import * as TestApp from '../../../test/App.js' import * as Runtime from '../../../test/runtime.js' import * as TestRelay from '../../../test/Relay.js' import * as TestTempo from '../../../test/Tempo.js' import * as Viem from '../Viem.js' import * as Chain from './Chain.js' import * as DepositAddress from './DepositAddress.js' import * as Evidence from './Evidence.js' import * as Reconciliation from './Reconciliation.js' import * as Subsidy from './Subsidy.js' const runtime = Runtime.get() const chain = { addressFormat: 'hex', id: `eip155:${runtime.chainId}`, kind: 'evm', name: 'Tempo Localnet', } as const describe.skipIf(runtime.mode !== 'localnet')('reconcileRequest', () => { test('settles an underfilled deposit through the native DEX', async ({ onTestFinished }) => { // Vitest retries reuse module state, so each attempt needs a fresh database. const db = TestApp.database() const destinationTokenAddress = runtime.fixtures?.crossTokenTransfer?.destinationToken if (!destinationTokenAddress) throw new Error('Expected a seeded local DEX pair.') const rpcUrl = runtime.rpcUrl if (!rpcUrl) throw new Error('Expected a local Tempo RPC URL.') const depositAccount = TestTempo.accounts[5]! const providerAccount = TestTempo.accounts[0]! const recipient = TestTempo.accounts[7]! const sourceAccount = TestTempo.accounts[4]! const subsidyAccount = TestTempo.accounts[6]! const client = TestTempo.getClient() const destinationToken = { address: destinationTokenAddress, currency: 'USD', decimals: 6, name: 'Local Activity USD', standard: 'TIP-20', symbol: 'LAUSD', tokenKey: `${chain.id}/erc20:${destinationTokenAddress}`, verified: true, } as const const sourceToken = { address: Addresses.pathUsd, currency: 'USD', decimals: 6, name: 'pathUSD', standard: 'TIP-20', symbol: 'pathUSD', tokenKey: `${chain.id}/erc20:${Addresses.pathUsd}`, verified: true, } as const await Organizations.create(db, { id: 'org_local_subsidy', name: 'Local subsidy' }) await RoutesSubsidies.upsertOrganization(db, { frequency: 'tx', maxAmount: '1', orgId: 'org_local_subsidy', }) await StripeCustomers.create(db, { orgId: 'org_local_subsidy', stripeCustomerId: 'cus_local_subsidy', }) await StripeCustomers.setStatus(db, 'org_local_subsidy', 'active') await Actions.faucet.fundSync(client, { account: sourceAccount, timeout: 60_000 }) await Actions.faucet.fundSync(client, { account: subsidyAccount, timeout: 60_000 }) const [sourceReceipt, destinationReceipt] = await Promise.all([ Actions.token.transferSync(TestTempo.getClient({ account: sourceAccount }), { amount: 1_000_000n, to: depositAccount.address, token: sourceToken.address, }), Actions.token.transferSync(TestTempo.getClient({ account: providerAccount }), { amount: 950_000n, to: recipient.address, token: destinationToken.address, }), ]) await RoutesCatalog.publish(db, { chainTokens: [], chains: [ { aliases: [], id: chain.id as Chain.Id, name: chain.name, parentChainId: null, rpcUrls: [rpcUrl], slug: 'tempo-localnet', }, ], routes: [], tokens: [], }) const address = await DepositAddress.create(db, { deliveryStrategy: 'provider', environment: 'production', orgId: 'org_local_subsidy', providerOutputToken: destinationToken, snapshot: { address: depositAccount.address, destinationChain: chain, destinationToken, provider: { id: 'local', name: 'Local' }, recipient: recipient.address, refundAddress: sourceAccount.address, sourceChain: chain, sourceToken, subsidize: true, }, }) const verifyTransfers = Evidence.createVerifier({ confirmations: { [chain.id]: 1 }, db, defaultChainId: runtime.chainId as Viem.ChainId, }) let rejectBroadcast = true const proxy = await TestRelay.createServer(async (request, response) => { const chunks: Uint8Array[] = [] for await (const chunk of request) chunks.push(chunk) const body = NodeBuffer.Buffer.concat(chunks).toString() const payload = JSON.parse(body) as { id: number; method: string } if (rejectBroadcast && payload.method === 'eth_sendRawTransaction') { response.writeHead(503, { 'content-type': 'application/json' }) response.end(JSON.stringify({ error: 'Broadcast temporarily unavailable.' })) return } const result = await fetch(rpcUrl, { body, headers: { 'content-type': 'application/json' }, method: 'POST', }) response.writeHead(result.status, { 'content-type': 'application/json' }) response.end(await result.text()) }) onTestFinished(async () => { await proxy.closeAsync() }) const settler = Subsidy.create({ account: subsidyAccount, getClient: () => Viem.getClient({ chainId: runtime.chainId as Viem.ChainId, rpc: { url: proxy.url }, }), policy: { maxAmount: '10' }, }) const request = { createdAt: new Date().toISOString(), destinationTransactionHashes: [destinationReceipt.receipt.transactionHash], id: 'local-deposit-request', refundTransactionHashes: [], sourceTransactionHashes: [sourceReceipt.receipt.transactionHash], status: 'success', updatedAt: new Date().toISOString(), } const incompleteRequest = { ...request, destinationTransactionHashes: [ ...request.destinationTransactionHashes, `0x${'cc'.repeat(32)}`, ], } const first = await Reconciliation.reconcileRequest(db, { address, request: incompleteRequest, subsidies: settler, verifyTransfers, }) const [awaitingEvidence] = await RoutesDeposits.listByAddress( db, { environment: 'production', orgId: 'org_local_subsidy' }, address.id, { limit: 1 }, ) await expect( Reconciliation.reconcileRequest(db, { address, request, subsidies: settler, verifyTransfers, }), ).rejects.toThrow('Broadcast temporarily unavailable.') const [persisted] = await RoutesDeposits.listByAddress( db, { environment: 'production', orgId: 'org_local_subsidy' }, address.id, { limit: 1 }, ) expect(persisted?.status).toBe('settling') rejectBroadcast = false const second = await Reconciliation.reconcileRequest(db, { address, request, subsidies: settler, verifyTransfers, }) const [pending] = await RoutesDeposits.listByAddress( db, { environment: 'production', orgId: 'org_local_subsidy' }, address.id, { limit: 1 }, ) expect(pending).toEqual(persisted) expect(second.updated).toBe(0) if (!pending?.settlementTransactionHash) throw new Error('Expected a persisted subsidy transaction.') const settlementReceipt = await waitForTransactionReceipt(client, { hash: pending.settlementTransactionHash as Hex.Hex, }) await RoutesSubsidies.removeOrganization(db, 'org_local_subsidy') await StripeCustomers.setStatus(db, 'org_local_subsidy', 'past_due') const third = await Reconciliation.reconcileRequest(db, { address, request, subsidies: settler, verifyTransfers, }) const [completed] = await RoutesDeposits.listByAddress( db, { environment: 'production', orgId: 'org_local_subsidy' }, address.id, { limit: 1 }, ) const memo = parseEventLogs({ abi: Abis.tip20, eventName: 'TransferWithMemo', logs: settlementReceipt.logs, strict: true, })[0]?.args.memo expect(awaitingEvidence).toMatchInlineSnapshot( { createdAt: expect.any(String), depositAddressId: expect.any(String), id: expect.any(String), providerOutputToken: { address: expect.any(String), tokenKey: expect.any(String), }, providerTransactionHashes: [expect.any(String), expect.any(String)], snapshot: { depositAddressId: expect.any(String), destinationToken: { address: expect.any(String), tokenKey: expect.any(String), }, destinationTransactionHashes: [expect.any(String)], recipient: expect.any(String), refundAddress: expect.any(String), sender: expect.any(String), sourceTransactionHashes: [expect.any(String)], }, sourceTransactionHash: expect.any(String), sourceTransferIndex: expect.any(Number), statusUpdatedAt: expect.any(String), updatedAt: expect.any(String), }, ` { "createdAt": Any, "depositAddressId": Any, "detectionTrigger": "poll", "environment": "production", "id": Any, "orgId": "org_local_subsidy", "pollObservedAt": null, "projectId": null, "providerDeliveredAt": null, "providerOutputAmount": { "baseUnits": "950000", "currency": "USD", "decimals": 6, "formatted": "0.95", }, "providerOutputToken": { "address": Any, "currency": "USD", "decimals": 6, "name": "Local Activity USD", "standard": "TIP-20", "symbol": "LAUSD", "tokenKey": Any, "verified": true, }, "providerRequestId": "local-deposit-request", "providerRequestIds": [ "local-deposit-request", ], "providerState": null, "providerSubsidy": null, "providerTransactionHashes": [ Any, Any, ], "providerTransferIndex": 0, "retryState": null, "settlementTransaction": null, "settlementTransactionHash": null, "snapshot": { "depositAddressId": Any, "destinationAmount": { "baseUnits": "950000", "currency": "USD", "decimals": 6, "formatted": "0.95", }, "destinationAmountRequired": { "baseUnits": "1000000", "currency": "USD", "decimals": 6, "formatted": "1", }, "destinationChain": { "addressFormat": "hex", "id": "eip155:1337", "kind": "evm", "name": "Tempo Localnet", }, "destinationToken": { "address": Any, "currency": "USD", "decimals": 6, "name": "Local Activity USD", "standard": "TIP-20", "symbol": "LAUSD", "tokenKey": Any, "verified": true, }, "destinationTransactionHashes": [ Any, ], "provider": { "id": "local", "name": "Local", }, "recipient": Any, "refundAddress": Any, "sender": Any, "sourceAmount": { "baseUnits": "1000000", "currency": "USD", "decimals": 6, "formatted": "1", }, "sourceChain": { "addressFormat": "hex", "id": "eip155:1337", "kind": "evm", "name": "Tempo Localnet", }, "sourceToken": { "address": "0x20c0000000000000000000000000000000000000", "currency": "USD", "decimals": 6, "name": "pathUSD", "standard": "TIP-20", "symbol": "pathUSD", "tokenKey": "eip155:1337/erc20:0x20c0000000000000000000000000000000000000", "verified": true, }, "sourceTransactionHashes": [ Any, ], }, "sourceChainId": "eip155:1337", "sourceTransactionHash": Any, "sourceTransferIndex": Any, "status": "bridging", "statusReason": null, "statusUpdatedAt": Any, "subsidyAmount": null, "subsidyMeterReportedAt": null, "tempoGasPaid": null, "updatedAt": Any, "version": 3, "webhookReceivedAt": null, } `, ) expect({ completed, first, memoMatchesDeposit: completed !== undefined && memo !== undefined && Hex.isEqual(memo, Hash.keccak256(Hex.fromString(completed.id))), second, third, }).toMatchInlineSnapshot( { completed: { createdAt: expect.any(String), depositAddressId: expect.any(String), id: expect.any(String), providerDeliveredAt: expect.any(String), providerOutputToken: { address: expect.any(String), tokenKey: expect.any(String), }, providerTransactionHashes: [expect.any(String), expect.any(String)], settlementTransaction: expect.any(String), settlementTransactionHash: expect.any(String), snapshot: { depositAddressId: expect.any(String), destinationToken: { address: expect.any(String), tokenKey: expect.any(String), }, destinationTransactionHashes: [expect.any(String), expect.any(String)], recipient: expect.any(String), refundAddress: expect.any(String), sender: expect.any(String), sourceTransactionHashes: [expect.any(String)], }, sourceTransactionHash: expect.any(String), sourceTransferIndex: expect.any(Number), statusUpdatedAt: expect.any(String), tempoGasPaid: expect.any(String), updatedAt: expect.any(String), }, }, ` { "completed": { "createdAt": Any, "depositAddressId": Any, "detectionTrigger": "poll", "environment": "production", "id": Any, "orgId": "org_local_subsidy", "pollObservedAt": null, "projectId": null, "providerDeliveredAt": Any, "providerOutputAmount": { "baseUnits": "950000", "currency": "USD", "decimals": 6, "formatted": "0.95", }, "providerOutputToken": { "address": Any, "currency": "USD", "decimals": 6, "name": "Local Activity USD", "standard": "TIP-20", "symbol": "LAUSD", "tokenKey": Any, "verified": true, }, "providerRequestId": "local-deposit-request", "providerRequestIds": [ "local-deposit-request", ], "providerState": null, "providerSubsidy": null, "providerTransactionHashes": [ Any, Any, ], "providerTransferIndex": 0, "retryState": null, "settlementTransaction": Any, "settlementTransactionHash": Any, "snapshot": { "depositAddressId": Any, "destinationAmount": { "baseUnits": "1000000", "currency": "USD", "decimals": 6, "formatted": "1", }, "destinationAmountRequired": { "baseUnits": "1000000", "currency": "USD", "decimals": 6, "formatted": "1", }, "destinationChain": { "addressFormat": "hex", "id": "eip155:1337", "kind": "evm", "name": "Tempo Localnet", }, "destinationToken": { "address": Any, "currency": "USD", "decimals": 6, "name": "Local Activity USD", "standard": "TIP-20", "symbol": "LAUSD", "tokenKey": Any, "verified": true, }, "destinationTransactionHashes": [ Any, Any, ], "provider": { "id": "local", "name": "Local", }, "recipient": Any, "refundAddress": Any, "sender": Any, "sourceAmount": { "baseUnits": "1000000", "currency": "USD", "decimals": 6, "formatted": "1", }, "sourceChain": { "addressFormat": "hex", "id": "eip155:1337", "kind": "evm", "name": "Tempo Localnet", }, "sourceToken": { "address": "0x20c0000000000000000000000000000000000000", "currency": "USD", "decimals": 6, "name": "pathUSD", "standard": "TIP-20", "symbol": "pathUSD", "tokenKey": "eip155:1337/erc20:0x20c0000000000000000000000000000000000000", "verified": true, }, "sourceTransactionHashes": [ Any, ], }, "sourceChainId": "eip155:1337", "sourceTransactionHash": Any, "sourceTransferIndex": Any, "status": "completed", "statusReason": null, "statusUpdatedAt": Any, "subsidyAmount": { "baseUnits": "50000", "currency": "USD", "decimals": 6, "formatted": "0.05", }, "subsidyMeterReportedAt": null, "tempoGasPaid": Any, "updatedAt": Any, "version": 5, "webhookReceivedAt": null, }, "first": { "created": 1, "terminal": false, "updated": 1, }, "memoMatchesDeposit": true, "second": { "created": 0, "followUp": "settlement", "terminal": false, "updated": 0, }, "third": { "created": 0, "terminal": true, "updated": 1, }, } `, ) }) })