envoy-pay 0.1.0
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 +99 -0
- package/LICENSE +200 -0
- package/README.md +529 -0
- package/dist/cjs/adapters/base.js +174 -0
- package/dist/cjs/adapters/base.js.map +1 -0
- package/dist/cjs/adapters/evm.js +339 -0
- package/dist/cjs/adapters/evm.js.map +1 -0
- package/dist/cjs/adapters/index.js +15 -0
- package/dist/cjs/adapters/index.js.map +1 -0
- package/dist/cjs/adapters/ows.js +204 -0
- package/dist/cjs/adapters/ows.js.map +1 -0
- package/dist/cjs/adapters/solana.js +210 -0
- package/dist/cjs/adapters/solana.js.map +1 -0
- package/dist/cjs/adapters/stellar.js +173 -0
- package/dist/cjs/adapters/stellar.js.map +1 -0
- package/dist/cjs/adapters/stripe.js +338 -0
- package/dist/cjs/adapters/stripe.js.map +1 -0
- package/dist/cjs/adapters/types.js +3 -0
- package/dist/cjs/adapters/types.js.map +1 -0
- package/dist/cjs/client.js +309 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/contracts/abis/EnvoyFacilitator.js +197 -0
- package/dist/cjs/contracts/abis/EnvoyFacilitator.js.map +1 -0
- package/dist/cjs/contracts/addresses.js +42 -0
- package/dist/cjs/contracts/addresses.js.map +1 -0
- package/dist/cjs/contracts/facilitator.js +225 -0
- package/dist/cjs/contracts/facilitator.js.map +1 -0
- package/dist/cjs/contracts/index.js +21 -0
- package/dist/cjs/contracts/index.js.map +1 -0
- package/dist/cjs/facilitator/facilitator-service.js +305 -0
- package/dist/cjs/facilitator/facilitator-service.js.map +1 -0
- package/dist/cjs/facilitator/fee-calculator.js +134 -0
- package/dist/cjs/facilitator/fee-calculator.js.map +1 -0
- package/dist/cjs/facilitator/index.js +12 -0
- package/dist/cjs/facilitator/index.js.map +1 -0
- package/dist/cjs/facilitator/types.js +99 -0
- package/dist/cjs/facilitator/types.js.map +1 -0
- package/dist/cjs/identity/agent-card.js +143 -0
- package/dist/cjs/identity/agent-card.js.map +1 -0
- package/dist/cjs/identity/agent-identity.js +166 -0
- package/dist/cjs/identity/agent-identity.js.map +1 -0
- package/dist/cjs/identity/did-resolver.js +149 -0
- package/dist/cjs/identity/did-resolver.js.map +1 -0
- package/dist/cjs/identity/erc8004/abis.js +218 -0
- package/dist/cjs/identity/erc8004/abis.js.map +1 -0
- package/dist/cjs/identity/erc8004/identity.js +268 -0
- package/dist/cjs/identity/erc8004/identity.js.map +1 -0
- package/dist/cjs/identity/erc8004/index.js +36 -0
- package/dist/cjs/identity/erc8004/index.js.map +1 -0
- package/dist/cjs/identity/erc8004/reputation.js +110 -0
- package/dist/cjs/identity/erc8004/reputation.js.map +1 -0
- package/dist/cjs/identity/erc8004/types.js +3 -0
- package/dist/cjs/identity/erc8004/types.js.map +1 -0
- package/dist/cjs/identity/index.js +61 -0
- package/dist/cjs/identity/index.js.map +1 -0
- package/dist/cjs/identity/owner-registry.js +140 -0
- package/dist/cjs/identity/owner-registry.js.map +1 -0
- package/dist/cjs/identity/reputation.js +193 -0
- package/dist/cjs/identity/reputation.js.map +1 -0
- package/dist/cjs/identity/types.js +16 -0
- package/dist/cjs/identity/types.js.map +1 -0
- package/dist/cjs/index.js +72 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/logger.js +7 -0
- package/dist/cjs/logger.js.map +1 -0
- package/dist/cjs/monitor/evm-watcher.js +165 -0
- package/dist/cjs/monitor/evm-watcher.js.map +1 -0
- package/dist/cjs/monitor/index.js +32 -0
- package/dist/cjs/monitor/index.js.map +1 -0
- package/dist/cjs/monitor/multi-watcher.js +94 -0
- package/dist/cjs/monitor/multi-watcher.js.map +1 -0
- package/dist/cjs/monitor/solana-watcher.js +172 -0
- package/dist/cjs/monitor/solana-watcher.js.map +1 -0
- package/dist/cjs/monitor/stellar-watcher.js +125 -0
- package/dist/cjs/monitor/stellar-watcher.js.map +1 -0
- package/dist/cjs/mpp.js +187 -0
- package/dist/cjs/mpp.js.map +1 -0
- package/dist/cjs/policy.js +71 -0
- package/dist/cjs/policy.js.map +1 -0
- package/dist/cjs/providers/bridge.js +323 -0
- package/dist/cjs/providers/bridge.js.map +1 -0
- package/dist/cjs/providers/onchainos.js +210 -0
- package/dist/cjs/providers/onchainos.js.map +1 -0
- package/dist/cjs/requests/eip681.js +77 -0
- package/dist/cjs/requests/eip681.js.map +1 -0
- package/dist/cjs/requests/index.js +18 -0
- package/dist/cjs/requests/index.js.map +1 -0
- package/dist/cjs/requests/sep7.js +61 -0
- package/dist/cjs/requests/sep7.js.map +1 -0
- package/dist/cjs/requests/solana-pay.js +51 -0
- package/dist/cjs/requests/solana-pay.js.map +1 -0
- package/dist/cjs/requests/universal.js +40 -0
- package/dist/cjs/requests/universal.js.map +1 -0
- package/dist/cjs/server/index.js +35 -0
- package/dist/cjs/server/index.js.map +1 -0
- package/dist/cjs/server/mpp-gate.js +118 -0
- package/dist/cjs/server/mpp-gate.js.map +1 -0
- package/dist/cjs/server/payment-gate.js +92 -0
- package/dist/cjs/server/payment-gate.js.map +1 -0
- package/dist/cjs/server/receipt.js +52 -0
- package/dist/cjs/server/receipt.js.map +1 -0
- package/dist/cjs/server/webhook.js +122 -0
- package/dist/cjs/server/webhook.js.map +1 -0
- package/dist/cjs/server/x402-gate.js +103 -0
- package/dist/cjs/server/x402-gate.js.map +1 -0
- package/dist/cjs/solana.js +14 -0
- package/dist/cjs/solana.js.map +1 -0
- package/dist/cjs/stellar.js +14 -0
- package/dist/cjs/stellar.js.map +1 -0
- package/dist/cjs/wallet/balance-aggregator.js +149 -0
- package/dist/cjs/wallet/balance-aggregator.js.map +1 -0
- package/dist/cjs/wallet/chain-router.js +254 -0
- package/dist/cjs/wallet/chain-router.js.map +1 -0
- package/dist/cjs/wallet/index.js +15 -0
- package/dist/cjs/wallet/index.js.map +1 -0
- package/dist/cjs/wallet/intent-resolver.js +145 -0
- package/dist/cjs/wallet/intent-resolver.js.map +1 -0
- package/dist/cjs/wallet/session-manager.js +170 -0
- package/dist/cjs/wallet/session-manager.js.map +1 -0
- package/dist/cjs/wallet/types.js +14 -0
- package/dist/cjs/wallet/types.js.map +1 -0
- package/dist/cjs/wallet/unified-wallet.js +253 -0
- package/dist/cjs/wallet/unified-wallet.js.map +1 -0
- package/dist/esm/adapters/base.js +170 -0
- package/dist/esm/adapters/base.js.map +1 -0
- package/dist/esm/adapters/evm.js +334 -0
- package/dist/esm/adapters/evm.js.map +1 -0
- package/dist/esm/adapters/index.js +6 -0
- package/dist/esm/adapters/index.js.map +1 -0
- package/dist/esm/adapters/ows.js +194 -0
- package/dist/esm/adapters/ows.js.map +1 -0
- package/dist/esm/adapters/solana.js +206 -0
- package/dist/esm/adapters/solana.js.map +1 -0
- package/dist/esm/adapters/stellar.js +136 -0
- package/dist/esm/adapters/stellar.js.map +1 -0
- package/dist/esm/adapters/stripe.js +334 -0
- package/dist/esm/adapters/stripe.js.map +1 -0
- package/dist/esm/adapters/types.js +2 -0
- package/dist/esm/adapters/types.js.map +1 -0
- package/dist/esm/client.js +302 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/contracts/abis/EnvoyFacilitator.js +194 -0
- package/dist/esm/contracts/abis/EnvoyFacilitator.js.map +1 -0
- package/dist/esm/contracts/addresses.js +38 -0
- package/dist/esm/contracts/addresses.js.map +1 -0
- package/dist/esm/contracts/facilitator.js +218 -0
- package/dist/esm/contracts/facilitator.js.map +1 -0
- package/dist/esm/contracts/index.js +8 -0
- package/dist/esm/contracts/index.js.map +1 -0
- package/dist/esm/facilitator/facilitator-service.js +301 -0
- package/dist/esm/facilitator/facilitator-service.js.map +1 -0
- package/dist/esm/facilitator/fee-calculator.js +130 -0
- package/dist/esm/facilitator/fee-calculator.js.map +1 -0
- package/dist/esm/facilitator/index.js +5 -0
- package/dist/esm/facilitator/index.js.map +1 -0
- package/dist/esm/facilitator/types.js +96 -0
- package/dist/esm/facilitator/types.js.map +1 -0
- package/dist/esm/identity/agent-card.js +139 -0
- package/dist/esm/identity/agent-card.js.map +1 -0
- package/dist/esm/identity/agent-identity.js +162 -0
- package/dist/esm/identity/agent-identity.js.map +1 -0
- package/dist/esm/identity/did-resolver.js +145 -0
- package/dist/esm/identity/did-resolver.js.map +1 -0
- package/dist/esm/identity/erc8004/abis.js +215 -0
- package/dist/esm/identity/erc8004/abis.js.map +1 -0
- package/dist/esm/identity/erc8004/identity.js +250 -0
- package/dist/esm/identity/erc8004/identity.js.map +1 -0
- package/dist/esm/identity/erc8004/index.js +13 -0
- package/dist/esm/identity/erc8004/index.js.map +1 -0
- package/dist/esm/identity/erc8004/reputation.js +105 -0
- package/dist/esm/identity/erc8004/reputation.js.map +1 -0
- package/dist/esm/identity/erc8004/types.js +2 -0
- package/dist/esm/identity/erc8004/types.js.map +1 -0
- package/dist/esm/identity/index.js +19 -0
- package/dist/esm/identity/index.js.map +1 -0
- package/dist/esm/identity/owner-registry.js +136 -0
- package/dist/esm/identity/owner-registry.js.map +1 -0
- package/dist/esm/identity/reputation.js +189 -0
- package/dist/esm/identity/reputation.js.map +1 -0
- package/dist/esm/identity/types.js +15 -0
- package/dist/esm/identity/types.js.map +1 -0
- package/dist/esm/index.js +37 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logger.js +3 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/monitor/evm-watcher.js +162 -0
- package/dist/esm/monitor/evm-watcher.js.map +1 -0
- package/dist/esm/monitor/index.js +25 -0
- package/dist/esm/monitor/index.js.map +1 -0
- package/dist/esm/monitor/multi-watcher.js +91 -0
- package/dist/esm/monitor/multi-watcher.js.map +1 -0
- package/dist/esm/monitor/solana-watcher.js +169 -0
- package/dist/esm/monitor/solana-watcher.js.map +1 -0
- package/dist/esm/monitor/stellar-watcher.js +122 -0
- package/dist/esm/monitor/stellar-watcher.js.map +1 -0
- package/dist/esm/mpp.js +175 -0
- package/dist/esm/mpp.js.map +1 -0
- package/dist/esm/policy.js +67 -0
- package/dist/esm/policy.js.map +1 -0
- package/dist/esm/providers/bridge.js +319 -0
- package/dist/esm/providers/bridge.js.map +1 -0
- package/dist/esm/providers/onchainos.js +172 -0
- package/dist/esm/providers/onchainos.js.map +1 -0
- package/dist/esm/requests/eip681.js +74 -0
- package/dist/esm/requests/eip681.js.map +1 -0
- package/dist/esm/requests/index.js +11 -0
- package/dist/esm/requests/index.js.map +1 -0
- package/dist/esm/requests/sep7.js +58 -0
- package/dist/esm/requests/sep7.js.map +1 -0
- package/dist/esm/requests/solana-pay.js +48 -0
- package/dist/esm/requests/solana-pay.js.map +1 -0
- package/dist/esm/requests/universal.js +37 -0
- package/dist/esm/requests/universal.js.map +1 -0
- package/dist/esm/server/index.js +27 -0
- package/dist/esm/server/index.js.map +1 -0
- package/dist/esm/server/mpp-gate.js +115 -0
- package/dist/esm/server/mpp-gate.js.map +1 -0
- package/dist/esm/server/payment-gate.js +89 -0
- package/dist/esm/server/payment-gate.js.map +1 -0
- package/dist/esm/server/receipt.js +48 -0
- package/dist/esm/server/receipt.js.map +1 -0
- package/dist/esm/server/webhook.js +119 -0
- package/dist/esm/server/webhook.js.map +1 -0
- package/dist/esm/server/x402-gate.js +100 -0
- package/dist/esm/server/x402-gate.js.map +1 -0
- package/dist/esm/solana.js +8 -0
- package/dist/esm/solana.js.map +1 -0
- package/dist/esm/stellar.js +8 -0
- package/dist/esm/stellar.js.map +1 -0
- package/dist/esm/wallet/balance-aggregator.js +145 -0
- package/dist/esm/wallet/balance-aggregator.js.map +1 -0
- package/dist/esm/wallet/chain-router.js +250 -0
- package/dist/esm/wallet/chain-router.js.map +1 -0
- package/dist/esm/wallet/index.js +7 -0
- package/dist/esm/wallet/index.js.map +1 -0
- package/dist/esm/wallet/intent-resolver.js +141 -0
- package/dist/esm/wallet/intent-resolver.js.map +1 -0
- package/dist/esm/wallet/session-manager.js +166 -0
- package/dist/esm/wallet/session-manager.js.map +1 -0
- package/dist/esm/wallet/types.js +13 -0
- package/dist/esm/wallet/types.js.map +1 -0
- package/dist/esm/wallet/unified-wallet.js +249 -0
- package/dist/esm/wallet/unified-wallet.js.map +1 -0
- package/dist/types/adapters/base.d.ts +53 -0
- package/dist/types/adapters/base.d.ts.map +1 -0
- package/dist/types/adapters/evm.d.ts +93 -0
- package/dist/types/adapters/evm.d.ts.map +1 -0
- package/dist/types/adapters/index.d.ts +7 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/ows.d.ts +123 -0
- package/dist/types/adapters/ows.d.ts.map +1 -0
- package/dist/types/adapters/solana.d.ts +77 -0
- package/dist/types/adapters/solana.d.ts.map +1 -0
- package/dist/types/adapters/stellar.d.ts +67 -0
- package/dist/types/adapters/stellar.d.ts.map +1 -0
- package/dist/types/adapters/stripe.d.ts +206 -0
- package/dist/types/adapters/stripe.d.ts.map +1 -0
- package/dist/types/adapters/types.d.ts +110 -0
- package/dist/types/adapters/types.d.ts.map +1 -0
- package/dist/types/client.d.ts +89 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/contracts/abis/EnvoyFacilitator.d.ts +296 -0
- package/dist/types/contracts/abis/EnvoyFacilitator.d.ts.map +1 -0
- package/dist/types/contracts/addresses.d.ts +24 -0
- package/dist/types/contracts/addresses.d.ts.map +1 -0
- package/dist/types/contracts/facilitator.d.ts +172 -0
- package/dist/types/contracts/facilitator.d.ts.map +1 -0
- package/dist/types/contracts/index.d.ts +6 -0
- package/dist/types/contracts/index.d.ts.map +1 -0
- package/dist/types/facilitator/facilitator-service.d.ts +69 -0
- package/dist/types/facilitator/facilitator-service.d.ts.map +1 -0
- package/dist/types/facilitator/fee-calculator.d.ts +48 -0
- package/dist/types/facilitator/fee-calculator.d.ts.map +1 -0
- package/dist/types/facilitator/index.d.ts +5 -0
- package/dist/types/facilitator/index.d.ts.map +1 -0
- package/dist/types/facilitator/types.d.ts +221 -0
- package/dist/types/facilitator/types.d.ts.map +1 -0
- package/dist/types/identity/agent-card.d.ts +83 -0
- package/dist/types/identity/agent-card.d.ts.map +1 -0
- package/dist/types/identity/agent-identity.d.ts +102 -0
- package/dist/types/identity/agent-identity.d.ts.map +1 -0
- package/dist/types/identity/did-resolver.d.ts +70 -0
- package/dist/types/identity/did-resolver.d.ts.map +1 -0
- package/dist/types/identity/erc8004/abis.d.ts +336 -0
- package/dist/types/identity/erc8004/abis.d.ts.map +1 -0
- package/dist/types/identity/erc8004/identity.d.ts +109 -0
- package/dist/types/identity/erc8004/identity.d.ts.map +1 -0
- package/dist/types/identity/erc8004/index.d.ts +15 -0
- package/dist/types/identity/erc8004/index.d.ts.map +1 -0
- package/dist/types/identity/erc8004/reputation.d.ts +52 -0
- package/dist/types/identity/erc8004/reputation.d.ts.map +1 -0
- package/dist/types/identity/erc8004/types.d.ts +31 -0
- package/dist/types/identity/erc8004/types.d.ts.map +1 -0
- package/dist/types/identity/index.d.ts +10 -0
- package/dist/types/identity/index.d.ts.map +1 -0
- package/dist/types/identity/owner-registry.d.ts +68 -0
- package/dist/types/identity/owner-registry.d.ts.map +1 -0
- package/dist/types/identity/reputation.d.ts +66 -0
- package/dist/types/identity/reputation.d.ts.map +1 -0
- package/dist/types/identity/types.d.ts +182 -0
- package/dist/types/identity/types.d.ts.map +1 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/logger.d.ts +10 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/monitor/evm-watcher.d.ts +40 -0
- package/dist/types/monitor/evm-watcher.d.ts.map +1 -0
- package/dist/types/monitor/index.d.ts +25 -0
- package/dist/types/monitor/index.d.ts.map +1 -0
- package/dist/types/monitor/multi-watcher.d.ts +48 -0
- package/dist/types/monitor/multi-watcher.d.ts.map +1 -0
- package/dist/types/monitor/solana-watcher.d.ts +36 -0
- package/dist/types/monitor/solana-watcher.d.ts.map +1 -0
- package/dist/types/monitor/stellar-watcher.d.ts +34 -0
- package/dist/types/monitor/stellar-watcher.d.ts.map +1 -0
- package/dist/types/mpp.d.ts +153 -0
- package/dist/types/mpp.d.ts.map +1 -0
- package/dist/types/policy.d.ts +40 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/providers/bridge.d.ts +162 -0
- package/dist/types/providers/bridge.d.ts.map +1 -0
- package/dist/types/providers/onchainos.d.ts +128 -0
- package/dist/types/providers/onchainos.d.ts.map +1 -0
- package/dist/types/requests/eip681.d.ts +51 -0
- package/dist/types/requests/eip681.d.ts.map +1 -0
- package/dist/types/requests/index.d.ts +11 -0
- package/dist/types/requests/index.d.ts.map +1 -0
- package/dist/types/requests/sep7.d.ts +56 -0
- package/dist/types/requests/sep7.d.ts.map +1 -0
- package/dist/types/requests/solana-pay.d.ts +50 -0
- package/dist/types/requests/solana-pay.d.ts.map +1 -0
- package/dist/types/requests/universal.d.ts +28 -0
- package/dist/types/requests/universal.d.ts.map +1 -0
- package/dist/types/server/index.d.ts +27 -0
- package/dist/types/server/index.d.ts.map +1 -0
- package/dist/types/server/mpp-gate.d.ts +66 -0
- package/dist/types/server/mpp-gate.d.ts.map +1 -0
- package/dist/types/server/payment-gate.d.ts +33 -0
- package/dist/types/server/payment-gate.d.ts.map +1 -0
- package/dist/types/server/receipt.d.ts +52 -0
- package/dist/types/server/receipt.d.ts.map +1 -0
- package/dist/types/server/webhook.d.ts +46 -0
- package/dist/types/server/webhook.d.ts.map +1 -0
- package/dist/types/server/x402-gate.d.ts +70 -0
- package/dist/types/server/x402-gate.d.ts.map +1 -0
- package/dist/types/solana.d.ts +7 -0
- package/dist/types/solana.d.ts.map +1 -0
- package/dist/types/stellar.d.ts +7 -0
- package/dist/types/stellar.d.ts.map +1 -0
- package/dist/types/wallet/balance-aggregator.d.ts +51 -0
- package/dist/types/wallet/balance-aggregator.d.ts.map +1 -0
- package/dist/types/wallet/chain-router.d.ts +44 -0
- package/dist/types/wallet/chain-router.d.ts.map +1 -0
- package/dist/types/wallet/index.d.ts +7 -0
- package/dist/types/wallet/index.d.ts.map +1 -0
- package/dist/types/wallet/intent-resolver.d.ts +39 -0
- package/dist/types/wallet/intent-resolver.d.ts.map +1 -0
- package/dist/types/wallet/session-manager.d.ts +57 -0
- package/dist/types/wallet/session-manager.d.ts.map +1 -0
- package/dist/types/wallet/types.d.ts +161 -0
- package/dist/types/wallet/types.d.ts.map +1 -0
- package/dist/types/wallet/unified-wallet.d.ts +122 -0
- package/dist/types/wallet/unified-wallet.d.ts.map +1 -0
- package/package.json +194 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createEvmWatcher } from './evm-watcher.js';
|
|
2
|
+
import { createStellarWatcher } from './stellar-watcher.js';
|
|
3
|
+
import { createSolanaWatcher } from './solana-watcher.js';
|
|
4
|
+
import { noopLogger } from '../logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a multi-chain payment watcher.
|
|
7
|
+
*
|
|
8
|
+
* @returns Unsubscribe function that stops ALL chain watchers.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const unsub = createMultiChainWatcher({
|
|
13
|
+
* evm: [
|
|
14
|
+
* { address: '0x...', rpcUrl: 'https://mainnet.base.org', chainName: 'Base', usdcContractAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
|
|
15
|
+
* { address: '0x...', rpcUrl: 'https://arb1.arbitrum.io/rpc', chainName: 'Arbitrum' },
|
|
16
|
+
* ],
|
|
17
|
+
* stellar: { accountId: 'GABCD...' },
|
|
18
|
+
* solana: { address: '7abc...' },
|
|
19
|
+
* onPayment: (event) => console.log(`${event.chain}: ${event.amountFormatted} ${event.asset}`),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Stop all watchers
|
|
23
|
+
* unsub();
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function createMultiChainWatcher(config) {
|
|
27
|
+
const log = config.logger ?? noopLogger;
|
|
28
|
+
const unsubscribers = [];
|
|
29
|
+
log('[multi-watcher] 🌐 Starting multi-chain payment monitor…');
|
|
30
|
+
// Start EVM watchers
|
|
31
|
+
if (config.evm) {
|
|
32
|
+
for (const evmConfig of config.evm) {
|
|
33
|
+
const unsub = createEvmWatcher({
|
|
34
|
+
...evmConfig,
|
|
35
|
+
onPayment: (event) => {
|
|
36
|
+
config.onPayment(event);
|
|
37
|
+
evmConfig.onPayment?.(event);
|
|
38
|
+
},
|
|
39
|
+
onError: (err) => {
|
|
40
|
+
config.onError?.(err);
|
|
41
|
+
evmConfig.onError?.(err);
|
|
42
|
+
},
|
|
43
|
+
logger: evmConfig.logger ?? log,
|
|
44
|
+
});
|
|
45
|
+
unsubscribers.push(unsub);
|
|
46
|
+
}
|
|
47
|
+
log(`[multi-watcher] ⛓️ ${config.evm.length} EVM chain(s) active`);
|
|
48
|
+
}
|
|
49
|
+
// Start Stellar watcher
|
|
50
|
+
if (config.stellar) {
|
|
51
|
+
const unsub = createStellarWatcher({
|
|
52
|
+
...config.stellar,
|
|
53
|
+
onPayment: (event) => {
|
|
54
|
+
config.onPayment(event);
|
|
55
|
+
config.stellar.onPayment?.(event);
|
|
56
|
+
},
|
|
57
|
+
onError: (err) => {
|
|
58
|
+
config.onError?.(err);
|
|
59
|
+
config.stellar.onError?.(err);
|
|
60
|
+
},
|
|
61
|
+
logger: config.stellar.logger ?? log,
|
|
62
|
+
});
|
|
63
|
+
unsubscribers.push(unsub);
|
|
64
|
+
log('[multi-watcher] ⭐ Stellar watcher active');
|
|
65
|
+
}
|
|
66
|
+
// Start Solana watcher
|
|
67
|
+
if (config.solana) {
|
|
68
|
+
const unsub = createSolanaWatcher({
|
|
69
|
+
...config.solana,
|
|
70
|
+
onPayment: (event) => {
|
|
71
|
+
config.onPayment(event);
|
|
72
|
+
config.solana.onPayment?.(event);
|
|
73
|
+
},
|
|
74
|
+
onError: (err) => {
|
|
75
|
+
config.onError?.(err);
|
|
76
|
+
config.solana.onError?.(err);
|
|
77
|
+
},
|
|
78
|
+
logger: config.solana.logger ?? log,
|
|
79
|
+
});
|
|
80
|
+
unsubscribers.push(unsub);
|
|
81
|
+
log('[multi-watcher] ◎ Solana watcher active');
|
|
82
|
+
}
|
|
83
|
+
log(`[multi-watcher] ✅ ${unsubscribers.length} watcher(s) running`);
|
|
84
|
+
return () => {
|
|
85
|
+
for (const unsub of unsubscribers) {
|
|
86
|
+
unsub();
|
|
87
|
+
}
|
|
88
|
+
log('[multi-watcher] 🛑 All watchers stopped');
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=multi-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-watcher.js","sourceRoot":"","sources":["../../../src/monitor/multi-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { noopLogger } from '../logger.js';
|
|
2
|
+
/** Mainnet USDC mint address. */
|
|
3
|
+
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a Solana payment watcher using JSON-RPC polling.
|
|
6
|
+
*
|
|
7
|
+
* Polls `getSignaturesForAddress` for new transactions, then
|
|
8
|
+
* inspects each transaction to detect:
|
|
9
|
+
* 1. Native SOL transfers TO the target address
|
|
10
|
+
* 2. SPL USDC transfers TO the target's Associated Token Account
|
|
11
|
+
*
|
|
12
|
+
* @returns Unsubscribe function to stop watching.
|
|
13
|
+
*/
|
|
14
|
+
export function createSolanaWatcher(config) {
|
|
15
|
+
const log = config.logger ?? noopLogger;
|
|
16
|
+
const rpcUrl = config.rpcUrl ?? 'https://api.mainnet-beta.solana.com';
|
|
17
|
+
const pollInterval = config.pollIntervalMs ?? 5000;
|
|
18
|
+
const usdcMint = config.usdcMint ?? USDC_MINT;
|
|
19
|
+
let running = true;
|
|
20
|
+
let lastSignature = null;
|
|
21
|
+
let timer = null;
|
|
22
|
+
async function rpcCall(method, params) {
|
|
23
|
+
const response = await fetch(rpcUrl, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
|
|
27
|
+
});
|
|
28
|
+
const json = await response.json();
|
|
29
|
+
if (json.error)
|
|
30
|
+
throw new Error(`Solana RPC error: ${json.error.message}`);
|
|
31
|
+
return json.result;
|
|
32
|
+
}
|
|
33
|
+
async function getNewSignatures() {
|
|
34
|
+
const params = [
|
|
35
|
+
config.address,
|
|
36
|
+
{ limit: 20, ...(lastSignature ? { until: lastSignature } : {}) },
|
|
37
|
+
];
|
|
38
|
+
const result = await rpcCall('getSignaturesForAddress', params);
|
|
39
|
+
if (!Array.isArray(result) || result.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
// Return newest first
|
|
42
|
+
return result
|
|
43
|
+
.filter((r) => r.confirmationStatus === 'confirmed' || r.confirmationStatus === 'finalized')
|
|
44
|
+
.map((r) => r.signature);
|
|
45
|
+
}
|
|
46
|
+
async function inspectTransaction(signature) {
|
|
47
|
+
try {
|
|
48
|
+
const tx = await rpcCall('getTransaction', [signature, { encoding: 'jsonParsed', maxSupportedTransactionVersion: 0 }]);
|
|
49
|
+
if (!tx?.meta || tx.meta.err)
|
|
50
|
+
return null;
|
|
51
|
+
const targetLower = config.address;
|
|
52
|
+
// Check parsed instructions for SOL transfers and SPL token transfers
|
|
53
|
+
const instructions = tx.transaction?.message?.instructions || [];
|
|
54
|
+
const innerInstructions = tx.meta?.innerInstructions || [];
|
|
55
|
+
const allInstructions = [
|
|
56
|
+
...instructions,
|
|
57
|
+
...innerInstructions.flatMap((inner) => inner.instructions || []),
|
|
58
|
+
];
|
|
59
|
+
for (const ix of allInstructions) {
|
|
60
|
+
const parsed = ix.parsed;
|
|
61
|
+
if (!parsed)
|
|
62
|
+
continue;
|
|
63
|
+
// Native SOL transfer
|
|
64
|
+
if (parsed.type === 'transfer' && ix.program === 'system') {
|
|
65
|
+
if (parsed.info?.destination === targetLower) {
|
|
66
|
+
const lamports = parsed.info.lamports.toString();
|
|
67
|
+
return {
|
|
68
|
+
amount: lamports,
|
|
69
|
+
amountFormatted: formatSol(lamports),
|
|
70
|
+
asset: 'SOL',
|
|
71
|
+
from: parsed.info.source || 'unknown',
|
|
72
|
+
transactionHash: signature,
|
|
73
|
+
chain: 'Solana',
|
|
74
|
+
caip2Id: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
75
|
+
timestamp: new Date((tx.blockTime || Date.now() / 1000) * 1000),
|
|
76
|
+
confirmations: 1,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// SPL Token transfer (including USDC)
|
|
81
|
+
if ((parsed.type === 'transfer' || parsed.type === 'transferChecked') &&
|
|
82
|
+
ix.program === 'spl-token') {
|
|
83
|
+
const info = parsed.info;
|
|
84
|
+
// Check if this is USDC (by mint address in transferChecked)
|
|
85
|
+
const isUsdc = info?.mint === usdcMint;
|
|
86
|
+
const amount = info?.amount || info?.tokenAmount?.amount;
|
|
87
|
+
if (amount && info?.destination) {
|
|
88
|
+
// We need to check if the destination ATA belongs to our address
|
|
89
|
+
// For simplicity, we check pre/post token balances
|
|
90
|
+
return {
|
|
91
|
+
amount: amount.toString(),
|
|
92
|
+
amountFormatted: isUsdc ? formatUsdc(amount.toString()) : amount.toString(),
|
|
93
|
+
asset: isUsdc ? 'USDC' : 'SPL',
|
|
94
|
+
from: info.source || info.authority || 'unknown',
|
|
95
|
+
transactionHash: signature,
|
|
96
|
+
chain: 'Solana',
|
|
97
|
+
caip2Id: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
98
|
+
timestamp: new Date((tx.blockTime || Date.now() / 1000) * 1000),
|
|
99
|
+
confirmations: 1,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
log(`[solana-watcher] ⚠️ Failed to inspect tx ${signature.slice(0, 16)}…: ${err.message}`);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function poll() {
|
|
112
|
+
if (!running)
|
|
113
|
+
return;
|
|
114
|
+
try {
|
|
115
|
+
const signatures = await getNewSignatures();
|
|
116
|
+
if (signatures.length > 0 && !lastSignature) {
|
|
117
|
+
// First poll — just record the latest signature
|
|
118
|
+
lastSignature = signatures[0];
|
|
119
|
+
log(`[solana-watcher] 🔍 Started watching from signature ${lastSignature.slice(0, 16)}… on Solana`);
|
|
120
|
+
schedulePoll();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (signatures.length === 0) {
|
|
124
|
+
schedulePoll();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
log(`[solana-watcher] 📦 Processing ${signatures.length} new transactions`);
|
|
128
|
+
for (const sig of signatures.reverse()) { // Oldest first
|
|
129
|
+
const payment = await inspectTransaction(sig);
|
|
130
|
+
if (payment) {
|
|
131
|
+
log(`[solana-watcher] 💰 Incoming: ${payment.amountFormatted} ${payment.asset} from ${payment.from.slice(0, 10)}…`);
|
|
132
|
+
config.onPayment(payment);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
lastSignature = signatures[0]; // Most recent
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
log(`[solana-watcher] ❌ Poll error: ${err.message}`);
|
|
139
|
+
config.onError?.(err);
|
|
140
|
+
}
|
|
141
|
+
schedulePoll();
|
|
142
|
+
}
|
|
143
|
+
function schedulePoll() {
|
|
144
|
+
if (running && pollInterval > 0) {
|
|
145
|
+
timer = setTimeout(poll, pollInterval);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Start polling
|
|
149
|
+
poll();
|
|
150
|
+
return () => {
|
|
151
|
+
running = false;
|
|
152
|
+
if (timer)
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
log(`[solana-watcher] 🛑 Stopped watching on Solana`);
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function formatSol(lamports) {
|
|
158
|
+
const n = BigInt(lamports);
|
|
159
|
+
const whole = n / 1000000000n;
|
|
160
|
+
const frac = ((n % 1000000000n) / 1000n).toString().padStart(6, '0');
|
|
161
|
+
return `${whole}.${frac}`;
|
|
162
|
+
}
|
|
163
|
+
function formatUsdc(atomicAmount) {
|
|
164
|
+
const n = BigInt(atomicAmount);
|
|
165
|
+
const whole = n / 1000000n;
|
|
166
|
+
const frac = (n % 1000000n).toString().padStart(6, '0');
|
|
167
|
+
return `${whole}.${frac}`;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=solana-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana-watcher.js","sourceRoot":"","sources":["../../../src/monitor/solana-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { noopLogger } from '../logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a Stellar payment watcher using Horizon SSE streaming.
|
|
4
|
+
*
|
|
5
|
+
* Listens for `payment` and `create_account` operations directed
|
|
6
|
+
* to the target account. Supports asset-based filtering.
|
|
7
|
+
*
|
|
8
|
+
* @returns Unsubscribe function to stop watching.
|
|
9
|
+
*/
|
|
10
|
+
export function createStellarWatcher(config) {
|
|
11
|
+
const log = config.logger ?? noopLogger;
|
|
12
|
+
const horizonUrl = config.horizonUrl ?? 'https://horizon.stellar.org';
|
|
13
|
+
const assetFilter = config.asset ?? 'all';
|
|
14
|
+
const cursor = config.cursor ?? 'now';
|
|
15
|
+
let abortController = null;
|
|
16
|
+
let running = true;
|
|
17
|
+
let reconnectTimer = null;
|
|
18
|
+
async function startStream() {
|
|
19
|
+
if (!running)
|
|
20
|
+
return;
|
|
21
|
+
const url = `${horizonUrl}/accounts/${config.accountId}/payments?cursor=${cursor}&order=asc`;
|
|
22
|
+
log(`[stellar-watcher] 🔍 Connecting to Horizon SSE: ${config.accountId.slice(0, 10)}…`);
|
|
23
|
+
abortController = new AbortController();
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(url, {
|
|
26
|
+
headers: { Accept: 'text/event-stream' },
|
|
27
|
+
signal: abortController.signal,
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(`Horizon HTTP ${response.status}: ${response.statusText}`);
|
|
31
|
+
}
|
|
32
|
+
if (!response.body) {
|
|
33
|
+
throw new Error('No response body for SSE stream');
|
|
34
|
+
}
|
|
35
|
+
const reader = response.body.getReader();
|
|
36
|
+
const decoder = new TextDecoder();
|
|
37
|
+
let buffer = '';
|
|
38
|
+
log(`[stellar-watcher] ✅ SSE connected, watching for payments…`);
|
|
39
|
+
while (running) {
|
|
40
|
+
const { done, value } = await reader.read();
|
|
41
|
+
if (done)
|
|
42
|
+
break;
|
|
43
|
+
buffer += decoder.decode(value, { stream: true });
|
|
44
|
+
// Parse SSE events
|
|
45
|
+
const lines = buffer.split('\n');
|
|
46
|
+
buffer = lines.pop() || '';
|
|
47
|
+
let currentData = '';
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (line.startsWith('data: ')) {
|
|
50
|
+
currentData += line.slice(6);
|
|
51
|
+
}
|
|
52
|
+
else if (line === '' && currentData) {
|
|
53
|
+
// End of event — process
|
|
54
|
+
if (currentData !== '"hello"' && currentData !== 'hello') {
|
|
55
|
+
processEvent(currentData);
|
|
56
|
+
}
|
|
57
|
+
currentData = '';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (err.name === 'AbortError') {
|
|
64
|
+
log('[stellar-watcher] 🛑 Stream aborted');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
log(`[stellar-watcher] ❌ Stream error: ${err.message}`);
|
|
68
|
+
config.onError?.(err);
|
|
69
|
+
// Reconnect after 5s
|
|
70
|
+
if (running) {
|
|
71
|
+
log('[stellar-watcher] 🔄 Reconnecting in 5s…');
|
|
72
|
+
reconnectTimer = setTimeout(startStream, 5000);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function processEvent(data) {
|
|
77
|
+
try {
|
|
78
|
+
const record = JSON.parse(data);
|
|
79
|
+
// Only process payments TO this account
|
|
80
|
+
if (record.to !== config.accountId)
|
|
81
|
+
return;
|
|
82
|
+
// Only process payment operations
|
|
83
|
+
if (record.type !== 'payment' && record.type !== 'create_account')
|
|
84
|
+
return;
|
|
85
|
+
// Determine asset
|
|
86
|
+
const asset = record.asset_type === 'native'
|
|
87
|
+
? 'XLM'
|
|
88
|
+
: record.asset_code || 'unknown';
|
|
89
|
+
// Apply asset filter
|
|
90
|
+
if (assetFilter !== 'all' && asset !== assetFilter)
|
|
91
|
+
return;
|
|
92
|
+
// Convert amount to atomic units (stroops = amount * 10^7)
|
|
93
|
+
const atomicAmount = Math.round(parseFloat(record.amount) * 10_000_000).toString();
|
|
94
|
+
const payment = {
|
|
95
|
+
amount: atomicAmount,
|
|
96
|
+
amountFormatted: record.amount,
|
|
97
|
+
asset,
|
|
98
|
+
from: record.from,
|
|
99
|
+
transactionHash: record.transaction_hash,
|
|
100
|
+
chain: 'Stellar',
|
|
101
|
+
caip2Id: 'stellar:pubnet',
|
|
102
|
+
timestamp: new Date(record.created_at),
|
|
103
|
+
};
|
|
104
|
+
log(`[stellar-watcher] 💰 Incoming: ${record.amount} ${asset} from ${record.from.slice(0, 10)}…`);
|
|
105
|
+
config.onPayment(payment);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
log(`[stellar-watcher] ⚠️ Failed to parse event: ${err.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Start the stream
|
|
112
|
+
startStream();
|
|
113
|
+
// Return unsubscribe
|
|
114
|
+
return () => {
|
|
115
|
+
running = false;
|
|
116
|
+
abortController?.abort();
|
|
117
|
+
if (reconnectTimer)
|
|
118
|
+
clearTimeout(reconnectTimer);
|
|
119
|
+
log(`[stellar-watcher] 🛑 Stopped watching ${config.accountId.slice(0, 10)}…`);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=stellar-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stellar-watcher.js","sourceRoot":"","sources":["../../../src/monitor/stellar-watcher.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/mpp.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPP — Machine Payments Protocol utilities.
|
|
3
|
+
*
|
|
4
|
+
* Handles parsing/building of MPP-standard headers:
|
|
5
|
+
* - WWW-Authenticate: Payment (Challenge)
|
|
6
|
+
* - Authorization: Payment (Credential)
|
|
7
|
+
* - Payment-Receipt (Receipt)
|
|
8
|
+
*
|
|
9
|
+
* @see https://mpp.dev/protocol/challenges
|
|
10
|
+
* @see https://mpp.dev/protocol/credentials
|
|
11
|
+
* @see https://mpp.dev/protocol/receipts
|
|
12
|
+
* @see https://paymentauth.org — IETF specification
|
|
13
|
+
*/
|
|
14
|
+
// ─── Base64url helpers ──────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Encode string to base64url (RFC 4648 §5).
|
|
17
|
+
* No padding, URL-safe alphabet.
|
|
18
|
+
*/
|
|
19
|
+
export function base64urlEncode(str) {
|
|
20
|
+
const b64 = Buffer.from(str, 'utf-8').toString('base64');
|
|
21
|
+
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Decode base64url string.
|
|
25
|
+
*/
|
|
26
|
+
export function base64urlDecode(str) {
|
|
27
|
+
// Restore standard base64
|
|
28
|
+
let b64 = str.replace(/-/g, '+').replace(/_/g, '/');
|
|
29
|
+
// Add padding
|
|
30
|
+
while (b64.length % 4 !== 0)
|
|
31
|
+
b64 += '=';
|
|
32
|
+
return Buffer.from(b64, 'base64').toString('utf-8');
|
|
33
|
+
}
|
|
34
|
+
// ─── Challenge parsing ──────────────────────────────────────────────
|
|
35
|
+
/**
|
|
36
|
+
* Parse a `WWW-Authenticate: Payment` header value into an MppChallenge.
|
|
37
|
+
*
|
|
38
|
+
* Format:
|
|
39
|
+
* ```
|
|
40
|
+
* Payment id="abc", realm="mpp.dev", method="stripe", intent="charge",
|
|
41
|
+
* expires="2025-01-15T12:05:00Z", request="eyJhbW91bnQ..."
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param header — Raw header value (with or without "Payment " prefix)
|
|
45
|
+
* @returns Parsed challenge object
|
|
46
|
+
* @throws Error if required fields are missing
|
|
47
|
+
*/
|
|
48
|
+
export function parseMppChallenge(header) {
|
|
49
|
+
// Strip "Payment " prefix if present
|
|
50
|
+
let raw = header.trim();
|
|
51
|
+
if (raw.startsWith('Payment ')) {
|
|
52
|
+
raw = raw.slice(8);
|
|
53
|
+
}
|
|
54
|
+
// Parse RFC 7235 auth-param format: key="value", key="value"
|
|
55
|
+
const params = parseAuthParams(raw);
|
|
56
|
+
// Validate required fields
|
|
57
|
+
const required = ['id', 'realm', 'method', 'intent', 'request'];
|
|
58
|
+
for (const key of required) {
|
|
59
|
+
if (!params[key]) {
|
|
60
|
+
throw new Error(`MPP Challenge missing required field: ${key}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
id: params.id,
|
|
65
|
+
realm: params.realm,
|
|
66
|
+
method: params.method,
|
|
67
|
+
intent: params.intent,
|
|
68
|
+
request: params.request,
|
|
69
|
+
expires: params.expires,
|
|
70
|
+
description: params.description,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse multiple `WWW-Authenticate: Payment` headers.
|
|
75
|
+
* Servers can offer multiple payment options.
|
|
76
|
+
*/
|
|
77
|
+
export function parseMppChallenges(headers) {
|
|
78
|
+
return headers.map(parseMppChallenge);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Decode the `request` field of a Challenge into a structured object.
|
|
82
|
+
*/
|
|
83
|
+
export function decodeChallengeRequest(challenge) {
|
|
84
|
+
const json = base64urlDecode(challenge.request);
|
|
85
|
+
return JSON.parse(json);
|
|
86
|
+
}
|
|
87
|
+
// ─── Credential building ────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Build an MPP Credential for the `Authorization: Payment` header.
|
|
90
|
+
*
|
|
91
|
+
* @param challenge — The challenge being responded to
|
|
92
|
+
* @param source — DID or identifier of the payer
|
|
93
|
+
* @param payload — Method-specific proof (SPT for Stripe, tx for Tempo)
|
|
94
|
+
* @returns Base64url-encoded credential string
|
|
95
|
+
*/
|
|
96
|
+
export function buildMppCredential(challenge, source, payload) {
|
|
97
|
+
const credential = {
|
|
98
|
+
challenge,
|
|
99
|
+
source,
|
|
100
|
+
payload,
|
|
101
|
+
};
|
|
102
|
+
return base64urlEncode(JSON.stringify(credential));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build the full `Authorization` header value.
|
|
106
|
+
*/
|
|
107
|
+
export function buildAuthorizationHeader(challenge, source, payload) {
|
|
108
|
+
return `Payment ${buildMppCredential(challenge, source, payload)}`;
|
|
109
|
+
}
|
|
110
|
+
// ─── Receipt parsing ────────────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Parse a `Payment-Receipt` header.
|
|
113
|
+
*/
|
|
114
|
+
export function parseMppReceipt(header) {
|
|
115
|
+
try {
|
|
116
|
+
const json = base64urlDecode(header.trim());
|
|
117
|
+
return JSON.parse(json);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return { raw: header };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ─── Protocol detection ─────────────────────────────────────────────
|
|
124
|
+
/**
|
|
125
|
+
* Detect whether a 402 response uses MPP or x402 protocol.
|
|
126
|
+
*
|
|
127
|
+
* MPP: has `WWW-Authenticate: Payment` header
|
|
128
|
+
* x402: has JSON body with `x402Version` and `accepts[]`
|
|
129
|
+
*/
|
|
130
|
+
export function detectProtocol(headers, body) {
|
|
131
|
+
// Check for MPP: WWW-Authenticate header with Payment scheme
|
|
132
|
+
const wwwAuth = headers['www-authenticate'] || headers['WWW-Authenticate'];
|
|
133
|
+
if (wwwAuth) {
|
|
134
|
+
const values = Array.isArray(wwwAuth) ? wwwAuth : [wwwAuth];
|
|
135
|
+
if (values.some((v) => v.trim().startsWith('Payment '))) {
|
|
136
|
+
return 'mpp';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Check for x402: JSON body with x402Version
|
|
140
|
+
if (body && typeof body === 'object' && ('x402Version' in body || 'accepts' in body)) {
|
|
141
|
+
return 'x402';
|
|
142
|
+
}
|
|
143
|
+
return 'unknown';
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Extract all MPP challenges from response headers.
|
|
147
|
+
* Handles both single and multiple WWW-Authenticate headers.
|
|
148
|
+
*/
|
|
149
|
+
export function extractMppChallenges(headers) {
|
|
150
|
+
const wwwAuth = headers['www-authenticate'] || headers['WWW-Authenticate'];
|
|
151
|
+
if (!wwwAuth)
|
|
152
|
+
return [];
|
|
153
|
+
const values = Array.isArray(wwwAuth) ? wwwAuth : [wwwAuth];
|
|
154
|
+
return values
|
|
155
|
+
.filter((v) => v.trim().startsWith('Payment '))
|
|
156
|
+
.map(parseMppChallenge);
|
|
157
|
+
}
|
|
158
|
+
// ─── Internal helpers ───────────────────────────────────────────────
|
|
159
|
+
/**
|
|
160
|
+
* Parse RFC 7235 auth-param format.
|
|
161
|
+
* `key="value", key="value"`
|
|
162
|
+
*/
|
|
163
|
+
function parseAuthParams(raw) {
|
|
164
|
+
const params = {};
|
|
165
|
+
// Match key="value" pairs, handling escaped quotes
|
|
166
|
+
const regex = /(\w+)="((?:[^"\\]|\\.)*)"/g;
|
|
167
|
+
let match;
|
|
168
|
+
while ((match = regex.exec(raw)) !== null) {
|
|
169
|
+
const [, key, value] = match;
|
|
170
|
+
// Unescape backslash-escaped characters
|
|
171
|
+
params[key] = value.replace(/\\(.)/g, '$1');
|
|
172
|
+
}
|
|
173
|
+
return params;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=mpp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mpp.js","sourceRoot":"","sources":["../../src/mpp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PolicyEngine — On-device budget controller for autonomous AI agents.
|
|
3
|
+
*
|
|
4
|
+
* Enforces per-transaction caps, monthly rolling budgets, and optional
|
|
5
|
+
* destination whitelists so that an agent can never overspend without
|
|
6
|
+
* explicit human approval.
|
|
7
|
+
*
|
|
8
|
+
* Fail-closed: if any check fails, the payment is rejected.
|
|
9
|
+
*/
|
|
10
|
+
import { noopLogger } from './logger.js';
|
|
11
|
+
export class PolicyEngine {
|
|
12
|
+
policy;
|
|
13
|
+
currentMonthSpent = 0;
|
|
14
|
+
log;
|
|
15
|
+
constructor(policy, logger) {
|
|
16
|
+
this.policy = policy;
|
|
17
|
+
this.log = logger ?? noopLogger;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns `true` if the proposed spend passes every policy gate.
|
|
21
|
+
* Fail-closed: any unrecognised state returns `false`.
|
|
22
|
+
*/
|
|
23
|
+
checkPolicy(amountUsd, destination) {
|
|
24
|
+
if (amountUsd <= 0) {
|
|
25
|
+
this.log(`[Policy] 🚫 Invalid amount: $${amountUsd}`);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (amountUsd > this.policy.maxAmountPerTransaction) {
|
|
29
|
+
this.log(`[Policy] 🚫 $${amountUsd} exceeds per-tx limit of $${this.policy.maxAmountPerTransaction}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (this.currentMonthSpent + amountUsd > this.policy.monthlyBudget) {
|
|
33
|
+
this.log(`[Policy] 🚫 $${amountUsd} would exceed monthly budget of $${this.policy.monthlyBudget} (spent: $${this.currentMonthSpent})`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (this.policy.allowedDestinations &&
|
|
37
|
+
this.policy.allowedDestinations.length > 0 &&
|
|
38
|
+
destination &&
|
|
39
|
+
!this.policy.allowedDestinations.includes(destination)) {
|
|
40
|
+
this.log(`[Policy] 🚫 Destination ${destination} not in whitelist`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/** Record a successful spend against the rolling budget. */
|
|
46
|
+
recordSpend(amountUsd) {
|
|
47
|
+
this.currentMonthSpent += amountUsd;
|
|
48
|
+
this.log(`[Policy] 📊 Spent $${amountUsd} — total: $${this.currentMonthSpent}/$${this.policy.monthlyBudget}`);
|
|
49
|
+
}
|
|
50
|
+
/** Get total USD spent in the current period. */
|
|
51
|
+
getSpent() {
|
|
52
|
+
return this.currentMonthSpent;
|
|
53
|
+
}
|
|
54
|
+
/** Get remaining USD budget. */
|
|
55
|
+
getRemainingBudget() {
|
|
56
|
+
return Math.max(0, this.policy.monthlyBudget - this.currentMonthSpent);
|
|
57
|
+
}
|
|
58
|
+
/** Reset the spend counter (e.g. on month rollover or for testing). */
|
|
59
|
+
resetBudget() {
|
|
60
|
+
this.currentMonthSpent = 0;
|
|
61
|
+
}
|
|
62
|
+
/** Get a snapshot of the current policy configuration. */
|
|
63
|
+
getPolicy() {
|
|
64
|
+
return { ...this.policy };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAU,UAAU,EAAE,MAAM,UAAU,CAAC;AAW9C,MAAM,OAAO,YAAY;IAKb;IAJF,iBAAiB,GAAG,CAAC,CAAC;IACtB,GAAG,CAAS;IAEpB,YACU,MAAoB,EAC5B,MAAe;QADP,WAAM,GAAN,MAAM,CAAc;QAG5B,IAAI,CAAC,GAAG,GAAG,MAAM,IAAI,UAAU,CAAC;IAClC,CAAC;IAED;;;OAGG;IACI,WAAW,CAAC,SAAiB,EAAE,WAAoB;QACxD,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,gCAAgC,SAAS,EAAE,CAAC,CAAC;YACtD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC;YACpD,IAAI,CAAC,GAAG,CACN,gBAAgB,SAAS,6BAA6B,IAAI,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAC5F,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,IAAI,CAAC,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YACnE,IAAI,CAAC,GAAG,CACN,gBAAgB,SAAS,oCAAoC,IAAI,CAAC,MAAM,CAAC,aAAa,aAAa,IAAI,CAAC,iBAAiB,GAAG,CAC7H,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IACE,IAAI,CAAC,MAAM,CAAC,mBAAmB;YAC/B,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC;YAC1C,WAAW;YACX,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,WAAW,CAAC,EACtD,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,2BAA2B,WAAW,mBAAmB,CAAC,CAAC;YACpE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4DAA4D;IACrD,WAAW,CAAC,SAAiB;QAClC,IAAI,CAAC,iBAAiB,IAAI,SAAS,CAAC;QACpC,IAAI,CAAC,GAAG,CACN,sBAAsB,SAAS,cAAc,IAAI,CAAC,iBAAiB,KAAK,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CACpG,CAAC;IACJ,CAAC;IAED,iDAAiD;IAC1C,QAAQ;QACb,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAED,gCAAgC;IACzB,kBAAkB;QACvB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACzE,CAAC;IAED,uEAAuE;IAChE,WAAW;QAChB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,0DAA0D;IACnD,SAAS;QACd,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;CACF"}
|