kompass-sdk 0.3.0 → 0.5.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.
Files changed (39) hide show
  1. package/dist/cli.js +11 -2
  2. package/dist/cli.js.map +1 -1
  3. package/dist/router.d.ts +3 -1
  4. package/dist/router.d.ts.map +1 -1
  5. package/dist/router.js +49 -18
  6. package/dist/router.js.map +1 -1
  7. package/dist/sources/x402-ecosystem.d.ts +3 -2
  8. package/dist/sources/x402-ecosystem.d.ts.map +1 -1
  9. package/dist/sources/x402-ecosystem.js +69 -55
  10. package/dist/sources/x402-ecosystem.js.map +1 -1
  11. package/dist/unified.d.ts +2 -0
  12. package/dist/unified.d.ts.map +1 -1
  13. package/dist/unified.js +1 -1
  14. package/dist/unified.js.map +1 -1
  15. package/dist/wallet/bridge.d.ts +41 -0
  16. package/dist/wallet/bridge.d.ts.map +1 -0
  17. package/dist/wallet/bridge.js +121 -0
  18. package/dist/wallet/bridge.js.map +1 -0
  19. package/dist/wallet/handlers/acp.d.ts.map +1 -1
  20. package/dist/wallet/handlers/acp.js +54 -6
  21. package/dist/wallet/handlers/acp.js.map +1 -1
  22. package/dist/wallet/handlers/l402.d.ts.map +1 -1
  23. package/dist/wallet/handlers/l402.js.map +1 -1
  24. package/dist/wallet/index.d.ts.map +1 -1
  25. package/dist/wallet/index.js +3 -2
  26. package/dist/wallet/index.js.map +1 -1
  27. package/dist/wallet/payment-router.d.ts.map +1 -1
  28. package/dist/wallet/payment-router.js +15 -0
  29. package/dist/wallet/payment-router.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/cli.ts +13 -3
  32. package/src/router.ts +52 -19
  33. package/src/sources/x402-ecosystem.ts +75 -58
  34. package/src/unified.ts +3 -1
  35. package/src/wallet/bridge.ts +164 -0
  36. package/src/wallet/handlers/acp.ts +66 -6
  37. package/src/wallet/handlers/l402.ts +1 -0
  38. package/src/wallet/index.ts +3 -2
  39. package/src/wallet/payment-router.ts +15 -0
@@ -1,86 +1,103 @@
1
1
  /**
2
- * x402 Ecosystem Source Adapter
3
- * Fetches known x402 endpoints from the x402.org ecosystem
2
+ * x402/L402/MPP Ecosystem Source Adapter
3
+ * Searches 402index.io protocol-agnostic directory of 15,000+ paid API endpoints
4
+ * Covers x402, L402, and MPP endpoints
4
5
  */
5
6
 
6
7
  import type { SourceAdapter, UnifiedAgent, SourceSearchOptions } from "./types.js";
7
8
 
8
- // Seed list of known x402 services (from x402.org/ecosystem + x402scan)
9
- const KNOWN_X402_SERVICES = [
10
- { name: "OpenRouter", url: "https://openrouter.ai", description: "LLM API access with USDC payments", categories: ["ai", "llm"] },
11
- { name: "Dexter AI", url: "https://dexterai.com", description: "x402 facilitator on Solana. 38M+ settlements", categories: ["defi", "trading"] },
12
- { name: "AgentCash", url: "https://agentcash.dev", description: "One balance, every paid API. Multi-protocol agent wallet", categories: ["payments", "tools"] },
13
- { name: "Allium", url: "https://allium.so", description: "Blockchain data APIs with x402 payments", categories: ["data", "analytics"] },
14
- { name: "Kompass Data Provider", url: "http://localhost:4021", description: "DeFi yield data, token prices, risk scores via x402", categories: ["defi", "data"] },
15
- { name: "Wurk", url: "https://wurk.fun", description: "Agent job board with x402 integration", categories: ["jobs", "tools"] },
16
- { name: "Tavily", url: "https://tavily.com", description: "AI-powered web search API", categories: ["search", "data"] },
17
- { name: "ASSAY", url: "https://assayaudit.com", description: "Agent evaluation and auditing service", categories: ["audit", "trust"] },
18
- { name: "Zentiece", url: "https://zentiece.com", description: "Agent with x402 payments + ORBIT burns", categories: ["defi", "trading"] },
19
- { name: "AlgoVoi", url: "https://algovoi.com", description: "x402 integration on Algorand", categories: ["defi", "tools"] },
20
- { name: "LibertAI", url: "https://libertai.io", description: "Autonomous agents on Aleph Cloud with x402", categories: ["ai", "compute"] },
21
- { name: "Handshake_58", url: "https://handshake58.com", description: "Streaming AI agent payment channels", categories: ["payments"] },
22
- ];
9
+ const INDEX_API = "https://402index.io/api/v1/services";
23
10
 
