import { Transaction } from 'viem/tempo' import * as TestApp from '../../../test/App.js' import * as Tempo from '../../../test/Tempo.js' import * as BillingSettings from '../../db/tables/billingSettings.js' import * as Organizations from '../../db/tables/organizations.js' import * as SponsorshipAttributions from '../../db/tables/sponsorshipAttributions.js' import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js' import * as StripeCustomers from '../../db/tables/stripeCustomers.js' import type * as Log from '../../internal/Log.js' import * as VerifiedTokens from '../../internal/VerifiedTokens.js' import * as Viem from '../../internal/Viem.js' const chainId = 8001 const siblingChainId = 8002 const feeToken = '0x20c0000000000000000000000000000000000000' const token = { address: feeToken, currency: 'USD', decimals: 6, name: 'Path USD', symbol: 'pathUSD', } satisfies VerifiedTokens.Token describe('Zone sponsorship billing', () => { test.each(['sandbox', 'sandbox_wildcard'])('confines the %s key to testnet', async (apiKey) => { const { app, db, entries } = await setup() await StripeCustomers.setStatus(db, 'org_zone', 'active') const body = await sponsor(app, { apiKey }) expect(body.error).toMatchInlineSnapshot(` { "code": -32602, "data": { "code": "production_api_key_required", }, "message": "Sponsorship rejected.", } `) expect(entries[0]?.sponsorship?.reason).toBe('production_api_key_required') expect(await SponsoredTransactions.listPending(db)).toHaveLength(0) }) test.each(['none', 'past_due'] as const)( 'requires active billing when status is %s', async (status) => { const { app, db, entries } = await setup() await StripeCustomers.setStatus(db, 'org_zone', status) const body = await sponsor(app) const code = status === 'past_due' ? 'billing_past_due' : 'billing_required' expect(body.error?.data).toEqual({ code }) expect(entries[0]?.sponsorship?.reason).toBe(code) expect(await SponsoredTransactions.listPending(db)).toHaveLength(0) }, ) test('records the Zone id and billable USD spend with active billing', async () => { const { app, db } = await setup() await StripeCustomers.setStatus(db, 'org_zone', 'active') const body = await sponsor(app) expect(body.error).toBeUndefined() expect(Transaction.deserialize(body.result!).feePayerSignature).toBeDefined() const [row] = await SponsoredTransactions.listPending(db) expect(row).toMatchObject({ billable: true, chainId, currency: 'usd', environment: 'production', feeMax: '500000', feeToken, orgId: 'org_zone', }) }) test.each([undefined, { ...token, currency: 'EUR' }, { ...token, decimals: 18 }])( 'rejects an unsupported Zone fee token %j', async (verified) => { const { app, db, entries } = await setup() await StripeCustomers.setStatus(db, 'org_zone', 'active') await VerifiedTokens.replace(db, chainId, verified ? [verified] : []) const body = await sponsor(app) expect(body.error?.data).toEqual({ code: 'fee_token_unsupported' }) expect(entries[0]?.sponsorship?.reason).toBe('fee_token_unsupported') expect(await SponsoredTransactions.listPending(db)).toHaveLength(0) }, ) test.each([ { maxFeePerGas: 500_000_000_000n, txFeeLimit: '0.10' }, { maxFeePerGas: 2_000_000_000_000n, txFeeLimit: '999999' }, ])('enforces the lower fee cap for $txFeeLimit', async ({ maxFeePerGas, txFeeLimit }) => { const { app, db, entries } = await setup() await StripeCustomers.setStatus(db, 'org_zone', 'active') await BillingSettings.upsert(db, { orgId: 'org_zone', txFeeLimit }) const body = await sponsor(app, { maxFeePerGas }) expect(body.error?.data).toEqual({ code: 'tx_fee_limit_exceeded' }) expect(entries[0]?.sponsorship?.reason).toBe('tx_fee_limit_exceeded') expect(await SponsoredTransactions.listPending(db)).toHaveLength(0) }) test.each([Viem.chainId.mainnet, chainId, siblingChainId])( 'counts existing chain %i spend before sponsoring the Zone', async (existingChainId) => { const { app, db, entries } = await setup() await StripeCustomers.setStatus(db, 'org_zone', 'active') await BillingSettings.upsert(db, { orgId: 'org_zone', spendLimit: '1' }) await SponsoredTransactions.upsert(db, { apiKeyId: 'key_existing', billable: true, chainId: existingChainId, environment: 'production', feeMax: '900000', orgId: 'org_zone', signPayload: `0x${'ab'.repeat(32)}`, transaction: `0x${'cd'.repeat(32)}`, }) const body = await sponsor(app) expect(body.error?.data).toEqual({ code: 'spend_limit_exceeded' }) expect(entries[0]?.sponsorship?.reason).toBe('spend_limit_exceeded') expect(await SponsoredTransactions.listPending(db)).toHaveLength(1) await BillingSettings.upsert(db, { orgId: 'org_zone', spendLimit: '2' }) expect((await sponsor(app)).error).toBeUndefined() expect(await SponsoredTransactions.listPending(db)).toHaveLength(2) }, ) test('counts Zone spend against the shared attribution promotion', async () => { const { app, db } = await setup() await StripeCustomers.setStatus(db, 'org_zone', 'active') await Organizations.create(db, { id: 'org_zone', name: 'Zone' }) await Organizations.setSponsorshipSubsidy(db, 'org_zone', { attributionSpendLimit: '0.75', durationDays: 90, }) await SponsorshipAttributions.resolveExternal(db, { externalId: 'account', orgId: 'org_zone', }) expect((await sponsor(app, { externalId: 'account', nonce: 0 })).error).toBeUndefined() expect( (await sponsor(app, { chainId: siblingChainId, externalId: 'account', nonce: 1 })).error, ).toBeUndefined() const rows = await SponsoredTransactions.listPending(db) expect(rows.find((row) => row.chainId === chainId)?.billable).toBe(false) expect(rows.find((row) => row.chainId === siblingChainId)?.billable).toBe(true) }) test('keeps sandbox sponsorship on a testnet-backed Zone non-billable', async () => { const { app, db } = await setup({ sourceId: Viem.chainId.testnet }) const body = await sponsor(app, { apiKey: 'sandbox' }) expect(body.error).toBeUndefined() expect(await SponsoredTransactions.listPending(db)).toMatchObject([ { billable: false, chainId, environment: 'sandbox' }, ]) }) }) async function setup(options: setup.Options = {}) { const db = TestApp.database() const entries: Log.Entry[] = [] const zones = [chainId, siblingChainId].map((id) => TestApp.zone({ chainId: id, rpcUrl: 'https://zone.example', sourceChainId: options.sourceId ?? Viem.chainId.mainnet, }), ) for (const id of [chainId, siblingChainId]) await VerifiedTokens.replace(db, id, [token]) await StripeCustomers.create(db, { orgId: 'org_zone', stripeCustomerId: 'cus_zone' }) const app = TestApp.create({ auth: { keys: [ { environment: 'production', id: 'key_production', orgId: 'org_zone', scopes: ['rpc-relay:sponsor', `zone:${chainId}:read`, `zone:${siblingChainId}:read`], token: 'production', }, { environment: 'sandbox', id: 'key_sandbox', orgId: 'org_zone', scopes: ['rpc-relay:sponsor', `zone:${chainId}:read`], token: 'sandbox', }, { environment: 'sandbox', id: 'key_sandbox_wildcard', orgId: 'org_zone', scopes: ['*'], token: 'sandbox_wildcard', }, ], }, db, logger: (entry) => void entries.push(entry), relay: { feePayer: { account: Tempo.accounts[10]!, feeToken } }, zones, }) return { app, db, entries } } declare namespace setup { /** Configured parent chain for the Zone fixtures. */ type Options = { /** Parent Tempo chain id. */ sourceId?: Viem.ChainId | undefined } } async function sponsor(app: ReturnType, options: sponsor.Options = {}) { const transaction = await Tempo.accounts[9]!.signTransaction({ chainId: options.chainId ?? chainId, feePayer: true, gas: 1_000_000n, maxFeePerGas: options.maxFeePerGas ?? 500_000_000_000n, maxPriorityFeePerGas: 0n, nonce: options.nonce ?? 0, to: Tempo.accounts[8]!.address, value: 0n, }) const response = await app.request('/rpc/sponsor', { body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_signRawTransaction', params: [transaction], }), headers: { 'content-type': 'application/json', 'tempo-api-key': options.apiKey ?? 'production', ...(options.externalId ? { 'tempo-attribution-id': options.externalId } : {}), }, method: 'POST', }) return (await response.json()) as { error?: { code: number; data?: { code: string }; message: string } | undefined result?: `0x76${string}` | undefined } } declare namespace sponsor { /** Inputs for one locally signed sponsorship request. */ type Options = { /** Credential selecting the key environment and scopes. */ apiKey?: string | undefined /** Transaction chain id. */ chainId?: number | undefined /** Account attribution shared across chains. */ externalId?: string | undefined /** Signed gas price cap in attodollars. */ maxFeePerGas?: bigint | undefined /** Sender nonce, distinguishing signed envelopes. */ nonce?: number | undefined } }