apow-cli 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -25,10 +25,6 @@ LLM_API_KEY=
25
25
  # SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
26
26
  # SQUID_INTEGRATOR_ID=
27
27
 
28
- # Ethereum mainnet RPC (only for `apow fund --chain ethereum`)
29
- # Default: https://ethereum-rpc.publicnode.com (free)
30
- # ETHEREUM_RPC_URL=
31
-
32
28
  # Contract addresses (defaults built-in, override only if needed)
33
29
  # MINING_AGENT_ADDRESS=0xB7caD3ca5F2BD8aEC2Eb67d6E8D448099B3bC03D
34
30
  # AGENT_COIN_ADDRESS=0x12577CF0D8a07363224D6909c54C056A183e13b3
package/README.md CHANGED
@@ -45,7 +45,6 @@ EOF
45
45
 
46
46
  # 3. Fund the wallet (bridge from any chain, auto-splits into ETH + USDC)
47
47
  npx apow-cli fund --chain solana --token sol # bridge SOL → ETH+USDC on Base
48
- npx apow-cli fund --chain ethereum --token eth # bridge ETH → ETH+USDC on Base
49
48
  # Or send ETH/USDC on Base directly
50
49
 
51
50
  # 4. Mint a mining rig NFT (proves agent identity via LLM, one-time)
@@ -92,7 +91,7 @@ npx apow-cli mine
92
91
  | Command | Description |
93
92
  |---------|-------------|
94
93
  | `apow setup` | Interactive setup wizard: configure wallet, RPC, and LLM |
95
- | `apow fund` | Fund your wallet: bridge from Solana/Ethereum, swap on Base, auto-split ETH+USDC |
94
+ | `apow fund` | Fund your wallet: bridge from Solana or send on Base, auto-split ETH+USDC |
96
95
  | `apow wallet new` | Generate a new mining wallet |
97
96
  | `apow wallet show` | Show configured wallet address |
98
97
  | `apow wallet export` | Export your wallet's private key |
@@ -116,8 +115,7 @@ LLM_MODEL=gpt-4o-mini # Required for minting only; mining uses optimized
116
115
  LLM_API_KEY=sk-... # Required for minting only
117
116
  # Bridging (only for `apow fund`)
118
117
  # SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
119
- # ETHEREUM_RPC_URL=https://ethereum-rpc.publicnode.com # free, for `--chain ethereum` only
120
- # SQUID_INTEGRATOR_ID= # free, get at squidrouter.com (deposit address flow only)
118
+ # SQUID_INTEGRATOR_ID= # free, get at squidrouter.com
121
119
  # Contract addresses (defaults built-in, override only if needed)
122
120
  # MINING_AGENT_ADDRESS=0xB7caD3ca5F2BD8aEC2Eb67d6E8D448099B3bC03D
123
121
  # AGENT_COIN_ADDRESS=0x12577CF0D8a07363224D6909c54C056A183e13b3
@@ -138,19 +136,14 @@ An LLM is required to mint your Mining Rig NFT (one-time identity verification).
138
136
  | Anthropic | `claude-sonnet-4-5-20250929` | ~$0.005 | Works but slower |
139
137
  | Ollama | `llama3.1` | Free | Local GPU required |
140
138
 
141
- ## Funding (v0.6.0+)
139
+ ## Funding (v0.7.0+)
142
140
 
143
- Mining requires two assets on Base: **ETH** (gas) and **USDC** (x402 RPC). The `fund` command accepts deposits in 6 forms across 3 chains, auto-bridges to Base, and auto-splits into both:
141
+ Mining requires two assets on Base: **ETH** (gas) and **USDC** (x402 RPC). The `fund` command bridges from Solana or accepts deposits on Base, and auto-splits into both:
144
142
 
145
143
  ```bash
146
- # From Solana
144
+ # From Solana (deposit address — send from any wallet, QR code included)
147
145
  apow fund --chain solana --token sol # bridge SOL → ETH, auto-swap portion to USDC
148
146
  apow fund --chain solana --token usdc # bridge USDC, auto-swap portion to ETH
149
- apow fund --chain solana --token sol --key <b58> # direct signing (fast, ~20s)
150
-
151
- # From Ethereum mainnet
152
- apow fund --chain ethereum --token eth # bridge ETH → Base, auto-swap portion to USDC
153
- apow fund --chain ethereum --token usdc # bridge USDC → Base, auto-swap portion to ETH
154
147
 
155
148
  # Already on Base
156
149
  apow fund --chain base --token eth # show address, wait for deposit, auto-split
@@ -160,6 +153,8 @@ apow fund --chain base --token usdc # show address, wait for depos
160
153
  apow fund --chain base --no-swap
161
154
  ```
162
155
 