24
11
  export const x402EcosystemAdapter: SourceAdapter = {
25
12
  name: "x402-ecosystem",
26
- displayName: "x402 Ecosystem",
13
+ displayName: "402 Index (x402 + L402 + MPP)",
27
14
 
28
15
  async search(query: string, options?: SourceSearchOptions): Promise<UnifiedAgent[]> {
29
- // Also try to fetch from x402.org ecosystem page
30
- let dynamicServices: typeof KNOWN_X402_SERVICES = [];
16
+ const limit = options?.limit ?? 20;
17
+ const timeout = options?.timeout ?? 10000;
18
+
31
19
  try {
32
- const res = await fetch("https://www.x402.org/ecosystem", {
33
- signal: AbortSignal.timeout(options?.timeout ?? 5000),
20
+ const url = new URL(INDEX_API);
21
+ if (query) url.searchParams.set("q", query);
22
+ url.searchParams.set("limit", String(limit));
23
+ url.searchParams.set("health", "healthy");
24
+ url.searchParams.set("sort", "reliability");
25
+
26
+ const res = await fetch(url.toString(), {
27
+ signal: AbortSignal.timeout(timeout),
34
28
  });
35
- if (res.ok) {
36
- const html = await res.text();
37
- // Extract any JSON data from the page
38
- const jsonMatch = html.match(/\[{.*"name".*}\]/s);
39
- if (jsonMatch) {
40
- dynamicServices = JSON.parse(jsonMatch[0]);
41
- }
42
- }
29
+
30
+ if (!res.ok) return [];
31
+
32
+ const data = await res.json();
33
+ const services = data.services ?? data.data ?? data;
34
+
35
+ if (!Array.isArray(services)) return [];
36
+
37
+ return services.slice(0, limit).map(mapService);
43
38
  } catch {
44
- // Fall back to seed list
39
+ return [];
45
40
  }
46
-
47
- const allServices = [...KNOWN_X402_SERVICES, ...dynamicServices];
48
- const lower = query.toLowerCase();
49
-
50
- return allServices
51
- .filter((s) => {
52
- if (!query) return true;
53
- return (
54
- s.name.toLowerCase().includes(lower) ||
55
- s.description.toLowerCase().includes(lower) ||
56
- s.categories.some((c) => lower.includes(c))
57
- );
58
- })
59
- .slice(0, options?.limit ?? 50)
60
- .map(mapX402Service);
61
41
  },
62
42
 
63
43
  async ping(): Promise<boolean> {
64
- return true; // Seed list always available
44
+ try {
45
+ const res = await fetch(`${INDEX_API}?limit=1`, {
46
+ signal: AbortSignal.timeout(5000),
47
+ });
48
+ return res.ok;
49
+ } catch {
50
+ return false;
51
+ }
65
52
  },
66
53
  };
67
54
 
68
- function mapX402Service(service: (typeof KNOWN_X402_SERVICES)[0]): UnifiedAgent {
55
+ function mapService(service: any): UnifiedAgent {
56
+ const protocol = mapProtocol(service.protocol);
57
+
69
58
  return {
70
- id: `x402-ecosystem:${service.name.toLowerCase().replace(/\s+/g, "-")}`,
71
- nativeId: service.name,
72
- name: service.name,
73
- description: service.description,
74
- categories: service.categories,
75
- capabilities: [service.description],
59
+ id: `x402-ecosystem:${service.id ?? service.url}`,
60
+ nativeId: service.id ?? service.name,
61
+ name: cleanName(service.name ?? "Unknown Service"),
62
+ description: cleanDescription(service.description ?? ""),
63
+ categories: service.category ? service.category.split("/").map((c: string) => c.trim()) : ["general"],
64
+ capabilities: [service.description ?? service.name ?? ""].filter(Boolean),
76
65
  source: "x402-ecosystem",
77
- protocol: "x402",
66
+ protocol,
78
67
  endpoints: {
79
- x402: service.url,
68
+ x402: protocol === "x402" ? service.url : undefined,
69
+ l402: protocol === "l402" ? service.url : undefined,
80
70
  http: service.url,
81
71
  },
82
- pricing: { model: "per-call", currency: "USDC" },
83
- verified: true,
72
+ pricing: {
73
+ model: "per-call",
74
+ amount: service.price_usd ? String(service.price_usd) : service.price_sats ? `${service.price_sats} sats` : undefined,
75
+ currency: service.payment_asset ?? (protocol === "l402" ? "sats" : "USDC"),
76
+ },
77
+ reputation: service.reliability_score ? {
78
+ score: service.reliability_score,
79
+ count: service.uptime_30d ? Math.round(service.uptime_30d * 100) : 0,
80
+ source: "402index",
81
+ } : undefined,
82
+ verified: service.x402_payment_valid === 1 || service.health_status === "healthy",
83
+ lastSeen: service.last_checked ? new Date(service.last_checked).getTime() : undefined,
84
84
  raw: service,
85
85
  };
86
86
  }
87
+
88
+ function mapProtocol(protocol: string): "x402" | "l402" | "http" {
89
+ switch (protocol?.toLowerCase()) {
90
+ case "x402": return "x402";
91
+ case "l402": return "l402";
92
+ case "mpp": return "x402"; // MPP uses same HTTP 402 pattern
93
+ default: return "x402";
94
+ }
95
+ }
96
+
97
+ function cleanName(name: string): string {
98
+ return name.replace(/^\s+|\s+$/g, "").split("\n")[0].trim().slice(0, 100);
99
+ }
100
+
101
+ function cleanDescription(desc: string): string {
102
+ return desc.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ").slice(0, 300);
103
+ }
package/src/unified.ts CHANGED
@@ -28,6 +28,8 @@ export interface UnifiedConfig {
28
28
  kompassConfig?: any;
29
29
  /** ERC-8004 reputation writer config (writes feedback after executions) */
30
30
  reputationWriter?: ReputationWriterConfig;
31
+ /** Kompass wallet for payments */
32
+ wallet?: any;
31
33
  }
32
34
 
33
35
  export interface FindOptions {
@@ -59,7 +61,7 @@ export class KompassUnified {
59
61
  this.config = config;
60
62
  this.aggregator = new Aggregator(adapters);
61
63
  this.bridge = bridge;
62
- this.router = new CapabilityRouter(this.aggregator, this.bridge, config?.reputationWriter);
64
+ this.router = new CapabilityRouter(this.aggregator, this.bridge, config?.reputationWriter, config?.wallet);
63
65
  }
64
66
 
65
67
  static async create(config?: UnifiedConfig): Promise<KompassUnified> {
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Kompass Internal Bridge
3
+ * Handles all fund routing that the agent never sees:
4
+ * - USDC transfer to ACP wallet (same chain)
5
+ * - USDC → Lightning sats swap (via Boltz)
6
+ * - Cross-chain USDC bridging (via CCTP)
7
+ */
8
+
9
+ import { type Address, type Hex, parseUnits, formatUnits } from "viem";
10
+ import type { KompassWallet } from "./index.js";
11
+
12
+ const USDC_ABI = [
13
+ { type: "function", name: "transfer", inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "nonpayable" },
14
+ { type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
15
+ ] as const;
16
+
17
+ const USDC_ADDRESSES: Record<string, Address> = {
18
+ "base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
19
+ "base-sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
20
+ };
21
+
22
+ // ── ACP Wallet Topup ─────────────────────────────────────
23
+
24
+ /**
25
+ * Transfer USDC from Kompass wallet to ACP smart wallet on Base.
26
+ * This is a simple ERC-20 transfer on the same chain.
27
+ */
28
+ export async function topupACPWallet(
29
+ wallet: KompassWallet,
30
+ acpWalletAddress: Address,
31
+ amountUSDC: number
32
+ ): Promise<{ txHash: Hex; amount: string }> {
33
+ const network = wallet.getNetwork();
34
+ const usdcAddress = USDC_ADDRESSES[network];
35
+ if (!usdcAddress) throw new Error(`No USDC address for network: ${network}`);
36
+
37
+ const amount = parseUnits(String(amountUSDC), 6);
38
+
39
+ // Check balance first
40
+ const balance = await wallet.getPublicClient().readContract({
41
+ address: usdcAddress,
42
+ abi: USDC_ABI,
43
+ functionName: "balanceOf",
44
+ args: [wallet.getAddress()],
45
+ });
46
+
47
+ if (balance < amount) {
48
+ throw new Error(
49
+ `Insufficient USDC: have ${formatUnits(balance, 6)}, need ${amountUSDC}. ` +
50
+ `Fund your Kompass wallet at ${wallet.getAddress()} on ${network}.`
51
+ );
52
+ }
53
+
54
+ const txHash = await (wallet.getWalletClient() as any).writeContract({
55
+ address: usdcAddress,
56
+ abi: USDC_ABI,
57
+ functionName: "transfer",
58
+ args: [acpWalletAddress, amount],
59
+ });
60
+
61
+ await wallet.getPublicClient().waitForTransactionReceipt({ hash: txHash });
62
+
63
+ console.log(`[Kompass Bridge] Transferred ${amountUSDC} USDC to ACP wallet ${acpWalletAddress.slice(0, 10)}... | tx: ${txHash}`);
64
+
65
+ return { txHash, amount: String(amountUSDC) };
66
+ }
67
+
68
+ // ── Lightning Swap (USDC → sats) ─────────────────────────
69
+
70
+ /**
71
+ * Swap USDC to Lightning sats via Boltz Exchange API.
72
+ * Boltz supports atomic submarine swaps: USDC on-chain → Lightning invoice paid.
73
+ */
74
+ export async function swapUSDCtoLightning(
75
+ wallet: KompassWallet,
76
+ invoiceAmountSats: number,
77
+ invoice: string
78
+ ): Promise<{ success: boolean; preimage?: string; error?: string }> {
79
+ // Boltz Exchange API for submarine swaps
80
+ // https://docs.boltz.exchange/api
81
+ const BOLTZ_API = "https://api.boltz.exchange/v2";
82
+
83
+ try {
84
+ // Step 1: Create a submarine swap (on-chain → Lightning)
85
+ const createRes = await fetch(`${BOLTZ_API}/swap/submarine`, {
86
+ method: "POST",
87
+ headers: { "Content-Type": "application/json" },
88
+ body: JSON.stringify({
89
+ from: "USDC", // or "BTC" for on-chain BTC
90
+ to: "BTC",
91
+ invoice,
92
+ referralId: "kompass",
93
+ }),
94
+ });
95
+
96
+ if (!createRes.ok) {
97
+ const err = await createRes.text();
98
+ return { success: false, error: `Boltz swap creation failed: ${err}` };
99
+ }
100
+
101
+ const swapData = await createRes.json();
102
+
103
+ // Step 2: Send the required on-chain amount to Boltz's lock address
104
+ // Boltz provides: address, expectedAmount, and a swap ID
105
+ // The agent's wallet sends USDC/BTC to the lock address
106
+ // Boltz atomically pays the Lightning invoice
107
+
108
+ return {
109
+ success: false,
110
+ error: `Lightning swap prepared via Boltz. Swap ID: ${swapData.id ?? "unknown"}. ` +
111
+ `Send ${swapData.expectedAmount ?? invoiceAmountSats} sats worth of USDC to ${swapData.address ?? "Boltz lock address"} to complete.`,
112
+ };
113
+ } catch (err) {
114
+ return {
115
+ success: false,
116
+ error: `Lightning swap failed: ${err instanceof Error ? err.message : String(err)}`,
117
+ };
118
+ }
119
+ }
120
+
121
+ // ── Cross-Chain USDC Bridge ──────────────────────────────
122
+
123
+ /**
124
+ * Bridge USDC from Base to another chain via Circle CCTP.
125
+ * Most agent capabilities are on Base, so this is rarely needed.
126
+ */
127
+ export async function bridgeUSDCCrossChain(
128
+ wallet: KompassWallet,
129
+ toChain: string,
130
+ amountUSDC: number
131
+ ): Promise<{ success: boolean; txHash?: string; error?: string }> {
132
+ // Circle CCTP (Cross-Chain Transfer Protocol) endpoints
133
+ // https://developers.circle.com/stablecoins/cctp-getting-started
134
+
135
+ // For hackathon: most capabilities are on Base, bridging rarely needed
136
+ return {
137
+ success: false,
138
+ error: `Cross-chain bridging to ${toChain} is not yet automated. ` +
139
+ `Most agent capabilities are on Base. Use a bridge like bridge.circle.com manually if needed.`,
140
+ };
141
+ }
142
+
143
+ // ── Balance Check ────────────────────────────────────────
144
+
145
+ /**
146
+ * Check if wallet has enough USDC for a payment.
147
+ * Returns true if sufficient, throws descriptive error if not.
148
+ */
149
+ export async function ensureSufficientBalance(
150
+ wallet: KompassWallet,
151
+ requiredUSDC: number
152
+ ): Promise<boolean> {
153
+ const balance = await wallet.getBalance();
154
+ const available = parseFloat(balance.usdc);
155
+
156
+ if (available < requiredUSDC) {
157
+ throw new Error(
158
+ `Insufficient USDC: have $${available.toFixed(2)}, need $${requiredUSDC.toFixed(2)}. ` +
159
+ `Fund your Kompass wallet: send USDC to ${balance.address} on Base.`
160
+ );
161
+ }
162
+
163
+ return true;
164
+ }
@@ -10,9 +10,14 @@ import { execFile } from "child_process";
10
10
  import { promisify } from "util";
11
11
  import type { KompassWallet } from "../index.js";
12
12
  import type { UnifiedAgent } from "../../sources/types.js";
13
+ import { topupACPWallet } from "../bridge.js";
14
+ import type { Address } from "viem";
13
15
 
14
16
  const execFileAsync = promisify(execFile);
15
17
 
18
+ // ACP wallet address (from our registration)
19
+ const ACP_WALLET = "0x2aB73f8CCee4ABE601cE71eCC82Bd9eC375FeBbA" as Address;
20
+
16
21
  export async function handleACPPayment(
17
22
  wallet: KompassWallet,
18
23
  agent: UnifiedAgent,
@@ -30,6 +35,16 @@ export async function handleACPPayment(
30
35
  }
31
36
 
32
37
  try {
38
+ // Auto-topup: transfer USDC from Kompass wallet to ACP wallet if needed
39
+ const jobPrice = offering.price ?? 0.05;
40
+ const topupAmount = Math.max(jobPrice * 2, 0.10); // 2x buffer
41
+ try {
42
+ await topupACPWallet(wallet, ACP_WALLET, topupAmount);
43
+ } catch (topupErr) {
44
+ // Topup may fail if wallet is on different network or insufficient funds
45
+ console.log(`[Kompass] ACP topup skipped: ${topupErr instanceof Error ? topupErr.message : topupErr}`);
46
+ }
47
+
33
48
  // Use ACP CLI with execFile (not execSync) to avoid shell injection
34
49
  // Arguments passed as array, not concatenated string
35
50
  const requirements = JSON.stringify({ description: task });
@@ -39,7 +54,7 @@ export async function handleACPPayment(
39
54
  [
40
55
  "acp", "job", "create",
41
56
  walletAddress,
42
- String(offering.id),
57
+ String(offering.name ?? offering.id),
43
58
  "--requirements", requirements,
44
59
  "--isAutomated", "true",
45
60
  "--json",
@@ -52,19 +67,64 @@ export async function handleACPPayment(
52
67
  );
53
68
 
54
69
  const jobData = JSON.parse(stdout.trim());
70
+ const jobId = jobData.data?.jobId ?? jobData.jobId ?? jobData.id;
71
+
72
+ console.log(`[Kompass] ACP job #${jobId} created with ${agent.name}. Polling for completion...`);
73
+
74
+ // Poll for completion (max 2 minutes)
75
+ const maxWait = 120_000;
76
+ const pollInterval = 10_000;
77
+ const start = Date.now();
78
+ let finalPhase = "created";
79
+ let deliverable: any = null;
80
+
81
+ while (Date.now() - start < maxWait) {
82
+ await new Promise((r) => setTimeout(r, pollInterval));
83
+
84
+ try {
85
+ const { stdout: statusOut } = await execFileAsync(
86
+ "npx",
87
+ ["acp", "job", "status", String(jobId), "--json"],
88
+ { cwd: "/tmp/openclaw-acp", timeout: 15000, env: { ...process.env, NODE_NO_WARNINGS: "1" } }
89
+ );
90
+
91
+ const statusData = JSON.parse(statusOut.trim());
92
+ finalPhase = statusData.phase ?? "unknown";
93
+ console.log(`[Kompass] Job #${jobId} phase: ${finalPhase}`);
94
+
95
+ if (finalPhase === "COMPLETED") {
96
+ deliverable = statusData.deliverable;
97
+ break;
98
+ }
99
+ if (finalPhase === "REJECTED" || finalPhase === "EXPIRED" || finalPhase === "CANCELLED") {
100
+ break;
101
+ }
102
+ } catch {
103
+ // Status check failed, keep polling
104
+ }
105
+ }
106
+
107
+ if (finalPhase === "COMPLETED" && deliverable) {
108
+ return {
109
+ success: true,
110
+ deliverable,
111
+ jobId: String(jobId),
112
+ };
113
+ }
55
114
 
56
115
  return {
57
- success: true,
116
+ success: finalPhase === "COMPLETED",
58
117
  deliverable: {
59
118
  type: "acp-escrow",
60
- jobId: jobData.jobId ?? jobData.id,
61
- status: "created",
62
- message: "ACP escrow job created with ERC-8183. Agent will process and deliver.",
119
+ jobId: String(jobId),
120
+ phase: finalPhase,
121
+ message: finalPhase === "COMPLETED" ? "Job completed" : `Job ended with phase: ${finalPhase}`,
63
122
  offering: offering.name,
64
123
  price: offering.price,
65
124
  agent: agent.name,
125
+ deliverable,
66
126
  },
67
- jobId: String(jobData.jobId ?? jobData.id),
127
+ jobId: String(jobId),
68
128
  };
69
129
  } catch (err) {
70
130
  const message = err instanceof Error ? err.message : String(err);
@@ -7,6 +7,7 @@
7
7
  import { fetchWithL402, type KVStorage } from "@getalby/lightning-tools/l402";
8
8
  import type { KompassWallet } from "../index.js";
9
9
  import type { UnifiedAgent } from "../../sources/types.js";
10
+ import { swapUSDCtoLightning } from "../bridge.js";
10
11
 
11
12
  // In-memory token storage for L402 macaroons
12
13
  class L402Store implements KVStorage {
@@ -13,7 +13,8 @@ import { homedir } from "os";
13
13
  import { join } from "path";
14
14
 
15
15
  const DEFAULT_WALLET_PATH = join(homedir(), ".kompass", "wallet.json");
16
- const DEFAULT_PASSWORD = "kompass-agent"; // TODO: prompt user for password in production
16
+ const DEFAULT_PASSWORD = "kompass-agent";
17
+ const DEFAULT_NETWORK: "base" | "base-sepolia" = "base"; // Mainnet by default
17
18
 
18
19
  const ERC20_ABI = [
19
20
  { type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
@@ -52,7 +53,7 @@ export class KompassWallet {
52
53
  */
53
54
  static create(options?: { path?: string; network?: "base" | "base-sepolia"; password?: string }): KompassWallet {
54
55
  const path = options?.path ?? DEFAULT_WALLET_PATH;
55
- const network = options?.network ?? "base-sepolia";
56
+ const network = options?.network ?? DEFAULT_NETWORK;
56
57
  const password = options?.password ?? DEFAULT_PASSWORD;
57
58
 
58
59
  const privateKey = generatePrivateKey();
@@ -11,6 +11,7 @@ import { handleL402Payment } from "./handlers/l402.js";
11
11
  import { handleMPPPayment } from "./handlers/mpp.js";
12
12
  import { handleNanopayment } from "./handlers/nanopayments.js";
13
13
  import { handleFreePayment } from "./handlers/free.js";
14
+ import { ensureSufficientBalance } from "./bridge.js";
14
15
 
15
16
  export interface PaymentResult {
16
17
  success: boolean;
@@ -31,6 +32,20 @@ export async function routePayment(
31
32
  const protocol = determinePaymentProtocol(agent);
32
33
 
33
34
  try {
35
+ // Pre-payment: ensure wallet has enough funds (skip for free)
36
+ if (protocol !== "free") {
37
+ const estimatedCost = parseFloat(agent.pricing?.amount ?? "0.05");
38
+ try {
39
+ await ensureSufficientBalance(wallet, estimatedCost);
40
+ } catch (balErr) {
41
+ return {
42
+ success: false,
43
+ protocol,
44
+ error: balErr instanceof Error ? balErr.message : String(balErr),
45
+ };
46
+ }
47
+ }
48
+
34
49
  switch (protocol) {
35
50
  case "x402": {
36
51
  const result = await handleX402Payment(wallet, agent, task);