mppx 0.8.14 → 0.8.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +40 -35
- package/dist/Challenge.js.map +1 -1
- package/dist/Constants.d.ts +17 -17
- package/dist/Constants.d.ts.map +1 -1
- package/dist/Credential.d.ts.map +1 -1
- package/dist/Errors.d.ts +3 -3
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js.map +1 -1
- package/dist/Method.d.ts +35 -5
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +7 -22
- package/dist/Method.js.map +1 -1
- package/dist/Store.d.ts +19 -0
- package/dist/Store.d.ts.map +1 -1
- package/dist/Store.js +43 -0
- package/dist/Store.js.map +1 -1
- package/dist/attestation/Client.d.ts +17 -0
- package/dist/attestation/Client.d.ts.map +1 -0
- package/dist/attestation/Client.js +28 -0
- package/dist/attestation/Client.js.map +1 -0
- package/dist/attestation/Constants.d.ts +15 -0
- package/dist/attestation/Constants.d.ts.map +1 -0
- package/dist/attestation/Constants.js +15 -0
- package/dist/attestation/Constants.js.map +1 -0
- package/dist/attestation/Server.d.ts +4 -0
- package/dist/attestation/Server.d.ts.map +1 -0
- package/dist/attestation/Server.js +8 -0
- package/dist/attestation/Server.js.map +1 -0
- package/dist/attestation/Types.d.ts +76 -0
- package/dist/attestation/Types.d.ts.map +1 -0
- package/dist/attestation/Types.js +2 -0
- package/dist/attestation/Types.js.map +1 -0
- package/dist/attestation/index.d.ts +6 -0
- package/dist/attestation/index.d.ts.map +1 -0
- package/dist/attestation/index.js +6 -0
- package/dist/attestation/index.js.map +1 -0
- package/dist/attestation/internal/HttpMessageSignature.d.ts +73 -0
- package/dist/attestation/internal/HttpMessageSignature.d.ts.map +1 -0
- package/dist/attestation/internal/HttpMessageSignature.js +320 -0
- package/dist/attestation/internal/HttpMessageSignature.js.map +1 -0
- package/dist/attestation/internal/SigningContext.d.ts +4 -0
- package/dist/attestation/internal/SigningContext.d.ts.map +1 -0
- package/dist/attestation/internal/SigningContext.js +11 -0
- package/dist/attestation/internal/SigningContext.js.map +1 -0
- package/dist/cli/account.d.ts +14 -14
- package/dist/cli/account.d.ts.map +1 -1
- package/dist/cli/cli.d.ts +6 -6
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +1 -0
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/cli/sessions/commands.d.ts +4 -4
- package/dist/cli/sessions/commands.d.ts.map +1 -1
- package/dist/cli/sessions/commands.js.map +1 -1
- package/dist/cli/sessions/store.d.ts.map +1 -1
- package/dist/cli/sessions/store.js.map +1 -1
- package/dist/cli/utils.d.ts +1 -1
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/validate/discovery.js.map +1 -1
- package/dist/cli/validate/index.d.ts +3 -3
- package/dist/cli/validate/index.d.ts.map +1 -1
- package/dist/cli/validate/payment.d.ts.map +1 -1
- package/dist/cli/validate/payment.js +18 -4
- package/dist/cli/validate/payment.js.map +1 -1
- package/dist/client/Mppx.d.ts +3 -0
- package/dist/client/Mppx.d.ts.map +1 -1
- package/dist/client/Mppx.js +13 -5
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/Transport.d.ts +3 -5
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +1 -17
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +92 -13
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/MethodChallenge.d.ts +2 -0
- package/dist/client/internal/MethodChallenge.d.ts.map +1 -1
- package/dist/client/internal/MethodChallenge.js +4 -0
- package/dist/client/internal/MethodChallenge.js.map +1 -1
- package/dist/client/internal/MethodResponse.d.ts +15 -0
- package/dist/client/internal/MethodResponse.d.ts.map +1 -1
- package/dist/client/internal/MethodResponse.js +10 -0
- package/dist/client/internal/MethodResponse.js.map +1 -1
- package/dist/client/node.js.map +1 -1
- package/dist/discovery/OpenApi.d.ts +3 -5
- package/dist/discovery/OpenApi.d.ts.map +1 -1
- package/dist/discovery/OpenApi.js +13 -34
- package/dist/discovery/OpenApi.js.map +1 -1
- package/dist/discovery/internal/Metadata.d.ts +20 -0
- package/dist/discovery/internal/Metadata.d.ts.map +1 -0
- package/dist/discovery/internal/Metadata.js +36 -0
- package/dist/discovery/internal/Metadata.js.map +1 -0
- package/dist/evm/Methods.d.ts +11 -11
- package/dist/evm/Methods.d.ts.map +1 -1
- package/dist/evm/Types.d.ts +17 -17
- package/dist/evm/Types.d.ts.map +1 -1
- package/dist/evm/client/Charge.d.ts +11 -11
- package/dist/evm/client/Methods.d.ts +11 -11
- package/dist/evm/server/Charge.d.ts +1 -1
- package/dist/evm/server/Charge.d.ts.map +1 -1
- package/dist/evm/server/Charge.js +2 -0
- package/dist/evm/server/Charge.js.map +1 -1
- package/dist/evm/server/Methods.d.ts +11 -11
- package/dist/internal/HeaderCodec.d.ts +2 -2
- package/dist/internal/HeaderCodec.d.ts.map +1 -1
- package/dist/internal/types.d.ts +6 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/middlewares/elysia.js.map +1 -1
- package/dist/middlewares/express.js.map +1 -1
- package/dist/middlewares/hono.js.map +1 -1
- package/dist/middlewares/nextjs.js.map +1 -1
- package/dist/proxy/Proxy.d.ts +2 -0
- package/dist/proxy/Proxy.d.ts.map +1 -1
- package/dist/proxy/Proxy.js +117 -9
- package/dist/proxy/Proxy.js.map +1 -1
- package/dist/proxy/Service.d.ts +36 -1
- package/dist/proxy/Service.d.ts.map +1 -1
- package/dist/proxy/Service.js +9 -8
- package/dist/proxy/Service.js.map +1 -1
- package/dist/proxy/services/anthropic.d.ts.map +1 -1
- package/dist/proxy/services/anthropic.js +1 -0
- package/dist/proxy/services/anthropic.js.map +1 -1
- package/dist/proxy/services/openai.d.ts.map +1 -1
- package/dist/proxy/services/openai.js +1 -0
- package/dist/proxy/services/openai.js.map +1 -1
- package/dist/proxy/services/stripe.d.ts.map +1 -1
- package/dist/proxy/services/stripe.js +1 -0
- package/dist/proxy/services/stripe.js.map +1 -1
- package/dist/server/Mppx.d.ts +75 -13
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +201 -23
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/internal/html/config.d.ts +9 -9
- package/dist/server/internal/html/config.d.ts.map +1 -1
- package/dist/server/internal/html/constants.d.ts +17 -17
- package/dist/server/internal/html/constants.d.ts.map +1 -1
- package/dist/stripe/Methods.d.ts +10 -10
- package/dist/stripe/Methods.d.ts.map +1 -1
- package/dist/stripe/client/Charge.d.ts +10 -10
- package/dist/stripe/client/Methods.d.ts +10 -10
- package/dist/stripe/internal/constants.d.ts +1 -1
- package/dist/stripe/internal/constants.js +1 -1
- package/dist/stripe/internal/types.d.ts +1 -0
- package/dist/stripe/internal/types.d.ts.map +1 -1
- package/dist/stripe/server/Charge.d.ts +11 -11
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +49 -1
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/Methods.d.ts +137 -48
- package/dist/stripe/server/Methods.d.ts.map +1 -1
- package/dist/stripe/server/Methods.js +259 -4
- package/dist/stripe/server/Methods.js.map +1 -1
- package/dist/stripe/server/internal/deposit-address.d.ts +13 -0
- package/dist/stripe/server/internal/deposit-address.d.ts.map +1 -0
- package/dist/stripe/server/internal/deposit-address.js +28 -0
- package/dist/stripe/server/internal/deposit-address.js.map +1 -0
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/stripe/server/internal/record-payment.d.ts +15 -0
- package/dist/stripe/server/internal/record-payment.d.ts.map +1 -0
- package/dist/stripe/server/internal/record-payment.js +42 -0
- package/dist/stripe/server/internal/record-payment.js.map +1 -0
- package/dist/stripe/server/internal/request.d.ts +25 -0
- package/dist/stripe/server/internal/request.d.ts.map +1 -0
- package/dist/stripe/server/internal/request.js +29 -0
- package/dist/stripe/server/internal/request.js.map +1 -0
- package/dist/tap/Constants.d.ts +28 -0
- package/dist/tap/Constants.d.ts.map +1 -0
- package/dist/tap/Constants.js +32 -0
- package/dist/tap/Constants.js.map +1 -0
- package/dist/tap/Types.d.ts +11 -0
- package/dist/tap/Types.d.ts.map +1 -0
- package/dist/tap/Types.js +2 -0
- package/dist/tap/Types.js.map +1 -0
- package/dist/tap/client/index.d.ts +26 -0
- package/dist/tap/client/index.d.ts.map +1 -0
- package/dist/tap/client/index.js +33 -0
- package/dist/tap/client/index.js.map +1 -0
- package/dist/tap/index.d.ts +5 -0
- package/dist/tap/index.d.ts.map +1 -0
- package/dist/tap/index.js +5 -0
- package/dist/tap/index.js.map +1 -0
- package/dist/tap/server/index.d.ts +20 -0
- package/dist/tap/server/index.d.ts.map +1 -0
- package/dist/tap/server/index.js +36 -0
- package/dist/tap/server/index.js.map +1 -0
- package/dist/tempo/Attribution.d.ts +1 -1
- package/dist/tempo/Methods.d.ts +60 -60
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Proof.d.ts +16 -16
- package/dist/tempo/client/Charge.d.ts +19 -19
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +1 -7
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +38 -38
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/Subscription.d.ts +23 -23
- package/dist/tempo/client/Subscription.js.map +1 -1
- package/dist/tempo/internal/address.d.ts +2 -1
- package/dist/tempo/internal/address.d.ts.map +1 -1
- package/dist/tempo/internal/address.js +3 -1
- package/dist/tempo/internal/address.js.map +1 -1
- package/dist/tempo/internal/auto-swap.d.ts.map +1 -1
- package/dist/tempo/internal/auto-swap.js +4 -19
- package/dist/tempo/internal/auto-swap.js.map +1 -1
- package/dist/tempo/internal/defaults.d.ts +6 -6
- package/dist/tempo/internal/defaults.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +12 -12
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/internal/proof.d.ts +17 -17
- package/dist/tempo/internal/proof.d.ts.map +1 -1
- package/dist/tempo/legacy/client/Session.d.ts +20 -20
- package/dist/tempo/legacy/client/Session.js.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
- package/dist/tempo/legacy/index.d.ts +1 -3
- package/dist/tempo/legacy/index.d.ts.map +1 -1
- package/dist/tempo/legacy/index.js +1 -3
- package/dist/tempo/legacy/index.js.map +1 -1
- package/dist/tempo/legacy/session/escrow.abi.d.ts +374 -374
- package/dist/tempo/legacy/session/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/server/Charge.d.ts +34 -12
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +44 -39
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +37 -52
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +0 -14
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Relay.d.ts +14 -5
- package/dist/tempo/server/Relay.d.ts.map +1 -1
- package/dist/tempo/server/Relay.js +6 -4
- package/dist/tempo/server/Relay.js.map +1 -1
- package/dist/tempo/server/SponsorBudget.d.ts.map +1 -1
- package/dist/tempo/server/SponsorBudget.js +7 -1
- package/dist/tempo/server/SponsorBudget.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +25 -25
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js +2 -0
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/index.d.ts +1 -1
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js +1 -1
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/server/internal/transport.d.ts +2 -0
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +9 -2
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/CredentialState.d.ts +1 -1
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -1
- package/dist/tempo/session/client/Runtime.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +19 -19
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +128 -14
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +62 -20
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/Transports.d.ts +9 -1
- package/dist/tempo/session/client/Transports.d.ts.map +1 -1
- package/dist/tempo/session/client/Transports.js +36 -13
- package/dist/tempo/session/client/Transports.js.map +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts +27 -0
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Chain.js +68 -7
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/tempo/session/precompile/Protocol.js.map +1 -1
- package/dist/tempo/session/precompile/Voucher.d.ts +4 -4
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
- package/dist/tempo/session/precompile/escrow.abi.d.ts +306 -306
- package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/session/server/ChannelStore.d.ts +9 -0
- package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -1
- package/dist/tempo/session/server/ChannelStore.js +16 -8
- package/dist/tempo/session/server/ChannelStore.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +30 -13
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +207 -13
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/MeteredStream.d.ts +3 -0
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
- package/dist/tempo/session/server/MeteredStream.js +3 -1
- package/dist/tempo/session/server/MeteredStream.js.map +1 -1
- package/dist/tempo/session/server/Session.d.ts +32 -21
- package/dist/tempo/session/server/Session.d.ts.map +1 -1
- package/dist/tempo/session/server/Session.js +116 -53
- package/dist/tempo/session/server/Session.js.map +1 -1
- package/dist/tempo/session/server/Sse.d.ts +3 -1
- package/dist/tempo/session/server/Sse.d.ts.map +1 -1
- package/dist/tempo/session/server/Sse.js +2 -1
- package/dist/tempo/session/server/Sse.js.map +1 -1
- package/dist/tempo/session/server/Transports.d.ts +1 -1
- package/dist/tempo/session/server/Transports.d.ts.map +1 -1
- package/dist/tempo/session/server/Transports.js +1 -0
- package/dist/tempo/session/server/Transports.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts +6 -1
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js +4 -0
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +38 -866
- package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.js +14 -3
- package/dist/tempo/subscription/KeyAuthorization.js.map +1 -1
- package/dist/viem/Account.d.ts.map +1 -1
- package/dist/viem/Account.js.map +1 -1
- package/dist/viem/Client.d.ts.map +1 -1
- package/dist/viem/Client.js +6 -1
- package/dist/viem/Client.js.map +1 -1
- package/dist/web-bot-auth/Constants.d.ts +20 -0
- package/dist/web-bot-auth/Constants.d.ts.map +1 -0
- package/dist/web-bot-auth/Constants.js +24 -0
- package/dist/web-bot-auth/Constants.js.map +1 -0
- package/dist/web-bot-auth/Types.d.ts +10 -0
- package/dist/web-bot-auth/Types.d.ts.map +1 -0
- package/dist/web-bot-auth/Types.js +2 -0
- package/dist/web-bot-auth/Types.js.map +1 -0
- package/dist/web-bot-auth/client/index.d.ts +29 -0
- package/dist/web-bot-auth/client/index.d.ts.map +1 -0
- package/dist/web-bot-auth/client/index.js +64 -0
- package/dist/web-bot-auth/client/index.js.map +1 -0
- package/dist/web-bot-auth/index.d.ts +7 -0
- package/dist/web-bot-auth/index.d.ts.map +1 -0
- package/dist/web-bot-auth/index.js +7 -0
- package/dist/web-bot-auth/index.js.map +1 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.d.ts +5 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.d.ts.map +1 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.js +20 -0
- package/dist/web-bot-auth/internal/JwkThumbprint.js.map +1 -0
- package/dist/web-bot-auth/internal/SignatureAgent.d.ts +3 -0
- package/dist/web-bot-auth/internal/SignatureAgent.d.ts.map +1 -0
- package/dist/web-bot-auth/internal/SignatureAgent.js +18 -0
- package/dist/web-bot-auth/internal/SignatureAgent.js.map +1 -0
- package/dist/web-bot-auth/server/index.d.ts +35 -0
- package/dist/web-bot-auth/server/index.d.ts.map +1 -0
- package/dist/web-bot-auth/server/index.js +88 -0
- package/dist/web-bot-auth/server/index.js.map +1 -0
- package/dist/x402/Assets.d.ts.map +1 -1
- package/dist/x402/Types.d.ts +6 -6
- package/dist/x402/Types.d.ts.map +1 -1
- package/dist/x402/server/EvmCharge.d.ts.map +1 -1
- package/dist/x402/server/EvmCharge.js +32 -20
- package/dist/x402/server/EvmCharge.js.map +1 -1
- package/package.json +20 -4
- package/src/Challenge.fuzz.test.ts +80 -47
- package/src/Challenge.test.ts +65 -0
- package/src/Challenge.ts +39 -35
- package/src/Method.ts +82 -68
- package/src/Store.test-d.ts +16 -0
- package/src/Store.test.ts +50 -0
- package/src/Store.ts +68 -0
- package/src/attestation/Attestation.integration.test.ts +208 -0
- package/src/attestation/Client.test.ts +83 -0
- package/src/attestation/Client.ts +34 -0
- package/src/attestation/Constants.ts +15 -0
- package/src/attestation/Server.test.ts +60 -0
- package/src/attestation/Server.ts +12 -0
- package/src/attestation/Types.test-d.ts +54 -0
- package/src/attestation/Types.ts +77 -0
- package/src/attestation/index.ts +5 -0
- package/src/attestation/internal/HttpMessageSignature.test.ts +101 -0
- package/src/attestation/internal/HttpMessageSignature.ts +429 -0
- package/src/attestation/internal/SigningContext.ts +13 -0
- package/src/bin.js +2 -0
- package/src/cli/cli.ts +1 -0
- package/src/cli/validate/payment.ts +18 -4
- package/src/cli/validate.test.ts +16 -0
- package/src/client/Mppx.test-d.ts +17 -0
- package/src/client/Mppx.test.ts +14 -14
- package/src/client/Mppx.ts +25 -4
- package/src/client/Transport.test.ts +12 -45
- package/src/client/Transport.ts +3 -24
- package/src/client/internal/Fetch.test.ts +77 -0
- package/src/client/internal/Fetch.ts +112 -14
- package/src/client/internal/MethodChallenge.ts +5 -0
- package/src/client/internal/MethodResponse.ts +28 -0
- package/src/discovery/OpenApi.test.ts +170 -0
- package/src/discovery/OpenApi.ts +16 -48
- package/src/discovery/internal/Metadata.ts +51 -0
- package/src/evm/server/Charge.ts +6 -1
- package/src/internal/AcceptPayment.test.ts +12 -8
- package/src/internal/types.ts +9 -0
- package/src/mcp/client/McpClient.integration.test.ts +19 -32
- package/src/middlewares/elysia.test.ts +2 -12
- package/src/middlewares/express.test.ts +2 -12
- package/src/middlewares/hono.test.ts +6 -9
- package/src/middlewares/nextjs.test.ts +2 -12
- package/src/proxy/Proxy.test.ts +26 -19
- package/src/proxy/Proxy.ts +147 -9
- package/src/proxy/Proxy.upstreamError.test.ts +364 -0
- package/src/proxy/Service.test.ts +73 -3
- package/src/proxy/Service.ts +50 -23
- package/src/proxy/services/anthropic.ts +1 -0
- package/src/proxy/services/hooks.test-d.ts +14 -0
- package/src/proxy/services/hooks.test.ts +44 -0
- package/src/proxy/services/openai.ts +1 -0
- package/src/proxy/services/stripe.ts +1 -0
- package/src/server/Methods.test-d.ts +88 -0
- package/src/server/Methods.test.ts +69 -0
- package/src/server/Mppx.test-d.ts +141 -7
- package/src/server/Mppx.test.ts +1017 -119
- package/src/server/Mppx.ts +350 -54
- package/src/stripe/internal/constants.ts +1 -1
- package/src/stripe/internal/types.ts +1 -0
- package/src/stripe/server/Charge.test-d.ts +16 -1
- package/src/stripe/server/Charge.test.ts +79 -0
- package/src/stripe/server/Charge.ts +54 -1
- package/src/stripe/server/Methods.test-d.ts +157 -0
- package/src/stripe/server/Methods.test.ts +403 -0
- package/src/stripe/server/Methods.ts +423 -6
- package/src/stripe/server/internal/deposit-address.ts +57 -0
- package/src/stripe/server/internal/html/node_modules/.bin/mppx +3 -3
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/stripe/server/internal/record-payment.ts +66 -0
- package/src/stripe/server/internal/request.ts +49 -0
- package/src/tap/Constants.ts +32 -0
- package/src/tap/Tap.test.ts +125 -0
- package/src/tap/Types.ts +11 -0
- package/src/tap/client/index.ts +54 -0
- package/src/tap/index.ts +4 -0
- package/src/tap/server/index.ts +48 -0
- package/src/tempo/PublicExports.test-d.ts +3 -3
- package/src/tempo/client/Charge.ts +1 -19
- package/src/tempo/internal/address.test.ts +42 -0
- package/src/tempo/internal/address.ts +3 -3
- package/src/tempo/internal/auto-swap.test.ts +4 -4
- package/src/tempo/internal/auto-swap.ts +7 -50
- package/src/tempo/legacy/README.md +5 -5
- package/src/tempo/legacy/index.ts +1 -3
- package/src/tempo/server/AtomicStore.test-d.ts +13 -0
- package/src/tempo/server/Charge.test.ts +19 -84
- package/src/tempo/server/Charge.ts +92 -56
- package/src/tempo/server/Methods.ts +0 -20
- package/src/tempo/server/Relay.test.ts +161 -7
- package/src/tempo/server/Relay.ts +15 -6
- package/src/tempo/server/SponsorBudget.test.ts +29 -0
- package/src/tempo/server/SponsorBudget.ts +7 -1
- package/src/tempo/server/Subscription.ts +4 -1
- package/src/tempo/server/index.ts +1 -1
- package/src/tempo/server/internal/html/node_modules/.bin/mppx +3 -3
- package/src/tempo/server/internal/html/package.json +1 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/transport.test.ts +22 -3
- package/src/tempo/server/internal/transport.ts +15 -3
- package/src/tempo/session/README.md +119 -0
- package/src/tempo/session/client/Session.test.ts +299 -33
- package/src/tempo/session/client/Session.ts +152 -16
- package/src/tempo/session/client/SessionManager.test.ts +216 -9
- package/src/tempo/session/client/SessionManager.ts +63 -17
- package/src/tempo/session/client/Transports.test.ts +22 -0
- package/src/tempo/session/client/Transports.ts +58 -22
- package/src/tempo/session/precompile/Chain.ts +131 -11
- package/src/tempo/session/server/ChannelStore.ts +33 -12
- package/src/tempo/session/server/CredentialVerification.test.ts +4 -4
- package/src/tempo/session/server/CredentialVerification.ts +340 -28
- package/src/tempo/session/server/MeteredStream.ts +5 -1
- package/src/tempo/session/server/Session.test.ts +483 -36
- package/src/tempo/session/server/Session.ts +159 -60
- package/src/tempo/session/server/Sse.test.ts +77 -0
- package/src/tempo/session/server/Sse.ts +8 -1
- package/src/tempo/session/server/Transports.ts +2 -1
- package/src/tempo/session/server/Ws.test.ts +171 -0
- package/src/tempo/session/server/Ws.ts +10 -1
- package/src/tempo/subscription/KeyAuthorization.test.ts +70 -1
- package/src/tempo/subscription/KeyAuthorization.ts +14 -3
- package/src/viem/Client.test.ts +18 -1
- package/src/viem/Client.ts +7 -1
- package/src/web-bot-auth/Constants.ts +24 -0
- package/src/web-bot-auth/Types.ts +9 -0
- package/src/web-bot-auth/WebBotAuth.test.ts +182 -0
- package/src/web-bot-auth/client/index.ts +84 -0
- package/src/web-bot-auth/index.ts +6 -0
- package/src/web-bot-auth/internal/JwkThumbprint.ts +21 -0
- package/src/web-bot-auth/internal/SignatureAgent.ts +18 -0
- package/src/web-bot-auth/server/index.ts +113 -0
- package/src/x402/Exact.e2e.test.ts +121 -81
- package/src/x402/Exact.localnet.test.ts +1 -1
- package/src/x402/server/EvmCharge.ts +35 -19
- package/dist/tempo/legacy/server/Session.d.ts +0 -208
- package/dist/tempo/legacy/server/Session.d.ts.map +0 -1
- package/dist/tempo/legacy/server/Session.js +0 -698
- package/dist/tempo/legacy/server/Session.js.map +0 -1
- package/dist/tempo/legacy/server/index.d.ts +0 -5
- package/dist/tempo/legacy/server/index.d.ts.map +0 -1
- package/dist/tempo/legacy/server/index.js +0 -5
- package/dist/tempo/legacy/server/index.js.map +0 -1
- package/dist/tempo/legacy/session/ChannelStore.d.ts +0 -22
- package/dist/tempo/legacy/session/ChannelStore.d.ts.map +0 -1
- package/dist/tempo/legacy/session/ChannelStore.js +0 -6
- package/dist/tempo/legacy/session/ChannelStore.js.map +0 -1
- package/src/tempo/legacy/server/Defaults.test-d.ts +0 -9
- package/src/tempo/legacy/server/Session.test.ts +0 -6370
- package/src/tempo/legacy/server/Session.ts +0 -1024
- package/src/tempo/legacy/server/index.ts +0 -4
- package/src/tempo/legacy/session/ChannelStore.test.ts +0 -58
- package/src/tempo/legacy/session/ChannelStore.ts +0 -39
|
@@ -1,1024 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-side session payment method for request/response flows.
|
|
3
|
-
*
|
|
4
|
-
* Handles the full channel lifecycle (open, voucher, top-up, close) and
|
|
5
|
-
* one-shot settlement. Each incoming request carries a session credential
|
|
6
|
-
* with a cumulative voucher that the server validates and records.
|
|
7
|
-
*
|
|
8
|
-
* Use `session()` for standard HTTP request/response patterns where each
|
|
9
|
-
* request is a discrete paid unit (for example, a page scrape or API call).
|
|
10
|
-
* For long-lived connections that emit multiple paid events over a single
|
|
11
|
-
* request, use {@link ../session/Sse} instead.
|
|
12
|
-
*/
|
|
13
|
-
import {
|
|
14
|
-
type Address,
|
|
15
|
-
type Hex,
|
|
16
|
-
parseUnits,
|
|
17
|
-
zeroAddress,
|
|
18
|
-
type Account as viem_Account,
|
|
19
|
-
type Client as viem_Client,
|
|
20
|
-
} from 'viem'
|
|
21
|
-
import { tempo as tempo_chain } from 'viem/tempo/chains'
|
|
22
|
-
|
|
23
|
-
import * as Constants from '../../../Constants.js'
|
|
24
|
-
import {
|
|
25
|
-
AmountExceedsDepositError,
|
|
26
|
-
BadRequestError,
|
|
27
|
-
ChannelClosedError,
|
|
28
|
-
ChannelNotFoundError,
|
|
29
|
-
DeltaTooSmallError,
|
|
30
|
-
InsufficientBalanceError,
|
|
31
|
-
InvalidSignatureError,
|
|
32
|
-
VerificationFailedError,
|
|
33
|
-
} from '../../../Errors.js'
|
|
34
|
-
import type { Challenge, Credential } from '../../../index.js'
|
|
35
|
-
import type { LooseOmit, NoExtraKeys } from '../../../internal/types.js'
|
|
36
|
-
import * as Method from '../../../Method.js'
|
|
37
|
-
import * as Store from '../../../Store.js'
|
|
38
|
-
import * as Client from '../../../viem/Client.js'
|
|
39
|
-
import type * as z from '../../../zod.js'
|
|
40
|
-
import * as Account from '../../internal/account.js'
|
|
41
|
-
import * as defaults from '../../internal/defaults.js'
|
|
42
|
-
import * as FeePayer from '../../internal/fee-payer.js'
|
|
43
|
-
import type * as types from '../../internal/types.js'
|
|
44
|
-
import * as Methods from '../../Methods.js'
|
|
45
|
-
import {
|
|
46
|
-
captureRequestBodyProbe,
|
|
47
|
-
isSessionContentRequest,
|
|
48
|
-
} from '../../server/internal/request-body.js'
|
|
49
|
-
import * as Transport from '../../server/internal/transport.js'
|
|
50
|
-
import { createSessionReceipt } from '../../session/precompile/Protocol.js'
|
|
51
|
-
import type { SessionReceipt } from '../../session/precompile/Protocol.js'
|
|
52
|
-
import {
|
|
53
|
-
broadcastOpenTransaction,
|
|
54
|
-
broadcastTopUpTransaction,
|
|
55
|
-
closeOnChain,
|
|
56
|
-
getOnChainChannel,
|
|
57
|
-
type OnChainChannel,
|
|
58
|
-
settleOnChain,
|
|
59
|
-
} from '../session/Chain.js'
|
|
60
|
-
import * as ChannelStore from '../session/ChannelStore.js'
|
|
61
|
-
import type { LegacySessionCredentialPayload, LegacySignedVoucher } from '../session/Types.js'
|
|
62
|
-
import { parseVoucherFromPayload, verifyVoucher } from '../session/Voucher.js'
|
|
63
|
-
|
|
64
|
-
/** Challenge methodDetails shape for session methods. */
|
|
65
|
-
type SessionMethodDetails = {
|
|
66
|
-
escrowContract: Address
|
|
67
|
-
chainId: number
|
|
68
|
-
channelId?: Hex | undefined
|
|
69
|
-
minVoucherDelta?: string | undefined
|
|
70
|
-
feePayer?: boolean | undefined
|
|
71
|
-
sessionProtocol?: Constants.SessionProtocol | undefined
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Creates a session payment server using the Method.toServer() pattern.
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```ts
|
|
79
|
-
* import { Mppx, tempo } from 'mppx/server'
|
|
80
|
-
*
|
|
81
|
-
* const mppx = Mppx.create({
|
|
82
|
-
* methods: [
|
|
83
|
-
* tempo.session({
|
|
84
|
-
* store: myStore,
|
|
85
|
-
* recipient: '0x...',
|
|
86
|
-
* currency: '0x...',
|
|
87
|
-
* escrowContract: '0x...',
|
|
88
|
-
* }),
|
|
89
|
-
* ],
|
|
90
|
-
* realm: 'my-app',
|
|
91
|
-
* secretKey: '...',
|
|
92
|
-
* })
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
export function session<const parameters extends session.Parameters>(
|
|
96
|
-
p?: NoExtraKeys<parameters, session.Parameters>,
|
|
97
|
-
) {
|
|
98
|
-
const parameters = p as parameters
|
|
99
|
-
const {
|
|
100
|
-
amount,
|
|
101
|
-
channelStateTtl = 5_000,
|
|
102
|
-
currency = defaults.resolveCurrency(parameters),
|
|
103
|
-
decimals = defaults.decimals,
|
|
104
|
-
feePayerPolicy,
|
|
105
|
-
store: rawStore = Store.memory(),
|
|
106
|
-
suggestedDeposit,
|
|
107
|
-
unitType,
|
|
108
|
-
waitForConfirmation = true,
|
|
109
|
-
} = parameters
|
|
110
|
-
|
|
111
|
-
const lastOnChainVerified = new Map<Hex, number>()
|
|
112
|
-
|
|
113
|
-
const store = ChannelStore.fromStore(
|
|
114
|
-
Store.from(rawStore, { keyPrefix: parameters.storeKeyPrefix }),
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
const { account, recipient, feePayer, feePayerUrl } = Account.resolve(parameters)
|
|
118
|
-
|
|
119
|
-
if (!account)
|
|
120
|
-
throw new Error(
|
|
121
|
-
'tempo.session() requires an `account` (viem Account, e.g. privateKeyToAccount("0x...")). An address string is not sufficient — the server needs a signing account for on-chain channel close and settlement.',
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
const getClient = Client.getResolver({
|
|
125
|
-
chain: tempo_chain,
|
|
126
|
-
feePayerUrl,
|
|
127
|
-
getClient: parameters.getClient,
|
|
128
|
-
rpcUrl: defaults.rpcUrl,
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
type Transport = parameters['sse'] extends false | undefined ? undefined : Transport.Sse
|
|
132
|
-
const transport = parameters.sse
|
|
133
|
-
? Transport.sse({
|
|
134
|
-
store,
|
|
135
|
-
...(typeof parameters.sse === 'object' ? parameters.sse : undefined),
|
|
136
|
-
})
|
|
137
|
-
: undefined
|
|
138
|
-
|
|
139
|
-
type Defaults = session.DeriveDefaults<parameters>
|
|
140
|
-
return Method.toServer<typeof Methods.session, Defaults, Transport>(Methods.session, {
|
|
141
|
-
defaults: {
|
|
142
|
-
amount,
|
|
143
|
-
currency,
|
|
144
|
-
decimals,
|
|
145
|
-
recipient,
|
|
146
|
-
suggestedDeposit,
|
|
147
|
-
unitType,
|
|
148
|
-
} as unknown as Defaults,
|
|
149
|
-
|
|
150
|
-
transport,
|
|
151
|
-
|
|
152
|
-
// TODO: dedupe `{charge,session}.request`
|
|
153
|
-
async request({ credential, request }) {
|
|
154
|
-
// Extract chainId from request or default.
|
|
155
|
-
const chainId = await (async () => {
|
|
156
|
-
if (request.chainId) return request.chainId
|
|
157
|
-
if (parameters.testnet) return defaults.chainId.testnet
|
|
158
|
-
return (await getClient({})).chain?.id
|
|
159
|
-
})()
|
|
160
|
-
|
|
161
|
-
// Validate chainId.
|
|
162
|
-
const client = await (async () => {
|
|
163
|
-
try {
|
|
164
|
-
return await getClient({ chainId })
|
|
165
|
-
} catch {
|
|
166
|
-
throw new Error(`No client configured with chainId ${chainId}.`)
|
|
167
|
-
}
|
|
168
|
-
})()
|
|
169
|
-
if (client.chain?.id !== chainId)
|
|
170
|
-
throw new Error(`Client not configured with chainId ${chainId}.`)
|
|
171
|
-
|
|
172
|
-
const resolvedEscrow =
|
|
173
|
-
request.escrowContract ??
|
|
174
|
-
parameters.escrowContract ??
|
|
175
|
-
defaults.escrowContract[chainId as keyof typeof defaults.escrowContract]
|
|
176
|
-
|
|
177
|
-
// Extract feePayer.
|
|
178
|
-
const resolvedFeePayer = (() => {
|
|
179
|
-
if (request.feePayer === false) return credential ? false : undefined
|
|
180
|
-
const account = typeof request.feePayer === 'object' ? request.feePayer : feePayer
|
|
181
|
-
const requested = account ?? feePayer ?? feePayerUrl
|
|
182
|
-
if (credential) return account ?? (feePayerUrl ? true : undefined)
|
|
183
|
-
if (requested) return true
|
|
184
|
-
return undefined
|
|
185
|
-
})()
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
...request,
|
|
189
|
-
chainId,
|
|
190
|
-
escrowContract: resolvedEscrow,
|
|
191
|
-
feePayer: resolvedFeePayer,
|
|
192
|
-
sessionProtocol: Constants.SessionProtocols.v1,
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
|
|
196
|
-
async verify({ credential, envelope, request }) {
|
|
197
|
-
const { challenge, payload } =
|
|
198
|
-
credential as Credential.Credential<LegacySessionCredentialPayload>
|
|
199
|
-
|
|
200
|
-
const resolvedRequest = (() => {
|
|
201
|
-
const parsed = Methods.session.schema.request.safeParse(request)
|
|
202
|
-
if (parsed.success) return parsed.data
|
|
203
|
-
// verifyCredential() passes the HMAC-bound challenge request, which is
|
|
204
|
-
// already in canonical output form and should not be transformed again.
|
|
205
|
-
return request as unknown as z.output<typeof Methods.session.schema.request>
|
|
206
|
-
})()
|
|
207
|
-
const methodDetails = resolvedRequest.methodDetails as SessionMethodDetails
|
|
208
|
-
const client = await getClient({ chainId: methodDetails.chainId })
|
|
209
|
-
|
|
210
|
-
const requestAllowsFeePayer =
|
|
211
|
-
request.feePayer !== false &&
|
|
212
|
-
(request.feePayer === undefined ||
|
|
213
|
-
request.feePayer === true ||
|
|
214
|
-
typeof request.feePayer === 'object')
|
|
215
|
-
const resolvedFeePayer =
|
|
216
|
-
methodDetails.feePayer === true && requestAllowsFeePayer
|
|
217
|
-
? typeof request.feePayer === 'object'
|
|
218
|
-
? request.feePayer
|
|
219
|
-
: feePayer
|
|
220
|
-
: undefined
|
|
221
|
-
const minVoucherDelta = parseUnits(parameters.minVoucherDelta ?? '0', decimals)
|
|
222
|
-
const effectiveMinVoucherDelta = methodDetails.minVoucherDelta
|
|
223
|
-
? BigInt(methodDetails.minVoucherDelta)
|
|
224
|
-
: minVoucherDelta
|
|
225
|
-
|
|
226
|
-
let sessionReceipt: SessionReceipt
|
|
227
|
-
|
|
228
|
-
switch (payload.action) {
|
|
229
|
-
case 'open':
|
|
230
|
-
sessionReceipt = await handleOpen(
|
|
231
|
-
store,
|
|
232
|
-
client,
|
|
233
|
-
challenge,
|
|
234
|
-
payload,
|
|
235
|
-
methodDetails,
|
|
236
|
-
resolvedFeePayer,
|
|
237
|
-
methodDetails.feePayer === true,
|
|
238
|
-
feePayerPolicy,
|
|
239
|
-
waitForConfirmation,
|
|
240
|
-
)
|
|
241
|
-
lastOnChainVerified.set(sessionReceipt.channelId, Date.now())
|
|
242
|
-
break
|
|
243
|
-
|
|
244
|
-
case 'topUp':
|
|
245
|
-
sessionReceipt = await handleTopUp(
|
|
246
|
-
store,
|
|
247
|
-
client,
|
|
248
|
-
challenge,
|
|
249
|
-
payload,
|
|
250
|
-
methodDetails,
|
|
251
|
-
resolvedFeePayer,
|
|
252
|
-
methodDetails.feePayer === true,
|
|
253
|
-
feePayerPolicy,
|
|
254
|
-
)
|
|
255
|
-
lastOnChainVerified.set(sessionReceipt.channelId, Date.now())
|
|
256
|
-
break
|
|
257
|
-
|
|
258
|
-
case 'voucher':
|
|
259
|
-
sessionReceipt = await handleVoucher(
|
|
260
|
-
store,
|
|
261
|
-
client,
|
|
262
|
-
effectiveMinVoucherDelta,
|
|
263
|
-
challenge,
|
|
264
|
-
payload,
|
|
265
|
-
methodDetails,
|
|
266
|
-
channelStateTtl,
|
|
267
|
-
lastOnChainVerified,
|
|
268
|
-
)
|
|
269
|
-
break
|
|
270
|
-
|
|
271
|
-
case 'close':
|
|
272
|
-
sessionReceipt = await handleClose(
|
|
273
|
-
store,
|
|
274
|
-
client,
|
|
275
|
-
challenge,
|
|
276
|
-
payload,
|
|
277
|
-
methodDetails,
|
|
278
|
-
account,
|
|
279
|
-
resolvedFeePayer,
|
|
280
|
-
)
|
|
281
|
-
break
|
|
282
|
-
|
|
283
|
-
default:
|
|
284
|
-
throw new BadRequestError({
|
|
285
|
-
reason: `unknown action: ${(payload as { action: string }).action}`,
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// In the default HTTP request/response mode, each successful content
|
|
290
|
-
// request consumes one unit immediately after the credential is accepted.
|
|
291
|
-
// This keeps equal-voucher replays bounded by the voucher's remaining
|
|
292
|
-
// balance instead of serving repeated responses for free.
|
|
293
|
-
if (
|
|
294
|
-
envelope &&
|
|
295
|
-
isSessionContentRequest(envelope.capturedRequest) &&
|
|
296
|
-
(payload.action === 'open' ||
|
|
297
|
-
(payload.action === 'voucher' && !isSseNegotiationRequest(envelope.capturedRequest)))
|
|
298
|
-
) {
|
|
299
|
-
const charged = await charge(
|
|
300
|
-
store,
|
|
301
|
-
sessionReceipt.channelId,
|
|
302
|
-
BigInt(resolvedRequest.amount),
|
|
303
|
-
)
|
|
304
|
-
sessionReceipt = {
|
|
305
|
-
...sessionReceipt,
|
|
306
|
-
spent: charged.spent.toString(),
|
|
307
|
-
units: charged.units,
|
|
308
|
-
}
|
|
309
|
-
if (parameters.sse) sessionReceipt = Transport.markPrepaidSessionTick(sessionReceipt)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return sessionReceipt
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
// This hook acts as a gate: when it returns a Response, `withReceipt()`
|
|
316
|
-
// in Mppx.ts short-circuits and returns that response directly without
|
|
317
|
-
// invoking the user's route handler. When it returns undefined, the
|
|
318
|
-
// user's handler runs normally and serves content.
|
|
319
|
-
//
|
|
320
|
-
// close and topUp are always gated (204) — they are pure management.
|
|
321
|
-
//
|
|
322
|
-
// open and voucher share the same captured-request classifier used
|
|
323
|
-
// during verification. Non-billable requests are treated as management
|
|
324
|
-
// updates; billable requests fall through to the application handler.
|
|
325
|
-
respond({ credential, envelope, input }) {
|
|
326
|
-
const { payload } = credential as Credential.Credential<LegacySessionCredentialPayload>
|
|
327
|
-
const request = envelope?.capturedRequest ?? captureRequestBodyProbe(input)
|
|
328
|
-
|
|
329
|
-
if (payload.action === 'close') return new Response(null, { status: 204 })
|
|
330
|
-
if (payload.action === 'topUp') return new Response(null, { status: 204 })
|
|
331
|
-
if (parameters.sse && payload.action === 'voucher' && isSseNegotiationRequest(request))
|
|
332
|
-
return new Response(null, { status: 204 })
|
|
333
|
-
|
|
334
|
-
if (isSessionContentRequest(request)) return undefined
|
|
335
|
-
return new Response(null, { status: 204 })
|
|
336
|
-
},
|
|
337
|
-
})
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function isSseNegotiationRequest(input: Pick<Method.CapturedRequest, 'headers'>): boolean {
|
|
341
|
-
return input.headers.get('Accept')?.includes('text/event-stream') ?? false
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/** Type helpers for the legacy contract-backed session server method. */
|
|
345
|
-
export declare namespace session {
|
|
346
|
-
type Defaults = LooseOmit<
|
|
347
|
-
Method.RequestDefaults<typeof Methods.session>,
|
|
348
|
-
'feePayer' | 'recipient'
|
|
349
|
-
>
|
|
350
|
-
|
|
351
|
-
type FeePayerPolicy = Partial<FeePayer.Policy>
|
|
352
|
-
|
|
353
|
-
type Parameters = {
|
|
354
|
-
/** TTL in milliseconds for cached on-chain channel state. After this duration, the server re-queries on-chain state during voucher handling to detect forced close requests. @default 5_000 */
|
|
355
|
-
channelStateTtl?: number | undefined
|
|
356
|
-
/** Override the fee-sponsor policy used for sponsored open/topUp transactions. */
|
|
357
|
-
feePayerPolicy?: FeePayerPolicy | undefined
|
|
358
|
-
/** Minimum voucher delta to accept (numeric string, default: "0"). */
|
|
359
|
-
minVoucherDelta?: string | undefined
|
|
360
|
-
/**
|
|
361
|
-
* Whether to wait for the open transaction to confirm on-chain before
|
|
362
|
-
* responding. @default true
|
|
363
|
-
*
|
|
364
|
-
* When `false`, the transaction is simulated via `eth_estimateGas` and
|
|
365
|
-
* broadcast without waiting for inclusion. The receipt will optimistically
|
|
366
|
-
* report `status: 'success'` based on simulation alone — if the
|
|
367
|
-
* transaction reverts on-chain after broadcast (e.g. due to a state
|
|
368
|
-
* change between simulation and inclusion), the receipt will not reflect
|
|
369
|
-
* the failure.
|
|
370
|
-
*/
|
|
371
|
-
waitForConfirmation?: boolean | undefined
|
|
372
|
-
/**
|
|
373
|
-
* Atomic store backend for channel state.
|
|
374
|
-
*
|
|
375
|
-
* Session state mutations must be linearizable across instances, so this
|
|
376
|
-
* requires a {@link Store.AtomicStore}. Use `Store.memory()` for tests or
|
|
377
|
-
* local single-process usage.
|
|
378
|
-
*/
|
|
379
|
-
store?: Store.AtomicStore | undefined
|
|
380
|
-
/**
|
|
381
|
-
* Prefix prepended to channel state store keys.
|
|
382
|
-
*
|
|
383
|
-
* By default, no prefix is applied.
|
|
384
|
-
*/
|
|
385
|
-
storeKeyPrefix?: string | undefined
|
|
386
|
-
/**
|
|
387
|
-
* Enable SSE streaming.
|
|
388
|
-
*
|
|
389
|
-
* Pass `true` to enable with defaults, or an options object
|
|
390
|
-
* to configure SSE (e.g. `{ poll: true }` for
|
|
391
|
-
* Cloudflare Workers compatibility).
|
|
392
|
-
*/
|
|
393
|
-
sse?: boolean | Transport.sse.Options | undefined
|
|
394
|
-
/** Testnet mode. */
|
|
395
|
-
testnet?: boolean | undefined
|
|
396
|
-
} & Account.resolve.Parameters &
|
|
397
|
-
Client.getResolver.Parameters &
|
|
398
|
-
Defaults
|
|
399
|
-
|
|
400
|
-
type DeriveDefaults<parameters extends Parameters> = types.DeriveDefaults<
|
|
401
|
-
parameters,
|
|
402
|
-
Defaults,
|
|
403
|
-
{
|
|
404
|
-
currency: string
|
|
405
|
-
decimals: number
|
|
406
|
-
}
|
|
407
|
-
>
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function assertSettlementSender(parameters: {
|
|
411
|
-
operation: 'close' | 'settle'
|
|
412
|
-
channelId: Hex
|
|
413
|
-
payee: Address
|
|
414
|
-
sender: Address | undefined
|
|
415
|
-
}) {
|
|
416
|
-
const { operation, channelId, payee, sender } = parameters
|
|
417
|
-
if (!sender) return
|
|
418
|
-
if (sender.toLowerCase() === payee.toLowerCase()) return
|
|
419
|
-
throw new BadRequestError({
|
|
420
|
-
reason:
|
|
421
|
-
`Cannot ${operation} channel ${channelId}: tx sender ${sender} is not the channel payee ${payee}. ` +
|
|
422
|
-
'If using an access key, pass a Tempo access-key account whose address is the payee wallet, not the raw delegated key address.',
|
|
423
|
-
})
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* One-shot settle: reads highest voucher from store and submits on-chain.
|
|
428
|
-
*/
|
|
429
|
-
export async function settle(
|
|
430
|
-
store: ChannelStore.ChannelStore,
|
|
431
|
-
client: viem_Client,
|
|
432
|
-
channelId: Hex,
|
|
433
|
-
options?: {
|
|
434
|
-
escrowContract?: Address | undefined
|
|
435
|
-
} & (
|
|
436
|
-
| { feePayer: viem_Account; account: viem_Account }
|
|
437
|
-
| { feePayer?: undefined; account?: viem_Account | undefined }
|
|
438
|
-
),
|
|
439
|
-
): Promise<Hex> {
|
|
440
|
-
const channel = await store.getChannel(channelId)
|
|
441
|
-
if (!channel) throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
442
|
-
if (!channel.highestVoucher) throw new VerificationFailedError({ reason: 'no voucher to settle' })
|
|
443
|
-
|
|
444
|
-
const chainId = client.chain?.id
|
|
445
|
-
const resolvedEscrow =
|
|
446
|
-
options?.escrowContract ??
|
|
447
|
-
defaults.escrowContract[chainId as keyof typeof defaults.escrowContract]
|
|
448
|
-
if (!resolvedEscrow) throw new Error(`No escrow contract for chainId ${chainId}.`)
|
|
449
|
-
|
|
450
|
-
assertSettlementSender({
|
|
451
|
-
operation: 'settle',
|
|
452
|
-
channelId,
|
|
453
|
-
payee: channel.payee,
|
|
454
|
-
sender: options?.account?.address ?? client.account?.address,
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
const settledAmount = channel.highestVoucher.cumulativeAmount
|
|
458
|
-
const txHash = await settleOnChain(client, resolvedEscrow, channel.highestVoucher, {
|
|
459
|
-
...(options?.feePayer && options?.account
|
|
460
|
-
? { feePayer: options.feePayer, account: options.account }
|
|
461
|
-
: { account: options?.account }),
|
|
462
|
-
candidateFeeTokens: [channel.token],
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
await store.updateChannel(channelId, (current) => {
|
|
466
|
-
if (!current) return null
|
|
467
|
-
const nextSettled =
|
|
468
|
-
settledAmount > current.settledOnChain ? settledAmount : current.settledOnChain
|
|
469
|
-
return { ...current, settledOnChain: nextSettled }
|
|
470
|
-
})
|
|
471
|
-
|
|
472
|
-
return txHash
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Charge against a channel's balance.
|
|
477
|
-
*
|
|
478
|
-
* Exported so consumers can deduct from a channel outside the `session()`
|
|
479
|
-
* handler (e.g., custom middleware, the SSE `serve()` loop, or direct tests).
|
|
480
|
-
*
|
|
481
|
-
* Delegates to the shared `deductFromChannel` atomic helper and translates
|
|
482
|
-
* failure modes into typed errors (`InsufficientBalanceError`, `ChannelClosedError`).
|
|
483
|
-
*/
|
|
484
|
-
export async function charge(
|
|
485
|
-
store: ChannelStore.ChannelStore,
|
|
486
|
-
channelId: Hex,
|
|
487
|
-
amount: bigint,
|
|
488
|
-
): Promise<ChannelStore.State> {
|
|
489
|
-
let result: Awaited<ReturnType<typeof ChannelStore.deductFromChannel>>
|
|
490
|
-
try {
|
|
491
|
-
result = await ChannelStore.deductFromChannel(store, channelId, amount)
|
|
492
|
-
} catch {
|
|
493
|
-
throw new ChannelClosedError({ reason: 'channel not found' })
|
|
494
|
-
}
|
|
495
|
-
if (!result.ok) {
|
|
496
|
-
if (result.channel.finalized) throw new ChannelClosedError({ reason: 'channel is finalized' })
|
|
497
|
-
if (result.channel.closeRequestedAt !== 0n)
|
|
498
|
-
throw new ChannelClosedError({ reason: 'channel has a pending close request' })
|
|
499
|
-
const available = result.channel.highestVoucherAmount - result.channel.spent
|
|
500
|
-
throw new InsufficientBalanceError({
|
|
501
|
-
reason: `requested ${amount}, available ${available}`,
|
|
502
|
-
})
|
|
503
|
-
}
|
|
504
|
-
return result.channel
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* Validate on-chain channel state.
|
|
509
|
-
*/
|
|
510
|
-
function validateOnChainChannel(
|
|
511
|
-
onChain: OnChainChannel,
|
|
512
|
-
recipient: Address,
|
|
513
|
-
currency: Address,
|
|
514
|
-
amount?: bigint,
|
|
515
|
-
): void {
|
|
516
|
-
if (onChain.deposit === 0n) {
|
|
517
|
-
throw new ChannelNotFoundError({ reason: 'channel not funded on-chain' })
|
|
518
|
-
}
|
|
519
|
-
if (onChain.finalized) {
|
|
520
|
-
throw new ChannelClosedError({ reason: 'channel is finalized on-chain' })
|
|
521
|
-
}
|
|
522
|
-
if (onChain.closeRequestedAt !== 0n) {
|
|
523
|
-
throw new ChannelClosedError({ reason: 'channel has a pending close request' })
|
|
524
|
-
}
|
|
525
|
-
if (onChain.payee.toLowerCase() !== recipient.toLowerCase()) {
|
|
526
|
-
throw new VerificationFailedError({
|
|
527
|
-
reason: 'on-chain payee does not match server destination',
|
|
528
|
-
})
|
|
529
|
-
}
|
|
530
|
-
if (onChain.token.toLowerCase() !== currency.toLowerCase()) {
|
|
531
|
-
throw new VerificationFailedError({ reason: 'on-chain token does not match server token' })
|
|
532
|
-
}
|
|
533
|
-
if (amount !== undefined && onChain.deposit - onChain.settled < amount) {
|
|
534
|
-
throw new InsufficientBalanceError({
|
|
535
|
-
reason: 'channel available balance insufficient for requested amount',
|
|
536
|
-
})
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Shared logic for verifying an incremental voucher and updating channel state.
|
|
542
|
-
* Used by both handleVoucher and (indirectly) handleOpen.
|
|
543
|
-
*/
|
|
544
|
-
async function verifyAndAcceptVoucher(parameters: {
|
|
545
|
-
store: ChannelStore.ChannelStore
|
|
546
|
-
minVoucherDelta: bigint
|
|
547
|
-
challenge: Challenge.Challenge
|
|
548
|
-
channel: ChannelStore.State
|
|
549
|
-
channelId: Hex
|
|
550
|
-
voucher: LegacySignedVoucher
|
|
551
|
-
onChain: OnChainChannel
|
|
552
|
-
}): Promise<SessionReceipt> {
|
|
553
|
-
const { store, minVoucherDelta, challenge, channel, channelId, voucher, onChain } = parameters
|
|
554
|
-
|
|
555
|
-
if (onChain.finalized) {
|
|
556
|
-
throw new ChannelClosedError({ reason: 'channel is finalized on-chain' })
|
|
557
|
-
}
|
|
558
|
-
if (onChain.closeRequestedAt !== 0n) {
|
|
559
|
-
throw new ChannelClosedError({ reason: 'channel has a pending close request' })
|
|
560
|
-
}
|
|
561
|
-
// Treat a zero deposit on an existing channel as settled/closed.
|
|
562
|
-
// During settlement the escrow contract may zero the deposit before
|
|
563
|
-
// setting the finalized flag, creating a brief window where
|
|
564
|
-
// finalized=false but deposit=0. Without this guard the voucher
|
|
565
|
-
// check below would return a 402 (AmountExceedsDepositError) instead
|
|
566
|
-
// of the correct 410 (ChannelClosedError).
|
|
567
|
-
if (onChain.deposit === 0n && onChain.payer !== zeroAddress) {
|
|
568
|
-
throw new ChannelClosedError({ reason: 'channel deposit is zero (settled)' })
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (voucher.cumulativeAmount <= onChain.settled) {
|
|
572
|
-
throw new VerificationFailedError({
|
|
573
|
-
reason: 'voucher cumulativeAmount is at or below on-chain settled amount',
|
|
574
|
-
})
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
if (voucher.cumulativeAmount > onChain.deposit) {
|
|
578
|
-
throw new AmountExceedsDepositError({ reason: 'voucher amount exceeds on-chain deposit' })
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
const isValid = await verifyVoucher(
|
|
582
|
-
channel.escrowContract,
|
|
583
|
-
channel.chainId,
|
|
584
|
-
voucher,
|
|
585
|
-
channel.authorizedSigner,
|
|
586
|
-
)
|
|
587
|
-
|
|
588
|
-
if (!isValid) {
|
|
589
|
-
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// Idempotent replay: a non-advancing voucher (at or below the highest
|
|
593
|
-
// accepted amount, but above the on-chain settled amount checked above)
|
|
594
|
-
// returns 200 OK with the current highest amount without advancing state,
|
|
595
|
-
// per the session spec's idempotency requirement.
|
|
596
|
-
if (voucher.cumulativeAmount <= channel.highestVoucherAmount) {
|
|
597
|
-
return createSessionReceipt({
|
|
598
|
-
challengeId: challenge.id,
|
|
599
|
-
channelId,
|
|
600
|
-
acceptedCumulative: channel.highestVoucherAmount,
|
|
601
|
-
spent: channel.spent,
|
|
602
|
-
units: channel.units,
|
|
603
|
-
})
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const delta = voucher.cumulativeAmount - channel.highestVoucherAmount
|
|
607
|
-
if (delta < minVoucherDelta) {
|
|
608
|
-
throw new DeltaTooSmallError({
|
|
609
|
-
reason: `voucher delta ${delta} below minimum ${minVoucherDelta}`,
|
|
610
|
-
})
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
const updated = await store.updateChannel(channelId, (current) => {
|
|
614
|
-
if (!current) throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
615
|
-
if (voucher.cumulativeAmount > current.highestVoucherAmount) {
|
|
616
|
-
return {
|
|
617
|
-
...current,
|
|
618
|
-
deposit: onChain.deposit,
|
|
619
|
-
highestVoucherAmount: voucher.cumulativeAmount,
|
|
620
|
-
highestVoucher: voucher,
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return current
|
|
624
|
-
})
|
|
625
|
-
if (!updated) throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
626
|
-
|
|
627
|
-
return createSessionReceipt({
|
|
628
|
-
challengeId: challenge.id,
|
|
629
|
-
channelId,
|
|
630
|
-
acceptedCumulative: updated.highestVoucherAmount,
|
|
631
|
-
spent: updated.spent,
|
|
632
|
-
units: updated.units,
|
|
633
|
-
})
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* Handle 'open' action - verify voucher, create channel, and broadcast.
|
|
638
|
-
*
|
|
639
|
-
* When `waitForConfirmation` is true (default), the open transaction is
|
|
640
|
-
* broadcast and confirmed on-chain before responding. When false, the
|
|
641
|
-
* transaction is validated and simulated via `eth_estimateGas`; the receipt
|
|
642
|
-
* is returned immediately and the broadcast runs in the background.
|
|
643
|
-
*/
|
|
644
|
-
async function handleOpen(
|
|
645
|
-
store: ChannelStore.ChannelStore,
|
|
646
|
-
client: viem_Client,
|
|
647
|
-
challenge: Challenge.Challenge,
|
|
648
|
-
payload: LegacySessionCredentialPayload & { action: 'open' },
|
|
649
|
-
methodDetails: SessionMethodDetails,
|
|
650
|
-
feePayer: viem_Account | undefined,
|
|
651
|
-
isSponsored: boolean,
|
|
652
|
-
feePayerPolicy: session.FeePayerPolicy | undefined,
|
|
653
|
-
waitForConfirmation: boolean,
|
|
654
|
-
): Promise<SessionReceipt> {
|
|
655
|
-
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
656
|
-
const voucher = parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature)
|
|
657
|
-
|
|
658
|
-
const recipient = challenge.request.recipient as Address
|
|
659
|
-
const currency = challenge.request.currency as Address
|
|
660
|
-
const amount = challenge.request.amount ? BigInt(challenge.request.amount as string) : undefined
|
|
661
|
-
|
|
662
|
-
const validateOpenVoucher = async (onChain: OnChainChannel) => {
|
|
663
|
-
validateOnChainChannel(onChain, recipient, currency, amount)
|
|
664
|
-
|
|
665
|
-
const authorizedSigner =
|
|
666
|
-
onChain.authorizedSigner === '0x0000000000000000000000000000000000000000'
|
|
667
|
-
? onChain.payer
|
|
668
|
-
: onChain.authorizedSigner
|
|
669
|
-
|
|
670
|
-
if (voucher.cumulativeAmount > onChain.deposit) {
|
|
671
|
-
throw new AmountExceedsDepositError({ reason: 'voucher amount exceeds on-chain deposit' })
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (voucher.cumulativeAmount <= onChain.settled) {
|
|
675
|
-
throw new VerificationFailedError({
|
|
676
|
-
reason: 'voucher cumulativeAmount is at or below on-chain settled amount',
|
|
677
|
-
})
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
const isValid = await verifyVoucher(
|
|
681
|
-
methodDetails.escrowContract,
|
|
682
|
-
methodDetails.chainId,
|
|
683
|
-
voucher,
|
|
684
|
-
authorizedSigner,
|
|
685
|
-
)
|
|
686
|
-
|
|
687
|
-
if (!isValid) {
|
|
688
|
-
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
return authorizedSigner
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
const { onChain, txHash } = await broadcastOpenTransaction({
|
|
695
|
-
client,
|
|
696
|
-
serializedTransaction: payload.transaction,
|
|
697
|
-
escrowContract: methodDetails.escrowContract,
|
|
698
|
-
channelId,
|
|
699
|
-
recipient,
|
|
700
|
-
currency,
|
|
701
|
-
challengeExpires: challenge.expires,
|
|
702
|
-
feePayerPolicy,
|
|
703
|
-
feePayer,
|
|
704
|
-
isSponsored,
|
|
705
|
-
beforeBroadcast: async (pendingOnChain) => {
|
|
706
|
-
await validateOpenVoucher(pendingOnChain)
|
|
707
|
-
},
|
|
708
|
-
waitForConfirmation,
|
|
709
|
-
})
|
|
710
|
-
|
|
711
|
-
const authorizedSigner = await validateOpenVoucher(onChain)
|
|
712
|
-
|
|
713
|
-
const updated = await store.updateChannel(channelId, (existing) => {
|
|
714
|
-
if (existing) {
|
|
715
|
-
if (voucher.cumulativeAmount <= existing.settledOnChain) {
|
|
716
|
-
throw new VerificationFailedError({
|
|
717
|
-
reason: 'voucher amount is at or below settled on-chain amount',
|
|
718
|
-
})
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
const settledOnChain =
|
|
722
|
-
onChain.settled > existing.settledOnChain ? onChain.settled : existing.settledOnChain
|
|
723
|
-
const spent = settledOnChain > existing.spent ? settledOnChain : existing.spent
|
|
724
|
-
|
|
725
|
-
if (voucher.cumulativeAmount > existing.highestVoucherAmount) {
|
|
726
|
-
return {
|
|
727
|
-
...existing,
|
|
728
|
-
deposit: onChain.deposit,
|
|
729
|
-
settledOnChain,
|
|
730
|
-
spent,
|
|
731
|
-
highestVoucherAmount: voucher.cumulativeAmount,
|
|
732
|
-
highestVoucher: voucher,
|
|
733
|
-
authorizedSigner,
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
return {
|
|
737
|
-
...existing,
|
|
738
|
-
deposit: onChain.deposit,
|
|
739
|
-
settledOnChain,
|
|
740
|
-
spent,
|
|
741
|
-
authorizedSigner,
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
return {
|
|
745
|
-
channelId,
|
|
746
|
-
chainId: methodDetails.chainId,
|
|
747
|
-
escrowContract: methodDetails.escrowContract,
|
|
748
|
-
closeRequestedAt: onChain.closeRequestedAt,
|
|
749
|
-
payer: onChain.payer,
|
|
750
|
-
payee: onChain.payee,
|
|
751
|
-
token: onChain.token,
|
|
752
|
-
authorizedSigner,
|
|
753
|
-
deposit: onChain.deposit,
|
|
754
|
-
settledOnChain: onChain.settled,
|
|
755
|
-
highestVoucherAmount: voucher.cumulativeAmount,
|
|
756
|
-
highestVoucher: voucher,
|
|
757
|
-
spent: onChain.settled,
|
|
758
|
-
units: 0,
|
|
759
|
-
finalized: false,
|
|
760
|
-
createdAt: new Date().toISOString(),
|
|
761
|
-
}
|
|
762
|
-
})
|
|
763
|
-
|
|
764
|
-
if (!updated) throw new VerificationFailedError({ reason: 'failed to create channel' })
|
|
765
|
-
|
|
766
|
-
return createSessionReceipt({
|
|
767
|
-
challengeId: challenge.id,
|
|
768
|
-
channelId: updated.channelId,
|
|
769
|
-
acceptedCumulative: updated.highestVoucherAmount,
|
|
770
|
-
spent: updated.spent,
|
|
771
|
-
units: updated.units,
|
|
772
|
-
txHash,
|
|
773
|
-
})
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
/**
|
|
777
|
-
* Handle 'topUp' action - broadcast topUp transaction and update channel deposit.
|
|
778
|
-
*
|
|
779
|
-
* Per spec Section 8.3.2, topUp payloads contain only the transaction and
|
|
780
|
-
* additionalDeposit — no voucher. The client must send a separate 'voucher'
|
|
781
|
-
* action to authorize spending the new funds.
|
|
782
|
-
*/
|
|
783
|
-
async function handleTopUp(
|
|
784
|
-
store: ChannelStore.ChannelStore,
|
|
785
|
-
client: viem_Client,
|
|
786
|
-
challenge: Challenge.Challenge,
|
|
787
|
-
payload: LegacySessionCredentialPayload & { action: 'topUp' },
|
|
788
|
-
methodDetails: SessionMethodDetails,
|
|
789
|
-
feePayer: viem_Account | undefined,
|
|
790
|
-
isSponsored: boolean,
|
|
791
|
-
feePayerPolicy: session.FeePayerPolicy | undefined,
|
|
792
|
-
): Promise<SessionReceipt> {
|
|
793
|
-
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
794
|
-
const channel = await store.getChannel(channelId)
|
|
795
|
-
if (!channel) {
|
|
796
|
-
throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
const declaredDeposit = BigInt(payload.additionalDeposit)
|
|
800
|
-
|
|
801
|
-
const { newDeposit: onChainDeposit } = await broadcastTopUpTransaction({
|
|
802
|
-
client,
|
|
803
|
-
serializedTransaction: payload.transaction,
|
|
804
|
-
escrowContract: methodDetails.escrowContract,
|
|
805
|
-
channelId,
|
|
806
|
-
currency: challenge.request.currency as Address,
|
|
807
|
-
declaredDeposit,
|
|
808
|
-
previousDeposit: channel.deposit,
|
|
809
|
-
challengeExpires: challenge.expires,
|
|
810
|
-
feePayerPolicy,
|
|
811
|
-
feePayer,
|
|
812
|
-
isSponsored,
|
|
813
|
-
})
|
|
814
|
-
|
|
815
|
-
const updated = await store.updateChannel(channelId, (current) => {
|
|
816
|
-
if (!current) throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
817
|
-
return { ...current, deposit: onChainDeposit }
|
|
818
|
-
})
|
|
819
|
-
|
|
820
|
-
return createSessionReceipt({
|
|
821
|
-
challengeId: challenge.id,
|
|
822
|
-
channelId: updated?.channelId ?? channel.channelId,
|
|
823
|
-
acceptedCumulative: updated?.highestVoucherAmount ?? channel.highestVoucherAmount,
|
|
824
|
-
spent: updated?.spent ?? channel.spent,
|
|
825
|
-
units: updated?.units ?? channel.units,
|
|
826
|
-
})
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/**
|
|
830
|
-
* Handle 'voucher' action - verify and accept a new voucher.
|
|
831
|
-
*/
|
|
832
|
-
async function handleVoucher(
|
|
833
|
-
store: ChannelStore.ChannelStore,
|
|
834
|
-
client: viem_Client,
|
|
835
|
-
minVoucherDelta: bigint,
|
|
836
|
-
challenge: Challenge.Challenge,
|
|
837
|
-
payload: LegacySessionCredentialPayload & { action: 'voucher' },
|
|
838
|
-
methodDetails: SessionMethodDetails,
|
|
839
|
-
channelStateTtl: number,
|
|
840
|
-
lastOnChainVerified: Map<Hex, number>,
|
|
841
|
-
): Promise<SessionReceipt> {
|
|
842
|
-
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
843
|
-
const channel = await store.getChannel(channelId)
|
|
844
|
-
if (!channel) {
|
|
845
|
-
throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
846
|
-
}
|
|
847
|
-
if (channel.finalized) {
|
|
848
|
-
throw new ChannelClosedError({ reason: 'channel is finalized' })
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const voucher = parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature)
|
|
852
|
-
|
|
853
|
-
// Use locally-stored channel state as a trusted cache instead of
|
|
854
|
-
// reading on-chain for every voucher. The on-chain state is verified
|
|
855
|
-
// during `open` and `topUp` actions — subsequent vouchers within the
|
|
856
|
-
// same session can safely use the cached deposit/signer values.
|
|
857
|
-
// This avoids an RPC round-trip per voucher, which is critical for
|
|
858
|
-
// high-frequency SSE streaming where vouchers arrive per-token.
|
|
859
|
-
//
|
|
860
|
-
// To guard against the payer initiating a forced close while vouchers
|
|
861
|
-
// are still being accepted, re-query on-chain state when the cache
|
|
862
|
-
// exceeds the configured staleness TTL (default: 5s).
|
|
863
|
-
const lastVerified = lastOnChainVerified.get(channelId) ?? 0
|
|
864
|
-
const isStale = Date.now() - lastVerified > channelStateTtl
|
|
865
|
-
|
|
866
|
-
const onChain = await (async () => {
|
|
867
|
-
if (isStale) {
|
|
868
|
-
const onChainChannel = await getOnChainChannel(client, channel.escrowContract, channelId)
|
|
869
|
-
lastOnChainVerified.set(channelId, Date.now())
|
|
870
|
-
// Persist closeRequestedAt so the cached path detects force-close
|
|
871
|
-
// between re-queries.
|
|
872
|
-
if (onChainChannel.closeRequestedAt !== 0n) {
|
|
873
|
-
await store.updateChannel(channelId, (current) =>
|
|
874
|
-
current ? { ...current, closeRequestedAt: onChainChannel.closeRequestedAt } : current,
|
|
875
|
-
)
|
|
876
|
-
}
|
|
877
|
-
return onChainChannel
|
|
878
|
-
}
|
|
879
|
-
return {
|
|
880
|
-
finalized: channel.finalized,
|
|
881
|
-
closeRequestedAt: channel.closeRequestedAt,
|
|
882
|
-
payer: channel.payer,
|
|
883
|
-
payee: channel.payee,
|
|
884
|
-
token: channel.token,
|
|
885
|
-
authorizedSigner: channel.authorizedSigner,
|
|
886
|
-
deposit: channel.deposit,
|
|
887
|
-
settled: channel.settledOnChain,
|
|
888
|
-
}
|
|
889
|
-
})()
|
|
890
|
-
|
|
891
|
-
return verifyAndAcceptVoucher({
|
|
892
|
-
store,
|
|
893
|
-
minVoucherDelta,
|
|
894
|
-
challenge,
|
|
895
|
-
channel,
|
|
896
|
-
channelId,
|
|
897
|
-
voucher,
|
|
898
|
-
onChain,
|
|
899
|
-
})
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
/**
|
|
903
|
-
* Handle 'close' action - verify final voucher and close channel.
|
|
904
|
-
*/
|
|
905
|
-
async function handleClose(
|
|
906
|
-
store: ChannelStore.ChannelStore,
|
|
907
|
-
client: viem_Client,
|
|
908
|
-
challenge: Challenge.Challenge,
|
|
909
|
-
payload: LegacySessionCredentialPayload & { action: 'close' },
|
|
910
|
-
methodDetails: SessionMethodDetails,
|
|
911
|
-
account?: viem_Account,
|
|
912
|
-
feePayer?: viem_Account,
|
|
913
|
-
): Promise<SessionReceipt> {
|
|
914
|
-
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
915
|
-
const channel = await store.getChannel(channelId)
|
|
916
|
-
if (!channel) {
|
|
917
|
-
throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
918
|
-
}
|
|
919
|
-
if (channel.finalized) {
|
|
920
|
-
throw new ChannelClosedError({ reason: 'channel is already finalized' })
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const voucher = parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature)
|
|
924
|
-
|
|
925
|
-
const onChain = await getOnChainChannel(client, channel.escrowContract, channelId)
|
|
926
|
-
|
|
927
|
-
if (onChain.finalized) {
|
|
928
|
-
throw new ChannelClosedError({ reason: 'channel is finalized on-chain' })
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
if (voucher.cumulativeAmount < channel.spent) {
|
|
932
|
-
throw new VerificationFailedError({
|
|
933
|
-
reason: `close voucher amount must be >= ${channel.spent} (spent)`,
|
|
934
|
-
})
|
|
935
|
-
}
|
|
936
|
-
const isUntouchedZeroClose =
|
|
937
|
-
voucher.cumulativeAmount === 0n && channel.spent === 0n && onChain.settled === 0n
|
|
938
|
-
if (!isUntouchedZeroClose && voucher.cumulativeAmount <= onChain.settled) {
|
|
939
|
-
throw new VerificationFailedError({
|
|
940
|
-
reason: `close voucher amount must be > ${onChain.settled} (on-chain settled)`,
|
|
941
|
-
})
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
if (voucher.cumulativeAmount > onChain.deposit) {
|
|
945
|
-
throw new AmountExceedsDepositError({
|
|
946
|
-
reason: 'close voucher amount exceeds on-chain deposit',
|
|
947
|
-
})
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
const isValid = await verifyVoucher(
|
|
951
|
-
channel.escrowContract,
|
|
952
|
-
channel.chainId,
|
|
953
|
-
voucher,
|
|
954
|
-
channel.authorizedSigner,
|
|
955
|
-
)
|
|
956
|
-
|
|
957
|
-
if (!isValid) {
|
|
958
|
-
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
const pendingCloseStartedAt = BigInt(Math.floor(Date.now() / 1000) || 1)
|
|
962
|
-
const previousCloseRequestedAt = channel.closeRequestedAt
|
|
963
|
-
let pendingCloseMarked = false
|
|
964
|
-
await store.updateChannel(channelId, (current) => {
|
|
965
|
-
if (!current) return null
|
|
966
|
-
if (current.finalized) throw new ChannelClosedError({ reason: 'channel is already finalized' })
|
|
967
|
-
if (current.closeRequestedAt !== 0n)
|
|
968
|
-
throw new ChannelClosedError({ reason: 'channel has a pending close request' })
|
|
969
|
-
if (voucher.cumulativeAmount < current.spent) {
|
|
970
|
-
throw new VerificationFailedError({
|
|
971
|
-
reason: `close voucher amount must be >= ${current.spent} (spent)`,
|
|
972
|
-
})
|
|
973
|
-
}
|
|
974
|
-
pendingCloseMarked = true
|
|
975
|
-
return { ...current, closeRequestedAt: pendingCloseStartedAt }
|
|
976
|
-
})
|
|
977
|
-
|
|
978
|
-
let txHash: Hex | undefined
|
|
979
|
-
try {
|
|
980
|
-
assertSettlementSender({
|
|
981
|
-
operation: 'close',
|
|
982
|
-
channelId: payload.channelId,
|
|
983
|
-
payee: onChain.payee,
|
|
984
|
-
sender: account?.address ?? client.account?.address,
|
|
985
|
-
})
|
|
986
|
-
|
|
987
|
-
txHash = await closeOnChain(client, channel.escrowContract, voucher, {
|
|
988
|
-
...(feePayer && account ? { feePayer, account } : { account }),
|
|
989
|
-
candidateFeeTokens: [channel.token],
|
|
990
|
-
})
|
|
991
|
-
} catch (error) {
|
|
992
|
-
if (pendingCloseMarked) {
|
|
993
|
-
await store.updateChannel(channelId, (current) =>
|
|
994
|
-
current && current.closeRequestedAt === pendingCloseStartedAt
|
|
995
|
-
? { ...current, closeRequestedAt: previousCloseRequestedAt }
|
|
996
|
-
: current,
|
|
997
|
-
)
|
|
998
|
-
}
|
|
999
|
-
throw error
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
const updated = await store.updateChannel(channelId, (current) => {
|
|
1003
|
-
if (!current) return null
|
|
1004
|
-
const updateVoucher = voucher.cumulativeAmount > current.highestVoucherAmount
|
|
1005
|
-
return {
|
|
1006
|
-
...current,
|
|
1007
|
-
deposit: onChain.deposit,
|
|
1008
|
-
...(updateVoucher && {
|
|
1009
|
-
highestVoucherAmount: voucher.cumulativeAmount,
|
|
1010
|
-
highestVoucher: voucher,
|
|
1011
|
-
}),
|
|
1012
|
-
finalized: true,
|
|
1013
|
-
}
|
|
1014
|
-
})
|
|
1015
|
-
|
|
1016
|
-
return createSessionReceipt({
|
|
1017
|
-
challengeId: challenge.id,
|
|
1018
|
-
channelId: updated?.channelId ?? channel.channelId,
|
|
1019
|
-
acceptedCumulative: voucher.cumulativeAmount,
|
|
1020
|
-
spent: updated?.spent ?? channel.spent,
|
|
1021
|
-
units: updated?.units ?? channel.units,
|
|
1022
|
-
...(txHash !== undefined && { txHash }),
|
|
1023
|
-
})
|
|
1024
|
-
}
|