156
+ **Solana bridging:** Uses [Squid Router](https://squidrouter.com/) (Chainflip). Generates a one-time deposit address with QR code — send from Phantom, Backpack, or any Solana wallet. Requires `SQUID_INTEGRATOR_ID` in `.env` (free at [squidrouter.com](https://app.squidrouter.com/)).
157
+
163
158
  **Auto-split targets:** 0.003 ETH (gas for ~100 mine txns) + 2.00 USDC (~100K x402 RPC calls). If both are already met, the CLI skips the swap.
164
159
 
165
160
  ## Speed Mining (v0.4.0+)
@@ -4,9 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.SLIPPAGE_BPS = exports.MIN_USDC = exports.MIN_ETH = exports.TOKENS = exports.CHAIN_IDS = void 0;
5
5
  exports.bridgeOutputAsset = bridgeOutputAsset;
6
6
  exports.CHAIN_IDS = {
7
- solana: { debridge: "7565164", squid: "solana" },
8
- ethereum: { debridge: "1", squid: "1" },
9
- base: { debridge: "8453", squid: "8453" },
7
+ solana: "solana",
8
+ base: "8453",
10
9
  };
11
10
  exports.TOKENS = {
12
11
  solana: {
@@ -14,11 +13,6 @@ exports.TOKENS = {
14
13
  nativeWrapped: "So11111111111111111111111111111111111111112",
15
14
  usdc: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
16
15
  },
17
- ethereum: {
18
- native: "0x0000000000000000000000000000000000000000",
19
- nativeSquid: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
20
- usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
21
- },
22
16
  base: {
23
17
  native: "0x0000000000000000000000000000000000000000",
24
18
  nativeSquid: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // Solana wallet utilities key parsing, balance checks, transaction signing.
2
+ // Solana balance utilities for deposit detection.
3
3
  // Uses dynamic import() for @solana/web3.js to avoid bloating startup.
4
4
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
5
  if (k2 === undefined) k2 = k;
@@ -36,76 +36,12 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  })();
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.getSolanaRpcUrl = getSolanaRpcUrl;
39
- exports.parseSolanaKey = parseSolanaKey;
40
- exports.getSolanaBalance = getSolanaBalance;
41
39
  exports.getAddressBalance = getAddressBalance;
42
40
  exports.getSplTokenBalance = getSplTokenBalance;
43
- exports.signAndSendTransaction = signAndSendTransaction;
44
41
  const DEFAULT_SOLANA_RPC = "https://api.mainnet-beta.solana.com";
45
- const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
46
- function base58Decode(str) {
47
- const bytes = [];
48
- for (const char of str) {
49
- const idx = BASE58_ALPHABET.indexOf(char);
50
- if (idx === -1)
51
- throw new Error(`Invalid base58 character: ${char}`);
52
- let carry = idx;
53
- for (let j = 0; j < bytes.length; j++) {
54
- carry += bytes[j] * 58;
55
- bytes[j] = carry & 0xff;
56
- carry >>= 8;
57
- }
58
- while (carry > 0) {
59
- bytes.push(carry & 0xff);
60
- carry >>= 8;
61
- }
62
- }
63
- // Preserve leading zeros
64
- for (const char of str) {
65
- if (char !== "1")
66
- break;
67
- bytes.push(0);
68
- }
69
- return new Uint8Array(bytes.reverse());
70
- }
71
42
  function getSolanaRpcUrl() {
72
43
  return process.env.SOLANA_RPC_URL || DEFAULT_SOLANA_RPC;
73
44
  }
74
- /** Parse a base58-encoded Solana secret key (64 bytes) or seed (32 bytes). */
75
- async function parseSolanaKey(input) {
76
- const { Keypair } = await Promise.resolve().then(() => __importStar(require("@solana/web3.js")));
77
- const trimmed = input.trim();
78
- // Try JSON array format first (Solana CLI keygen output)
79
- if (trimmed.startsWith("[")) {
80
- try {
81
- const arr = JSON.parse(trimmed);
82
- const keypair = Keypair.fromSecretKey(new Uint8Array(arr));
83
- return { keypair, publicKey: keypair.publicKey.toBase58() };
84
- }
85
- catch {
86
- throw new Error("Invalid Solana key: looks like JSON but could not parse.");
87
- }
88
- }
89
- // Base58 secret key (Phantom, Backpack export format)
90
- const decoded = base58Decode(trimmed);
91
- if (decoded.length === 64) {
92
- const keypair = Keypair.fromSecretKey(decoded);
93
- return { keypair, publicKey: keypair.publicKey.toBase58() };
94
- }
95
- if (decoded.length === 32) {
96
- // 32-byte seed
97
- const keypair = Keypair.fromSeed(decoded);
98
- return { keypair, publicKey: keypair.publicKey.toBase58() };
99
- }
100
- throw new Error(`Invalid Solana key: expected 64 bytes (secret key) or 32 bytes (seed), got ${decoded.length}. Provide the full base58-encoded secret key from your wallet.`);
101
- }
102
- /** Get SOL balance for a public key (in SOL, not lamports). */
103
- async function getSolanaBalance(publicKeyBase58) {
104
- const { Connection, PublicKey } = await Promise.resolve().then(() => __importStar(require("@solana/web3.js")));
105
- const connection = new Connection(getSolanaRpcUrl(), "confirmed");
106
- const lamports = await connection.getBalance(new PublicKey(publicKeyBase58));
107
- return lamports / 1e9;
108
- }
109
45
  /** Get SOL balance for any address (used to detect deposits). */
110
46
  async function getAddressBalance(address) {
111
47
  const { Connection, PublicKey } = await Promise.resolve().then(() => __importStar(require("@solana/web3.js")));
@@ -125,31 +61,3 @@ async function getSplTokenBalance(publicKeyBase58, mintAddress) {
125
61
  const parsed = accounts.value[0].account.data.parsed;
126
62
  return parsed?.info?.tokenAmount?.uiAmount ?? 0;
127
63
  }
128
- /** Deserialize, sign, and submit a serialized Solana transaction (hex or base64). */
129
- async function signAndSendTransaction(serializedTx, keypair) {
130
- const { Connection, VersionedTransaction, Transaction } = await Promise.resolve().then(() => __importStar(require("@solana/web3.js")));
131
- const connection = new Connection(getSolanaRpcUrl(), "confirmed");
132
- // deBridge returns hex (0x...), other bridges may return base64
133
- const txBuffer = serializedTx.startsWith("0x")
134
- ? Buffer.from(serializedTx.slice(2), "hex")
135
- : Buffer.from(serializedTx, "base64");
136
- // Get fresh blockhash (deBridge-generated txs may have stale ones)
137
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
138
- // Try versioned transaction first, fall back to legacy.
139
- // Skip preflight: deBridge Jupiter routes may fail simulation but succeed on-chain.
140
- let signature;
141
- try {
142
- const tx = VersionedTransaction.deserialize(txBuffer);
143
- tx.message.recentBlockhash = blockhash;
144
- tx.sign([keypair]);
145
- signature = await connection.sendTransaction(tx, { skipPreflight: true });
146
- }
147
- catch {
148
- const tx = Transaction.from(txBuffer);
149
- tx.recentBlockhash = blockhash;
150
- tx.sign(keypair);
151
- signature = await connection.sendRawTransaction(tx.serialize(), { skipPreflight: true });
152
- }
153
- await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight }, "confirmed");
154
- return signature;
155
- }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  // Squid Router bridge — deposit address flow.
3
- // Supports SOL→Base and Ethereum→Base via Chainflip multi-hop (~1-3 minutes).
3
+ // Supports SOL→Base via Chainflip multi-hop (~1-3 minutes).
4
4
  // Requires SQUID_INTEGRATOR_ID (free, apply at squidrouter.com).
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SQUID_ROUTES = void 0;
@@ -10,33 +10,17 @@ const constants_1 = require("./constants");
10
10
  const SQUID_API = "https://v2.api.squidrouter.com/v2";
11
11
  exports.SQUID_ROUTES = {
12
12
  sol_to_eth: {
13
- fromChain: constants_1.CHAIN_IDS.solana.squid,
13
+ fromChain: constants_1.CHAIN_IDS.solana,
14
14
  fromToken: constants_1.TOKENS.solana.nativeWrapped,
15
- toChain: constants_1.CHAIN_IDS.base.squid,
15
+ toChain: constants_1.CHAIN_IDS.base,
16
16
  toToken: constants_1.TOKENS.base.nativeSquid,
17
17
  srcDecimals: 9,
18
18
  dstDecimals: 18,
19
19
  },
20
20
  sol_usdc_to_base_usdc: {
21
- fromChain: constants_1.CHAIN_IDS.solana.squid,
21
+ fromChain: constants_1.CHAIN_IDS.solana,
22
22
  fromToken: constants_1.TOKENS.solana.usdc,
23
- toChain: constants_1.CHAIN_IDS.base.squid,
24
- toToken: constants_1.TOKENS.base.usdc,
25
- srcDecimals: 6,
26
- dstDecimals: 6,
27
- },
28
- eth_to_base_eth: {
29
- fromChain: constants_1.CHAIN_IDS.ethereum.squid,
30
- fromToken: constants_1.TOKENS.ethereum.nativeSquid,
31
- toChain: constants_1.CHAIN_IDS.base.squid,
32
- toToken: constants_1.TOKENS.base.nativeSquid,
33
- srcDecimals: 18,
34
- dstDecimals: 18,
35
- },
36
- eth_usdc_to_base_usdc: {
37
- fromChain: constants_1.CHAIN_IDS.ethereum.squid,
38
- fromToken: constants_1.TOKENS.ethereum.usdc,
39
- toChain: constants_1.CHAIN_IDS.base.squid,
23
+ toChain: constants_1.CHAIN_IDS.base,
40
24
  toToken: constants_1.TOKENS.base.usdc,
41
25
  srcDecimals: 6,
42
26
  dstDecimals: 6,
@@ -45,9 +29,8 @@ exports.SQUID_ROUTES = {
45
29
  function getIntegratorId() {
46
30
  const id = process.env.SQUID_INTEGRATOR_ID;
47
31
  if (!id) {
48
- throw new Error("SQUID_INTEGRATOR_ID is required for the deposit address flow.\n" +
49
- "Get one free at https://app.squidrouter.com/\n" +
50
- "Or use direct signing instead: apow fund --chain solana --key <base58>");
32
+ throw new Error("SQUID_INTEGRATOR_ID is required for bridging from Solana.\n" +
33
+ "Get one free at https://app.squidrouter.com/");
51
34
  }
52
35
  return id;
53
36
  }
package/dist/errors.js CHANGED
@@ -110,19 +110,11 @@ const patterns = [
110
110
  }),
111
111
  },
112
112
  {
113
- test: (m) => m.includes("deBridge") && (m.includes("route") || m.includes("unavailable")),
113
+ test: (m) => m.includes("Squid") && (m.includes("route") || m.includes("deposit")),
114
114
  classify: () => ({
115
115
  category: "transient",
116
116
  userMessage: "Bridge route temporarily unavailable",
117
- recovery: "Try a different bridge method or wait a few minutes",
118
- }),
119
- },
120
- {
121
- test: (m) => m.includes("ethereum-rpc.publicnode") || (m.includes("ETHEREUM_RPC") && m.includes("unreachable")),
122
- classify: () => ({
123
- category: "setup",
124
- userMessage: "Ethereum mainnet RPC unreachable",
125
- recovery: "Set ETHEREUM_RPC_URL in .env (default: https://ethereum-rpc.publicnode.com)",
117
+ recovery: "Wait a few minutes and try again",
126
118
  }),
127
119
  },
128
120
  ];
package/dist/fund.js CHANGED
@@ -1,15 +1,13 @@
1
1
  "use strict";
2
2
  // Fund command — unified funding for mining on Base.
3
- // Accepts deposits in 6 forms across 3 chains, auto-bridges to Base,
3
+ // Accepts deposits from Solana (via Squid Router bridge) or Base (direct send),
4
4
  // auto-splits into ETH (gas) + USDC (x402 RPC).
5
5
  //
6
6
  // Deposit types:
7
7
  // 1. Solana SOL → bridge → ETH on Base → swap portion to USDC
8
8
  // 2. Solana USDC → bridge → USDC on Base → swap portion to ETH
9
- // 3. Ethereum ETH bridge ETH on Base → swap portion to USDC
10
- // 4. Ethereum USDC bridge USDC on Base → swap portion to ETH
11
- // 5. Base ETH → (already there) → swap portion to USDC
12
- // 6. Base USDC → (already there) → swap portion to ETH
9
+ // 3. Base ETH (already there) → swap portion to USDC
10
+ // 4. Base USDC (already there) → swap portion to ETH
13
11
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
12
  if (k2 === undefined) k2 = k;
15
13
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -47,11 +45,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
47
45
  exports.runFundFlow = runFundFlow;
48
46
  const viem_1 = require("viem");
49
47
  const constants_1 = require("./bridge/constants");
50
- const debridge_1 = require("./bridge/debridge");
51
48
  const squid_1 = require("./bridge/squid");
52
49
  const uniswap_1 = require("./bridge/uniswap");
53
50
  const wallet_1 = require("./wallet");
54
- const config_1 = require("./config");
55
51
  const ui = __importStar(require("./ui"));
56
52
  async function fetchPrices() {
57
53
  const res = await fetch("https://api.coingecko.com/api/v3/simple/price?ids=solana,ethereum&vs_currencies=usd");
@@ -189,94 +185,9 @@ async function showFinalBalances() {
189
185
  console.log("");
190
186
  }
191
187
  // ---------------------------------------------------------------------------
192
- // Solana direct bridge (deBridge DLN)
188
+ // Solana fund (Squid Router deposit address)
193
189
  // ---------------------------------------------------------------------------
194
- async function runSolanaDirectBridge(solanaKeyInput, baseAddress, sourceToken, targetEth) {
195
- const solanaSpinner = ui.spinner("Checking Solana balance...");
196
- const solana = await Promise.resolve().then(() => __importStar(require("./bridge/solana")));
197
- let kp;
198
- try {
199
- kp = await solana.parseSolanaKey(solanaKeyInput);
200
- }
201
- catch (err) {
202
- solanaSpinner.fail("Invalid Solana key");
203
- throw err;
204
- }
205
- if (sourceToken === "usdc") {
206
- const usdcBal = await solana.getSplTokenBalance(kp.publicKey, constants_1.TOKENS.solana.usdc);
207
- solanaSpinner.stop(`Solana USDC balance: ${usdcBal.toFixed(2)} USDC`);
208
- const priceSpinner = ui.spinner("Fetching prices...");
209
- const prices = await fetchPrices();
210
- priceSpinner.stop(`ETH price: $${prices.ethPriceUsd.toFixed(0)}`);
211
- const usdcAmount = targetEth * prices.ethPriceUsd * 1.1; // 10% buffer
212
- if (usdcBal < usdcAmount) {
213
- ui.error(`Insufficient USDC. Need ~${usdcAmount.toFixed(2)} USDC, have ${usdcBal.toFixed(2)} USDC.`);
214
- return;
215
- }
216
- console.log("");
217
- ui.table([
218
- ["Bridging", `${usdcAmount.toFixed(2)} USDC → USDC on Base`],
219
- ["Via", "deBridge DLN (~20 seconds)"],
220
- ["From", `${kp.publicKey.slice(0, 4)}...${kp.publicKey.slice(-4)}`],
221
- ["To", `${baseAddress.slice(0, 6)}...${baseAddress.slice(-4)}`],
222
- ]);
223
- console.log("");
224
- const proceed = await ui.confirm("Confirm bridge?");
225
- if (!proceed) {
226
- console.log(" Cancelled.");
227
- return;
228
- }
229
- const bridgeSpinner = ui.spinner("Signing bridge transaction...");
230
- const result = await (0, debridge_1.bridgeFromSolana)(kp.keypair, baseAddress, usdcAmount, debridge_1.ROUTES.sol_usdc_to_base_usdc);
231
- bridgeSpinner.stop(`Submitted! Order: ${result.orderId.slice(0, 12)}...`);
232
- const pollSpinner = ui.spinner("Waiting for bridge fulfillment... (~20s)");
233
- const fulfillment = await (0, debridge_1.pollOrderStatus)(result.orderId, (s) => pollSpinner.update(`Bridge status: ${s}`));
234
- pollSpinner.stop("Bridge complete! USDC arrived on Base");
235
- await autoSplit("usdc", prices, false);
236
- await showFinalBalances();
237
- }
238
- else {
239
- // SOL → ETH
240
- const balance = await solana.getSolanaBalance(kp.publicKey);
241
- solanaSpinner.stop(`Solana balance: ${balance.toFixed(4)} SOL`);
242
- const priceSpinner = ui.spinner("Fetching prices...");
243
- const prices = await fetchPrices();
244
- const solAmount = amountNeededForEth(targetEth, prices.solPriceUsd, prices.ethPriceUsd);
245
- priceSpinner.stop(`SOL/ETH rate: ${prices.solPerEth.toFixed(1)} SOL = 1 ETH`);
246
- if (balance < solAmount) {
247
- ui.error(`Insufficient SOL. Need ~${solAmount.toFixed(4)} SOL, have ${balance.toFixed(4)} SOL.`);
248
- return;
249
- }
250
- console.log("");
251
- ui.table([
252
- ["Bridging", `${solAmount.toFixed(4)} SOL → ~${targetEth.toFixed(4)} ETH on Base`],
253
- ["Via", "deBridge DLN (~20 seconds)"],
254
- ["From", `${kp.publicKey.slice(0, 4)}...${kp.publicKey.slice(-4)}`],
255
- ["To", `${baseAddress.slice(0, 6)}...${baseAddress.slice(-4)}`],
256
- ]);
257
- console.log("");
258
- const proceed = await ui.confirm("Confirm bridge?");
259
- if (!proceed) {
260
- console.log(" Cancelled.");
261
- return;
262
- }
263
- const bridgeSpinner = ui.spinner("Signing bridge transaction...");
264
- const result = await (0, debridge_1.bridgeFromSolana)(kp.keypair, baseAddress, solAmount, debridge_1.ROUTES.sol_to_eth);
265
- bridgeSpinner.stop(`Submitted! Order: ${result.orderId.slice(0, 12)}...`);
266
- const pollSpinner = ui.spinner("Waiting for bridge fulfillment... (~20s)");
267
- const fulfillment = await (0, debridge_1.pollOrderStatus)(result.orderId, (s) => pollSpinner.update(`Bridge status: ${s}`));
268
- const received = fulfillment.received
269
- ? (Number(fulfillment.received) / 1e18).toFixed(6)
270
- : `~${targetEth.toFixed(4)}`;
271
- pollSpinner.stop(`Bridge complete! ${received} ETH arrived on Base`);
272
- await autoSplit("eth", prices, false);
273
- await showFinalBalances();
274
- }
275
- }
276
- // ---------------------------------------------------------------------------
277
- // Solana deposit address bridge (Squid Router)
278
- // ---------------------------------------------------------------------------
279
- async function runSolanaDepositBridge(baseAddress, sourceToken, targetEth) {
190
+ async function runSolanaFund(baseAddress, sourceToken, targetEth) {
280
191
  const priceSpinner = ui.spinner("Fetching prices...");
281
192
  const prices = await fetchPrices();
282
193
  priceSpinner.stop(`ETH price: $${prices.ethPriceUsd.toFixed(0)}`);
@@ -377,109 +288,6 @@ async function runSolanaDepositBridge(baseAddress, sourceToken, targetEth) {
377
288
  await showFinalBalances();
378
289
  }
379
290
  // ---------------------------------------------------------------------------
380
- // Ethereum direct bridge (deBridge DLN)
381
- // ---------------------------------------------------------------------------
382
- async function runEthereumDirectBridge(baseAddress, sourceToken, targetEth) {
383
- if (!config_1.config.privateKey) {
384
- ui.error("No PRIVATE_KEY configured. Your wallet key is used on Ethereum mainnet too.");
385
- return;
386
- }
387
- const priceSpinner = ui.spinner("Fetching prices...");
388
- const prices = await fetchPrices();
389
- priceSpinner.stop(`ETH price: $${prices.ethPriceUsd.toFixed(0)}`);
390
- const route = sourceToken === "usdc"
391
- ? debridge_1.ROUTES.eth_usdc_to_base_usdc
392
- : debridge_1.ROUTES.eth_to_base_eth;
393
- const amount = sourceToken === "usdc"
394
- ? targetEth * prices.ethPriceUsd * 1.1
395
- : targetEth * 1.1;
396
- const tokenLabel = sourceToken === "usdc" ? "USDC" : "ETH";
397
- const receivedLabel = sourceToken === "usdc" ? "USDC" : "ETH";
398
- console.log("");
399
- ui.table([
400
- ["Bridging", `${amount.toFixed(sourceToken === "usdc" ? 2 : 6)} ${tokenLabel} → ${receivedLabel} on Base`],
401
- ["Via", "deBridge DLN (~20 seconds)"],
402
- ["From", `Ethereum mainnet (${baseAddress.slice(0, 6)}...${baseAddress.slice(-4)})`],
403
- ["To", `Base (same address)`],
404
- ]);
405
- console.log("");
406
- const proceed = await ui.confirm("Confirm bridge?");
407
- if (!proceed) {
408
- console.log(" Cancelled.");
409
- return;
410
- }
411
- const bridgeSpinner = ui.spinner("Signing bridge transaction on Ethereum...");
412
- try {
413
- const result = await (0, debridge_1.bridgeFromEvm)(config_1.config.privateKey, baseAddress, amount, route);
414
- bridgeSpinner.stop(`Submitted! Order: ${result.orderId.slice(0, 12)}...`);
415
- const pollSpinner = ui.spinner("Waiting for bridge fulfillment... (~20s)");
416
- const fulfillment = await (0, debridge_1.pollOrderStatus)(result.orderId, (s) => pollSpinner.update(`Bridge status: ${s}`));
417
- pollSpinner.stop(`Bridge complete! ${receivedLabel} arrived on Base`);
418
- const outputAsset = (0, constants_1.bridgeOutputAsset)(sourceToken);
419
- await autoSplit(outputAsset, prices, false);
420
- await showFinalBalances();
421
- }
422
- catch (err) {
423
- bridgeSpinner.fail("Bridge failed");
424
- throw err;
425
- }
426
- }
427
- // ---------------------------------------------------------------------------
428
- // Ethereum deposit address bridge (Squid Router)
429
- // ---------------------------------------------------------------------------
430
- async function runEthereumDepositBridge(baseAddress, sourceToken, targetEth) {
431
- const priceSpinner = ui.spinner("Fetching prices...");
432
- const prices = await fetchPrices();
433
- priceSpinner.stop(`ETH price: $${prices.ethPriceUsd.toFixed(0)}`);
434
- const route = sourceToken === "usdc"
435
- ? squid_1.SQUID_ROUTES.eth_usdc_to_base_usdc
436
- : squid_1.SQUID_ROUTES.eth_to_base_eth;
437
- const amount = sourceToken === "usdc"
438
- ? targetEth * prices.ethPriceUsd * 1.1
439
- : targetEth * 1.1;
440
- const tokenLabel = sourceToken === "usdc" ? "USDC" : "ETH";
441
- const addrSpinner = ui.spinner("Generating deposit address...");
442
- const squid = await Promise.resolve().then(() => __importStar(require("./bridge/squid")));
443
- let deposit;
444
- try {
445
- deposit = await squid.getDepositAddress(baseAddress, amount, route);
446
- }
447
- catch (err) {
448
- addrSpinner.fail("Failed to get deposit address");
449
- throw err;
450
- }
451
- addrSpinner.stop("Deposit address ready");
452
- console.log("");
453
- console.log(` ${ui.bold(`Send ${tokenLabel} on Ethereum mainnet to:`)}`);
454
- console.log("");
455
- console.log(` ${ui.cyan(deposit.depositAddress)}`);
456
- console.log("");
457
- await showQrCode(deposit.depositAddress);
458
- console.log("");
459
- ui.table([
460
- ["Amount", `~${amount.toFixed(sourceToken === "usdc" ? 2 : 6)} ${tokenLabel}`],
461
- ["Network", "Ethereum mainnet"],
462
- ["You'll receive", `~${deposit.expectedReceive} ${sourceToken === "usdc" ? "USDC" : "ETH"} on Base`],
463
- ["Bridge", "Squid Router (Chainflip)"],
464
- ["Time", "~1-3 minutes"],
465
- ]);
466
- console.log("");
467
- if (deposit.expiresAt) {
468
- ui.warn(`Deposit address expires: ${deposit.expiresAt}`);
469
- console.log("");
470
- }
471
- // Wait for user to send manually — poll Squid status directly
472
- const bridgeSpinner = ui.spinner(`Send ${tokenLabel} and waiting for bridge... (Ctrl+C to cancel)`);
473
- // For Ethereum deposits, we rely on Squid status API rather than polling an RPC
474
- const result = await (0, squid_1.pollBridgeStatus)(deposit.requestId, route.dstDecimals, (status) => bridgeSpinner.update(`Bridge status: ${status}`), 900_000);
475
- const received = result.received || deposit.expectedReceive;
476
- const receivedAsset = sourceToken === "usdc" ? "USDC" : "ETH";
477
- bridgeSpinner.stop(`Bridge complete! ${received} ${receivedAsset} arrived`);
478
- const outputAsset = (0, constants_1.bridgeOutputAsset)(sourceToken);
479
- await autoSplit(outputAsset, prices, false);
480
- await showFinalBalances();
481
- }
482
- // ---------------------------------------------------------------------------
483
291
  // Base manual send (already on the right chain)
484
292
  // ---------------------------------------------------------------------------
485
293
  async function runBaseFund(baseAddress, sourceToken, noSwap) {
@@ -548,15 +356,10 @@ async function runBaseFund(baseAddress, sourceToken, noSwap) {
548
356
  async function selectSourceChain() {
549
357
  console.log(" Where are your funds?");
550
358
  console.log(` ${ui.cyan("1.")} Solana (SOL or USDC)`);
551
- console.log(` ${ui.cyan("2.")} Ethereum mainnet (ETH or USDC)`);
552
- console.log(` ${ui.cyan("3.")} Base (send ETH or USDC manually)`);
359
+ console.log(` ${ui.cyan("2.")} Base (send ETH or USDC directly)`);
553
360
  console.log("");
554
361
  const choice = await ui.prompt("Choice", "1");
555
- switch (choice) {
556
- case "2": return "ethereum";
557
- case "3": return "base";
558
- default: return "solana";
559
- }
362
+ return choice === "2" ? "base" : "solana";
560
363
  }
561
364
  async function selectSourceToken(chain) {
562
365
  const nativeLabel = chain === "solana" ? "SOL" : "ETH";
@@ -574,8 +377,6 @@ function parseSourceChain(value) {
574
377
  const v = value.toLowerCase();
575
378
  if (v === "solana" || v === "sol")
576
379
  return "solana";
577
- if (v === "ethereum" || v === "eth")
578
- return "ethereum";
579
380
  if (v === "base")
580
381
  return "base";
581
382
  return undefined;
@@ -639,43 +440,7 @@ async function runFundFlow(options) {
639
440
  // Route to the appropriate flow
640
441
  switch (chain) {
641
442
  case "solana": {
642
- if (options.key) {
643
- await runSolanaDirectBridge(options.key, baseAddress, token, targetEth);
644
- }
645
- else {
646
- console.log("");
647
- console.log(" Bridge method:");
648
- console.log(` ${ui.cyan("A.")} Direct signing (~20s, need Solana private key)`);
649
- console.log(` ${ui.cyan("B.")} Deposit address (~1-3 min, send from any wallet)`);
650
- console.log("");
651
- const method = await ui.prompt("Choice", "A");
652
- if (method.toUpperCase() === "A") {
653
- const key = await ui.promptSecret("Solana private key (base58)");
654
- if (!key) {
655
- ui.error("No key provided.");
656
- return;
657
- }
658
- await runSolanaDirectBridge(key, baseAddress, token, targetEth);
659
- }
660
- else {
661
- await runSolanaDepositBridge(baseAddress, token, targetEth);
662
- }
663
- }
664
- break;
665
- }
666
- case "ethereum": {
667
- console.log("");
668
- console.log(" Bridge method:");
669
- console.log(` ${ui.cyan("A.")} Direct signing (~20s, uses your PRIVATE_KEY on mainnet)`);
670
- console.log(` ${ui.cyan("B.")} Deposit address (~1-3 min, send from any wallet)`);
671
- console.log("");
672
- const method = await ui.prompt("Choice", "A");
673
- if (method.toUpperCase() === "A") {
674
- await runEthereumDirectBridge(baseAddress, token, targetEth);
675
- }
676
- else {
677
- await runEthereumDepositBridge(baseAddress, token, targetEth);
678
- }
443
+ await runSolanaFund(baseAddress, token, targetEth);
679
444
  break;
680
445
  }
681
446
  case "base": {
package/dist/index.js CHANGED
@@ -260,10 +260,9 @@ async function main() {
260
260
  });
261
261
  program
262
262
  .command("fund")
263
- .description("Fund your wallet — bridge from Solana/Ethereum or swap on Base")
264
- .option("--chain <chain>", "Source chain: solana, ethereum, base")
265
- .option("--token <token>", "Source token: sol, eth, usdc")
266
- .option("--key <base58>", "Solana private key for direct signing")
263
+ .description("Fund your wallet — bridge from Solana or send on Base")
264
+ .option("--chain <chain>", "Source chain: solana, base")
265
+ .option("--token <token>", "Source token: sol, usdc, eth")
267
266
  .option("--amount <eth>", "Target ETH amount (default: 0.005)")
268
267
  .option("--no-swap", "Skip auto-split after bridging")
269
268
  .hook("preAction", async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apow-cli",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Mine AGENT tokens on Base L2 with Agentic Proof of Work",
5
5
  "keywords": [
6
6
  "apow",
package/skill.md CHANGED
@@ -73,8 +73,7 @@ EOF
73
73
 
74
74
  # 3. Fund the wallet (bridge from any chain, auto-splits into ETH + USDC)
75
75
  npx apow-cli fund --chain solana --token sol # bridge SOL → ETH+USDC on Base
76
- # Or: npx apow-cli fund --chain solana --token sol --key <base58> # direct signing (~20s)
77
- # Or: npx apow-cli fund --chain ethereum --token eth # bridge from Ethereum mainnet
76
+ # Or: npx apow-cli fund --chain base # send ETH or USDC on Base directly
78
77
  # Or: ask your user to send ETH + USDC on Base directly
79
78
 
80
79
  # 4. Mint + mine (fully autonomous from here)
@@ -152,33 +151,20 @@ Your mining wallet needs ETH on Base for gas and the mint fee.
152
151
 
153
152
  ### Built-in Bridge: `apow fund` (Recommended)
154
153
 
155
- The CLI accepts deposits in 6 forms across 3 chains, auto-bridges to Base, and auto-splits into ETH (gas) + USDC (x402 RPC):
154
+ The CLI bridges from Solana via [Squid Router](https://squidrouter.com/) (Chainflip), or accepts deposits directly on Base. Auto-splits into ETH (gas) + USDC (x402 RPC):
156
155
 
157
156
  ```bash
158
157
  npx apow-cli fund # Interactive: choose chain + token
159
158
  npx apow-cli fund --chain solana --token sol # Bridge SOL → ETH+USDC on Base
160
- npx apow-cli fund --chain solana --token sol --key <b58> # Direct Solana signing (~20s)
161
159
  npx apow-cli fund --chain solana --token usdc # Bridge Solana USDC → Base
162
- npx apow-cli fund --chain ethereum --token eth # Bridge ETH from mainnet
163
- npx apow-cli fund --chain ethereum --token usdc # Bridge USDC from mainnet
164
160
  npx apow-cli fund --chain base # Show address, wait for deposit
165
161
  npx apow-cli fund --chain base --no-swap # Skip auto-split
166
162
  ```
167
163
 
168
- **Bridge methods per chain:**
169
-
170
- | Chain | Direct signing | Deposit address |
171
- |-------|---------------|-----------------|
172
- | Solana | deBridge DLN (~20s, `--key`) | Squid Router (~1-3 min) |
173
- | Ethereum | deBridge DLN (~20s, uses PRIVATE_KEY on mainnet) | Squid Router (~1-3 min) |
174
- | Base | N/A (already on Base) | Show address + QR code |
164
+ **Solana bridging:** Generates a one-time deposit address with QR code. Send tokens from any Solana wallet (Phantom, Backpack, etc.). Requires `SQUID_INTEGRATOR_ID` in `.env` (free at [squidrouter.com](https://app.squidrouter.com/)). Bridge time: ~1-3 minutes via Chainflip.
175
165
 
176
166
  **Auto-split:** After bridging, the CLI checks ETH and USDC balances. If either is below the minimum (0.003 ETH for gas, 2.00 USDC for x402 RPC), it swaps the needed amount via Uniswap V3 on Base. Use `--no-swap` to skip.
177
167
 
178
- **Direct signing (Solana `--key`):** Provide your base58 Solana secret key. The CLI calls deBridge DLN to create a bridge order, signs the Solana transaction locally, submits it, and polls until funds arrive on Base. No API key needed.
179
-
180
- **Deposit address (no `--key`):** Requires `SQUID_INTEGRATOR_ID` in `.env` (free, apply at [squidrouter.com](https://app.squidrouter.com/)). Generates a one-time deposit address with a QR code. Send tokens from any wallet and the bridge handles the rest.
181
-
182
168
  ### Manual Funding Options
183
169
 
184
170
  If you prefer not to use the built-in bridge:
@@ -280,7 +266,6 @@ CHAIN=base
280
266
  | `RPC_URL` | No | Alchemy x402 | Base JSON-RPC endpoint. Default uses Alchemy x402 (premium, paid via USDC). Set to override with a custom endpoint. |
281
267
  | `CHAIN` | No | `base` | Network selector; auto-detects `baseSepolia` if RPC URL contains "sepolia" |
282
268
  | `SOLANA_RPC_URL` | No | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint (only for `apow fund --chain solana`) |
283
- | `ETHEREUM_RPC_URL` | No | `https://ethereum-rpc.publicnode.com` | Ethereum mainnet RPC (only for `apow fund --chain ethereum`) |
284
269
  | `SQUID_INTEGRATOR_ID` | No | - | Squid Router integrator ID for deposit address flow (free at [squidrouter.com](https://app.squidrouter.com/)) |
285
270
 
286
271
  ### LLM Provider Recommendations (for Minting)
@@ -638,12 +623,11 @@ The CLI makes only these network calls:
638
623
  2. **LLM API** (to user-configured provider): sends only word-puzzle prompts for SMHL solving, never wallet data
639
624
  3. **Bridge APIs** (only when using `apow fund`):
640
625
  - **CoinGecko** (`api.coingecko.com`): SOL/ETH price quotes
641
- - **deBridge DLN** (`dln.debridge.finance`): bridge order creation and status (direct signing flow)
642
- - **Squid Router** (`v2.api.squidrouter.com`): deposit address generation (deposit address flow)
626
+ - **Squid Router** (`v2.api.squidrouter.com`): deposit address generation and bridge status
643
627
  - **Uniswap V3** (on-chain, Base): ETH/USDC swaps for auto-split
644
- - **Solana RPC** (`api.mainnet-beta.solana.com` or custom): balance checks and tx submission
628
+ - **Solana RPC** (`api.mainnet-beta.solana.com` or custom): balance checks
645
629
 
646
- No private keys are transmitted to bridge providers. deBridge returns a serialized Solana transaction that is signed locally. Squid generates a deposit address, and the user sends SOL themselves.
630
+ No private keys are transmitted to bridge providers. Squid generates a deposit address, and the user sends tokens from their own wallet.
647
631
 
648
632
  ### LLM Calls Are Data-Isolated
649
633
 
@@ -1,251 +0,0 @@
1
- "use strict";
2
- // deBridge DLN bridge — direct signing flow.
3
- // Supports SOL→Base and Ethereum→Base bridging.
4
- // No API key needed.
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
- Object.defineProperty(o, "default", { enumerable: true, value: v });
18
- }) : function(o, v) {
19
- o["default"] = v;
20
- });
21
- var __importStar = (this && this.__importStar) || (function () {
22
- var ownKeys = function(o) {
23
- ownKeys = Object.getOwnPropertyNames || function (o) {
24
- var ar = [];
25
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
- return ar;
27
- };
28
- return ownKeys(o);
29
- };
30
- return function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
- })();
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.ROUTES = void 0;
40
- exports.bridgeFromSolana = bridgeFromSolana;
41
- exports.bridgeFromEvm = bridgeFromEvm;
42
- exports.pollOrderStatus = pollOrderStatus;
43
- const viem_1 = require("viem");
44
- const chains_1 = require("viem/chains");
45
- const accounts_1 = require("viem/accounts");
46
- const constants_1 = require("./constants");
47
- const solana = __importStar(require("./solana"));
48
- const DLN_API = "https://dln.debridge.finance/v1.0";
49
- exports.ROUTES = {
50
- sol_to_eth: {
51
- srcChainId: constants_1.CHAIN_IDS.solana.debridge,
52
- srcToken: constants_1.TOKENS.solana.native,
53
- srcDecimals: 9,
54
- dstChainId: constants_1.CHAIN_IDS.base.debridge,
55
- dstToken: constants_1.TOKENS.base.native,
56
- },
57
- sol_usdc_to_base_usdc: {
58
- srcChainId: constants_1.CHAIN_IDS.solana.debridge,
59
- srcToken: constants_1.TOKENS.solana.usdc,
60
- srcDecimals: 6,
61
- dstChainId: constants_1.CHAIN_IDS.base.debridge,
62
- dstToken: constants_1.TOKENS.base.usdc,
63
- },
64
- eth_to_base_eth: {
65
- srcChainId: constants_1.CHAIN_IDS.ethereum.debridge,
66
- srcToken: constants_1.TOKENS.ethereum.native,
67
- srcDecimals: 18,
68
- dstChainId: constants_1.CHAIN_IDS.base.debridge,
69
- dstToken: constants_1.TOKENS.base.native,
70
- },
71
- eth_usdc_to_base_usdc: {
72
- srcChainId: constants_1.CHAIN_IDS.ethereum.debridge,
73
- srcToken: constants_1.TOKENS.ethereum.usdc,
74
- srcDecimals: 6,
75
- dstChainId: constants_1.CHAIN_IDS.base.debridge,
76
- dstToken: constants_1.TOKENS.base.usdc,
77
- },
78
- };
79
- /**
80
- * Create DLN order params for any route.
81
- */
82
- function buildOrderParams(route, amount, senderAddress, baseAddress) {
83
- const rawAmount = Math.floor(amount * 10 ** route.srcDecimals);
84
- return new URLSearchParams({
85
- srcChainId: route.srcChainId,
86
- srcChainTokenIn: route.srcToken,
87
- srcChainTokenInAmount: rawAmount.toString(),
88
- dstChainId: route.dstChainId,
89
- dstChainTokenOut: route.dstToken,
90
- dstChainTokenOutRecipient: baseAddress,
91
- senderAddress,
92
- srcChainOrderAuthorityAddress: senderAddress,
93
- dstChainOrderAuthorityAddress: baseAddress,
94
- });
95
- }
96
- /**
97
- * Bridge from Solana to Base via deBridge DLN (direct signing).
98
- * Works for SOL→ETH and USDC→USDC routes.
99
- */
100
- async function bridgeFromSolana(solanaKeypair, baseAddress, amount, route = exports.ROUTES.sol_to_eth) {
101
- const startTime = Date.now();
102
- const srcPublicKey = solanaKeypair.publicKey.toBase58();
103
- const params = buildOrderParams(route, amount, srcPublicKey, baseAddress);
104
- const response = await fetch(`${DLN_API}/dln/order/create-tx?${params}`);
105
- if (!response.ok) {
106
- const body = await response.text();
107
- throw new Error(`deBridge API error (${response.status}): ${body}`);
108
- }
109
- const data = (await response.json());
110
- if (data.errorCode || data.error) {
111
- throw new Error(`deBridge error: ${data.error || data.message || JSON.stringify(data)}`);
112
- }
113
- const orderId = data.orderId;
114
- const txData = data.tx?.data;
115
- if (!txData) {
116
- throw new Error("deBridge API returned no transaction data");
117
- }
118
- const txSignature = await solana.signAndSendTransaction(txData, solanaKeypair);
119
- return {
120
- orderId,
121
- txSignature,
122
- status: "submitted",
123
- timeMs: Date.now() - startTime,
124
- };
125
- }
126
- /**
127
- * Bridge from Ethereum mainnet to Base via deBridge DLN (direct signing).
128
- * Uses the same PRIVATE_KEY — same address on all EVM chains.
129
- * Works for ETH→ETH and USDC→USDC routes.
130
- */
131
- async function bridgeFromEvm(privateKey, baseAddress, amount, route = exports.ROUTES.eth_to_base_eth) {
132
- const startTime = Date.now();
133
- const evmAccount = (0, accounts_1.privateKeyToAccount)(privateKey);
134
- const rpcUrl = process.env.ETHEREUM_RPC_URL ?? "https://ethereum-rpc.publicnode.com";
135
- const ethWalletClient = (0, viem_1.createWalletClient)({
136
- account: evmAccount,
137
- chain: chains_1.mainnet,
138
- transport: (0, viem_1.http)(rpcUrl),
139
- });
140
- const params = buildOrderParams(route, amount, evmAccount.address, baseAddress);
141
- const response = await fetch(`${DLN_API}/dln/order/create-tx?${params}`);
142
- if (!response.ok) {
143
- const body = await response.text();
144
- throw new Error(`deBridge API error (${response.status}): ${body}`);
145
- }
146
- const data = (await response.json());
147
- if (data.errorCode || data.error) {
148
- throw new Error(`deBridge error: ${data.error || data.message || JSON.stringify(data)}`);
149
- }
150
- const orderId = data.orderId;
151
- // For EVM, the API returns tx params instead of serialized tx
152
- const tx = data.tx;
153
- if (!tx || !tx.to) {
154
- throw new Error("deBridge API returned no EVM transaction data");
155
- }
156
- // For USDC routes, handle token approval if needed
157
- if (tx.allowanceTarget && tx.allowanceValue) {
158
- const { createPublicClient } = await Promise.resolve().then(() => __importStar(require("viem")));
159
- const ethPublicClient = createPublicClient({
160
- chain: chains_1.mainnet,
161
- transport: (0, viem_1.http)(rpcUrl),
162
- });
163
- const erc20Abi = [
164
- {
165
- type: "function",
166
- name: "allowance",
167
- inputs: [
168
- { name: "owner", type: "address" },
169
- { name: "spender", type: "address" },
170
- ],
171
- outputs: [{ name: "", type: "uint256" }],
172
- stateMutability: "view",
173
- },
174
- {
175
- type: "function",
176
- name: "approve",
177
- inputs: [
178
- { name: "spender", type: "address" },
179
- { name: "amount", type: "uint256" },
180
- ],
181
- outputs: [{ name: "", type: "bool" }],
182
- stateMutability: "nonpayable",
183
- },
184
- ];
185
- const currentAllowance = (await ethPublicClient.readContract({
186
- address: route.srcToken,
187
- abi: erc20Abi,
188
- functionName: "allowance",
189
- args: [evmAccount.address, tx.allowanceTarget],
190
- }));
191
- if (currentAllowance < BigInt(tx.allowanceValue)) {
192
- const approveTx = await ethWalletClient.writeContract({
193
- address: route.srcToken,
194
- abi: erc20Abi,
195
- functionName: "approve",
196
- args: [tx.allowanceTarget, BigInt(tx.allowanceValue)],
197
- });
198
- await ethPublicClient.waitForTransactionReceipt({ hash: approveTx });
199
- }
200
- }
201
- const txHash = await ethWalletClient.sendTransaction({
202
- to: tx.to,
203
- data: tx.data,
204
- value: tx.value ? BigInt(tx.value) : 0n,
205
- });
206
- return {
207
- orderId,
208
- txSignature: txHash,
209
- status: "submitted",
210
- timeMs: Date.now() - startTime,
211
- };
212
- }
213
- /**
214
- * Poll deBridge order status until fulfilled, cancelled, or timeout.
215
- * Works for all deBridge orders regardless of source chain.
216
- */
217
- async function pollOrderStatus(orderId, onUpdate, timeoutMs = 300_000) {
218
- const deadline = Date.now() + timeoutMs;
219
- while (Date.now() < deadline) {
220
- try {
221
- const response = await fetch(`${DLN_API}/dln/order/${orderId}/status`);
222
- if (response.ok) {
223
- const data = (await response.json());
224
- const status = data.status || data.orderStatus || "unknown";
225
- if (onUpdate)
226
- onUpdate(status);
227
- if (status === "Fulfilled" ||
228
- status === "ClaimedUnlock" ||
229
- status === "SentUnlock") {
230
- return {
231
- status: "fulfilled",
232
- received: data.fulfilledDstAmount
233
- ? data.fulfilledDstAmount.toString()
234
- : undefined,
235
- };
236
- }
237
- if (status === "Cancelled" || status === "CancelledByMaker") {
238
- throw new Error(`Bridge order was cancelled: ${status}`);
239
- }
240
- }
241
- }
242
- catch (err) {
243
- if (err instanceof Error && err.message.includes("cancelled"))
244
- throw err;
245
- // Transient fetch error — keep polling
246
- }
247
- await new Promise((r) => setTimeout(r, 5000));
248
- }
249
- throw new Error("Bridge order timed out after 5 minutes. Check deBridge explorer for order: " +
250
- orderId);
251
- }