@hypurrquant/defi-cli 0.2.3 → 0.2.5

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/dist/main.js CHANGED
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // src/main.ts
4
+ import { config } from "dotenv";
5
+ import { resolve as resolve4 } from "path";
6
+
3
7
  // src/cli.ts
4
8
  import { Command } from "commander";
5
9
  import { createRequire } from "module";
6
10
 
7
11
  // src/executor.ts
8
- import { createPublicClient as createPublicClient2, createWalletClient, http as http2 } from "viem";
12
+ import { createPublicClient as createPublicClient2, createWalletClient, http as http2, parseAbi as parseAbi3, encodeFunctionData as encodeFunctionData3 } from "viem";
9
13
  import { privateKeyToAccount } from "viem/accounts";
10
14
 
11
15
  // ../defi-core/dist/index.js
@@ -1012,8 +1016,13 @@ var Registry = class _Registry {
1012
1016
  };
1013
1017
 
1014
1018
  // src/executor.ts
1019
+ var ERC20_ABI = parseAbi3([
1020
+ "function allowance(address owner, address spender) external view returns (uint256)",
1021
+ "function approve(address spender, uint256 amount) external returns (bool)"
1022
+ ]);
1015
1023
  var GAS_BUFFER_BPS = 12000n;
1016
- var DEFAULT_PRIORITY_FEE_WEI = 100000000n;
1024
+ var DEFAULT_PRIORITY_FEE_WEI = 20000000000n;
1025
+ var MAX_GAS_LIMIT = 5000000000n;
1017
1026
  var Executor = class _Executor {
1018
1027
  dryRun;
1019
1028
  rpcUrl;
@@ -1027,6 +1036,62 @@ var Executor = class _Executor {
1027
1036
  static applyGasBuffer(gas) {
1028
1037
  return gas * GAS_BUFFER_BPS / 10000n;
1029
1038
  }
1039
+ /**
1040
+ * Check allowance for a single token/spender pair and send an approve tx if needed.
1041
+ * Only called in broadcast mode (not dry-run).
1042
+ */
1043
+ async checkAndApprove(token, spender, amount, owner, publicClient, walletClient) {
1044
+ const allowance = await publicClient.readContract({
1045
+ address: token,
1046
+ abi: ERC20_ABI,
1047
+ functionName: "allowance",
1048
+ args: [owner, spender]
1049
+ });
1050
+ if (allowance >= amount) return;
1051
+ process.stderr.write(
1052
+ ` Approving ${amount} of ${token} for ${spender}...
1053
+ `
1054
+ );
1055
+ const approveData = encodeFunctionData3({
1056
+ abi: ERC20_ABI,
1057
+ functionName: "approve",
1058
+ args: [spender, amount]
1059
+ });
1060
+ const rpcUrl = this.rpcUrl;
1061
+ const gasLimit = await (async () => {
1062
+ try {
1063
+ const estimated = await publicClient.estimateGas({
1064
+ to: token,
1065
+ data: approveData,
1066
+ account: owner
1067
+ });
1068
+ const buffered = _Executor.applyGasBuffer(estimated);
1069
+ return buffered > MAX_GAS_LIMIT ? MAX_GAS_LIMIT : buffered;
1070
+ } catch {
1071
+ return 80000n;
1072
+ }
1073
+ })();
1074
+ const [maxFeePerGas, maxPriorityFeePerGas] = await this.fetchEip1559Fees(rpcUrl);
1075
+ const approveTxHash = await walletClient.sendTransaction({
1076
+ chain: null,
1077
+ account: walletClient.account,
1078
+ to: token,
1079
+ data: approveData,
1080
+ gas: gasLimit > 0n ? gasLimit : void 0,
1081
+ maxFeePerGas: maxFeePerGas > 0n ? maxFeePerGas : void 0,
1082
+ maxPriorityFeePerGas: maxPriorityFeePerGas > 0n ? maxPriorityFeePerGas : void 0
1083
+ });
1084
+ const approveTxUrl = this.explorerUrl ? `${this.explorerUrl}/tx/${approveTxHash}` : void 0;
1085
+ process.stderr.write(` Approve tx: ${approveTxHash}
1086
+ `);
1087
+ if (approveTxUrl) process.stderr.write(` Explorer: ${approveTxUrl}
1088
+ `);
1089
+ await publicClient.waitForTransactionReceipt({ hash: approveTxHash });
1090
+ process.stderr.write(
1091
+ ` Approved ${amount} of ${token} for ${spender}
1092
+ `
1093
+ );
1094
+ }
1030
1095
  /** Fetch EIP-1559 fee params from the network. Returns [maxFeePerGas, maxPriorityFeePerGas]. */
1031
1096
  async fetchEip1559Fees(rpcUrl) {
1032
1097
  try {
@@ -1053,7 +1118,10 @@ var Executor = class _Executor {
1053
1118
  value: tx.value,
1054
1119
  account: from
1055
1120
  });
1056
- if (estimated > 0n) return _Executor.applyGasBuffer(estimated);
1121
+ if (estimated > 0n) {
1122
+ const buffered = _Executor.applyGasBuffer(estimated);
1123
+ return buffered > MAX_GAS_LIMIT ? MAX_GAS_LIMIT : buffered;
1124
+ }
1057
1125
  } catch {
1058
1126
  }
1059
1127
  return tx.gas_estimate ? BigInt(tx.gas_estimate) : 0n;
@@ -1139,6 +1207,18 @@ var Executor = class _Executor {
1139
1207
  }
1140
1208
  const publicClient = createPublicClient2({ transport: http2(rpcUrl) });
1141
1209
  const walletClient = createWalletClient({ account, transport: http2(rpcUrl) });
1210
+ if (tx.approvals && tx.approvals.length > 0) {
1211
+ for (const approval of tx.approvals) {
1212
+ await this.checkAndApprove(
1213
+ approval.token,
1214
+ approval.spender,
1215
+ approval.amount,
1216
+ account.address,
1217
+ publicClient,
1218
+ walletClient
1219
+ );
1220
+ }
1221
+ }
1142
1222
  const gasLimit = await this.estimateGasWithBuffer(rpcUrl, tx, account.address);
1143
1223
  const [maxFeePerGas, maxPriorityFeePerGas] = await this.fetchEip1559Fees(rpcUrl);
1144
1224
  process.stderr.write(`Broadcasting transaction to ${rpcUrl}...
@@ -1715,10 +1795,10 @@ function registerSchema(parent, getOpts) {
1715
1795
  }
1716
1796
 
1717
1797
  // ../defi-protocols/dist/index.js
1718
- import { encodeFunctionData as encodeFunctionData3, parseAbi as parseAbi3, createPublicClient as createPublicClient4, http as http4, decodeAbiParameters } from "viem";
1798
+ import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4, createPublicClient as createPublicClient4, http as http4, decodeAbiParameters } from "viem";
1719
1799
  import { encodeFunctionData as encodeFunctionData22, parseAbi as parseAbi22, createPublicClient as createPublicClient22, http as http22, decodeFunctionResult as decodeFunctionResult2, decodeAbiParameters as decodeAbiParameters2 } from "viem";
1720
1800
  import { encodeFunctionData as encodeFunctionData32, parseAbi as parseAbi32, createPublicClient as createPublicClient32, http as http32, decodeAbiParameters as decodeAbiParameters3, concatHex, zeroAddress } from "viem";
1721
- import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4, zeroAddress as zeroAddress2 } from "viem";
1801
+ import { encodeFunctionData as encodeFunctionData42, parseAbi as parseAbi42, zeroAddress as zeroAddress2 } from "viem";
1722
1802
  import { encodeFunctionData as encodeFunctionData5, parseAbi as parseAbi5 } from "viem";
1723
1803
  import { encodeFunctionData as encodeFunctionData6, parseAbi as parseAbi6, createPublicClient as createPublicClient42, http as http42, decodeAbiParameters as decodeAbiParameters4 } from "viem";
1724
1804
  import { encodeFunctionData as encodeFunctionData7, parseAbi as parseAbi7, zeroAddress as zeroAddress3 } from "viem";
@@ -1741,19 +1821,19 @@ import { parseAbi as parseAbi23, encodeFunctionData as encodeFunctionData21 } fr
1741
1821
  import { parseAbi as parseAbi24, encodeFunctionData as encodeFunctionData222 } from "viem";
1742
1822
  import { createPublicClient as createPublicClient19, http as http19, parseAbi as parseAbi25 } from "viem";
1743
1823
  var DEFAULT_FEE = 3e3;
1744
- var swapRouterAbi = parseAbi3([
1824
+ var swapRouterAbi = parseAbi4([
1745
1825
  "struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; }",
1746
1826
  "function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut)"
1747
1827
  ]);
1748
- var quoterAbi = parseAbi3([
1828
+ var quoterAbi = parseAbi4([
1749
1829
  "struct QuoteExactInputSingleParams { address tokenIn; address tokenOut; uint256 amountIn; uint24 fee; uint160 sqrtPriceLimitX96; }",
1750
1830
  "function quoteExactInputSingle(QuoteExactInputSingleParams memory params) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)"
1751
1831
  ]);
1752
- var ramsesQuoterAbi = parseAbi3([
1832
+ var ramsesQuoterAbi = parseAbi4([
1753
1833
  "struct QuoteExactInputSingleParams { address tokenIn; address tokenOut; uint256 amountIn; int24 tickSpacing; uint160 sqrtPriceLimitX96; }",
1754
1834
  "function quoteExactInputSingle(QuoteExactInputSingleParams memory params) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)"
1755
1835
  ]);
1756
- var positionManagerAbi = parseAbi3([
1836
+ var positionManagerAbi = parseAbi4([
1757
1837
  "struct MintParams { address token0; address token1; uint24 fee; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; }",
1758
1838
  "function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
1759
1839
  ]);
@@ -1786,7 +1866,7 @@ var UniswapV3Adapter = class {
1786
1866
  async buildSwap(params) {
1787
1867
  const deadline = BigInt(params.deadline ?? 18446744073709551615n);
1788
1868
  const amountOutMinimum = 0n;
1789
- const data = encodeFunctionData3({
1869
+ const data = encodeFunctionData4({
1790
1870
  abi: swapRouterAbi,
1791
1871
  functionName: "exactInputSingle",
1792
1872
  args: [
@@ -1807,7 +1887,8 @@ var UniswapV3Adapter = class {
1807
1887
  to: this.router,
1808
1888
  data,
1809
1889
  value: 0n,
1810
- gas_estimate: 2e5
1890
+ gas_estimate: 2e5,
1891
+ approvals: [{ token: params.token_in, spender: this.router, amount: params.amount_in }]
1811
1892
  };
1812
1893
  }
1813
1894
  async quote(params) {
@@ -1822,7 +1903,7 @@ var UniswapV3Adapter = class {
1822
1903
  tickSpacings.map(async (ts) => {
1823
1904
  const result = await client2.call({
1824
1905
  to: this.quoter,
1825
- data: encodeFunctionData3({
1906
+ data: encodeFunctionData4({
1826
1907
  abi: ramsesQuoterAbi,
1827
1908
  functionName: "quoteExactInputSingle",
1828
1909
  args: [
@@ -1868,7 +1949,7 @@ var UniswapV3Adapter = class {
1868
1949
  feeTiers.map(async (fee) => {
1869
1950
  const result = await client2.call({
1870
1951
  to: this.quoter,
1871
- data: encodeFunctionData3({
1952
+ data: encodeFunctionData4({
1872
1953
  abi: quoterAbi,
1873
1954
  functionName: "quoteExactInputSingle",
1874
1955
  args: [
@@ -1907,7 +1988,7 @@ var UniswapV3Adapter = class {
1907
1988
  }
1908
1989
  }
1909
1990
  const client = createPublicClient4({ transport: http4(this.rpcUrl) });
1910
- const callData = encodeFunctionData3({
1991
+ const callData = encodeFunctionData4({
1911
1992
  abi: swapRouterAbi,
1912
1993
  functionName: "exactInputSingle",
1913
1994
  args: [
@@ -1953,7 +2034,7 @@ var UniswapV3Adapter = class {
1953
2034
  const [token0, token1, rawAmount0, rawAmount1] = params.token_a.toLowerCase() < params.token_b.toLowerCase() ? [params.token_a, params.token_b, params.amount_a, params.amount_b] : [params.token_b, params.token_a, params.amount_b, params.amount_a];
1954
2035
  const amount0 = rawAmount0 === 0n && rawAmount1 > 0n ? 1n : rawAmount0;
1955
2036
  const amount1 = rawAmount1 === 0n && rawAmount0 > 0n ? 1n : rawAmount1;
1956
- const data = encodeFunctionData3({
2037
+ const data = encodeFunctionData4({
1957
2038
  abi: positionManagerAbi,
1958
2039
  functionName: "mint",
1959
2040
  args: [
@@ -1977,7 +2058,11 @@ var UniswapV3Adapter = class {
1977
2058
  to: pm,
1978
2059
  data,
1979
2060
  value: 0n,
1980
- gas_estimate: 5e5
2061
+ gas_estimate: 5e5,
2062
+ approvals: [
2063
+ { token: token0, spender: pm, amount: amount0 },
2064
+ { token: token1, spender: pm, amount: amount1 }
2065
+ ]
1981
2066
  };
1982
2067
  }
1983
2068
  async buildRemoveLiquidity(_params) {
@@ -2036,7 +2121,8 @@ var UniswapV2Adapter = class {
2036
2121
  to: this.router,
2037
2122
  data,
2038
2123
  value: 0n,
2039
- gas_estimate: 15e4
2124
+ gas_estimate: 15e4,
2125
+ approvals: [{ token: params.token_in, spender: this.router, amount: params.amount_in }]
2040
2126
  };
2041
2127
  }
2042
2128
  async quote(params) {
@@ -2155,7 +2241,11 @@ var UniswapV2Adapter = class {
2155
2241
  to: this.router,
2156
2242
  data,
2157
2243
  value: 0n,
2158
- gas_estimate: 3e5
2244
+ gas_estimate: 3e5,
2245
+ approvals: [
2246
+ { token: params.token_a, spender: this.router, amount: params.amount_a },
2247
+ { token: params.token_b, spender: this.router, amount: params.amount_b }
2248
+ ]
2159
2249
  };
2160
2250
  }
2161
2251
  async buildRemoveLiquidity(params) {
@@ -2242,7 +2332,8 @@ var AlgebraV3Adapter = class {
2242
2332
  to: this.router,
2243
2333
  data,
2244
2334
  value: 0n,
2245
- gas_estimate: 25e4
2335
+ gas_estimate: 25e4,
2336
+ approvals: [{ token: params.token_in, spender: this.router, amount: params.amount_in }]
2246
2337
  };
2247
2338
  }
2248
2339
  async quote(params) {
@@ -2360,7 +2451,11 @@ var AlgebraV3Adapter = class {
2360
2451
  to: pm,
2361
2452
  data,
2362
2453
  value: 0n,
2363
- gas_estimate: 5e5
2454
+ gas_estimate: 5e5,
2455
+ approvals: [
2456
+ { token: token0, spender: pm, amount: amount0 },
2457
+ { token: token1, spender: pm, amount: amount1 }
2458
+ ]
2364
2459
  };
2365
2460
  }
2366
2461
  async buildRemoveLiquidity(_params) {
@@ -2369,7 +2464,7 @@ var AlgebraV3Adapter = class {
2369
2464
  );
2370
2465
  }
2371
2466
  };
2372
- var abi3 = parseAbi4([
2467
+ var abi3 = parseAbi42([
2373
2468
  "function swapSingleTokenExactIn(address pool, address tokenIn, address tokenOut, uint256 exactAmountIn, uint256 minAmountOut, uint256 deadline, bool wethIsEth, bytes calldata userData) external returns (uint256 amountOut)"
2374
2469
  ]);
2375
2470
  var BalancerV3Adapter = class {
@@ -2389,7 +2484,7 @@ var BalancerV3Adapter = class {
2389
2484
  async buildSwap(params) {
2390
2485
  const minAmountOut = 0n;
2391
2486
  const deadline = BigInt(params.deadline ?? 18446744073709551615n);
2392
- const data = encodeFunctionData4({
2487
+ const data = encodeFunctionData42({
2393
2488
  abi: abi3,
2394
2489
  functionName: "swapSingleTokenExactIn",
2395
2490
  args: [
@@ -2537,7 +2632,8 @@ var SolidlyAdapter = class {
2537
2632
  to: this.router,
2538
2633
  data,
2539
2634
  value: 0n,
2540
- gas_estimate: 2e5
2635
+ gas_estimate: 2e5,
2636
+ approvals: [{ token: params.token_in, spender: this.router, amount: params.amount_in }]
2541
2637
  };
2542
2638
  }
2543
2639
  async callGetAmountsOut(client, callData) {
@@ -2620,7 +2716,11 @@ var SolidlyAdapter = class {
2620
2716
  to: this.router,
2621
2717
  data,
2622
2718
  value: 0n,
2623
- gas_estimate: 35e4
2719
+ gas_estimate: 35e4,
2720
+ approvals: [
2721
+ { token: params.token_a, spender: this.router, amount: params.amount_a },
2722
+ { token: params.token_b, spender: this.router, amount: params.amount_b }
2723
+ ]
2624
2724
  };
2625
2725
  }
2626
2726
  async buildRemoveLiquidity(params) {
@@ -2746,7 +2846,7 @@ var SolidlyGaugeAdapter = class {
2746
2846
  return this.protocolName;
2747
2847
  }
2748
2848
  // IGauge
2749
- async buildDeposit(gauge, amount, tokenId) {
2849
+ async buildDeposit(gauge, amount, tokenId, lpToken) {
2750
2850
  if (tokenId !== void 0) {
2751
2851
  const data2 = encodeFunctionData8({
2752
2852
  abi: gaugeAbi,
@@ -2758,7 +2858,8 @@ var SolidlyGaugeAdapter = class {
2758
2858
  to: gauge,
2759
2859
  data: data2,
2760
2860
  value: 0n,
2761
- gas_estimate: 2e5
2861
+ gas_estimate: 2e5,
2862
+ approvals: lpToken ? [{ token: lpToken, spender: gauge, amount }] : void 0
2762
2863
  };
2763
2864
  }
2764
2865
  const data = encodeFunctionData8({
@@ -2771,7 +2872,8 @@ var SolidlyGaugeAdapter = class {
2771
2872
  to: gauge,
2772
2873
  data,
2773
2874
  value: 0n,
2774
- gas_estimate: 2e5
2875
+ gas_estimate: 2e5,
2876
+ approvals: lpToken ? [{ token: lpToken, spender: gauge, amount }] : void 0
2775
2877
  };
2776
2878
  }
2777
2879
  async buildWithdraw(gauge, amount) {
@@ -3071,7 +3173,7 @@ var POOL_ABI = parseAbi10([
3071
3173
  "function getUserAccountData(address user) external view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)",
3072
3174
  "function getReserveData(address asset) external view returns (uint256 configuration, uint128 liquidityIndex, uint128 currentLiquidityRate, uint128 variableBorrowIndex, uint128 currentVariableBorrowRate, uint128 currentStableBorrowRate, uint40 lastUpdateTimestamp, uint16 id, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint128 accruedToTreasury, uint128 unbacked, uint128 isolationModeTotalDebt)"
3073
3175
  ]);
3074
- var ERC20_ABI = parseAbi10([
3176
+ var ERC20_ABI2 = parseAbi10([
3075
3177
  "function totalSupply() external view returns (uint256)"
3076
3178
  ]);
3077
3179
  var INCENTIVES_ABI = parseAbi10([
@@ -3124,7 +3226,8 @@ var AaveV3Adapter = class {
3124
3226
  to: this.pool,
3125
3227
  data,
3126
3228
  value: 0n,
3127
- gas_estimate: 3e5
3229
+ gas_estimate: 3e5,
3230
+ approvals: [{ token: params.asset, spender: this.pool, amount: params.amount }]
3128
3231
  };
3129
3232
  }
3130
3233
  async buildBorrow(params) {
@@ -3154,7 +3257,8 @@ var AaveV3Adapter = class {
3154
3257
  to: this.pool,
3155
3258
  data,
3156
3259
  value: 0n,
3157
- gas_estimate: 3e5
3260
+ gas_estimate: 3e5,
3261
+ approvals: [{ token: params.asset, spender: this.pool, amount: params.amount }]
3158
3262
  };
3159
3263
  }
3160
3264
  async buildWithdraw(params) {
@@ -3196,12 +3300,12 @@ var AaveV3Adapter = class {
3196
3300
  const [totalSupply, totalBorrow] = await Promise.all([
3197
3301
  client.readContract({
3198
3302
  address: aTokenAddress,
3199
- abi: ERC20_ABI,
3303
+ abi: ERC20_ABI2,
3200
3304
  functionName: "totalSupply"
3201
3305
  }).catch(() => 0n),
3202
3306
  client.readContract({
3203
3307
  address: variableDebtTokenAddress,
3204
- abi: ERC20_ABI,
3308
+ abi: ERC20_ABI2,
3205
3309
  functionName: "totalSupply"
3206
3310
  }).catch(() => 0n)
3207
3311
  ]);
@@ -3425,7 +3529,7 @@ var POOL_ABI2 = parseAbi11([
3425
3529
  // [9]=variableDebtTokenAddress, [10]=interestRateStrategyAddress, [11]=id
3426
3530
  "function getReserveData(address asset) external view returns (uint256 configuration, uint128 liquidityIndex, uint128 variableBorrowIndex, uint128 currentLiquidityRate, uint128 currentVariableBorrowRate, uint128 currentStableBorrowRate, uint40 lastUpdateTimestamp, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint8 id)"
3427
3531
  ]);
3428
- var ERC20_ABI2 = parseAbi11([
3532
+ var ERC20_ABI22 = parseAbi11([
3429
3533
  "function totalSupply() external view returns (uint256)"
3430
3534
  ]);
3431
3535
  function u256ToF642(v) {
@@ -3458,7 +3562,8 @@ var AaveV2Adapter = class {
3458
3562
  to: this.pool,
3459
3563
  data,
3460
3564
  value: 0n,
3461
- gas_estimate: 3e5
3565
+ gas_estimate: 3e5,
3566
+ approvals: [{ token: params.asset, spender: this.pool, amount: params.amount }]
3462
3567
  };
3463
3568
  }
3464
3569
  async buildBorrow(params) {
@@ -3488,7 +3593,8 @@ var AaveV2Adapter = class {
3488
3593
  to: this.pool,
3489
3594
  data,
3490
3595
  value: 0n,
3491
- gas_estimate: 3e5
3596
+ gas_estimate: 3e5,
3597
+ approvals: [{ token: params.asset, spender: this.pool, amount: params.amount }]
3492
3598
  };
3493
3599
  }
3494
3600
  async buildWithdraw(params) {
@@ -3530,12 +3636,12 @@ var AaveV2Adapter = class {
3530
3636
  const [totalSupply, totalBorrow] = await Promise.all([
3531
3637
  client.readContract({
3532
3638
  address: aTokenAddress,
3533
- abi: ERC20_ABI2,
3639
+ abi: ERC20_ABI22,
3534
3640
  functionName: "totalSupply"
3535
3641
  }).catch(() => 0n),
3536
3642
  client.readContract({
3537
3643
  address: variableDebtTokenAddress,
3538
- abi: ERC20_ABI2,
3644
+ abi: ERC20_ABI22,
3539
3645
  functionName: "totalSupply"
3540
3646
  }).catch(() => 0n)
3541
3647
  ]);
@@ -8223,6 +8329,150 @@ function registerFarm(parent, getOpts, makeExecutor2) {
8223
8329
  });
8224
8330
  }
8225
8331
 
8332
+ // src/commands/setup.ts
8333
+ import pc2 from "picocolors";
8334
+ import { createInterface } from "readline";
8335
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
8336
+ import { resolve as resolve3 } from "path";
8337
+ var DEFI_DIR = resolve3(process.env.HOME || "~", ".defi");
8338
+ var ENV_FILE = resolve3(DEFI_DIR, ".env");
8339
+ function ensureDefiDir() {
8340
+ if (!existsSync3(DEFI_DIR)) mkdirSync2(DEFI_DIR, { recursive: true, mode: 448 });
8341
+ }
8342
+ function loadEnvFile() {
8343
+ if (!existsSync3(ENV_FILE)) return {};
8344
+ const lines = readFileSync3(ENV_FILE, "utf-8").split("\n");
8345
+ const env = {};
8346
+ for (const line of lines) {
8347
+ const trimmed = line.trim();
8348
+ if (!trimmed || trimmed.startsWith("#")) continue;
8349
+ const eqIdx = trimmed.indexOf("=");
8350
+ if (eqIdx > 0) {
8351
+ env[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
8352
+ }
8353
+ }
8354
+ return env;
8355
+ }
8356
+ function writeEnvFile(env) {
8357
+ ensureDefiDir();
8358
+ const lines = [
8359
+ "# defi-cli configuration",
8360
+ "# Generated by 'defi setup' \u2014 edit freely",
8361
+ ""
8362
+ ];
8363
+ for (const [key, value] of Object.entries(env)) {
8364
+ lines.push(`${key}=${value}`);
8365
+ }
8366
+ lines.push("");
8367
+ writeFileSync2(ENV_FILE, lines.join("\n"), { mode: 384 });
8368
+ }
8369
+ function ask(rl, question) {
8370
+ return new Promise((res) => rl.question(question, (answer) => res(answer.trim())));
8371
+ }
8372
+ function isValidAddress(s) {
8373
+ return /^0x[0-9a-fA-F]{40}$/.test(s);
8374
+ }
8375
+ function isValidPrivateKey(s) {
8376
+ return /^0x[0-9a-fA-F]{64}$/.test(s);
8377
+ }
8378
+ async function deriveAddress(privateKey) {
8379
+ try {
8380
+ const { privateKeyToAccount: privateKeyToAccount3 } = await import("viem/accounts");
8381
+ const account = privateKeyToAccount3(privateKey);
8382
+ return account.address;
8383
+ } catch {
8384
+ return null;
8385
+ }
8386
+ }
8387
+ function registerSetup(program2) {
8388
+ program2.command("setup").alias("init").description("Interactive setup wizard \u2014 configure wallet & RPC URLs").action(async () => {
8389
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
8390
+ try {
8391
+ console.log(pc2.cyan(pc2.bold("\n defi-cli Setup Wizard\n")));
8392
+ const existing = loadEnvFile();
8393
+ if (Object.keys(existing).length > 0) {
8394
+ console.log(pc2.white(" Current configuration:"));
8395
+ for (const [key, value] of Object.entries(existing)) {
8396
+ const masked = key.toLowerCase().includes("key") ? value.slice(0, 6) + "..." + value.slice(-4) : value;
8397
+ console.log(` ${pc2.cyan(key.padEnd(24))} ${pc2.gray(masked)}`);
8398
+ }
8399
+ console.log();
8400
+ const overwrite = await ask(rl, " Overwrite existing config? (y/N): ");
8401
+ if (overwrite.toLowerCase() !== "y" && overwrite.toLowerCase() !== "yes") {
8402
+ console.log(pc2.gray("\n Keeping existing configuration.\n"));
8403
+ rl.close();
8404
+ return;
8405
+ }
8406
+ console.log();
8407
+ }
8408
+ const newEnv = {};
8409
+ console.log(pc2.cyan(pc2.bold(" Wallet")));
8410
+ const privateKey = await ask(rl, " Private key (optional, for --broadcast, 0x...): ");
8411
+ if (privateKey) {
8412
+ const normalized = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
8413
+ if (!isValidPrivateKey(normalized)) {
8414
+ console.log(pc2.red(" Invalid private key (must be 0x + 64 hex chars). Skipped."));
8415
+ } else {
8416
+ newEnv.DEFI_PRIVATE_KEY = normalized;
8417
+ const derived = await deriveAddress(normalized);
8418
+ if (derived) {
8419
+ newEnv.DEFI_WALLET_ADDRESS = derived;
8420
+ console.log(` ${pc2.green("OK")} derived address: ${pc2.gray(derived)}`);
8421
+ }
8422
+ }
8423
+ }
8424
+ if (!newEnv.DEFI_WALLET_ADDRESS) {
8425
+ const address = await ask(rl, " Wallet address (0x...): ");
8426
+ if (address) {
8427
+ if (!isValidAddress(address)) {
8428
+ console.log(pc2.yellow(" Invalid address format. Skipping."));
8429
+ } else {
8430
+ newEnv.DEFI_WALLET_ADDRESS = address;
8431
+ console.log(` ${pc2.green("OK")} ${pc2.gray(address)}`);
8432
+ }
8433
+ }
8434
+ }
8435
+ console.log(pc2.cyan(pc2.bold("\n RPC URLs")) + pc2.gray(" (press Enter to use public defaults)"));
8436
+ const hyperevmRpc = await ask(rl, " HyperEVM RPC URL: ");
8437
+ if (hyperevmRpc) {
8438
+ newEnv.HYPEREVM_RPC_URL = hyperevmRpc;
8439
+ console.log(` ${pc2.green("OK")} HyperEVM RPC set`);
8440
+ }
8441
+ const mantleRpc = await ask(rl, " Mantle RPC URL: ");
8442
+ if (mantleRpc) {
8443
+ newEnv.MANTLE_RPC_URL = mantleRpc;
8444
+ console.log(` ${pc2.green("OK")} Mantle RPC set`);
8445
+ }
8446
+ const finalEnv = { ...existing, ...newEnv };
8447
+ writeEnvFile(finalEnv);
8448
+ console.log(pc2.cyan(pc2.bold("\n Setup Complete!\n")));
8449
+ console.log(` Config: ${pc2.gray(ENV_FILE)}`);
8450
+ if (finalEnv.DEFI_WALLET_ADDRESS) {
8451
+ console.log(` Wallet: ${pc2.gray(finalEnv.DEFI_WALLET_ADDRESS)}`);
8452
+ }
8453
+ if (finalEnv.DEFI_PRIVATE_KEY) {
8454
+ console.log(` Key: ${pc2.green("configured")}`);
8455
+ }
8456
+ if (finalEnv.HYPEREVM_RPC_URL) {
8457
+ console.log(` HyperEVM RPC: ${pc2.gray(finalEnv.HYPEREVM_RPC_URL)}`);
8458
+ }
8459
+ if (finalEnv.MANTLE_RPC_URL) {
8460
+ console.log(` Mantle RPC: ${pc2.gray(finalEnv.MANTLE_RPC_URL)}`);
8461
+ }
8462
+ console.log(pc2.bold(pc2.white("\n Next steps:")));
8463
+ console.log(` ${pc2.green("defi portfolio")} view balances & positions`);
8464
+ console.log(` ${pc2.green("defi scan")} scan for exploits`);
8465
+ console.log(` ${pc2.green("defi dex quote")} get a swap quote`);
8466
+ console.log(` ${pc2.green("defi --help")} browse all commands
8467
+ `);
8468
+ rl.close();
8469
+ } catch (err) {
8470
+ rl.close();
8471
+ throw err;
8472
+ }
8473
+ });
8474
+ }
8475
+
8226
8476
  // src/cli.ts
8227
8477
  var _require = createRequire(import.meta.url);
8228
8478
  var _pkg = _require("../package.json");
@@ -8274,6 +8524,7 @@ registerSwap(program, getOutputMode, makeExecutor);
8274
8524
  registerBridge(program, getOutputMode);
8275
8525
  registerNft(program, getOutputMode);
8276
8526
  registerFarm(program, getOutputMode, makeExecutor);
8527
+ registerSetup(program);
8277
8528
  program.command("agent").description("Agent mode: read JSON commands from stdin (for AI agents)").action(async () => {
8278
8529
  const executor = makeExecutor();
8279
8530
  process.stderr.write("Agent mode: reading JSON commands from stdin...\n");
@@ -8282,7 +8533,7 @@ program.command("agent").description("Agent mode: read JSON commands from stdin
8282
8533
  });
8283
8534
 
8284
8535
  // src/landing.ts
8285
- import pc2 from "picocolors";
8536
+ import pc3 from "picocolors";
8286
8537
  import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31, formatUnits } from "viem";
8287
8538
  var HYPEREVM_DISPLAY = ["HYPE", "WHYPE", "USDC", "USDT0", "USDe", "kHYPE", "wstHYPE"];
8288
8539
  var MANTLE_DISPLAY = ["MNT", "WMNT", "USDC", "USDT", "WETH", "mETH"];
@@ -8376,20 +8627,20 @@ async function showLandingPage(isJson) {
8376
8627
  const version = pkg.version;
8377
8628
  if (!wallet) {
8378
8629
  console.log("");
8379
- console.log(pc2.bold(pc2.cyan(" DeFi CLI v" + version)));
8630
+ console.log(pc3.bold(pc3.cyan(" DeFi CLI v" + version)));
8380
8631
  console.log("");
8381
- console.log(pc2.yellow(" Wallet not configured."));
8632
+ console.log(pc3.yellow(" Wallet not configured."));
8382
8633
  console.log(" Set DEFI_WALLET_ADDRESS to see your balances:");
8383
8634
  console.log("");
8384
- console.log(pc2.dim(" export DEFI_WALLET_ADDRESS=0x..."));
8635
+ console.log(pc3.dim(" export DEFI_WALLET_ADDRESS=0x..."));
8385
8636
  console.log("");
8386
8637
  console.log(" Commands:");
8387
- console.log(pc2.dim(" defi status Protocol overview"));
8388
- console.log(pc2.dim(" defi lending rates Compare lending APYs"));
8389
- console.log(pc2.dim(" defi dex quote Get swap quotes"));
8390
- console.log(pc2.dim(" defi portfolio View all positions"));
8391
- console.log(pc2.dim(" defi scan Exploit detection"));
8392
- console.log(pc2.dim(" defi --help Full command list"));
8638
+ console.log(pc3.dim(" defi status Protocol overview"));
8639
+ console.log(pc3.dim(" defi lending rates Compare lending APYs"));
8640
+ console.log(pc3.dim(" defi dex quote Get swap quotes"));
8641
+ console.log(pc3.dim(" defi portfolio View all positions"));
8642
+ console.log(pc3.dim(" defi scan Exploit detection"));
8643
+ console.log(pc3.dim(" defi --help Full command list"));
8393
8644
  console.log("");
8394
8645
  return;
8395
8646
  }
@@ -8411,20 +8662,20 @@ async function showLandingPage(isJson) {
8411
8662
  const divider = "\u2500".repeat(colWidth - 2);
8412
8663
  console.log("");
8413
8664
  console.log(
8414
- pc2.bold(pc2.cyan(" DeFi CLI v" + version)) + pc2.dim(" \u2014 ") + pc2.bold(heChain.name) + pc2.dim(" \xB7 ") + pc2.bold(mantleChain.name)
8665
+ pc3.bold(pc3.cyan(" DeFi CLI v" + version)) + pc3.dim(" \u2014 ") + pc3.bold(heChain.name) + pc3.dim(" \xB7 ") + pc3.bold(mantleChain.name)
8415
8666
  );
8416
8667
  console.log("");
8417
- console.log(" Wallet: " + pc2.yellow(shortenAddress(wallet)));
8668
+ console.log(" Wallet: " + pc3.yellow(shortenAddress(wallet)));
8418
8669
  console.log("");
8419
8670
  const heHeader = padRight(
8420
- " " + pc2.bold(heChain.name),
8671
+ " " + pc3.bold(heChain.name),
8421
8672
  colWidth + 10
8422
8673
  /* account for ANSI */
8423
8674
  );
8424
- const mantleHeader = pc2.bold(mantleChain.name);
8675
+ const mantleHeader = pc3.bold(mantleChain.name);
8425
8676
  console.log(heHeader + " " + mantleHeader);
8426
- const heDivider = padRight(" " + pc2.dim(divider), colWidth + 10);
8427
- const mantleDivider = pc2.dim(divider);
8677
+ const heDivider = padRight(" " + pc3.dim(divider), colWidth + 10);
8678
+ const mantleDivider = pc3.dim(divider);
8428
8679
  console.log(heDivider + " " + mantleDivider);
8429
8680
  const maxRows = Math.max(heBalances.length, mantleBalances.length);
8430
8681
  for (let i = 0; i < maxRows; i++) {
@@ -8432,25 +8683,27 @@ async function showLandingPage(isJson) {
8432
8683
  const mantleEntry = mantleBalances[i];
8433
8684
  const heText = heEntry ? formatBalanceLine(heEntry.symbol, heEntry.balance) : "";
8434
8685
  const mantleText = mantleEntry ? formatBalanceLine(mantleEntry.symbol, mantleEntry.balance) : "";
8435
- const heColored = heEntry ? heEntry.balance === "0.00" || heEntry.balance === "?" ? pc2.dim(heText) : heText : "";
8436
- const mantleColored = mantleEntry ? mantleEntry.balance === "0.00" || mantleEntry.balance === "?" ? pc2.dim(mantleText) : mantleText : "";
8686
+ const heColored = heEntry ? heEntry.balance === "0.00" || heEntry.balance === "?" ? pc3.dim(heText) : heText : "";
8687
+ const mantleColored = mantleEntry ? mantleEntry.balance === "0.00" || mantleEntry.balance === "?" ? pc3.dim(mantleText) : mantleText : "";
8437
8688
  const visibleLen = heText.length;
8438
8689
  const padNeeded = colWidth - visibleLen;
8439
8690
  const paddedHe = heColored + (padNeeded > 0 ? " ".repeat(padNeeded) : "");
8440
8691
  console.log(paddedHe + " " + mantleColored);
8441
8692
  }
8442
8693
  console.log("");
8443
- console.log(" " + pc2.bold("Commands:"));
8444
- console.log(" " + pc2.cyan("defi status") + " Protocol overview");
8445
- console.log(" " + pc2.cyan("defi lending rates") + " Compare lending APYs");
8446
- console.log(" " + pc2.cyan("defi dex quote") + " Get swap quotes");
8447
- console.log(" " + pc2.cyan("defi portfolio") + " View all positions");
8448
- console.log(" " + pc2.cyan("defi scan") + " Exploit detection");
8449
- console.log(" " + pc2.cyan("defi --help") + " Full command list");
8694
+ console.log(" " + pc3.bold("Commands:"));
8695
+ console.log(" " + pc3.cyan("defi status") + " Protocol overview");
8696
+ console.log(" " + pc3.cyan("defi lending rates") + " Compare lending APYs");
8697
+ console.log(" " + pc3.cyan("defi dex quote") + " Get swap quotes");
8698
+ console.log(" " + pc3.cyan("defi portfolio") + " View all positions");
8699
+ console.log(" " + pc3.cyan("defi scan") + " Exploit detection");
8700
+ console.log(" " + pc3.cyan("defi --help") + " Full command list");
8450
8701
  console.log("");
8451
8702
  }
8452
8703
 
8453
8704
  // src/main.ts
8705
+ config({ path: resolve4(process.env.HOME || "~", ".defi", ".env"), quiet: true });
8706
+ config({ quiet: true });
8454
8707
  async function main() {
8455
8708
  try {
8456
8709
  const rawArgs = process.argv.slice(2);
@@ -8479,7 +8732,9 @@ async function main() {
8479
8732
  "bridge",
8480
8733
  "nft",
8481
8734
  "farm",
8482
- "agent"
8735
+ "agent",
8736
+ "setup",
8737
+ "init"
8483
8738
  ]);
8484
8739
  const hasSubcommand = rawArgs.some((a) => !a.startsWith("-") && knownSubcommands.has(a));
8485
8740
  const isJson = rawArgs.includes("--json") || rawArgs.includes("--ndjson");