agentwallet-sdk 5.0.0 → 5.0.2
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/README.md +221 -0
- package/dist/abi.d.ts +397 -0
- package/dist/abi.d.ts.map +1 -0
- package/dist/abi.js +255 -0
- package/dist/abi.js.map +1 -0
- package/dist/ap2/index.d.ts +185 -0
- package/dist/ap2/index.d.ts.map +1 -0
- package/dist/ap2/index.js +255 -0
- package/dist/ap2/index.js.map +1 -0
- package/dist/bridge/abis.d.ts +64 -0
- package/dist/bridge/abis.d.ts.map +1 -0
- package/dist/bridge/abis.js +129 -0
- package/dist/bridge/abis.js.map +1 -0
- package/dist/bridge/client.d.ts +35 -0
- package/dist/bridge/client.d.ts.map +1 -0
- package/dist/bridge/client.js +245 -0
- package/dist/bridge/client.js.map +1 -0
- package/dist/bridge/index.d.ts +4 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +3 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/types.d.ts +68 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +93 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/bridge/unified.d.ts +101 -0
- package/dist/bridge/unified.d.ts.map +1 -0
- package/dist/bridge/unified.js +284 -0
- package/dist/bridge/unified.js.map +1 -0
- package/dist/chains.d.ts +62 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +108 -0
- package/dist/chains.js.map +1 -0
- package/dist/escrow/MutualStakeEscrow.d.ts +75 -0
- package/dist/escrow/MutualStakeEscrow.d.ts.map +1 -0
- package/dist/escrow/MutualStakeEscrow.js +339 -0
- package/dist/escrow/MutualStakeEscrow.js.map +1 -0
- package/dist/escrow/types.d.ts +59 -0
- package/dist/escrow/types.d.ts.map +1 -0
- package/dist/escrow/types.js +11 -0
- package/dist/escrow/types.js.map +1 -0
- package/dist/escrow/verifiers.d.ts +26 -0
- package/dist/escrow/verifiers.d.ts.map +1 -0
- package/dist/escrow/verifiers.js +53 -0
- package/dist/escrow/verifiers.js.map +1 -0
- package/dist/fiat/index.d.ts +10 -0
- package/dist/fiat/index.d.ts.map +1 -0
- package/dist/fiat/index.js +9 -0
- package/dist/fiat/index.js.map +1 -0
- package/dist/fiat/onramp.d.ts +101 -0
- package/dist/fiat/onramp.d.ts.map +1 -0
- package/dist/fiat/onramp.js +155 -0
- package/dist/fiat/onramp.js.map +1 -0
- package/dist/fiat/providers/index.d.ts +16 -0
- package/dist/fiat/providers/index.d.ts.map +1 -0
- package/dist/fiat/providers/index.js +30 -0
- package/dist/fiat/providers/index.js.map +1 -0
- package/dist/fiat/providers/moonpay.d.ts +22 -0
- package/dist/fiat/providers/moonpay.d.ts.map +1 -0
- package/dist/fiat/providers/moonpay.js +107 -0
- package/dist/fiat/providers/moonpay.js.map +1 -0
- package/dist/fiat/providers/stripe.d.ts +26 -0
- package/dist/fiat/providers/stripe.d.ts.map +1 -0
- package/dist/fiat/providers/stripe.js +135 -0
- package/dist/fiat/providers/stripe.js.map +1 -0
- package/dist/fiat/providers/transak.d.ts +26 -0
- package/dist/fiat/providers/transak.d.ts.map +1 -0
- package/dist/fiat/providers/transak.js +119 -0
- package/dist/fiat/providers/transak.js.map +1 -0
- package/dist/fiat/types.d.ts +106 -0
- package/dist/fiat/types.d.ts.map +1 -0
- package/dist/fiat/types.js +13 -0
- package/dist/fiat/types.js.map +1 -0
- package/dist/flash/executor.d.ts +119 -0
- package/dist/flash/executor.d.ts.map +1 -0
- package/dist/flash/executor.js +195 -0
- package/dist/flash/executor.js.map +1 -0
- package/dist/flash/index.d.ts +28 -0
- package/dist/flash/index.d.ts.map +1 -0
- package/dist/flash/index.js +29 -0
- package/dist/flash/index.js.map +1 -0
- package/dist/flash/scanner.d.ts +133 -0
- package/dist/flash/scanner.d.ts.map +1 -0
- package/dist/flash/scanner.js +212 -0
- package/dist/flash/scanner.js.map +1 -0
- package/dist/flash/types.d.ts +136 -0
- package/dist/flash/types.d.ts.map +1 -0
- package/dist/flash/types.js +23 -0
- package/dist/flash/types.js.map +1 -0
- package/dist/gas/index.d.ts +4 -0
- package/dist/gas/index.d.ts.map +1 -0
- package/dist/gas/index.js +3 -0
- package/dist/gas/index.js.map +1 -0
- package/dist/gas/sponsor.d.ts +70 -0
- package/dist/gas/sponsor.d.ts.map +1 -0
- package/dist/gas/sponsor.js +193 -0
- package/dist/gas/sponsor.js.map +1 -0
- package/dist/gas/types.d.ts +76 -0
- package/dist/gas/types.d.ts.map +1 -0
- package/dist/gas/types.js +21 -0
- package/dist/gas/types.js.map +1 -0
- package/dist/identity/agent-identity.d.ts +276 -0
- package/dist/identity/agent-identity.d.ts.map +1 -0
- package/dist/identity/agent-identity.js +300 -0
- package/dist/identity/agent-identity.js.map +1 -0
- package/dist/identity/erc6551.d.ts +441 -0
- package/dist/identity/erc6551.d.ts.map +1 -0
- package/dist/identity/erc6551.js +517 -0
- package/dist/identity/erc6551.js.map +1 -0
- package/dist/identity/erc8004.d.ts +305 -0
- package/dist/identity/erc8004.d.ts.map +1 -0
- package/dist/identity/erc8004.js +413 -0
- package/dist/identity/erc8004.js.map +1 -0
- package/dist/identity/index.d.ts +7 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +4 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/reputation.d.ts +318 -0
- package/dist/identity/reputation.d.ts.map +1 -0
- package/dist/identity/reputation.js +272 -0
- package/dist/identity/reputation.js.map +1 -0
- package/dist/identity/validation.d.ts +284 -0
- package/dist/identity/validation.d.ts.map +1 -0
- package/dist/identity/validation.js +226 -0
- package/dist/identity/validation.js.map +1 -0
- package/dist/index.d.ts +38803 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +376 -0
- package/dist/index.js.map +1 -0
- package/dist/mev/index.d.ts +4 -0
- package/dist/mev/index.d.ts.map +1 -0
- package/dist/mev/index.js +8 -0
- package/dist/mev/index.js.map +1 -0
- package/dist/mev/protection.d.ts +54 -0
- package/dist/mev/protection.d.ts.map +1 -0
- package/dist/mev/protection.js +185 -0
- package/dist/mev/protection.js.map +1 -0
- package/dist/mev/risk.d.ts +19 -0
- package/dist/mev/risk.d.ts.map +1 -0
- package/dist/mev/risk.js +95 -0
- package/dist/mev/risk.js.map +1 -0
- package/dist/mev/types.d.ts +49 -0
- package/dist/mev/types.d.ts.map +1 -0
- package/dist/mev/types.js +2 -0
- package/dist/mev/types.js.map +1 -0
- package/dist/plugins/elizaos.d.ts +52 -0
- package/dist/plugins/elizaos.d.ts.map +1 -0
- package/dist/plugins/elizaos.js +89 -0
- package/dist/plugins/elizaos.js.map +1 -0
- package/dist/policy/SpendingPolicy.d.ts +106 -0
- package/dist/policy/SpendingPolicy.d.ts.map +1 -0
- package/dist/policy/SpendingPolicy.js +155 -0
- package/dist/policy/SpendingPolicy.js.map +1 -0
- package/dist/policy/SpendingPolicy.test.d.ts +2 -0
- package/dist/policy/SpendingPolicy.test.d.ts.map +1 -0
- package/dist/policy/SpendingPolicy.test.js +143 -0
- package/dist/policy/SpendingPolicy.test.js.map +1 -0
- package/dist/settlement/index.d.ts +4 -0
- package/dist/settlement/index.d.ts.map +1 -0
- package/dist/settlement/index.js +3 -0
- package/dist/settlement/index.js.map +1 -0
- package/dist/settlement/types.d.ts +66 -0
- package/dist/settlement/types.d.ts.map +1 -0
- package/dist/settlement/types.js +37 -0
- package/dist/settlement/types.js.map +1 -0
- package/dist/settlement/verifier.d.ts +75 -0
- package/dist/settlement/verifier.d.ts.map +1 -0
- package/dist/settlement/verifier.js +354 -0
- package/dist/settlement/verifier.js.map +1 -0
- package/dist/solana/bridge.d.ts +144 -0
- package/dist/solana/bridge.d.ts.map +1 -0
- package/dist/solana/bridge.js +352 -0
- package/dist/solana/bridge.js.map +1 -0
- package/dist/solana/index.d.ts +8 -0
- package/dist/solana/index.d.ts.map +1 -0
- package/dist/solana/index.js +6 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/solana/swap.d.ts +85 -0
- package/dist/solana/swap.d.ts.map +1 -0
- package/dist/solana/swap.js +173 -0
- package/dist/solana/swap.js.map +1 -0
- package/dist/solana/types.d.ts +126 -0
- package/dist/solana/types.d.ts.map +1 -0
- package/dist/solana/types.js +10 -0
- package/dist/solana/types.js.map +1 -0
- package/dist/solana/wallet.d.ts +83 -0
- package/dist/solana/wallet.d.ts.map +1 -0
- package/dist/solana/wallet.js +164 -0
- package/dist/solana/wallet.js.map +1 -0
- package/dist/solana/x402.d.ts +69 -0
- package/dist/solana/x402.d.ts.map +1 -0
- package/dist/solana/x402.js +154 -0
- package/dist/solana/x402.js.map +1 -0
- package/dist/solver/adapter.d.ts +47 -0
- package/dist/solver/adapter.d.ts.map +1 -0
- package/dist/solver/adapter.js +146 -0
- package/dist/solver/adapter.js.map +1 -0
- package/dist/solver/analyzer.d.ts +48 -0
- package/dist/solver/analyzer.d.ts.map +1 -0
- package/dist/solver/analyzer.js +171 -0
- package/dist/solver/analyzer.js.map +1 -0
- package/dist/solver/builder.d.ts +31 -0
- package/dist/solver/builder.d.ts.map +1 -0
- package/dist/solver/builder.js +60 -0
- package/dist/solver/builder.js.map +1 -0
- package/dist/solver/index.d.ts +22 -0
- package/dist/solver/index.d.ts.map +1 -0
- package/dist/solver/index.js +25 -0
- package/dist/solver/index.js.map +1 -0
- package/dist/solver/types.d.ts +115 -0
- package/dist/solver/types.d.ts.map +1 -0
- package/dist/solver/types.js +10 -0
- package/dist/solver/types.js.map +1 -0
- package/dist/spend-guard/index.d.ts +125 -0
- package/dist/spend-guard/index.d.ts.map +1 -0
- package/dist/spend-guard/index.js +150 -0
- package/dist/spend-guard/index.js.map +1 -0
- package/dist/swap/SwapModule.d.ts +34 -0
- package/dist/swap/SwapModule.d.ts.map +1 -0
- package/dist/swap/SwapModule.js +144 -0
- package/dist/swap/SwapModule.js.map +1 -0
- package/dist/swap/abi.d.ts +51 -0
- package/dist/swap/abi.d.ts.map +1 -0
- package/dist/swap/abi.js +118 -0
- package/dist/swap/abi.js.map +1 -0
- package/dist/swap/index.d.ts +5 -0
- package/dist/swap/index.d.ts.map +1 -0
- package/dist/swap/index.js +4 -0
- package/dist/swap/index.js.map +1 -0
- package/dist/swap/router/cache.d.ts +13 -0
- package/dist/swap/router/cache.d.ts.map +1 -0
- package/dist/swap/router/cache.js +30 -0
- package/dist/swap/router/cache.js.map +1 -0
- package/dist/swap/router/flashbots.d.ts +10 -0
- package/dist/swap/router/flashbots.d.ts.map +1 -0
- package/dist/swap/router/flashbots.js +43 -0
- package/dist/swap/router/flashbots.js.map +1 -0
- package/dist/swap/router/health.d.ts +17 -0
- package/dist/swap/router/health.d.ts.map +1 -0
- package/dist/swap/router/health.js +38 -0
- package/dist/swap/router/health.js.map +1 -0
- package/dist/swap/router/index.d.ts +10 -0
- package/dist/swap/router/index.d.ts.map +1 -0
- package/dist/swap/router/index.js +10 -0
- package/dist/swap/router/index.js.map +1 -0
- package/dist/swap/router/providers/cowswap.d.ts +11 -0
- package/dist/swap/router/providers/cowswap.d.ts.map +1 -0
- package/dist/swap/router/providers/cowswap.js +79 -0
- package/dist/swap/router/providers/cowswap.js.map +1 -0
- package/dist/swap/router/providers/index.d.ts +20 -0
- package/dist/swap/router/providers/index.d.ts.map +1 -0
- package/dist/swap/router/providers/index.js +32 -0
- package/dist/swap/router/providers/index.js.map +1 -0
- package/dist/swap/router/providers/jupiter.d.ts +12 -0
- package/dist/swap/router/providers/jupiter.d.ts.map +1 -0
- package/dist/swap/router/providers/jupiter.js +73 -0
- package/dist/swap/router/providers/jupiter.js.map +1 -0
- package/dist/swap/router/providers/lifi.d.ts +11 -0
- package/dist/swap/router/providers/lifi.d.ts.map +1 -0
- package/dist/swap/router/providers/lifi.js +123 -0
- package/dist/swap/router/providers/lifi.js.map +1 -0
- package/dist/swap/router/providers/oneinch.d.ts +13 -0
- package/dist/swap/router/providers/oneinch.d.ts.map +1 -0
- package/dist/swap/router/providers/oneinch.js +71 -0
- package/dist/swap/router/providers/oneinch.js.map +1 -0
- package/dist/swap/router/providers/paraswap.d.ts +11 -0
- package/dist/swap/router/providers/paraswap.d.ts.map +1 -0
- package/dist/swap/router/providers/paraswap.js +73 -0
- package/dist/swap/router/providers/paraswap.js.map +1 -0
- package/dist/swap/router/providers/uniswap.d.ts +31 -0
- package/dist/swap/router/providers/uniswap.d.ts.map +1 -0
- package/dist/swap/router/providers/uniswap.js +237 -0
- package/dist/swap/router/providers/uniswap.js.map +1 -0
- package/dist/swap/router/providers/zerox.d.ts +13 -0
- package/dist/swap/router/providers/zerox.d.ts.map +1 -0
- package/dist/swap/router/providers/zerox.js +94 -0
- package/dist/swap/router/providers/zerox.js.map +1 -0
- package/dist/swap/router/router.d.ts +86 -0
- package/dist/swap/router/router.d.ts.map +1 -0
- package/dist/swap/router/router.js +224 -0
- package/dist/swap/router/router.js.map +1 -0
- package/dist/swap/router/rsi/engine.d.ts +60 -0
- package/dist/swap/router/rsi/engine.d.ts.map +1 -0
- package/dist/swap/router/rsi/engine.js +483 -0
- package/dist/swap/router/rsi/engine.js.map +1 -0
- package/dist/swap/router/rsi/index.d.ts +3 -0
- package/dist/swap/router/rsi/index.d.ts.map +1 -0
- package/dist/swap/router/rsi/index.js +3 -0
- package/dist/swap/router/rsi/index.js.map +1 -0
- package/dist/swap/router/rsi/types.d.ts +106 -0
- package/dist/swap/router/rsi/types.d.ts.map +1 -0
- package/dist/swap/router/rsi/types.js +3 -0
- package/dist/swap/router/rsi/types.js.map +1 -0
- package/dist/swap/router/types.d.ts +120 -0
- package/dist/swap/router/types.d.ts.map +1 -0
- package/dist/swap/router/types.js +16 -0
- package/dist/swap/router/types.js.map +1 -0
- package/dist/swap/types.d.ts +51 -0
- package/dist/swap/types.d.ts.map +1 -0
- package/dist/swap/types.js +17 -0
- package/dist/swap/types.js.map +1 -0
- package/dist/tax/engine.d.ts +131 -0
- package/dist/tax/engine.d.ts.map +1 -0
- package/dist/tax/engine.js +307 -0
- package/dist/tax/engine.js.map +1 -0
- package/dist/tax/index.d.ts +9 -0
- package/dist/tax/index.d.ts.map +1 -0
- package/dist/tax/index.js +12 -0
- package/dist/tax/index.js.map +1 -0
- package/dist/tax/lots.d.ts +60 -0
- package/dist/tax/lots.d.ts.map +1 -0
- package/dist/tax/lots.js +129 -0
- package/dist/tax/lots.js.map +1 -0
- package/dist/tax/types.d.ts +113 -0
- package/dist/tax/types.d.ts.map +1 -0
- package/dist/tax/types.js +18 -0
- package/dist/tax/types.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/x402/__tests__/budget.test.d.ts +2 -0
- package/dist/x402/__tests__/budget.test.d.ts.map +1 -0
- package/dist/x402/__tests__/budget.test.js +114 -0
- package/dist/x402/__tests__/budget.test.js.map +1 -0
- package/dist/x402/__tests__/client.test.d.ts +2 -0
- package/dist/x402/__tests__/client.test.d.ts.map +1 -0
- package/dist/x402/__tests__/client.test.js +107 -0
- package/dist/x402/__tests__/client.test.js.map +1 -0
- package/dist/x402/budget.d.ts +52 -0
- package/dist/x402/budget.d.ts.map +1 -0
- package/dist/x402/budget.js +113 -0
- package/dist/x402/budget.js.map +1 -0
- package/dist/x402/chains/abstract/index.d.ts +135 -0
- package/dist/x402/chains/abstract/index.d.ts.map +1 -0
- package/dist/x402/chains/abstract/index.js +190 -0
- package/dist/x402/chains/abstract/index.js.map +1 -0
- package/dist/x402/client.d.ts +60 -0
- package/dist/x402/client.d.ts.map +1 -0
- package/dist/x402/client.js +205 -0
- package/dist/x402/client.js.map +1 -0
- package/dist/x402/index.d.ts +8 -0
- package/dist/x402/index.d.ts.map +1 -0
- package/dist/x402/index.js +8 -0
- package/dist/x402/index.js.map +1 -0
- package/dist/x402/middleware.d.ts +37 -0
- package/dist/x402/middleware.d.ts.map +1 -0
- package/dist/x402/middleware.js +65 -0
- package/dist/x402/middleware.js.map +1 -0
- package/dist/x402/types.d.ts +91 -0
- package/dist/x402/types.d.ts.map +1 -0
- package/dist/x402/types.js +9 -0
- package/dist/x402/types.js.map +1 -0
- package/dist/yield/index.d.ts +26 -0
- package/dist/yield/index.d.ts.map +1 -0
- package/dist/yield/index.js +29 -0
- package/dist/yield/index.js.map +1 -0
- package/dist/yield/rates.d.ts +114 -0
- package/dist/yield/rates.d.ts.map +1 -0
- package/dist/yield/rates.js +351 -0
- package/dist/yield/rates.js.map +1 -0
- package/dist/yield/types.d.ts +134 -0
- package/dist/yield/types.d.ts.map +1 -0
- package/dist/yield/types.js +24 -0
- package/dist/yield/types.js.map +1 -0
- package/dist/yield/vault.d.ts +112 -0
- package/dist/yield/vault.d.ts.map +1 -0
- package/dist/yield/vault.js +264 -0
- package/dist/yield/vault.js.map +1 -0
- package/package.json +46 -7
- package/index.js +0 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpendingPolicy — Programmable spending guardrails for AI agents.
|
|
3
|
+
*
|
|
4
|
+
* Beats PolicyLayer.com to market with:
|
|
5
|
+
* - MerchantAllowlist — allowlist-only merchant enforcement
|
|
6
|
+
* - RollingSpendCap — time-windowed spend limits
|
|
7
|
+
* - DraftThenApprove — human-in-the-loop for large transactions
|
|
8
|
+
* - AuditTrail — immutable local log of every payment attempt
|
|
9
|
+
* - FailClosed — policy errors always reject, never approve
|
|
10
|
+
*
|
|
11
|
+
* @module policy/SpendingPolicy
|
|
12
|
+
*/
|
|
13
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
14
|
+
let _idCounter = 0;
|
|
15
|
+
function nextId(prefix) {
|
|
16
|
+
return `${prefix}-${Date.now()}-${++_idCounter}`;
|
|
17
|
+
}
|
|
18
|
+
// ─── SpendingPolicy ───────────────────────────────────────────────────────────
|
|
19
|
+
export class SpendingPolicy {
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.allowlist = new Set((config.merchantAllowlist ?? []).map((m) => m.toLowerCase()));
|
|
23
|
+
this.spendWindow = [];
|
|
24
|
+
this.auditLog = [];
|
|
25
|
+
this.drafts = new Map();
|
|
26
|
+
}
|
|
27
|
+
// ─── Public Interface ───────────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* FailClosed: wraps the actual check logic so that ANY unhandled error
|
|
30
|
+
* produces a rejection rather than inadvertently approving a payment.
|
|
31
|
+
*/
|
|
32
|
+
async check(payment) {
|
|
33
|
+
try {
|
|
34
|
+
return await this._check(payment);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
const reason = `Policy engine error (fail-closed): ${err instanceof Error ? err.message : String(err)}`;
|
|
38
|
+
const result = { status: 'rejected', reason };
|
|
39
|
+
await this.log(payment, result).catch(() => { });
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Write a payment attempt to the immutable audit log.
|
|
45
|
+
*/
|
|
46
|
+
async log(payment, result) {
|
|
47
|
+
const entry = {
|
|
48
|
+
id: nextId('audit'),
|
|
49
|
+
timestamp: payment.timestamp ?? new Date().toISOString(),
|
|
50
|
+
merchant: payment.merchant,
|
|
51
|
+
amount: payment.amount,
|
|
52
|
+
status: result.status,
|
|
53
|
+
reason: result.reason,
|
|
54
|
+
draftId: result.draftId,
|
|
55
|
+
};
|
|
56
|
+
this.auditLog.push(entry);
|
|
57
|
+
}
|
|
58
|
+
/** Return a copy of the current merchant allowlist. */
|
|
59
|
+
getMerchantAllowlist() {
|
|
60
|
+
return Array.from(this.allowlist);
|
|
61
|
+
}
|
|
62
|
+
/** Add a merchant address/domain to the allowlist at runtime. */
|
|
63
|
+
addMerchant(address) {
|
|
64
|
+
this.allowlist.add(address.toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
/** Return the full, immutable audit log (copy). */
|
|
67
|
+
getAuditLog() {
|
|
68
|
+
return [...this.auditLog];
|
|
69
|
+
}
|
|
70
|
+
// ─── Draft queue management ─────────────────────────────────────────────────
|
|
71
|
+
/** Approve a queued draft by its draftId. Returns false if not found. */
|
|
72
|
+
approveDraft(draftId) {
|
|
73
|
+
const draft = this.drafts.get(draftId);
|
|
74
|
+
if (!draft)
|
|
75
|
+
return false;
|
|
76
|
+
draft.approved = true;
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
/** Reject a queued draft by its draftId. Returns false if not found. */
|
|
80
|
+
rejectDraft(draftId) {
|
|
81
|
+
const draft = this.drafts.get(draftId);
|
|
82
|
+
if (!draft)
|
|
83
|
+
return false;
|
|
84
|
+
draft.rejected = true;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
/** Return all pending (not yet approved or rejected) drafts. */
|
|
88
|
+
getPendingDrafts() {
|
|
89
|
+
return Array.from(this.drafts.values()).filter((d) => !d.approved && !d.rejected);
|
|
90
|
+
}
|
|
91
|
+
/** Return all drafts. */
|
|
92
|
+
getAllDrafts() {
|
|
93
|
+
return Array.from(this.drafts.values());
|
|
94
|
+
}
|
|
95
|
+
// ─── Private Logic ──────────────────────────────────────────────────────────
|
|
96
|
+
async _check(payment) {
|
|
97
|
+
// 1. MerchantAllowlist check (only enforced when allowlist is non-empty)
|
|
98
|
+
if (this.allowlist.size > 0) {
|
|
99
|
+
const merchant = payment.merchant.toLowerCase();
|
|
100
|
+
if (!this.allowlist.has(merchant)) {
|
|
101
|
+
const result = {
|
|
102
|
+
status: 'rejected',
|
|
103
|
+
reason: `Merchant "${payment.merchant}" is not on the allowlist.`,
|
|
104
|
+
};
|
|
105
|
+
await this.log(payment, result);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// 2. RollingSpendCap check
|
|
110
|
+
if (this.config.rollingCap) {
|
|
111
|
+
const { maxAmount, windowMs } = this.config.rollingCap;
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
const windowStart = now - windowMs;
|
|
114
|
+
// Purge entries outside the window
|
|
115
|
+
this.spendWindow = this.spendWindow.filter((e) => e.ts >= windowStart);
|
|
116
|
+
const spent = this.spendWindow.reduce((sum, e) => sum + e.amount, 0);
|
|
117
|
+
if (spent + payment.amount > maxAmount) {
|
|
118
|
+
const result = {
|
|
119
|
+
status: 'rejected',
|
|
120
|
+
reason: `Rolling spend cap exceeded: spent ${spent}, cap ${maxAmount}, attempted ${payment.amount}.`,
|
|
121
|
+
};
|
|
122
|
+
await this.log(payment, result);
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// 3. DraftThenApprove check
|
|
127
|
+
if (this.config.draftThreshold !== undefined &&
|
|
128
|
+
payment.amount >= this.config.draftThreshold) {
|
|
129
|
+
const draftId = nextId('draft');
|
|
130
|
+
const draft = {
|
|
131
|
+
draftId,
|
|
132
|
+
payment,
|
|
133
|
+
queuedAt: payment.timestamp ?? new Date().toISOString(),
|
|
134
|
+
approved: false,
|
|
135
|
+
rejected: false,
|
|
136
|
+
};
|
|
137
|
+
this.drafts.set(draftId, draft);
|
|
138
|
+
const result = {
|
|
139
|
+
status: 'draft',
|
|
140
|
+
reason: `Amount ${payment.amount} meets or exceeds draft threshold ${this.config.draftThreshold}. Awaiting approval.`,
|
|
141
|
+
draftId,
|
|
142
|
+
};
|
|
143
|
+
await this.log(payment, result);
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
// 4. Approved — record spend in rolling window
|
|
147
|
+
if (this.config.rollingCap) {
|
|
148
|
+
this.spendWindow.push({ amount: payment.amount, ts: Date.now() });
|
|
149
|
+
}
|
|
150
|
+
const result = { status: 'approved' };
|
|
151
|
+
await this.log(payment, result);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=SpendingPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpendingPolicy.js","sourceRoot":"","sources":["../../src/policy/SpendingPolicy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpendingPolicy.test.d.ts","sourceRoot":"","sources":["../../src/policy/SpendingPolicy.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { SpendingPolicy } from './SpendingPolicy.js';
|
|
3
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
4
|
+
function makePayment(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
merchant: '0xAllowedMerchant',
|
|
7
|
+
amount: 10,
|
|
8
|
+
...overrides,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function makePolicy(config) {
|
|
12
|
+
return new SpendingPolicy(config);
|
|
13
|
+
}
|
|
14
|
+
// ─── MerchantAllowlist ────────────────────────────────────────────────────────
|
|
15
|
+
describe('MerchantAllowlist', () => {
|
|
16
|
+
it('allowlisted merchant passes', async () => {
|
|
17
|
+
const policy = makePolicy({ merchantAllowlist: ['0xallowedmerchant'] });
|
|
18
|
+
const result = await policy.check(makePayment({ merchant: '0xAllowedMerchant' }));
|
|
19
|
+
expect(result.status).toBe('approved');
|
|
20
|
+
});
|
|
21
|
+
it('non-allowlisted merchant is blocked', async () => {
|
|
22
|
+
const policy = makePolicy({ merchantAllowlist: ['0xallowedmerchant'] });
|
|
23
|
+
const result = await policy.check(makePayment({ merchant: '0xEvilMerchant' }));
|
|
24
|
+
expect(result.status).toBe('rejected');
|
|
25
|
+
expect(result.reason).toMatch(/not on the allowlist/i);
|
|
26
|
+
});
|
|
27
|
+
it('addMerchant allows previously blocked merchant', async () => {
|
|
28
|
+
const policy = makePolicy({ merchantAllowlist: ['0xallowedmerchant'] });
|
|
29
|
+
policy.addMerchant('0xNewMerchant');
|
|
30
|
+
const result = await policy.check(makePayment({ merchant: '0xNewMerchant' }));
|
|
31
|
+
expect(result.status).toBe('approved');
|
|
32
|
+
});
|
|
33
|
+
it('getMerchantAllowlist returns current allowlist', () => {
|
|
34
|
+
const policy = makePolicy({ merchantAllowlist: ['0xA', '0xB'] });
|
|
35
|
+
const list = policy.getMerchantAllowlist();
|
|
36
|
+
expect(list).toContain('0xa');
|
|
37
|
+
expect(list).toContain('0xb');
|
|
38
|
+
expect(list.length).toBe(2);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
// ─── RollingSpendCap ──────────────────────────────────────────────────────────
|
|
42
|
+
describe('RollingSpendCap', () => {
|
|
43
|
+
it('allows payment under the rolling cap', async () => {
|
|
44
|
+
const policy = makePolicy({
|
|
45
|
+
rollingCap: { maxAmount: 100, windowMs: 86400000 },
|
|
46
|
+
});
|
|
47
|
+
const result = await policy.check(makePayment({ amount: 50 }));
|
|
48
|
+
expect(result.status).toBe('approved');
|
|
49
|
+
});
|
|
50
|
+
it('blocks payment that would exceed the rolling cap', async () => {
|
|
51
|
+
const policy = makePolicy({
|
|
52
|
+
rollingCap: { maxAmount: 100, windowMs: 86400000 },
|
|
53
|
+
});
|
|
54
|
+
// First spend: 80
|
|
55
|
+
await policy.check(makePayment({ amount: 80 }));
|
|
56
|
+
// Second spend: 30 — total 110 > 100
|
|
57
|
+
const result = await policy.check(makePayment({ amount: 30 }));
|
|
58
|
+
expect(result.status).toBe('rejected');
|
|
59
|
+
expect(result.reason).toMatch(/rolling spend cap exceeded/i);
|
|
60
|
+
});
|
|
61
|
+
it('allows payment exactly at the rolling cap', async () => {
|
|
62
|
+
const policy = makePolicy({
|
|
63
|
+
rollingCap: { maxAmount: 100, windowMs: 86400000 },
|
|
64
|
+
});
|
|
65
|
+
await policy.check(makePayment({ amount: 50 }));
|
|
66
|
+
const result = await policy.check(makePayment({ amount: 50 }));
|
|
67
|
+
expect(result.status).toBe('approved');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
// ─── DraftThenApprove ─────────────────────────────────────────────────────────
|
|
71
|
+
describe('DraftThenApprove', () => {
|
|
72
|
+
it('queues payment at or above draft threshold', async () => {
|
|
73
|
+
const policy = makePolicy({ draftThreshold: 500 });
|
|
74
|
+
const result = await policy.check(makePayment({ amount: 500 }));
|
|
75
|
+
expect(result.status).toBe('draft');
|
|
76
|
+
expect(result.draftId).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
it('approves payment below draft threshold immediately', async () => {
|
|
79
|
+
const policy = makePolicy({ draftThreshold: 500 });
|
|
80
|
+
const result = await policy.check(makePayment({ amount: 499 }));
|
|
81
|
+
expect(result.status).toBe('approved');
|
|
82
|
+
});
|
|
83
|
+
it('queued draft appears in getPendingDrafts()', async () => {
|
|
84
|
+
const policy = makePolicy({ draftThreshold: 100 });
|
|
85
|
+
const result = await policy.check(makePayment({ amount: 200 }));
|
|
86
|
+
const pending = policy.getPendingDrafts();
|
|
87
|
+
expect(pending.length).toBe(1);
|
|
88
|
+
expect(pending[0].draftId).toBe(result.draftId);
|
|
89
|
+
});
|
|
90
|
+
it('approveDraft marks draft approved and removes from pending', async () => {
|
|
91
|
+
const policy = makePolicy({ draftThreshold: 100 });
|
|
92
|
+
const result = await policy.check(makePayment({ amount: 200 }));
|
|
93
|
+
const draftId = result.draftId;
|
|
94
|
+
expect(policy.approveDraft(draftId)).toBe(true);
|
|
95
|
+
expect(policy.getPendingDrafts().length).toBe(0);
|
|
96
|
+
const draft = policy.getAllDrafts().find((d) => d.draftId === draftId);
|
|
97
|
+
expect(draft.approved).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
// ─── AuditTrail ───────────────────────────────────────────────────────────────
|
|
101
|
+
describe('AuditTrail', () => {
|
|
102
|
+
it('creates an audit entry for every payment attempt', async () => {
|
|
103
|
+
const policy = makePolicy({});
|
|
104
|
+
await policy.check(makePayment({ amount: 10 }));
|
|
105
|
+
await policy.check(makePayment({ amount: 20 }));
|
|
106
|
+
const log = policy.getAuditLog();
|
|
107
|
+
expect(log.length).toBe(2);
|
|
108
|
+
});
|
|
109
|
+
it('audit entry records merchant, amount, and status', async () => {
|
|
110
|
+
const policy = makePolicy({ merchantAllowlist: ['0xgood'] });
|
|
111
|
+
await policy.check(makePayment({ merchant: '0xBad', amount: 99 }));
|
|
112
|
+
const entry = policy.getAuditLog()[0];
|
|
113
|
+
expect(entry.merchant).toBe('0xBad');
|
|
114
|
+
expect(entry.amount).toBe(99);
|
|
115
|
+
expect(entry.status).toBe('rejected');
|
|
116
|
+
});
|
|
117
|
+
it('audit log is append-only (getAuditLog returns copy)', async () => {
|
|
118
|
+
const policy = makePolicy({});
|
|
119
|
+
await policy.check(makePayment());
|
|
120
|
+
const log = policy.getAuditLog();
|
|
121
|
+
log.push({ id: 'tamper', timestamp: '', merchant: '', amount: 0, status: 'approved' });
|
|
122
|
+
// Original log should still have only 1 entry
|
|
123
|
+
expect(policy.getAuditLog().length).toBe(1);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
// ─── FailClosed ───────────────────────────────────────────────────────────────
|
|
127
|
+
describe('FailClosed', () => {
|
|
128
|
+
it('rejects payment when policy engine throws an error', async () => {
|
|
129
|
+
const policy = makePolicy({ merchantAllowlist: ['0xgood'] });
|
|
130
|
+
// Corrupt internal state to trigger an error inside _check
|
|
131
|
+
// We spy on the private method by monkey-patching to throw
|
|
132
|
+
const original = policy._check.bind(policy);
|
|
133
|
+
policy._check = async () => {
|
|
134
|
+
throw new Error('Simulated internal policy crash');
|
|
135
|
+
};
|
|
136
|
+
const result = await policy.check(makePayment());
|
|
137
|
+
expect(result.status).toBe('rejected');
|
|
138
|
+
expect(result.reason).toMatch(/fail-closed/i);
|
|
139
|
+
// Restore
|
|
140
|
+
policy._check = original;
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=SpendingPolicy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpendingPolicy.test.js","sourceRoot":"","sources":["../../src/policy/SpendingPolicy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { SettlementVerifier } from './verifier.js';
|
|
2
|
+
export type { SettlementVerifierConfig, SettlementStatus, SettlementResult, BridgeSettlementResult, BridgeSettlementStatus, PaymentSettlementResult, } from './types.js';
|
|
3
|
+
export { SETTLEMENT_ALCHEMY_NETWORKS, SETTLEMENT_PUBLIC_RPCS, CIRCLE_ATTESTATION_API, ERC20_TRANSFER_TOPIC, SOLANA_RPC_URL, } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/settlement/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACV,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/settlement/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AASnD,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface SettlementVerifierConfig {
|
|
2
|
+
/** Alchemy API key (optional — falls back to public RPC if not provided) */
|
|
3
|
+
alchemyApiKey?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Status of an on-chain settlement */
|
|
6
|
+
export type SettlementStatus = 'confirmed' | 'pending' | 'failed' | 'not_found';
|
|
7
|
+
/** Result of verifying a single transaction */
|
|
8
|
+
export interface SettlementResult {
|
|
9
|
+
status: SettlementStatus;
|
|
10
|
+
txHash: string;
|
|
11
|
+
/** Block number where the tx was included (0 if pending/not found) */
|
|
12
|
+
blockNumber: bigint;
|
|
13
|
+
/** Number of confirmations at query time */
|
|
14
|
+
confirmations: number;
|
|
15
|
+
/** Gas used by the transaction */
|
|
16
|
+
gasUsed?: bigint;
|
|
17
|
+
/** Whether the tx succeeded (status=1) or reverted (status=0) */
|
|
18
|
+
success?: boolean;
|
|
19
|
+
/** Any error message if verification failed */
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Result of verifying a CCTP bridge transfer end-to-end */
|
|
23
|
+
export interface BridgeSettlementResult {
|
|
24
|
+
/** Overall bridge status */
|
|
25
|
+
status: BridgeSettlementStatus;
|
|
26
|
+
/** Source chain burn transaction */
|
|
27
|
+
sourceTx: SettlementResult;
|
|
28
|
+
/** Destination chain mint transaction (if found) */
|
|
29
|
+
destinationTx?: SettlementResult;
|
|
30
|
+
/** Circle attestation status */
|
|
31
|
+
attestationStatus?: 'pending_confirmations' | 'complete' | 'error';
|
|
32
|
+
/** Amount that was bridged (from source tx logs) */
|
|
33
|
+
amount?: bigint;
|
|
34
|
+
/** Destination address that received funds */
|
|
35
|
+
recipient?: string;
|
|
36
|
+
/** Any error or warning messages */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
export type BridgeSettlementStatus = 'source_confirmed_awaiting_attestation' | 'attestation_complete_awaiting_mint' | 'complete' | 'source_failed' | 'source_pending' | 'not_found' | 'error';
|
|
40
|
+
/** Result of verifying an x402 payment */
|
|
41
|
+
export interface PaymentSettlementResult {
|
|
42
|
+
status: SettlementStatus;
|
|
43
|
+
txHash: string;
|
|
44
|
+
/** Whether the payment went to the expected payee */
|
|
45
|
+
payeeVerified: boolean;
|
|
46
|
+
/** Whether the amount matches */
|
|
47
|
+
amountVerified: boolean;
|
|
48
|
+
/** Actual amount transferred (from event logs) */
|
|
49
|
+
actualAmount?: bigint;
|
|
50
|
+
/** Actual payee address (from event logs) */
|
|
51
|
+
actualPayee?: string;
|
|
52
|
+
/** Token contract address */
|
|
53
|
+
tokenAddress?: string;
|
|
54
|
+
error?: string;
|
|
55
|
+
}
|
|
56
|
+
/** Alchemy network slugs for settlement verification (subset: EVM chains with Alchemy support) */
|
|
57
|
+
export declare const SETTLEMENT_ALCHEMY_NETWORKS: Record<string, string>;
|
|
58
|
+
/** Public RPC fallbacks for chains without Alchemy Gas Manager support */
|
|
59
|
+
export declare const SETTLEMENT_PUBLIC_RPCS: Record<string, string>;
|
|
60
|
+
/** Circle CCTP attestation API base URL */
|
|
61
|
+
export declare const CIRCLE_ATTESTATION_API = "https://iris-api.circle.com/attestations";
|
|
62
|
+
/** ERC20 Transfer event topic: keccak256("Transfer(address,address,uint256)") */
|
|
63
|
+
export declare const ERC20_TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
64
|
+
/** Solana RPC endpoint */
|
|
65
|
+
export declare const SOLANA_RPC_URL = "https://api.mainnet-beta.solana.com";
|
|
66
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/settlement/types.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uCAAuC;AACvC,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEhF,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,oCAAoC;IACpC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,oDAAoD;IACpD,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,gCAAgC;IAChC,iBAAiB,CAAC,EAAE,uBAAuB,GAAG,UAAU,GAAG,OAAO,CAAC;IACnE,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,sBAAsB,GAC9B,uCAAuC,GACvC,oCAAoC,GACpC,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,WAAW,GACX,OAAO,CAAC;AAEZ,0CAA0C;AAC1C,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,aAAa,EAAE,OAAO,CAAC;IACvB,iCAAiC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kGAAkG;AAClG,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO9D,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiBzD,CAAC;AAEF,2CAA2C;AAC3C,eAAO,MAAM,sBAAsB,6CAA6C,CAAC;AAEjF,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,uEACqC,CAAC;AAEvE,0BAA0B;AAC1B,eAAO,MAAM,cAAc,wCAAwC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Settlement Verification — Types
|
|
2
|
+
// Verify that transactions, bridge transfers, and x402 payments actually settled on-chain.
|
|
3
|
+
/** Alchemy network slugs for settlement verification (subset: EVM chains with Alchemy support) */
|
|
4
|
+
export const SETTLEMENT_ALCHEMY_NETWORKS = {
|
|
5
|
+
ethereum: 'eth-mainnet',
|
|
6
|
+
base: 'base-mainnet',
|
|
7
|
+
arbitrum: 'arb-mainnet',
|
|
8
|
+
optimism: 'opt-mainnet',
|
|
9
|
+
polygon: 'polygon-mainnet',
|
|
10
|
+
worldchain: 'worldchain-mainnet',
|
|
11
|
+
};
|
|
12
|
+
/** Public RPC fallbacks for chains without Alchemy Gas Manager support */
|
|
13
|
+
export const SETTLEMENT_PUBLIC_RPCS = {
|
|
14
|
+
ethereum: 'https://eth.llamarpc.com',
|
|
15
|
+
base: 'https://mainnet.base.org',
|
|
16
|
+
arbitrum: 'https://arb1.arbitrum.io/rpc',
|
|
17
|
+
optimism: 'https://mainnet.optimism.io',
|
|
18
|
+
polygon: 'https://polygon-rpc.com',
|
|
19
|
+
worldchain: 'https://worldchain-mainnet.g.alchemy.com/public',
|
|
20
|
+
avalanche: 'https://api.avax.network/ext/bc/C/rpc',
|
|
21
|
+
unichain: 'https://mainnet.unichain.org',
|
|
22
|
+
linea: 'https://rpc.linea.build',
|
|
23
|
+
sonic: 'https://rpc.soniclabs.com',
|
|
24
|
+
sei: 'https://evm-rpc.sei-apis.com',
|
|
25
|
+
ink: 'https://rpc-gel.inkonchain.com',
|
|
26
|
+
hyperevm: 'https://rpc.hyperliquid.xyz/evm',
|
|
27
|
+
plume: 'https://rpc.plumenetwork.xyz',
|
|
28
|
+
xdc: 'https://rpc.xinfin.network',
|
|
29
|
+
codex: 'https://rpc.codex.storage',
|
|
30
|
+
};
|
|
31
|
+
/** Circle CCTP attestation API base URL */
|
|
32
|
+
export const CIRCLE_ATTESTATION_API = 'https://iris-api.circle.com/attestations';
|
|
33
|
+
/** ERC20 Transfer event topic: keccak256("Transfer(address,address,uint256)") */
|
|
34
|
+
export const ERC20_TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
|
|
35
|
+
/** Solana RPC endpoint */
|
|
36
|
+
export const SOLANA_RPC_URL = 'https://api.mainnet-beta.solana.com';
|
|
37
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/settlement/types.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,2FAA2F;AAsE3F,kGAAkG;AAClG,MAAM,CAAC,MAAM,2BAA2B,GAA2B;IACjE,QAAQ,EAAE,aAAa;IACvB,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,iBAAiB;IAC1B,UAAU,EAAE,oBAAoB;CACjC,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,sBAAsB,GAA2B;IAC5D,QAAQ,EAAE,0BAA0B;IACpC,IAAI,EAAE,0BAA0B;IAChC,QAAQ,EAAE,8BAA8B;IACxC,QAAQ,EAAE,6BAA6B;IACvC,OAAO,EAAE,yBAAyB;IAClC,UAAU,EAAE,iDAAiD;IAC7D,SAAS,EAAE,uCAAuC;IAClD,QAAQ,EAAE,8BAA8B;IACxC,KAAK,EAAE,yBAAyB;IAChC,KAAK,EAAE,2BAA2B;IAClC,GAAG,EAAE,8BAA8B;IACnC,GAAG,EAAE,gCAAgC;IACrC,QAAQ,EAAE,iCAAiC;IAC3C,KAAK,EAAE,8BAA8B;IACrC,GAAG,EAAE,4BAA4B;IACjC,KAAK,EAAE,2BAA2B;CACnC,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,0CAA0C,CAAC;AAEjF,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAC/B,oEAAoE,CAAC;AAEvE,0BAA0B;AAC1B,MAAM,CAAC,MAAM,cAAc,GAAG,qCAAqC,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { SettlementVerifierConfig, SettlementResult, BridgeSettlementResult, PaymentSettlementResult } from './types.js';
|
|
2
|
+
export declare class SettlementVerifier {
|
|
3
|
+
private readonly alchemyApiKey;
|
|
4
|
+
constructor(config?: SettlementVerifierConfig);
|
|
5
|
+
/**
|
|
6
|
+
* Verify that an EVM transaction settled on-chain.
|
|
7
|
+
*
|
|
8
|
+
* Uses Alchemy RPC if apiKey is configured and chain is supported,
|
|
9
|
+
* otherwise falls back to public RPCs.
|
|
10
|
+
*
|
|
11
|
+
* @param params.chain - SDK chain name (e.g. 'base', 'arbitrum')
|
|
12
|
+
* @param params.txHash - Transaction hash to verify
|
|
13
|
+
* @param params.confirmations - Minimum confirmations required (default: 1)
|
|
14
|
+
*/
|
|
15
|
+
verifyEvmTransaction(params: {
|
|
16
|
+
chain: string;
|
|
17
|
+
txHash: string;
|
|
18
|
+
confirmations?: number;
|
|
19
|
+
}): Promise<SettlementResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Verify that a Solana transaction settled on-chain.
|
|
22
|
+
*
|
|
23
|
+
* @param params.signature - Solana transaction signature (base58)
|
|
24
|
+
* @param params.commitment - Commitment level: 'confirmed' or 'finalized' (default: 'confirmed')
|
|
25
|
+
*/
|
|
26
|
+
verifySolanaTransaction(params: {
|
|
27
|
+
signature: string;
|
|
28
|
+
commitment?: 'confirmed' | 'finalized';
|
|
29
|
+
}): Promise<SettlementResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Verify a CCTP bridge transfer settled end-to-end:
|
|
32
|
+
* 1. Verifies source chain burn tx succeeded
|
|
33
|
+
* 2. Checks Circle attestation API for the message hash
|
|
34
|
+
* 3. Attempts to find destination mint tx (if attestation is complete)
|
|
35
|
+
*
|
|
36
|
+
* @param params.sourceTxHash - Burn tx hash on source chain
|
|
37
|
+
* @param params.sourceChain - Source chain name (e.g. 'solana', 'base')
|
|
38
|
+
* @param params.destinationChain - Destination chain name
|
|
39
|
+
* @param params.expectedAmount - Expected USDC amount in base units (6 decimals)
|
|
40
|
+
*/
|
|
41
|
+
verifyBridgeSettlement(params: {
|
|
42
|
+
sourceTxHash: string;
|
|
43
|
+
sourceChain: string;
|
|
44
|
+
destinationChain: string;
|
|
45
|
+
expectedAmount: bigint;
|
|
46
|
+
}): Promise<BridgeSettlementResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Verify an x402 payment settled on-chain by inspecting ERC20 Transfer event logs.
|
|
49
|
+
*
|
|
50
|
+
* Checks that the tx:
|
|
51
|
+
* - Succeeded (status=1)
|
|
52
|
+
* - Emitted an ERC20 Transfer event to expectedPayee
|
|
53
|
+
* - The transferred amount matches expectedAmount
|
|
54
|
+
*
|
|
55
|
+
* @param params.chain - EVM chain name
|
|
56
|
+
* @param params.txHash - Transaction hash
|
|
57
|
+
* @param params.expectedPayee - Address that should have received the payment
|
|
58
|
+
* @param params.expectedAmount - Amount in USDC base units (6 decimals)
|
|
59
|
+
*/
|
|
60
|
+
verifyX402Payment(params: {
|
|
61
|
+
chain: string;
|
|
62
|
+
txHash: string;
|
|
63
|
+
expectedPayee: string;
|
|
64
|
+
expectedAmount: bigint;
|
|
65
|
+
}): Promise<PaymentSettlementResult>;
|
|
66
|
+
/** Get the RPC URL for a chain, preferring Alchemy over public RPCs */
|
|
67
|
+
private _getRpcUrl;
|
|
68
|
+
/** Make an EVM JSON-RPC call */
|
|
69
|
+
private _evmRpc;
|
|
70
|
+
/** Make a Solana JSON-RPC call */
|
|
71
|
+
private _solanaRpc;
|
|
72
|
+
/** Check Circle attestation API for a CCTP burn message */
|
|
73
|
+
private _checkCircleAttestation;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/settlement/verifier.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AASpB,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;gBAEvC,MAAM,GAAE,wBAA6B;IAMjD;;;;;;;;;OASG;IACG,oBAAoB,CAAC,MAAM,EAAE;QACjC,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2E7B;;;;;OAKG;IACG,uBAAuB,CAAC,MAAM,EAAE;QACpC,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;KACxC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqD7B;;;;;;;;;;OAUG;IACG,sBAAsB,CAAC,MAAM,EAAE;QACnC,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAkEnC;;;;;;;;;;;;OAYG;IACG,iBAAiB,CAAC,MAAM,EAAE;QAC9B,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAqFpC,uEAAuE;IACvE,OAAO,CAAC,UAAU;IAYlB,gCAAgC;YAClB,OAAO;IAoBrB,kCAAkC;YACpB,UAAU;IAmBxB,2DAA2D;YAC7C,uBAAuB;CA0BtC"}
|