@elizaos/plugin-wallet 2.0.0-beta.1
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/LICENSE +21 -0
- package/README.md +64 -0
- package/auto-enable.ts +76 -0
- package/dist/LpManagementService-BWrQ5-cO.mjs +353 -0
- package/dist/MockLpService-D_Apn4Fd.mjs +99 -0
- package/dist/aerodrome-CfnESC32.mjs +890 -0
- package/dist/chunk-hT5z_Zn9.mjs +35 -0
- package/dist/index.d.mts +34727 -0
- package/dist/index.mjs +21590 -0
- package/dist/lib/server-wallet-trade.d.mts +34 -0
- package/dist/lib/server-wallet-trade.mjs +306 -0
- package/dist/meteora-BPX39hZo.mjs +22640 -0
- package/dist/orca-Bybp1HXO.mjs +249 -0
- package/dist/pancakeswp-CkEXlXti.mjs +604 -0
- package/dist/plugin-ZO_MTyd0.mjs +529 -0
- package/dist/raydium-rfaM9yEf.mjs +539 -0
- package/dist/sdk/index.d.mts +32492 -0
- package/dist/sdk/index.mjs +6415 -0
- package/dist/types-D5252NZk.mjs +487 -0
- package/dist/uniswap-CReXgXVN.mjs +573 -0
- package/dist/wallet-action.d.mts +6 -0
- package/dist/wallet-action.mjs +820 -0
- package/package.json +152 -0
- package/src/actions/failure-codes.ts +79 -0
- package/src/actions/index.ts +1 -0
- package/src/analytics/birdeye/actions/wallet-search-address.ts +9 -0
- package/src/analytics/birdeye/birdeye-task.ts +175 -0
- package/src/analytics/birdeye/birdeye.ts +813 -0
- package/src/analytics/birdeye/constants.ts +74 -0
- package/src/analytics/birdeye/providers/agent-portfolio-provider.ts +18 -0
- package/src/analytics/birdeye/providers/market.ts +227 -0
- package/src/analytics/birdeye/providers/portfolio-factory.test.ts +138 -0
- package/src/analytics/birdeye/providers/portfolio-factory.ts +252 -0
- package/src/analytics/birdeye/providers/trending.ts +365 -0
- package/src/analytics/birdeye/providers/wallet.ts +14 -0
- package/src/analytics/birdeye/search-category.test.ts +207 -0
- package/src/analytics/birdeye/search-category.ts +506 -0
- package/src/analytics/birdeye/service.ts +992 -0
- package/src/analytics/birdeye/tasks/birdeye.ts +232 -0
- package/src/analytics/birdeye/types/api/common.ts +305 -0
- package/src/analytics/birdeye/types/api/defi.ts +220 -0
- package/src/analytics/birdeye/types/api/pair.ts +200 -0
- package/src/analytics/birdeye/types/api/search.ts +86 -0
- package/src/analytics/birdeye/types/api/token.ts +635 -0
- package/src/analytics/birdeye/types/api/trader.ts +76 -0
- package/src/analytics/birdeye/types/api/wallet.ts +181 -0
- package/src/analytics/birdeye/types/shared.ts +106 -0
- package/src/analytics/birdeye/utils.ts +700 -0
- package/src/analytics/dexscreener/errors.ts +28 -0
- package/src/analytics/dexscreener/index.ts +3 -0
- package/src/analytics/dexscreener/search-category.test.ts +49 -0
- package/src/analytics/dexscreener/search-category.ts +42 -0
- package/src/analytics/dexscreener/service.ts +595 -0
- package/src/analytics/dexscreener/types.ts +128 -0
- package/src/analytics/lpinfo/index.d.ts +7 -0
- package/src/analytics/lpinfo/index.ts +52 -0
- package/src/analytics/lpinfo/kamino/README.md +102 -0
- package/src/analytics/lpinfo/kamino/index.ts +24 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoLiquidityProvider.ts +422 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoPoolProvider.ts +365 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoProvider.ts +496 -0
- package/src/analytics/lpinfo/kamino/services/kaminoLiquidityService.ts +1123 -0
- package/src/analytics/lpinfo/kamino/services/kaminoService.ts +758 -0
- package/src/analytics/lpinfo/steer/README.md +169 -0
- package/src/analytics/lpinfo/steer/index.ts +23 -0
- package/src/analytics/lpinfo/steer/providers/steerLiquidityProvider.ts +544 -0
- package/src/analytics/lpinfo/steer/services/steerLiquidityService.ts +1690 -0
- package/src/analytics/lpinfo/steer/steer-display-types.ts +99 -0
- package/src/analytics/news/index.ts +52 -0
- package/src/analytics/news/interfaces/types.ts +222 -0
- package/src/analytics/news/providers/defiNewsProvider.ts +734 -0
- package/src/analytics/news/services/newsDataService.ts +332 -0
- package/src/analytics/news/utils/formatters.ts +151 -0
- package/src/analytics/token-info/action.ts +240 -0
- package/src/analytics/token-info/index.ts +3 -0
- package/src/analytics/token-info/params.ts +215 -0
- package/src/analytics/token-info/providers.ts +681 -0
- package/src/analytics/token-info/service.ts +168 -0
- package/src/analytics/token-info/types.ts +74 -0
- package/src/audit/audit-log.ts +45 -0
- package/src/browser-shim/build-shim.ts +123 -0
- package/src/browser-shim/index.ts +5 -0
- package/src/browser-shim/shim.template.js +563 -0
- package/src/chains/evm/.github/workflows/npm-deploy.yml +112 -0
- package/src/chains/evm/LICENSE +21 -0
- package/src/chains/evm/README.md +106 -0
- package/src/chains/evm/actions/helpers.ts +147 -0
- package/src/chains/evm/actions/swap.ts +839 -0
- package/src/chains/evm/actions/transfer.ts +254 -0
- package/src/chains/evm/biome.json +61 -0
- package/src/chains/evm/bridge-router.ts +660 -0
- package/src/chains/evm/build.ts +89 -0
- package/src/chains/evm/chain-handler.ts +416 -0
- package/src/chains/evm/constants.ts +23 -0
- package/src/chains/evm/contracts/artifacts/OZGovernor.json +1707 -0
- package/src/chains/evm/contracts/artifacts/TimelockController.json +1007 -0
- package/src/chains/evm/contracts/artifacts/VoteToken.json +895 -0
- package/src/chains/evm/dex/aerodrome/index.ts +34 -0
- package/src/chains/evm/dex/aerodrome/services/AerodromeLpService.ts +558 -0
- package/src/chains/evm/dex/aerodrome/types.ts +318 -0
- package/src/chains/evm/dex/pancakeswp/index.ts +35 -0
- package/src/chains/evm/dex/pancakeswp/services/PancakeSwapV3LpService.ts +743 -0
- package/src/chains/evm/dex/pancakeswp/types.ts +65 -0
- package/src/chains/evm/dex/uniswap/index.ts +35 -0
- package/src/chains/evm/dex/uniswap/services/UniswapV3LpService.ts +759 -0
- package/src/chains/evm/dex/uniswap/types.ts +390 -0
- package/src/chains/evm/generated/specs/spec-helpers.ts +73 -0
- package/src/chains/evm/generated/specs/specs.ts +151 -0
- package/src/chains/evm/gov-router.ts +250 -0
- package/src/chains/evm/index.browser.ts +16 -0
- package/src/chains/evm/index.ts +31 -0
- package/src/chains/evm/prompts.ts +193 -0
- package/src/chains/evm/providers/get-balance.ts +123 -0
- package/src/chains/evm/providers/wallet.ts +715 -0
- package/src/chains/evm/routes/sign.ts +333 -0
- package/src/chains/evm/rpc-providers.ts +410 -0
- package/src/chains/evm/service.ts +140 -0
- package/src/chains/evm/templates/index.ts +10 -0
- package/src/chains/evm/types/index.ts +432 -0
- package/src/chains/evm/vitest.config.ts +18 -0
- package/src/chains/registry.ts +668 -0
- package/src/chains/solana/README.md +367 -0
- package/src/chains/wallet-action.ts +533 -0
- package/src/chains/wallet-router.test.ts +296 -0
- package/src/contracts.ts +65 -0
- package/src/core-augmentation.ts +10 -0
- package/src/index.ts +71 -0
- package/src/lib/server-wallet-trade.ts +192 -0
- package/src/lib/wallet-export-guard.ts +330 -0
- package/src/lp/actions/liquidity.ts +827 -0
- package/src/lp/e2e/real-token-tests.ts +428 -0
- package/src/lp/e2e/scenarios.ts +470 -0
- package/src/lp/e2e/test-utils.ts +145 -0
- package/src/lp/lp-manager-entry.ts +303 -0
- package/src/lp/services/ConcentratedLiquidityService.ts +120 -0
- package/src/lp/services/DexInteractionService.ts +226 -0
- package/src/lp/services/LpManagementService.test.ts +148 -0
- package/src/lp/services/LpManagementService.ts +632 -0
- package/src/lp/services/UserLpProfileService.ts +163 -0
- package/src/lp/services/VaultService.ts +153 -0
- package/src/lp/services/YieldOptimizationService.ts +344 -0
- package/src/lp/services/__tests__/MockLpService.ts +146 -0
- package/src/lp/tasks/LpAutoRebalanceTask.ts +117 -0
- package/src/lp/tasks/__tests__/LpAutoRebalanceTask.test.ts +370 -0
- package/src/lp/types.ts +582 -0
- package/src/lp/utils/solanaClient.ts +143 -0
- package/src/plugin.ts +125 -0
- package/src/policy/policy.ts +19 -0
- package/src/providers/canonical-provider.ts +27 -0
- package/src/providers/unified-wallet-provider.ts +79 -0
- package/src/register-routes.ts +11 -0
- package/src/routes/plugin.ts +47 -0
- package/src/routes/wallet-market-overview-route.ts +869 -0
- package/src/sdk/abi.ts +258 -0
- package/src/sdk/bridge/abis.ts +126 -0
- package/src/sdk/bridge/client.ts +518 -0
- package/src/sdk/bridge/index.ts +56 -0
- package/src/sdk/bridge/solana.ts +604 -0
- package/src/sdk/bridge/types.ts +202 -0
- package/src/sdk/convenience.ts +347 -0
- package/src/sdk/escrow/MutualStakeEscrow.ts +480 -0
- package/src/sdk/escrow/types.ts +64 -0
- package/src/sdk/escrow/verifiers.ts +73 -0
- package/src/sdk/identity/erc8004.ts +692 -0
- package/src/sdk/identity/reputation.ts +449 -0
- package/src/sdk/identity/uaid.ts +497 -0
- package/src/sdk/identity/validation.ts +372 -0
- package/src/sdk/index.ts +763 -0
- package/src/sdk/policy/SpendingPolicy.ts +260 -0
- package/src/sdk/policy/UptoBillingPolicy.ts +320 -0
- package/src/sdk/router/PaymentRouter.ts +215 -0
- package/src/sdk/router/index.ts +8 -0
- package/src/sdk/swap/SwapModule.ts +310 -0
- package/src/sdk/swap/abi.ts +117 -0
- package/src/sdk/swap/index.ts +34 -0
- package/src/sdk/swap/types.ts +135 -0
- package/src/sdk/tokens/decimals.ts +140 -0
- package/src/sdk/tokens/registry.ts +911 -0
- package/src/sdk/tokens/solana.ts +419 -0
- package/src/sdk/tokens/transfers.ts +327 -0
- package/src/sdk/types.ts +158 -0
- package/src/sdk/wallet-core.ts +115 -0
- package/src/sdk/x402/budget.ts +168 -0
- package/src/sdk/x402/chains/abstract/index.ts +280 -0
- package/src/sdk/x402/client.ts +320 -0
- package/src/sdk/x402/index.ts +46 -0
- package/src/sdk/x402/middleware.ts +92 -0
- package/src/sdk/x402/multi-asset.ts +144 -0
- package/src/sdk/x402/types.ts +156 -0
- package/src/services/wallet-backend-service.ts +328 -0
- package/src/types/wallet-router.ts +227 -0
- package/src/utils/intent-trajectory.ts +106 -0
- package/src/wallet/backend.ts +62 -0
- package/src/wallet/errors.ts +49 -0
- package/src/wallet/index.ts +27 -0
- package/src/wallet/local-eoa-backend.ts +201 -0
- package/src/wallet/pending.ts +60 -0
- package/src/wallet/select-backend.ts +47 -0
- package/src/wallet/steward-backend.ts +161 -0
- package/src/wallet-action.ts +1 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module x402/client
|
|
3
|
+
* x402 Payment Client — automatic HTTP 402 payment handling for AgentWallet.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the standard fetch API to transparently handle 402 Payment Required
|
|
6
|
+
* responses using the x402 protocol. Supports multi-chain, multi-asset payments
|
|
7
|
+
* (USDC, USDT, DAI, WETH, and any token in the TokenRegistry) across all 11
|
|
8
|
+
* mainnet chains configured in x402/types.ts.
|
|
9
|
+
*
|
|
10
|
+
* Flow: fetch → 402 detected → parse payment requirements → resolve asset address
|
|
11
|
+
* via TokenRegistry → budget check → execute ERC-20 transfer → retry with
|
|
12
|
+
* X-PAYMENT header.
|
|
13
|
+
*/
|
|
14
|
+
// x402 Client — automatic 402 payment handling for AgentWallet (v6: multi-asset)
|
|
15
|
+
import type { Address, Hash } from "viem";
|
|
16
|
+
import {
|
|
17
|
+
type AgentWallet,
|
|
18
|
+
agentTransferToken,
|
|
19
|
+
checkBudget,
|
|
20
|
+
} from "../wallet-core.js";
|
|
21
|
+
import { X402BudgetTracker } from "./budget.js";
|
|
22
|
+
import { resolveAssetAddress } from "./multi-asset.js";
|
|
23
|
+
import type {
|
|
24
|
+
X402ClientConfig,
|
|
25
|
+
X402PaymentPayload,
|
|
26
|
+
X402PaymentRequired,
|
|
27
|
+
X402PaymentRequirements,
|
|
28
|
+
X402TransactionLog,
|
|
29
|
+
} from "./types.js";
|
|
30
|
+
import { DEFAULT_SUPPORTED_NETWORKS } from "./types.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* [MAX-ADDED] x402 Payment Client for AgentWallet.
|
|
34
|
+
*
|
|
35
|
+
* Handles the full x402 payment flow:
|
|
36
|
+
* 1. Detects 402 Payment Required responses
|
|
37
|
+
* 2. Parses payment instructions from PAYMENT-REQUIRED header
|
|
38
|
+
* 3. Validates against budget controls
|
|
39
|
+
* 4. Executes USDC payment via AgentWallet contract
|
|
40
|
+
* 5. Retries original request with payment proof
|
|
41
|
+
*/
|
|
42
|
+
export class X402Client {
|
|
43
|
+
private wallet: AgentWallet;
|
|
44
|
+
private config: X402ClientConfig;
|
|
45
|
+
private budget: X402BudgetTracker;
|
|
46
|
+
private supportedNetworks: Set<string>;
|
|
47
|
+
|
|
48
|
+
constructor(wallet: AgentWallet, config: X402ClientConfig = {}) {
|
|
49
|
+
this.wallet = wallet;
|
|
50
|
+
this.config = {
|
|
51
|
+
autoPay: true,
|
|
52
|
+
maxRetries: 1,
|
|
53
|
+
supportedNetworks: [...DEFAULT_SUPPORTED_NETWORKS],
|
|
54
|
+
...config,
|
|
55
|
+
};
|
|
56
|
+
this.budget = new X402BudgetTracker(config);
|
|
57
|
+
this.supportedNetworks = new Set(this.config.supportedNetworks);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Make an x402-aware fetch request. Automatically handles 402 responses.
|
|
62
|
+
*/
|
|
63
|
+
async fetch(url: string | URL, init?: RequestInit): Promise<Response> {
|
|
64
|
+
const urlStr = url.toString();
|
|
65
|
+
const response = await globalThis.fetch(url, init);
|
|
66
|
+
|
|
67
|
+
if (response.status !== 402) {
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!this.config.autoPay) {
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Parse the 402 response
|
|
76
|
+
const paymentRequired = await this.parse402Response(response);
|
|
77
|
+
if (!paymentRequired) {
|
|
78
|
+
return response; // Couldn't parse — return original 402
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Find a compatible payment option
|
|
82
|
+
const selected = this.selectPaymentOption(paymentRequired.accepts);
|
|
83
|
+
if (!selected) {
|
|
84
|
+
return response; // No compatible payment option
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check budget
|
|
88
|
+
const amount = BigInt(selected.amount);
|
|
89
|
+
const service = new URL(urlStr).hostname;
|
|
90
|
+
const budgetCheck = this.budget.checkBudget(service, amount);
|
|
91
|
+
if (!budgetCheck.allowed) {
|
|
92
|
+
throw new X402BudgetExceededError(
|
|
93
|
+
budgetCheck.reason ?? "Budget check failed",
|
|
94
|
+
urlStr,
|
|
95
|
+
selected,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Callback check
|
|
100
|
+
if (this.config.onBeforePayment) {
|
|
101
|
+
const proceed = await this.config.onBeforePayment(selected, urlStr);
|
|
102
|
+
if (!proceed) {
|
|
103
|
+
return response;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Execute payment
|
|
108
|
+
const paymentResult = await this.executePayment(selected);
|
|
109
|
+
|
|
110
|
+
// Build payment payload
|
|
111
|
+
const paymentPayload: X402PaymentPayload = {
|
|
112
|
+
x402Version: paymentRequired.x402Version,
|
|
113
|
+
resource: paymentRequired.resource,
|
|
114
|
+
accepted: selected,
|
|
115
|
+
payload: {
|
|
116
|
+
txHash: paymentResult.txHash,
|
|
117
|
+
network: selected.network,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Log the transaction
|
|
122
|
+
const log: X402TransactionLog = {
|
|
123
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
124
|
+
service,
|
|
125
|
+
url: urlStr,
|
|
126
|
+
amount,
|
|
127
|
+
token: selected.asset as Address,
|
|
128
|
+
recipient: selected.payTo as Address,
|
|
129
|
+
txHash: paymentResult.txHash,
|
|
130
|
+
network: selected.network,
|
|
131
|
+
scheme: selected.scheme,
|
|
132
|
+
success: true,
|
|
133
|
+
};
|
|
134
|
+
this.budget.recordPayment(log);
|
|
135
|
+
this.config.onPaymentComplete?.(log);
|
|
136
|
+
|
|
137
|
+
// Retry request with payment proof
|
|
138
|
+
const retryHeaders = new Headers(init?.headers);
|
|
139
|
+
const payloadB64 = btoa(JSON.stringify(paymentPayload));
|
|
140
|
+
retryHeaders.set("X-PAYMENT", payloadB64);
|
|
141
|
+
|
|
142
|
+
const retryResponse = await globalThis.fetch(url, {
|
|
143
|
+
...init,
|
|
144
|
+
headers: retryHeaders,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return retryResponse;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Parse a 402 response to extract payment requirements.
|
|
152
|
+
*/
|
|
153
|
+
async parse402Response(
|
|
154
|
+
response: Response,
|
|
155
|
+
): Promise<X402PaymentRequired | null> {
|
|
156
|
+
// Try PAYMENT-REQUIRED header first (standard x402)
|
|
157
|
+
const headerValue =
|
|
158
|
+
response.headers.get("payment-required") ??
|
|
159
|
+
response.headers.get("x-payment-required");
|
|
160
|
+
|
|
161
|
+
if (headerValue) {
|
|
162
|
+
try {
|
|
163
|
+
const decoded = JSON.parse(atob(headerValue));
|
|
164
|
+
return decoded as X402PaymentRequired;
|
|
165
|
+
} catch {
|
|
166
|
+
// Fall through to body parsing
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Try JSON body as fallback
|
|
171
|
+
try {
|
|
172
|
+
const body = await response.clone().json();
|
|
173
|
+
if (body.x402Version && body.accepts) {
|
|
174
|
+
return body as X402PaymentRequired;
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
// Not parseable
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Select the best compatible payment option from offered requirements.
|
|
185
|
+
* Prefers: Base network, stablecoins, exact scheme.
|
|
186
|
+
*
|
|
187
|
+
* v6 change: resolves assets via TokenRegistry in addition to USDC_ADDRESSES.
|
|
188
|
+
* Now accepts any ERC-20 whose address is in the TokenRegistry for the network.
|
|
189
|
+
*/
|
|
190
|
+
selectPaymentOption(
|
|
191
|
+
accepts: X402PaymentRequirements[],
|
|
192
|
+
): X402PaymentRequirements | null {
|
|
193
|
+
// Filter to supported networks and resolvable assets
|
|
194
|
+
const compatible = accepts.filter((req) => {
|
|
195
|
+
if (!this.supportedNetworks.has(req.network)) return false;
|
|
196
|
+
|
|
197
|
+
// Config override: explicit supportedAssets list
|
|
198
|
+
if (this.config.supportedAssets?.[req.network]) {
|
|
199
|
+
return this.config.supportedAssets[req.network].some(
|
|
200
|
+
(a) => a.toLowerCase() === req.asset.toLowerCase(),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// v6: resolve via TokenRegistry — accept any known ERC-20 on this network
|
|
205
|
+
const resolved = resolveAssetAddress(req.asset, req.network);
|
|
206
|
+
return resolved != null;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
if (compatible.length === 0) return null;
|
|
210
|
+
|
|
211
|
+
// Prefer "exact" scheme, then lowest amount
|
|
212
|
+
const exact = compatible.filter((r) => r.scheme === "exact");
|
|
213
|
+
const candidates = exact.length > 0 ? exact : compatible;
|
|
214
|
+
candidates.sort((a, b) => Number(BigInt(a.amount) - BigInt(b.amount)));
|
|
215
|
+
|
|
216
|
+
return candidates[0];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Execute the payment via AgentWallet's agentTransferToken.
|
|
221
|
+
*
|
|
222
|
+
* v6 change: resolves asset address via TokenRegistry before executing.
|
|
223
|
+
* The 402 response may specify an asset by symbol ("USDC") or by address.
|
|
224
|
+
*/
|
|
225
|
+
private async executePayment(
|
|
226
|
+
req: X402PaymentRequirements,
|
|
227
|
+
): Promise<{ txHash: Hash }> {
|
|
228
|
+
// Resolve the actual contract address for the requested asset
|
|
229
|
+
const resolvedAddress = resolveAssetAddress(req.asset, req.network);
|
|
230
|
+
if (!resolvedAddress) {
|
|
231
|
+
throw new X402PaymentError(
|
|
232
|
+
`Cannot resolve asset "${req.asset}" on network "${req.network}" to a contract address`,
|
|
233
|
+
req,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// First check on-chain budget
|
|
238
|
+
const onChainBudget = await checkBudget(this.wallet, resolvedAddress);
|
|
239
|
+
const amount = BigInt(req.amount);
|
|
240
|
+
|
|
241
|
+
if (amount > onChainBudget.perTxLimit) {
|
|
242
|
+
throw new X402PaymentError(
|
|
243
|
+
`Amount ${amount} exceeds on-chain per-tx limit ${onChainBudget.perTxLimit}`,
|
|
244
|
+
req,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (amount > onChainBudget.remainingInPeriod) {
|
|
249
|
+
throw new X402PaymentError(
|
|
250
|
+
`Amount ${amount} exceeds remaining period budget ${onChainBudget.remainingInPeriod}`,
|
|
251
|
+
req,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Calculate and transfer protocol fee (0.77% = 77 bps)
|
|
256
|
+
const X402_PROTOCOL_FEE_BPS = 77n;
|
|
257
|
+
const FEE_COLLECTOR: Address = "0xff86829393C6C26A4EC122bE0Cc3E466Ef876AdD";
|
|
258
|
+
const feeAmount = (amount * X402_PROTOCOL_FEE_BPS) / 10000n;
|
|
259
|
+
|
|
260
|
+
if (feeAmount > 0n) {
|
|
261
|
+
await agentTransferToken(this.wallet, {
|
|
262
|
+
token: req.asset as Address,
|
|
263
|
+
to: FEE_COLLECTOR,
|
|
264
|
+
amount: feeAmount,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Execute the ERC20 transfer via AgentWallet (full amount to payee)
|
|
269
|
+
const txHash = await agentTransferToken(this.wallet, {
|
|
270
|
+
token: resolvedAddress,
|
|
271
|
+
to: req.payTo as Address,
|
|
272
|
+
amount,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return { txHash };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ─── Budget Access ───
|
|
279
|
+
|
|
280
|
+
/** Get the budget tracker for direct inspection */
|
|
281
|
+
get budgetTracker(): X402BudgetTracker {
|
|
282
|
+
return this.budget;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Get transaction log */
|
|
286
|
+
getTransactionLog(filter?: {
|
|
287
|
+
service?: string;
|
|
288
|
+
since?: number;
|
|
289
|
+
}): X402TransactionLog[] {
|
|
290
|
+
return this.budget.getTransactionLog(filter);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Get daily spend summary */
|
|
294
|
+
getDailySpendSummary() {
|
|
295
|
+
return this.budget.getDailySpendSummary();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ─── Error Types ───
|
|
300
|
+
|
|
301
|
+
export class X402PaymentError extends Error {
|
|
302
|
+
constructor(
|
|
303
|
+
message: string,
|
|
304
|
+
public readonly paymentRequirements: X402PaymentRequirements,
|
|
305
|
+
) {
|
|
306
|
+
super(`x402 payment error: ${message}`);
|
|
307
|
+
this.name = "X402PaymentError";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export class X402BudgetExceededError extends Error {
|
|
312
|
+
constructor(
|
|
313
|
+
public readonly reason: string,
|
|
314
|
+
public readonly url: string,
|
|
315
|
+
public readonly paymentRequirements: X402PaymentRequirements,
|
|
316
|
+
) {
|
|
317
|
+
super(`x402 budget exceeded: ${reason}`);
|
|
318
|
+
this.name = "X402BudgetExceededError";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// [MAX-ADDED] x402 Protocol Module — HTTP 402 payment support for AgentWallet
|
|
2
|
+
|
|
3
|
+
export { X402BudgetTracker } from "./budget.js";
|
|
4
|
+
export type {
|
|
5
|
+
AbstractDelegatedPaymentConfig,
|
|
6
|
+
AbstractPaymentResult,
|
|
7
|
+
DelegatedPaymentPermit,
|
|
8
|
+
} from "./chains/abstract/index.js";
|
|
9
|
+
// ─── Chain-Specific Adapters ──────────────────────────────────────────────────
|
|
10
|
+
export {
|
|
11
|
+
ABSTRACT_APPROVED_FACILITATORS,
|
|
12
|
+
ABSTRACT_CHAIN_IDS,
|
|
13
|
+
ABSTRACT_SUPPORTED_CHAINS,
|
|
14
|
+
ABSTRACT_USDC,
|
|
15
|
+
AbstractDelegatedFacilitatorAdapter,
|
|
16
|
+
} from "./chains/abstract/index.js";
|
|
17
|
+
export {
|
|
18
|
+
X402BudgetExceededError,
|
|
19
|
+
X402Client,
|
|
20
|
+
X402PaymentError,
|
|
21
|
+
} from "./client.js";
|
|
22
|
+
export {
|
|
23
|
+
createX402Client,
|
|
24
|
+
createX402Fetch,
|
|
25
|
+
wrapWithX402,
|
|
26
|
+
} from "./middleware.js";
|
|
27
|
+
|
|
28
|
+
// v6: Multi-asset resolution utilities
|
|
29
|
+
export {
|
|
30
|
+
buildSupportedAssets,
|
|
31
|
+
isStablecoin,
|
|
32
|
+
parseNetworkChainId,
|
|
33
|
+
resolveAssetAddress,
|
|
34
|
+
resolveAssetDecimals,
|
|
35
|
+
} from "./multi-asset.js";
|
|
36
|
+
export type {
|
|
37
|
+
X402ClientConfig,
|
|
38
|
+
X402PaymentPayload,
|
|
39
|
+
X402PaymentRequired,
|
|
40
|
+
X402PaymentRequirements,
|
|
41
|
+
X402ResourceInfo,
|
|
42
|
+
X402ServiceBudget,
|
|
43
|
+
X402SettlementResponse,
|
|
44
|
+
X402TransactionLog,
|
|
45
|
+
} from "./types.js";
|
|
46
|
+
export { DEFAULT_SUPPORTED_NETWORKS, USDC_ADDRESSES } from "./types.js";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// [MAX-ADDED] x402 Middleware — wraps fetch/axios to be x402-aware
|
|
2
|
+
|
|
3
|
+
import type { AgentWallet } from "../wallet-core.js";
|
|
4
|
+
import { X402Client } from "./client.js";
|
|
5
|
+
import type { X402ClientConfig } from "./types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* [MAX-ADDED] Create an x402-aware HTTP client.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const client = createX402Client(wallet, { globalDailyLimit: 10_000_000n });
|
|
12
|
+
* const response = await client.fetch('https://api.example.com/data');
|
|
13
|
+
* // If the endpoint returns 402, payment is handled automatically
|
|
14
|
+
*
|
|
15
|
+
* @param wallet - AgentWallet instance from createWallet()
|
|
16
|
+
* @param config - Optional x402 client configuration
|
|
17
|
+
* @returns X402Client with .fetch() method and budget controls
|
|
18
|
+
*/
|
|
19
|
+
export function createX402Client(
|
|
20
|
+
wallet: AgentWallet,
|
|
21
|
+
config?: X402ClientConfig,
|
|
22
|
+
): X402Client {
|
|
23
|
+
return new X402Client(wallet, config);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* [MAX-ADDED] Create an x402-aware fetch function (drop-in replacement).
|
|
28
|
+
*
|
|
29
|
+
* Usage:
|
|
30
|
+
* const x402Fetch = createX402Fetch(wallet);
|
|
31
|
+
* const response = await x402Fetch('https://api.example.com/data');
|
|
32
|
+
*
|
|
33
|
+
* @param wallet - AgentWallet instance from createWallet()
|
|
34
|
+
* @param config - Optional x402 client configuration
|
|
35
|
+
* @returns A fetch-compatible function that handles 402 payments
|
|
36
|
+
*/
|
|
37
|
+
export function createX402Fetch(
|
|
38
|
+
wallet: AgentWallet,
|
|
39
|
+
config?: X402ClientConfig,
|
|
40
|
+
): typeof globalThis.fetch {
|
|
41
|
+
const client = new X402Client(wallet, config);
|
|
42
|
+
const base = globalThis.fetch;
|
|
43
|
+
const impl = (input: string | URL | Request, init?: RequestInit) => {
|
|
44
|
+
const url = input instanceof Request ? input.url : input.toString();
|
|
45
|
+
return client.fetch(url, init);
|
|
46
|
+
};
|
|
47
|
+
return Object.assign(impl, {
|
|
48
|
+
preconnect: base.preconnect.bind(base),
|
|
49
|
+
}) as typeof globalThis.fetch;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* [MAX-ADDED] Wrap an existing fetch-like function to be x402-aware.
|
|
54
|
+
* Useful for wrapping custom HTTP clients or test mocks.
|
|
55
|
+
*
|
|
56
|
+
* @param fetchFn - The original fetch function to wrap
|
|
57
|
+
* @param wallet - AgentWallet instance
|
|
58
|
+
* @param config - Optional x402 client configuration
|
|
59
|
+
*/
|
|
60
|
+
export function wrapWithX402(
|
|
61
|
+
fetchFn: typeof globalThis.fetch,
|
|
62
|
+
wallet: AgentWallet,
|
|
63
|
+
config?: X402ClientConfig,
|
|
64
|
+
): typeof globalThis.fetch {
|
|
65
|
+
const wrappedClient = new X402Client(wallet, config);
|
|
66
|
+
|
|
67
|
+
const impl = async (
|
|
68
|
+
input: string | URL | Request,
|
|
69
|
+
init?: RequestInit,
|
|
70
|
+
): Promise<Response> => {
|
|
71
|
+
const url = input instanceof Request ? input.url : input.toString();
|
|
72
|
+
const response = await fetchFn(input, init);
|
|
73
|
+
|
|
74
|
+
if (response.status !== 402) {
|
|
75
|
+
return response;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Parse and handle 402 via the client
|
|
79
|
+
const paymentRequired = await wrappedClient.parse402Response(response);
|
|
80
|
+
if (!paymentRequired) return response;
|
|
81
|
+
|
|
82
|
+
const selected = wrappedClient.selectPaymentOption(paymentRequired.accepts);
|
|
83
|
+
if (!selected) return response;
|
|
84
|
+
|
|
85
|
+
// Use the client's fetch for retry (which calls globalThis.fetch)
|
|
86
|
+
return wrappedClient.fetch(url, init);
|
|
87
|
+
};
|
|
88
|
+
const base = globalThis.fetch;
|
|
89
|
+
return Object.assign(impl, {
|
|
90
|
+
preconnect: base.preconnect.bind(base),
|
|
91
|
+
}) as typeof globalThis.fetch;
|
|
92
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module x402/multi-asset
|
|
3
|
+
* Multi-asset support utilities for the x402 payment client.
|
|
4
|
+
*
|
|
5
|
+
* Resolves asset names/symbols → token addresses via the global TokenRegistry.
|
|
6
|
+
* This extends x402 beyond USDC-only to support any token the 402 response requests.
|
|
7
|
+
*
|
|
8
|
+
* All address lookups use the TokenRegistry from src/tokens/registry.ts.
|
|
9
|
+
* Fallback: USDC_ADDRESSES from x402/types.ts.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Address } from "viem";
|
|
13
|
+
import { getGlobalRegistry } from "../tokens/registry.js";
|
|
14
|
+
import { USDC_ADDRESSES } from "./types.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Network string format used in x402: "chainName:chainId" (e.g. "base:8453").
|
|
18
|
+
* Returns the chainId portion as a number.
|
|
19
|
+
*/
|
|
20
|
+
export function parseNetworkChainId(network: string): number | null {
|
|
21
|
+
const parts = network.split(":");
|
|
22
|
+
if (parts.length < 2) return null;
|
|
23
|
+
const id = parseInt(parts[parts.length - 1], 10);
|
|
24
|
+
return Number.isNaN(id) ? null : id;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve an asset address in the context of a 402 response.
|
|
29
|
+
* The asset field may be:
|
|
30
|
+
* 1. A token symbol ("USDC", "WETH", etc.) → look up in registry by symbol
|
|
31
|
+
* 2. A hex contract address → look up in registry by address (must be registered)
|
|
32
|
+
*
|
|
33
|
+
* Only returns an address if the asset is known in the TokenRegistry.
|
|
34
|
+
* Unknown addresses are NOT accepted — they must be explicitly added or
|
|
35
|
+
* whitelisted via supportedAssets config.
|
|
36
|
+
*
|
|
37
|
+
* @param asset - Asset identifier from the 402 response
|
|
38
|
+
* @param network - Network string from the 402 response (e.g. "base:8453")
|
|
39
|
+
* @returns Resolved contract address, or null if not found in registry
|
|
40
|
+
*/
|
|
41
|
+
export function resolveAssetAddress(
|
|
42
|
+
asset: string,
|
|
43
|
+
network: string,
|
|
44
|
+
): Address | null {
|
|
45
|
+
const chainId = parseNetworkChainId(network);
|
|
46
|
+
if (chainId == null) return null;
|
|
47
|
+
|
|
48
|
+
const registry = getGlobalRegistry();
|
|
49
|
+
|
|
50
|
+
// If it looks like an EVM address, check registry by address
|
|
51
|
+
if (/^0x[0-9a-fA-F]{40}$/.test(asset)) {
|
|
52
|
+
const entry = registry.getTokenByAddress(asset as Address, chainId);
|
|
53
|
+
if (entry) return entry.address;
|
|
54
|
+
return null; // unknown address — reject for safety
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Try registry lookup by symbol (e.g. "USDC", "WETH")
|
|
58
|
+
const entry = registry.getToken(asset.toUpperCase(), chainId);
|
|
59
|
+
if (entry) return entry.address;
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get token decimals for a given asset+network pair.
|
|
66
|
+
* Used to correctly format amounts in x402 payments.
|
|
67
|
+
*
|
|
68
|
+
* @param asset - Asset address (hex) or symbol
|
|
69
|
+
* @param network - Network string (e.g. "base:8453")
|
|
70
|
+
* @returns Decimals, defaults to 6 for USDC-like stable, 18 for everything else
|
|
71
|
+
*/
|
|
72
|
+
export function resolveAssetDecimals(asset: string, network: string): number {
|
|
73
|
+
const chainId = parseNetworkChainId(network);
|
|
74
|
+
if (chainId == null) return 6;
|
|
75
|
+
|
|
76
|
+
const registry = getGlobalRegistry();
|
|
77
|
+
|
|
78
|
+
// Try by address first
|
|
79
|
+
if (/^0x[0-9a-fA-F]{40}$/.test(asset)) {
|
|
80
|
+
const entry = registry.getTokenByAddress(asset as Address, chainId);
|
|
81
|
+
if (entry) return entry.decimals;
|
|
82
|
+
// Unknown token — default to 18
|
|
83
|
+
return 18;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Try by symbol
|
|
87
|
+
const entry = registry.getToken(asset.toUpperCase(), chainId);
|
|
88
|
+
if (entry) return entry.decimals;
|
|
89
|
+
|
|
90
|
+
return 18;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build a set of accepted payment assets for an x402 client, using the
|
|
95
|
+
* global registry to auto-populate known tokens per network.
|
|
96
|
+
*
|
|
97
|
+
* @param networks - List of network strings (e.g. ["base:8453", "arbitrum:42161"])
|
|
98
|
+
* @param symbols - Token symbols to include (e.g. ["USDC", "USDT", "WETH"])
|
|
99
|
+
* @returns Map from network string → array of token addresses
|
|
100
|
+
*/
|
|
101
|
+
export function buildSupportedAssets(
|
|
102
|
+
networks: string[],
|
|
103
|
+
symbols: string[] = ["USDC", "USDT", "DAI", "WETH"],
|
|
104
|
+
): Record<string, Address[]> {
|
|
105
|
+
const result: Record<string, Address[]> = {};
|
|
106
|
+
const registry = getGlobalRegistry();
|
|
107
|
+
|
|
108
|
+
for (const network of networks) {
|
|
109
|
+
const chainId = parseNetworkChainId(network);
|
|
110
|
+
if (chainId == null) continue;
|
|
111
|
+
|
|
112
|
+
const addrs: Address[] = [];
|
|
113
|
+
|
|
114
|
+
for (const symbol of symbols) {
|
|
115
|
+
const entry = registry.getToken(symbol, chainId);
|
|
116
|
+
if (entry && !entry.isNative) {
|
|
117
|
+
addrs.push(entry.address);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Always include USDC from the existing USDC_ADDRESSES table as fallback
|
|
122
|
+
const usdcFallback = USDC_ADDRESSES[network];
|
|
123
|
+
if (
|
|
124
|
+
usdcFallback &&
|
|
125
|
+
!addrs.some((a) => a.toLowerCase() === usdcFallback.toLowerCase())
|
|
126
|
+
) {
|
|
127
|
+
addrs.push(usdcFallback);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (addrs.length > 0) {
|
|
131
|
+
result[network] = addrs;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if a given asset address is a known stablecoin (6 decimals) on the network.
|
|
140
|
+
*/
|
|
141
|
+
export function isStablecoin(asset: string, network: string): boolean {
|
|
142
|
+
const decimals = resolveAssetDecimals(asset, network);
|
|
143
|
+
return decimals === 6;
|
|
144
|
+
}
|