import * as http from 'node:http' import * as Mppx from 'mppx' import { Mppx as MppxClient, tempo as tempoClient } from 'mppx/client' import { Mppx as MppxServer, tempo as tempoServer } from 'mppx/server' import { privateKeyToAccount } from 'viem/accounts' import { Actions } from 'viem/tempo' import * as TestApp from '../../../test/App.js' import * as Tempo from '../../../test/Tempo.js' import * as App from '../../App.js' import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js' import * as Store from '../../internal/Store.js' import * as Mpp from './Mpp.js' import * as Adapter from './internal/Adapter.js' import * as Idempotency from './internal/Idempotency.js' const key = { id: 'key_mpp', orgId: 'org_test', projectId: 'prj_mpp', scopes: ['mpp:write'], token: 'secret_mpp', } satisfies TestApp.kvStore.Key const organizationKey = { id: 'key_mpp_org', orgId: 'org_test', scopes: ['mpp:write'], token: 'secret_mpp_org', } satisfies TestApp.kvStore.Key const sandboxKey = { ...key, environment: 'sandbox', id: 'key_mpp_sandbox', token: 'secret_mpp_sandbox', } satisfies TestApp.kvStore.Key const idempotencyKey = 'mpp_broadcast_01j3j1k2l3m4n5p6q7r8s9t0u' const modes = ['pull', 'push'] as const const input = { challenge: { description: 'Tempo API payment', id: 'ch_01j3j1k2l3m4n5p6q7r8s9t0u', intent: 'charge', method: 'tempo', opaque: 'eyJhbGciOiJIUzI1NiJ9', realm: 'merchant.example', request: { amount: '1', currency: 'USD' }, }, payload: { signature: `0x${'aa'.repeat(65)}`, type: 'transaction' }, } satisfies Mpp.RelayInput const errorCodes = [ 'unsupported', 'policy_denied', 'invalid_payment', 'insufficient_funds', 'expired', 'already_used', 'screen_rejected', 'simulation_failed', 'broadcast_failed', 'temporarily_unavailable', 'unknown', ] as const const errorDescriptions = [ 'Method, intent, network, or asset is unsupported.', 'Payment violates relay or account policy.', 'Payload, signature, parties, or payment requirements are invalid.', 'Sender lacks funds.', 'Challenge or payment authorization expired.', 'Credential or transaction already used.', 'Originator or recipient failed screening.', 'Transaction simulation failed before broadcast.', 'Transaction broadcast or confirmation failed.', 'Retry may succeed.', 'Unexpected relay failure.', ] as const describe('MPP relay', () => { test('verifies a valid Tempo credential without broadcasting it', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const response = await post(app, '/v1/mpp/validate', await credential()) expect(response.status).toBe(200) expect(await response.json()).toEqual({ success: true }) }) test('verifies a zero-amount proof credential', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential({ amount: '0' }) const response = await post(app, '/v1/mpp/validate', input) expect(payloadType(input)).toBe('proof') expect(input.source).toBeDefined() expect(response.status).toBe(200) expect(await response.json()).toEqual({ success: true }) }) test('validates and finalizes a pushed credential without a second submission', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential({ mode: 'push', supportedModes: ['push'] }) const hash = credentialHash(input) expect(payloadType(input)).toBe('hash') expect(await (await post(app, '/v1/mpp/validate', input)).json()).toEqual({ success: true }) const first = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input)).json(), ) expect(first).toMatchObject({ receipt: { reference: hash }, success: true }) const replay = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input)).json(), ) expect(replay.success).toBe(false) if (replay.success) throw new Error('Expected the relay to reject the pushed credential replay.') expect([Mpp.relayErrorCode.alreadyUsed, Mpp.relayErrorCode.invalidPayment]).toContain( replay.error.code, ) }) test('replays a pushed credential receipt for an idempotency key', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential({ mode: 'push', supportedModes: ['push'] }) const first = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input, { idempotencyKey })).json(), ) const replay = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input, { idempotencyKey })).json(), ) expect(payloadType(input)).toBe('hash') expect(first.success).toBe(true) expect(replay).toEqual(first) }) test('validates a pulled credential before broadcasting it once', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential({ mode: 'pull', supportedModes: ['pull'] }) expect(payloadType(input)).toBe('transaction') expect(await (await post(app, '/v1/mpp/validate', input)).json()).toEqual({ success: true }) const first = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input)).json(), ) expect(first.success).toBe(true) const replay = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input)).json(), ) expect(replay.success).toBe(false) if (replay.success) throw new Error('Expected the relay to reject the pulled credential replay.') expect([Mpp.relayErrorCode.alreadyUsed, Mpp.relayErrorCode.invalidPayment]).toContain( replay.error.code, ) }) test('does not finalize an unverifiable pushed hash', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential({ mode: 'push', supportedModes: ['push'] }) const invalid = { ...input, payload: { ...(input.payload as CredentialPayload), hash: `0x${'ff'.repeat(32)}` }, } satisfies Mpp.RelayInput expect(payloadType(invalid)).toBe('hash') const verification = Mpp.schema.VerifyResponse.parse( await (await post(app, '/v1/mpp/validate', invalid)).json(), ) expect(verification.success).toBe(false) const broadcast = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', invalid)).json(), ) expect(broadcast.success).toBe(false) }) test.each(modes)('settles one concurrent %s credential across app instances', async (mode) => { const state = Store.memory() const input = await credential({ mode, supportedModes: [mode] }) const first = TestApp.create({ auth: { keys: [key] }, mpp: { state } }) const second = TestApp.create({ auth: { keys: [key] }, mpp: { state } }) const responses = await Promise.all([ post(first, '/v1/mpp/broadcast', input), post(second, '/v1/mpp/broadcast', input), ]) const results = await Promise.all( responses.map(async (response) => Mpp.schema.BroadcastResponse.parse(await response.json())), ) const successful = results.filter((result) => result.success) const rejected = results.find((result) => !result.success) expect(successful).toHaveLength(1) expect(rejected?.success).toBe(false) if (!rejected || rejected.success) throw new Error('Expected one credential settlement to be rejected.') expect([Mpp.relayErrorCode.alreadyUsed, Mpp.relayErrorCode.invalidPayment]).toContain( rejected.error.code, ) }) test.each(modes)('screens the sender and recipient of a %s credential', async (mode) => { const addresses: string[] = [] const adapter = Adapter.create({ chainId: Tempo.chain.id, getClient: () => Tempo.client, provider: { async screen({ address }) { addresses.push(address) return { allowed: true } }, }, screeningStore: Store.memory(), state: Store.memory(), }) await expect( adapter.verify(await credential({ mode, supportedModes: [mode] })), ).resolves.toEqual({ success: true, }) expect(addresses.sort()).toEqual( [Tempo.accounts[0].address, Tempo.accounts[1].address] .map((address) => address.toLowerCase()) .sort(), ) }) test.each([ { address: Tempo.accounts[1].address, mode: 'push' }, { address: Tempo.accounts[0].address, mode: 'push' }, { address: Tempo.accounts[1].address, mode: 'pull' }, { address: Tempo.accounts[0].address, mode: 'pull' }, ] as const)('rejects a screened $mode credential participant', async ({ address, mode }) => { const adapter = Adapter.create({ chainId: Tempo.chain.id, getClient: () => Tempo.client, provider: { async screen(options) { return { allowed: options.address !== address.toLowerCase() } }, }, screeningStore: Store.memory(), state: Store.memory(), }) await expect( adapter.verify(await credential({ mode, supportedModes: [mode] })), ).resolves.toEqual({ error: { code: Mpp.relayErrorCode.screenRejected, message: 'Address screening rejected the payment.', }, success: false, }) }) test('returns a screen rejection after validating a Tempo credential', async () => { const adapter = Adapter.create({ chainId: Tempo.chain.id, getClient: () => Tempo.client, provider: { async screen() { return { allowed: false } }, }, screeningStore: Store.memory(), state: Store.memory(), }) await expect(adapter.verify(await credential())).resolves.toEqual({ error: { code: Mpp.relayErrorCode.screenRejected, message: 'Address screening rejected the payment.', }, success: false, }) }) test('broadcasts once and replays the terminal receipt for an idempotency key', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential() const first = await post(app, '/v1/mpp/broadcast', input, { idempotencyKey }) const second = await post(app, '/v1/mpp/broadcast', input, { idempotencyKey }) expect(first.status).toBe(200) expect(second.status).toBe(200) expect(await second.json()).toEqual(await first.json()) }) test('lets MPPX reject a repeated credential without an idempotency key', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const input = await credential() const first = await post(app, '/v1/mpp/broadcast', input) const second = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', input)).json(), ) expect(Mpp.schema.BroadcastResponse.parse(await first.json()).success).toBe(true) expect(second.success).toBe(false) if (second.success) throw new Error('Expected MPPX to reject the repeated credential.') expect([Mpp.relayErrorCode.alreadyUsed, Mpp.relayErrorCode.invalidPayment]).toContain( second.error.code, ) }) test('returns a retryable result while the idempotent broadcast is pending', async () => { const state = TestApp.kvStore({ keys: [key] }) const claim = await Idempotency.claim({ identity: { apiKeyId: key.id, idempotencyKey, inputHash: Idempotency.inputHash(input) }, state, }) if (claim.type !== 'claimed') throw new Error('Expected an idempotency key claim.') const app = TestApp.create({ auth: { keys: [key] }, mpp: { state } }) const response = await post(app, '/v1/mpp/broadcast', input, { idempotencyKey }) expect(response.status).toBe(200) expect(await response.json()).toEqual({ error: { code: Mpp.relayErrorCode.temporarilyUnavailable, message: 'MPP credential broadcast is already in progress.', }, success: false, }) }) test('releases failed credentials for a corrected retry', async () => { const state = TestApp.kvStore({ keys: [key] }) const app = TestApp.create({ auth: { keys: [key] }, mpp: { state } }) const response = await post(app, '/v1/mpp/broadcast', input, { idempotencyKey }) expect(response.status).toBe(200) expect(Mpp.schema.BroadcastResponse.parse(await response.json()).success).toBe(false) const corrected = Mpp.schema.BroadcastResponse.parse( await ( await post( app, '/v1/mpp/broadcast', await credential({ mode: 'pull', supportedModes: ['pull'] }), { idempotencyKey }, ) ).json(), ) expect(corrected.success).toBe(true) }) test('does not replay a receipt for a different credential sharing an idempotency key', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const first = await credential({ mode: 'push', supportedModes: ['push'] }) const second = await credential({ mode: 'push', supportedModes: ['push'] }) expect( Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', first, { idempotencyKey })).json(), ).success, ).toBe(true) const collision = Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', second, { idempotencyKey })).json(), ) expect(collision).toEqual({ error: { code: Mpp.relayErrorCode.invalidPayment, message: 'Idempotency-Key was already used for a different credential.', }, success: false, }) expect( Mpp.schema.BroadcastResponse.parse( await (await post(app, '/v1/mpp/broadcast', second)).json(), ).success, ).toBe(true) }) test('co-signs and records an organization-attributed sponsored pull credential', async () => { const db = TestApp.database() await Actions.faucet.fundSync(Tempo.client, { account: Tempo.accounts[2], timeout: 60_000 }) const app = TestApp.create({ auth: { keys: [organizationKey] }, db, mpp: { feePayer: Tempo.accounts[2], state: Store.memory() }, }) const input = await credential({ feePayer: true, mode: 'pull', supportedModes: ['pull'] }) expect(payloadType(input)).toBe('transaction') expect(input.challenge.request['methodDetails']).toMatchObject({ feePayer: true }) expect( Mpp.schema.BroadcastResponse.parse( await ( await post(app, '/v1/mpp/broadcast', input, { apiKey: organizationKey.token }) ).json(), ).success, ).toBe(true) expect(await SponsoredTransactions.listPending(db)).toMatchObject([ { apiKeyId: organizationKey.id, chainId: Tempo.chain.id, environment: 'production', orgId: organizationKey.orgId, projectId: null, status: 'pending', }, ]) }) test('runs MPPX verification, broadcast, and replay through a live HTTP server', async () => { const app = TestApp.create({ auth: { keys: [key] } }) const server = await startServer(App.listener(app)) try { const input = await credentialFromServer() const verification = await postHttp(server.url, '/v1/mpp/validate', input) const first = await postHttp(server.url, '/v1/mpp/broadcast', input, { idempotencyKey }) const second = await postHttp(server.url, '/v1/mpp/broadcast', input, { idempotencyKey }) expect(verification.status).toBe(200) expect(await verification.json()).toEqual({ success: true }) expect(first.status).toBe(200) expect(second.status).toBe(200) const receipt = Mpp.schema.BroadcastResponse.parse(await first.json()) if (!receipt.success) throw new Error('Expected the MPP relay to broadcast the credential.') expect(receipt.receipt.method).toBe('tempo') expect(Mpp.schema.BroadcastResponse.parse(await second.json())).toEqual(receipt) } finally { await server.close() } }) test.each(['/v1/mpp/validate', '/v1/mpp/broadcast'])( '%s validates credentials for API keys with the MPP scope', async (path) => { const app = TestApp.create({ auth: { keys: [key] } }) const response = await app.fetch( new Request(`http://tempo-api.test${path}`, { body: JSON.stringify(input), headers: { 'content-type': 'application/json', authorization: 'Bearer secret_mpp' }, method: 'POST', }), ) expect(response.status).toBe(200) expect(Mpp.schema.VerifyResponse.safeParse(await response.json()).success).toBe(true) }, ) test('rejects unscoped API keys', async () => { const app = TestApp.create({ auth: { keys: [key, TestApp.key] } }) const response = await app.fetch( new Request('http://tempo-api.test/v1/mpp/validate', { body: JSON.stringify(input), headers: { 'content-type': 'application/json', authorization: `Bearer ${TestApp.key.token}`, }, method: 'POST', }), ) expect(response.status).toBe(403) expect(await response.json()).toMatchObject({ error: { code: 'api_key_forbidden' } }) }) test('requires API-key authentication', async () => { const app = TestApp.create() const response = await app.fetch( new Request('http://tempo-api.test/v1/mpp/validate', { body: JSON.stringify(input), headers: { 'content-type': 'application/json' }, method: 'POST', }), ) expect(response.status).toBe(401) expect(await response.json()).toMatchObject({ error: { code: 'api_key_missing' } }) }) test('rejects an unbilled mainnet fee-payer broadcast before MPPX processing', async () => { const app = TestApp.create({ auth: { keys: [key] }, mpp: { feePayer: privateKeyToAccount(`0x${'11'.repeat(32)}`), state: Store.memory(), }, }) const response = await post(app, '/v1/mpp/broadcast', { ...input, challenge: { ...input.challenge, request: { methodDetails: { chainId: 4217, feePayer: true } }, }, }) expect(response.status).toBe(200) expect(await response.json()).toEqual({ error: { code: Mpp.relayErrorCode.policyDenied, message: 'Active billing is required for mainnet fee sponsorship.', }, success: false, }) }) test('rejects sandbox keys that request mainnet fee sponsorship', async () => { const app = TestApp.create({ auth: { keys: [sandboxKey] }, mpp: { feePayer: privateKeyToAccount(`0x${'11'.repeat(32)}`), state: Store.memory(), }, }) const response = await app.fetch( new Request('http://tempo-api.test/v1/mpp/broadcast', { body: JSON.stringify({ ...input, challenge: { ...input.challenge, request: { methodDetails: { chainId: 4217, feePayer: true } }, }, }), headers: { authorization: `Bearer ${sandboxKey.token}`, 'content-type': 'application/json', }, method: 'POST', }), ) expect(response.status).toBe(200) expect(await response.json()).toEqual({ error: { code: Mpp.relayErrorCode.policyDenied, message: 'Mainnet fee sponsorship requires a production API key.', }, success: false, }) }) test.each(['/v1/mpp/validate', '/v1/mpp/broadcast'])( '%s rejects malformed credential input before relay processing', async (path) => { const app = TestApp.create({ auth: { keys: [key] } }) const response = await app.fetch( new Request(`http://tempo-api.test${path}`, { body: JSON.stringify({}), headers: { 'content-type': 'application/json', authorization: 'Bearer secret_mpp' }, method: 'POST', }), ) expect(response.status).toBe(400) expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } }) }, ) test.each(['/v1/mpp/validate', '/v1/mpp/broadcast'])( '%s requires a credential payload', async (path) => { const app = TestApp.create({ auth: { keys: [key] } }) const response = await app.fetch( new Request(`http://tempo-api.test${path}`, { body: JSON.stringify({ challenge: input.challenge }), headers: { 'content-type': 'application/json', authorization: 'Bearer secret_mpp' }, method: 'POST', }), ) expect(response.status).toBe(400) expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } }) }, ) test('publishes the MPP endpoints', async () => { const app = TestApp.create() const spec = (await (await app.request('/openapi.json')).json()) as { paths: Record< string, { post?: { description?: string parameters?: readonly { in?: string; name?: string }[] responses?: Record } } > tags?: readonly { description?: string; name?: string }[] } expect(spec.tags).toContainEqual(expect.objectContaining({ name: 'MPP' })) for (const path of ['/v1/mpp/validate', '/v1/mpp/broadcast']) { expect(spec.paths[path]?.post?.responses?.['200']).toBeDefined() expect(spec.paths[path]?.post?.responses?.['503']).toBeUndefined() } expect(spec.paths['/v1/mpp/validate']?.post?.description).toContain( 'It does not settle, broadcast, reserve funds, or consume the credential.', ) expect(spec.paths['/v1/mpp/broadcast']?.post?.description).toContain('Idempotency-Key') const response = JSON.stringify(spec.paths['/v1/mpp/validate']?.post?.responses?.['200']) for (const description of errorDescriptions) expect(response).toContain(description) expect(spec.paths['/v1/mpp/broadcast']?.post?.parameters).toContainEqual({ description: 'Optional opaque retry key, scoped to the API key and credential. Reusing it after a successful broadcast returns the original receipt without another submission. Reusing it with a different credential returns `invalid_payment`. While the first request is running, duplicates return `temporarily_unavailable`. Failed attempts are not retained and may be retried with the same key.', in: 'header', name: 'Idempotency-Key', required: false, schema: { example: idempotencyKey, type: 'string' }, }) }) }) type CredentialPayload = { hash?: string; type?: unknown } async function credential(options: credential.Options = {}): Promise { const issuer = createIssuer(options) const challenge = await issuer.challenge.tempo.charge({ amount: options.amount ?? '1' }) const authorization = await createClient(options).createCredential( new Response(null, { headers: { 'www-authenticate': Mppx.Challenge.serialize(challenge) }, status: 402, }), ) return Mppx.Credential.deserialize(authorization) } declare namespace credential { type Options = { amount?: string | undefined feePayer?: boolean | undefined mode?: 'pull' | 'push' | undefined supportedModes?: ('pull' | 'push')[] | undefined } } async function credentialFromServer(): Promise { const issuer = createIssuer() const server = await startServer( MppxServer.toNodeListener(issuer.charge({ amount: '1' })) as http.RequestListener, ) try { const paymentRequired = await fetch(server.url) expect(paymentRequired.status).toBe(402) return Mppx.Credential.deserialize(await createClient().createCredential(paymentRequired)) } finally { await server.close() } } function createClient(options: credential.Options = {}) { return MppxClient.create({ methods: [ tempoClient({ account: Tempo.accounts[1], getClient: () => Tempo.client, ...(options.mode === undefined ? {} : { mode: options.mode }), }), ], polyfill: false, }) } function createIssuer(options: credential.Options = {}) { return MppxServer.create({ methods: [ tempoServer.charge({ account: Tempo.accounts[0], currency: Tempo.currency, ...(options.feePayer ? { feePayer: true } : {}), getClient: () => Tempo.client, ...(options.supportedModes === undefined ? {} : { supportedModes: options.supportedModes }), }), ], realm: 'merchant.example', secretKey: 'test-mpp-secret-key-for-payment-sessions', }) } function credentialHash(input: Mpp.RelayInput) { const payload = input.payload as CredentialPayload if (payload.type !== 'hash' || typeof payload.hash !== 'string') throw new Error('Expected a pushed hash credential.') return payload.hash } function payloadType(input: Mpp.RelayInput) { return (input.payload as CredentialPayload).type } function post( app: { fetch: (request: Request) => Response | Promise }, path: string, input: Mpp.RelayInput, options: post.Options = {}, ) { return app.fetch( new Request(`http://tempo-api.test${path}`, { body: JSON.stringify(input), headers: { 'content-type': 'application/json', authorization: `Bearer ${options.apiKey ?? key.token}`, ...(options.idempotencyKey ? { 'idempotency-key': options.idempotencyKey } : {}), }, method: 'POST', }), ) } declare namespace post { type Options = { apiKey?: string | undefined idempotencyKey?: string | undefined } } function postHttp(url: string, path: string, input: Mpp.RelayInput, options: post.Options = {}) { return fetch(new URL(path, url), { body: JSON.stringify(input), headers: { 'content-type': 'application/json', authorization: `Bearer ${key.token}`, ...(options.idempotencyKey ? { 'idempotency-key': options.idempotencyKey } : {}), }, method: 'POST', }) } async function startServer(listener: http.RequestListener): Promise { const server = http.createServer(listener) await new Promise((resolve, reject) => { server.once('error', reject) server.listen(0, '127.0.0.1', () => { server.off('error', reject) resolve() }) }) const address = server.address() if (!address || typeof address === 'string') { await closeServer(server) throw new Error('Expected a TCP address.') } return { close: () => closeServer(server), url: `http://127.0.0.1:${address.port}` } } function closeServer(server: http.Server) { return new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())) }) } type Server = { close(): Promise url: string } describe('MPP relay schemas', () => { test('parses public relay contracts', () => { expect(Mpp.schema.RelayInput.parse(input)).toEqual(input) expect(Mpp.schema.VerifyResponse.parse({ success: true })).toEqual({ success: true }) expect( Mpp.schema.BroadcastResponse.parse({ receipt: { method: 'tempo', reference: `0x${'22'.repeat(32)}`, timestamp: '2026-07-20T18:30:00.000Z', }, success: true, }), ).toMatchInlineSnapshot(` { "receipt": { "method": "tempo", "reference": "0x2222222222222222222222222222222222222222222222222222222222222222", "timestamp": "2026-07-20T18:30:00.000Z", }, "success": true, } `) }) test.each(errorCodes)('accepts the %s error code', (code) => { expect(Mpp.schema.RelayError.parse({ code })).toEqual({ code }) }) test('publishes the relay error code type', () => { expectTypeOf().toEqualTypeOf<(typeof errorCodes)[number]>() }) test('rejects an unknown error code', () => { expect(Mpp.schema.RelayError.safeParse({ code: 'unexpected' }).success).toBe(false) }) })