@hypurrquant/defi-cli 0.3.3 → 0.3.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
@@ -1212,7 +1212,7 @@ var Executor = class _Executor {
1212
1212
  const [maxFee, priorityFee] = await this.fetchEip1559Fees(rpcUrl);
1213
1213
  return {
1214
1214
  tx_hash: void 0,
1215
- status: "simulated",
1215
+ status: TxStatus.Simulated,
1216
1216
  gas_used: gasEstimate > 0n ? Number(gasEstimate) : void 0,
1217
1217
  description: tx.description,
1218
1218
  details: {
@@ -1232,7 +1232,7 @@ var Executor = class _Executor {
1232
1232
  const revertReason = extractRevertReason(errMsg);
1233
1233
  return {
1234
1234
  tx_hash: void 0,
1235
- status: "simulation_failed",
1235
+ status: TxStatus.SimulationFailed,
1236
1236
  gas_used: tx.gas_estimate,
1237
1237
  description: tx.description,
1238
1238
  details: {
@@ -1254,7 +1254,7 @@ var Executor = class _Executor {
1254
1254
  }
1255
1255
  return {
1256
1256
  tx_hash: void 0,
1257
- status: "dry_run",
1257
+ status: TxStatus.DryRun,
1258
1258
  gas_used: tx.gas_estimate,
1259
1259
  description: tx.description,
1260
1260
  details: {
@@ -1339,7 +1339,7 @@ var Executor = class _Executor {
1339
1339
  `);
1340
1340
  process.stderr.write("Waiting for confirmation...\n");
1341
1341
  const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
1342
- const status = receipt.status === "success" ? "confirmed" : "failed";
1342
+ const status = receipt.status === "success" ? TxStatus.Confirmed : TxStatus.Failed;
1343
1343
  let mintedTokenId;
1344
1344
  if (receipt.status === "success" && receipt.logs) {
1345
1345
  const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
@@ -1907,6 +1907,9 @@ function registerSchema(parent, getOpts) {
1907
1907
  });
1908
1908
  }
1909
1909
 
1910
+ // src/commands/lp.ts
1911
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
1912
+
1910
1913
  // ../defi-protocols/dist/index.js
1911
1914
  import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4, createPublicClient as createPublicClient4, http as http4, decodeAbiParameters } from "viem";
1912
1915
  import { encodeFunctionData as encodeFunctionData22, parseAbi as parseAbi22, createPublicClient as createPublicClient22, http as http22, decodeFunctionResult as decodeFunctionResult2, decodeAbiParameters as decodeAbiParameters2 } from "viem";
@@ -4095,128 +4098,6 @@ var masterchefAbi = parseAbi11([
4095
4098
  "function getNumberOfFarms() view returns (uint256)",
4096
4099
  "function getPidByPool(address pool) view returns (uint256)"
4097
4100
  ]);
4098
- var MasterChefAdapter = class {
4099
- protocolName;
4100
- masterchef;
4101
- rpcUrl;
4102
- constructor(entry, rpcUrl) {
4103
- this.protocolName = entry.name;
4104
- const masterchef = entry.contracts?.["masterchef"];
4105
- if (!masterchef) {
4106
- throw new DefiError("CONTRACT_ERROR", "Missing 'masterchef' contract");
4107
- }
4108
- this.masterchef = masterchef;
4109
- this.rpcUrl = rpcUrl;
4110
- }
4111
- name() {
4112
- return this.protocolName;
4113
- }
4114
- /**
4115
- * Deposit LP tokens into a MasterChef farm.
4116
- * `gauge` is the pool address (unused for calldata — MasterChef is the target).
4117
- * `tokenId` carries the farm pid.
4118
- */
4119
- async buildDeposit(gauge, amount, tokenId) {
4120
- const pid = tokenId ?? 0n;
4121
- const data = encodeFunctionData11({
4122
- abi: masterchefAbi,
4123
- functionName: "deposit",
4124
- args: [pid, amount]
4125
- });
4126
- return {
4127
- description: `[${this.protocolName}] Deposit ${amount} LP to farm pid=${pid} (pool ${gauge})`,
4128
- to: this.masterchef,
4129
- data,
4130
- value: 0n,
4131
- gas_estimate: 2e5
4132
- };
4133
- }
4134
- /**
4135
- * Withdraw LP tokens from a MasterChef farm.
4136
- * `gauge` is used to look up the pid description only; call site should pass pid via tokenId
4137
- * on the deposit flow. Here pid defaults to 0 — callers should encode the pid in the gauge
4138
- * address slot or wrap this adapter with a pid-aware helper.
4139
- */
4140
- async buildWithdraw(gauge, amount) {
4141
- const pid = 0n;
4142
- const data = encodeFunctionData11({
4143
- abi: masterchefAbi,
4144
- functionName: "withdraw",
4145
- args: [pid, amount]
4146
- });
4147
- return {
4148
- description: `[${this.protocolName}] Withdraw ${amount} LP from farm pid=${pid} (pool ${gauge})`,
4149
- to: this.masterchef,
4150
- data,
4151
- value: 0n,
4152
- gas_estimate: 2e5
4153
- };
4154
- }
4155
- /** Withdraw LP tokens specifying a pid explicitly (MasterChef extension beyond IGauge). */
4156
- async buildWithdrawPid(pid, amount) {
4157
- const data = encodeFunctionData11({
4158
- abi: masterchefAbi,
4159
- functionName: "withdraw",
4160
- args: [pid, amount]
4161
- });
4162
- return {
4163
- description: `[${this.protocolName}] Withdraw ${amount} LP from farm pid=${pid}`,
4164
- to: this.masterchef,
4165
- data,
4166
- value: 0n,
4167
- gas_estimate: 2e5
4168
- };
4169
- }
4170
- /** Claim pending MOE rewards. IGauge interface provides no pid — defaults to pid=0. */
4171
- async buildClaimRewards(gauge) {
4172
- const pid = 0n;
4173
- const data = encodeFunctionData11({
4174
- abi: masterchefAbi,
4175
- functionName: "claim",
4176
- args: [[pid]]
4177
- });
4178
- return {
4179
- description: `[${this.protocolName}] Claim MOE rewards for farm pid=${pid} (pool ${gauge})`,
4180
- to: this.masterchef,
4181
- data,
4182
- value: 0n,
4183
- gas_estimate: 2e5
4184
- };
4185
- }
4186
- /** Claim pending MOE rewards for a specific pid (MasterChef extension beyond IGauge). */
4187
- async buildClaimRewardsPid(pid) {
4188
- const data = encodeFunctionData11({
4189
- abi: masterchefAbi,
4190
- functionName: "claim",
4191
- args: [[pid]]
4192
- });
4193
- return {
4194
- description: `[${this.protocolName}] Claim MOE rewards for farm pid=${pid}`,
4195
- to: this.masterchef,
4196
- data,
4197
- value: 0n,
4198
- gas_estimate: 2e5
4199
- };
4200
- }
4201
- /** Get pending MOE rewards for a user. Requires rpcUrl. */
4202
- async getPendingRewards(_gauge, user) {
4203
- if (!this.rpcUrl) {
4204
- throw DefiError.unsupported(`[${this.protocolName}] getPendingRewards requires RPC`);
4205
- }
4206
- const client = createPublicClient7({ transport: http7(this.rpcUrl) });
4207
- const rewards = await client.readContract({
4208
- address: this.masterchef,
4209
- abi: masterchefAbi,
4210
- functionName: "pendingRewards",
4211
- args: [user, [0n]]
4212
- });
4213
- return rewards.map((amount) => ({
4214
- token: this.masterchef,
4215
- symbol: "MOE",
4216
- amount
4217
- }));
4218
- }
4219
- };
4220
4101
  var lbRouterAbi = parseAbi12([
4221
4102
  "struct LiquidityParameters { address tokenX; address tokenY; uint256 binStep; uint256 amountX; uint256 amountY; uint256 amountXMin; uint256 amountYMin; uint256 activeIdDesired; uint256 idSlippage; int256[] deltaIds; uint256[] distributionX; uint256[] distributionY; address to; address refundTo; uint256 deadline; }",
4222
4103
  "function addLiquidity(LiquidityParameters calldata liquidityParameters) external returns (uint256 amountXAdded, uint256 amountYAdded, uint256 amountXLeft, uint256 amountYLeft, uint256[] memory depositIds, uint256[] memory liquidityMinted)",
@@ -6838,46 +6719,6 @@ var GENERIC_LST_ABI = parseAbi24([
6838
6719
  "function stake() external payable returns (uint256)",
6839
6720
  "function unstake(uint256 amount) external returns (uint256)"
6840
6721
  ]);
6841
- var GenericLstAdapter = class {
6842
- protocolName;
6843
- staking;
6844
- constructor(entry, _rpcUrl) {
6845
- this.protocolName = entry.name;
6846
- const staking = entry.contracts?.["staking"];
6847
- if (!staking) throw DefiError.contractError("Missing 'staking' contract");
6848
- this.staking = staking;
6849
- }
6850
- name() {
6851
- return this.protocolName;
6852
- }
6853
- async buildStake(params) {
6854
- const data = encodeFunctionData222({ abi: GENERIC_LST_ABI, functionName: "stake" });
6855
- return {
6856
- description: `[${this.protocolName}] Stake ${params.amount} HYPE`,
6857
- to: this.staking,
6858
- data,
6859
- value: params.amount,
6860
- gas_estimate: 2e5
6861
- };
6862
- }
6863
- async buildUnstake(params) {
6864
- const data = encodeFunctionData222({
6865
- abi: GENERIC_LST_ABI,
6866
- functionName: "unstake",
6867
- args: [params.amount]
6868
- });
6869
- return {
6870
- description: `[${this.protocolName}] Unstake ${params.amount}`,
6871
- to: this.staking,
6872
- data,
6873
- value: 0n,
6874
- gas_estimate: 2e5
6875
- };
6876
- }
6877
- async getInfo() {
6878
- throw DefiError.unsupported(`[${this.protocolName}] getInfo requires RPC`);
6879
- }
6880
- };
6881
6722
  var STHYPE_ABI = parseAbi25([
6882
6723
  "function submit(address referral) external payable returns (uint256)",
6883
6724
  "function requestWithdrawals(uint256[] amounts, address owner) external returns (uint256[] requestIds)"
@@ -6885,70 +6726,6 @@ var STHYPE_ABI = parseAbi25([
6885
6726
  var ERC20_ABI3 = parseAbi25([
6886
6727
  "function totalSupply() external view returns (uint256)"
6887
6728
  ]);
6888
- var StHypeAdapter = class {
6889
- protocolName;
6890
- staking;
6891
- sthypeToken;
6892
- rpcUrl;
6893
- constructor(entry, rpcUrl) {
6894
- this.protocolName = entry.name;
6895
- this.rpcUrl = rpcUrl;
6896
- const staking = entry.contracts?.["staking"];
6897
- if (!staking) throw DefiError.contractError("Missing 'staking' contract");
6898
- this.staking = staking;
6899
- this.sthypeToken = entry.contracts?.["sthype_token"];
6900
- }
6901
- name() {
6902
- return this.protocolName;
6903
- }
6904
- async buildStake(params) {
6905
- const data = encodeFunctionData23({
6906
- abi: STHYPE_ABI,
6907
- functionName: "submit",
6908
- args: [zeroAddress12]
6909
- });
6910
- return {
6911
- description: `[${this.protocolName}] Stake ${params.amount} HYPE for stHYPE`,
6912
- to: this.staking,
6913
- data,
6914
- value: params.amount,
6915
- gas_estimate: 2e5
6916
- };
6917
- }
6918
- async buildUnstake(params) {
6919
- const data = encodeFunctionData23({
6920
- abi: STHYPE_ABI,
6921
- functionName: "requestWithdrawals",
6922
- args: [[params.amount], params.recipient]
6923
- });
6924
- return {
6925
- description: `[${this.protocolName}] Request unstake ${params.amount} stHYPE`,
6926
- to: this.staking,
6927
- data,
6928
- value: 0n,
6929
- gas_estimate: 2e5
6930
- };
6931
- }
6932
- async getInfo() {
6933
- if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
6934
- const client = createPublicClient19({ transport: http19(this.rpcUrl) });
6935
- const tokenAddr = this.sthypeToken ?? this.staking;
6936
- const totalSupply = await client.readContract({
6937
- address: tokenAddr,
6938
- abi: ERC20_ABI3,
6939
- functionName: "totalSupply"
6940
- }).catch((e) => {
6941
- throw DefiError.rpcError(`[${this.protocolName}] totalSupply failed: ${e}`);
6942
- });
6943
- return {
6944
- protocol: this.protocolName,
6945
- staked_token: zeroAddress12,
6946
- liquid_token: tokenAddr,
6947
- exchange_rate: 1,
6948
- total_staked: totalSupply
6949
- };
6950
- }
6951
- };
6952
6729
  var KINETIQ_ABI = parseAbi26([
6953
6730
  "function stake() external payable returns (uint256)",
6954
6731
  "function requestUnstake(uint256 amount) external returns (uint256)",
@@ -6957,72 +6734,6 @@ var KINETIQ_ABI = parseAbi26([
6957
6734
  var ORACLE_ABI3 = parseAbi26([
6958
6735
  "function getAssetPrice(address asset) external view returns (uint256)"
6959
6736
  ]);
6960
- var WHYPE = "0x5555555555555555555555555555555555555555";
6961
- var HYPERLEND_ORACLE = "0xc9fb4fbe842d57ea1df3e641a281827493a63030";
6962
- var KinetiqAdapter = class {
6963
- protocolName;
6964
- staking;
6965
- liquidToken;
6966
- rpcUrl;
6967
- constructor(entry, rpcUrl) {
6968
- this.protocolName = entry.name;
6969
- this.rpcUrl = rpcUrl;
6970
- const staking = entry.contracts?.["staking"];
6971
- if (!staking) throw DefiError.contractError("Missing 'staking' contract address");
6972
- this.staking = staking;
6973
- this.liquidToken = entry.contracts?.["khype_token"] ?? staking;
6974
- }
6975
- name() {
6976
- return this.protocolName;
6977
- }
6978
- async buildStake(params) {
6979
- const data = encodeFunctionData24({ abi: KINETIQ_ABI, functionName: "stake" });
6980
- return {
6981
- description: `[${this.protocolName}] Stake ${params.amount} HYPE for kHYPE`,
6982
- to: this.staking,
6983
- data,
6984
- value: params.amount,
6985
- gas_estimate: 3e5
6986
- };
6987
- }
6988
- async buildUnstake(params) {
6989
- const data = encodeFunctionData24({
6990
- abi: KINETIQ_ABI,
6991
- functionName: "requestUnstake",
6992
- args: [params.amount]
6993
- });
6994
- return {
6995
- description: `[${this.protocolName}] Request unstake ${params.amount} kHYPE`,
6996
- to: this.staking,
6997
- data,
6998
- value: 0n,
6999
- gas_estimate: 3e5
7000
- };
7001
- }
7002
- async getInfo() {
7003
- if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
7004
- const client = createPublicClient20({ transport: http20(this.rpcUrl) });
7005
- const totalStaked = await client.readContract({
7006
- address: this.staking,
7007
- abi: KINETIQ_ABI,
7008
- functionName: "totalStaked"
7009
- }).catch((e) => {
7010
- throw DefiError.rpcError(`[${this.protocolName}] totalStaked failed: ${e}`);
7011
- });
7012
- const [khypePrice, hypePrice] = await Promise.all([
7013
- client.readContract({ address: HYPERLEND_ORACLE, abi: ORACLE_ABI3, functionName: "getAssetPrice", args: [this.liquidToken] }).catch(() => 0n),
7014
- client.readContract({ address: HYPERLEND_ORACLE, abi: ORACLE_ABI3, functionName: "getAssetPrice", args: [WHYPE] }).catch(() => 0n)
7015
- ]);
7016
- const rateF64 = hypePrice > 0n && khypePrice > 0n ? Number(khypePrice * 10n ** 18n / hypePrice) / 1e18 : 1;
7017
- return {
7018
- protocol: this.protocolName,
7019
- staked_token: zeroAddress13,
7020
- liquid_token: this.liquidToken,
7021
- exchange_rate: rateF64,
7022
- total_staked: totalStaked
7023
- };
7024
- }
7025
- };
7026
6737
  var HLP_ABI = parseAbi27([
7027
6738
  "function deposit(uint256 amount) external returns (uint256)",
7028
6739
  "function withdraw(uint256 shares) external returns (uint256)"
@@ -7039,59 +6750,6 @@ var ERC721_ABI = parseAbi29([
7039
6750
  "function balanceOf(address owner) returns (uint256)",
7040
6751
  "function tokenURI(uint256 tokenId) returns (string)"
7041
6752
  ]);
7042
- var ERC721Adapter = class {
7043
- protocolName;
7044
- rpcUrl;
7045
- constructor(entry, rpcUrl) {
7046
- this.protocolName = entry.name;
7047
- this.rpcUrl = rpcUrl;
7048
- }
7049
- name() {
7050
- return this.protocolName;
7051
- }
7052
- async getCollectionInfo(collection) {
7053
- if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
7054
- const client = createPublicClient21({ transport: http21(this.rpcUrl) });
7055
- const [collectionName, symbol, totalSupply] = await Promise.all([
7056
- client.readContract({ address: collection, abi: ERC721_ABI, functionName: "name" }).catch((e) => {
7057
- throw DefiError.rpcError(`[${this.protocolName}] name failed: ${e}`);
7058
- }),
7059
- client.readContract({ address: collection, abi: ERC721_ABI, functionName: "symbol" }).catch((e) => {
7060
- throw DefiError.rpcError(`[${this.protocolName}] symbol failed: ${e}`);
7061
- }),
7062
- client.readContract({ address: collection, abi: ERC721_ABI, functionName: "totalSupply" }).catch(() => void 0)
7063
- ]);
7064
- return {
7065
- address: collection,
7066
- name: collectionName,
7067
- symbol,
7068
- total_supply: totalSupply
7069
- };
7070
- }
7071
- async getTokenInfo(collection, tokenId) {
7072
- if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
7073
- const client = createPublicClient21({ transport: http21(this.rpcUrl) });
7074
- const [owner, tokenUri] = await Promise.all([
7075
- client.readContract({ address: collection, abi: ERC721_ABI, functionName: "ownerOf", args: [tokenId] }).catch((e) => {
7076
- throw DefiError.rpcError(`[${this.protocolName}] ownerOf failed: ${e}`);
7077
- }),
7078
- client.readContract({ address: collection, abi: ERC721_ABI, functionName: "tokenURI", args: [tokenId] }).catch(() => void 0)
7079
- ]);
7080
- return {
7081
- collection,
7082
- token_id: tokenId,
7083
- owner,
7084
- token_uri: tokenUri
7085
- };
7086
- }
7087
- async getBalance(owner, collection) {
7088
- if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
7089
- const client = createPublicClient21({ transport: http21(this.rpcUrl) });
7090
- return client.readContract({ address: collection, abi: ERC721_ABI, functionName: "balanceOf", args: [owner] }).catch((e) => {
7091
- throw DefiError.rpcError(`[${this.protocolName}] balanceOf failed: ${e}`);
7092
- });
7093
- }
7094
- };
7095
6753
  function createDex(entry, rpcUrl) {
7096
6754
  switch (entry.interface) {
7097
6755
  case "uniswap_v3":
@@ -7155,19 +6813,6 @@ function createVault(entry, rpcUrl) {
7155
6813
  throw DefiError.unsupported(`Vault interface '${entry.interface}' not yet implemented`);
7156
6814
  }
7157
6815
  }
7158
- function createLiquidStaking(entry, rpcUrl) {
7159
- switch (entry.interface) {
7160
- case "kinetiq_staking":
7161
- return new KinetiqAdapter(entry, rpcUrl);
7162
- case "sthype_staking":
7163
- return new StHypeAdapter(entry, rpcUrl);
7164
- case "hyperbeat_lst":
7165
- case "kintsu":
7166
- return new GenericLstAdapter(entry, rpcUrl);
7167
- default:
7168
- return new GenericLstAdapter(entry, rpcUrl);
7169
- }
7170
- }
7171
6816
  function createGauge(entry, rpcUrl) {
7172
6817
  if (entry.interface === "hybra" || entry.contracts?.["gauge_manager"]) {
7173
6818
  return new HybraGaugeAdapter(entry, rpcUrl);
@@ -7181,19 +6826,6 @@ function createGauge(entry, rpcUrl) {
7181
6826
  throw DefiError.unsupported(`Gauge interface '${entry.interface}' not supported`);
7182
6827
  }
7183
6828
  }
7184
- function createMasterChef(entry, rpcUrl) {
7185
- return new MasterChefAdapter(entry, rpcUrl);
7186
- }
7187
- function createNft(entry, rpcUrl) {
7188
- switch (entry.interface) {
7189
- case "erc721":
7190
- return new ERC721Adapter(entry, rpcUrl);
7191
- case "marketplace":
7192
- throw DefiError.unsupported(`NFT marketplace '${entry.name}' is not queryable as ERC-721. Use a specific collection address.`);
7193
- default:
7194
- throw DefiError.unsupported(`NFT interface '${entry.interface}' not supported`);
7195
- }
7196
- }
7197
6829
  function createOracleFromLending(entry, rpcUrl) {
7198
6830
  switch (entry.interface) {
7199
6831
  case "aave_v3":
@@ -7267,61 +6899,131 @@ var DexSpotPrice = class {
7267
6899
  }
7268
6900
  };
7269
6901
 
7270
- // src/commands/dex.ts
7271
- function registerDex(parent, getOpts, makeExecutor2) {
7272
- const dex = parent.command("dex").description("DEX operations: swap, quote, compare");
7273
- dex.command("quote").description("Get a swap quote without executing").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-in <token>", "Input token symbol or address").requiredOption("--token-out <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").action(async (opts) => {
6902
+ // src/commands/lp.ts
6903
+ function resolveAccount(optOwner) {
6904
+ if (optOwner) return optOwner;
6905
+ const walletAddr = process.env["DEFI_WALLET_ADDRESS"];
6906
+ if (walletAddr) return walletAddr;
6907
+ const privateKey = process.env["DEFI_PRIVATE_KEY"];
6908
+ if (privateKey) return privateKeyToAccount2(privateKey).address;
6909
+ throw new Error("--address, DEFI_WALLET_ADDRESS, or DEFI_PRIVATE_KEY is required");
6910
+ }
6911
+ function resolvePoolAddress(registry, protocolSlug, pool) {
6912
+ if (pool.startsWith("0x")) return pool;
6913
+ return registry.resolvePool(protocolSlug, pool).address;
6914
+ }
6915
+ function registerLP(parent, getOpts, makeExecutor2) {
6916
+ const lp = parent.command("lp").description("Unified LP operations: discover, add, farm, claim, remove, positions");
6917
+ lp.command("discover").description("Scan all protocols for fee + emission pools (gauges, farming, LB rewards)").option("--protocol <protocol>", "Filter to a single protocol slug").option("--emission-only", "Only show emission (gauge/farming) pools, skip fee-only").action(async (opts) => {
7274
6918
  const chainName = parent.opts().chain ?? "hyperevm";
7275
6919
  const registry = Registry.loadEmbedded();
7276
6920
  const chain = registry.getChain(chainName);
7277
- const protocol = registry.getProtocol(opts.protocol);
7278
- const adapter = createDex(protocol, chain.effectiveRpcUrl());
7279
- const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
7280
- const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
7281
- const result = await adapter.quote({ protocol: protocol.name, token_in: tokenIn, token_out: tokenOut, amount_in: BigInt(opts.amount) });
7282
- printOutput(result, getOpts());
6921
+ const rpcUrl = chain.effectiveRpcUrl();
6922
+ const allProtocols = registry.getProtocolsForChain(chainName);
6923
+ const protocols = opts.protocol ? [registry.getProtocol(opts.protocol)] : allProtocols;
6924
+ const results = [];
6925
+ await Promise.allSettled(
6926
+ protocols.map(async (protocol) => {
6927
+ try {
6928
+ if (["solidly_v2", "solidly_cl", "algebra_v3", "hybra"].includes(protocol.interface)) {
6929
+ const adapter = createGauge(protocol, rpcUrl);
6930
+ if (adapter.discoverGaugedPools) {
6931
+ const pools = await adapter.discoverGaugedPools();
6932
+ for (const p of pools) {
6933
+ results.push({
6934
+ protocol: protocol.slug,
6935
+ pool: p.pool,
6936
+ pair: `${p.token0}/${p.token1}`,
6937
+ type: "EMISSION",
6938
+ source: "gauge"
6939
+ });
6940
+ }
6941
+ }
6942
+ }
6943
+ if (protocol.interface === "algebra_v3" && protocol.contracts?.["farming_center"]) {
6944
+ const adapter = createKittenSwapFarming(protocol, rpcUrl);
6945
+ const pools = await adapter.discoverFarmingPools();
6946
+ for (const p of pools) {
6947
+ results.push({
6948
+ protocol: protocol.slug,
6949
+ pool: p.pool,
6950
+ type: "EMISSION",
6951
+ source: "farming",
6952
+ total_reward: p.totalReward.toString(),
6953
+ bonus_reward: p.bonusReward.toString(),
6954
+ active: p.active
6955
+ });
6956
+ }
6957
+ }
6958
+ if (protocol.interface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
6959
+ const adapter = createMerchantMoeLB(protocol, rpcUrl);
6960
+ const pools = await adapter.discoverRewardedPools();
6961
+ for (const p of pools) {
6962
+ if (!opts.emissionOnly || !p.stopped) {
6963
+ results.push({
6964
+ protocol: protocol.slug,
6965
+ pool: p.pool,
6966
+ pair: `${p.symbolX}/${p.symbolY}`,
6967
+ type: "EMISSION",
6968
+ source: "lb_hooks",
6969
+ stopped: p.stopped,
6970
+ moePerDay: p.moePerDay,
6971
+ aprPercent: p.aprPercent,
6972
+ rangeTvlUsd: p.rangeTvlUsd,
6973
+ isTopPool: p.isTopPool
6974
+ });
6975
+ }
6976
+ }
6977
+ }
6978
+ } catch {
6979
+ }
6980
+ })
6981
+ );
6982
+ if (opts.emissionOnly) {
6983
+ printOutput(results.filter((r) => r.type === "EMISSION"), getOpts());
6984
+ } else {
6985
+ printOutput(results, getOpts());
6986
+ }
7283
6987
  });
7284
- dex.command("swap").description("Execute a token swap").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-in <token>", "Input token").requiredOption("--token-out <token>", "Output token").requiredOption("--amount <amount>", "Amount in wei").option("--slippage <bps>", "Slippage tolerance in bps", "50").option("--recipient <address>", "Recipient address").action(async (opts) => {
6988
+ lp.command("add").description("Add liquidity to a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--recipient <address>", "Recipient address").option("--tick-lower <tick>", "Lower tick for concentrated LP (default: full range)").option("--tick-upper <tick>", "Upper tick for concentrated LP (default: full range)").option("--range <percent>", "\xB1N% concentrated range around current price (e.g. --range 2)").action(async (opts) => {
7285
6989
  const executor = makeExecutor2();
7286
6990
  const chainName = parent.opts().chain ?? "hyperevm";
7287
6991
  const registry = Registry.loadEmbedded();
7288
6992
  const chain = registry.getChain(chainName);
7289
6993
  const protocol = registry.getProtocol(opts.protocol);
7290
6994
  const adapter = createDex(protocol, chain.effectiveRpcUrl());
7291
- const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
7292
- const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
7293
- const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7294
- const tx = await adapter.buildSwap({
6995
+ const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
6996
+ const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
6997
+ const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
6998
+ const poolAddr = opts.pool ? resolvePoolAddress(registry, opts.protocol, opts.pool) : void 0;
6999
+ const tx = await adapter.buildAddLiquidity({
7295
7000
  protocol: protocol.name,
7296
- token_in: tokenIn,
7297
- token_out: tokenOut,
7298
- amount_in: BigInt(opts.amount),
7299
- slippage: { bps: parseInt(opts.slippage) },
7300
- recipient
7001
+ token_a: tokenA,
7002
+ token_b: tokenB,
7003
+ amount_a: BigInt(opts.amountA),
7004
+ amount_b: BigInt(opts.amountB),
7005
+ recipient,
7006
+ tick_lower: opts.tickLower !== void 0 ? parseInt(opts.tickLower) : void 0,
7007
+ tick_upper: opts.tickUpper !== void 0 ? parseInt(opts.tickUpper) : void 0,
7008
+ range_pct: opts.range !== void 0 ? parseFloat(opts.range) : void 0,
7009
+ pool: poolAddr
7301
7010
  });
7302
7011
  const result = await executor.execute(tx);
7303
7012
  printOutput(result, getOpts());
7304
7013
  });
7305
- dex.command("lp-add").description("Add liquidity to a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--recipient <address>", "Recipient address").option("--tick-lower <tick>", "Lower tick for concentrated LP (default: full range)").option("--tick-upper <tick>", "Upper tick for concentrated LP (default: full range)").option("--range <percent>", "\xB1N% concentrated range around current price (e.g. --range 2 for \xB12%)").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").action(async (opts) => {
7014
+ lp.command("farm").description("Add liquidity and auto-stake into gauge/farming for emissions").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--gauge <address>", "Gauge address (required for solidly/hybra if not resolved automatically)").option("--recipient <address>", "Recipient / owner address").option("--tick-lower <tick>", "Lower tick for concentrated LP").option("--tick-upper <tick>", "Upper tick for concentrated LP").option("--range <percent>", "\xB1N% concentrated range around current price").action(async (opts) => {
7306
7015
  const executor = makeExecutor2();
7307
7016
  const chainName = parent.opts().chain ?? "hyperevm";
7308
7017
  const registry = Registry.loadEmbedded();
7309
7018
  const chain = registry.getChain(chainName);
7310
7019
  const protocol = registry.getProtocol(opts.protocol);
7311
- const adapter = createDex(protocol, chain.effectiveRpcUrl());
7020
+ const rpcUrl = chain.effectiveRpcUrl();
7021
+ const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
7022
+ const poolAddr = opts.pool ? resolvePoolAddress(registry, opts.protocol, opts.pool) : void 0;
7312
7023
  const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
7313
7024
  const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
7314
- const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7315
- let poolAddr;
7316
- if (opts.pool) {
7317
- if (opts.pool.startsWith("0x")) {
7318
- poolAddr = opts.pool;
7319
- } else {
7320
- const poolInfo = registry.resolvePool(opts.protocol, opts.pool);
7321
- poolAddr = poolInfo.address;
7322
- }
7323
- }
7324
- const tx = await adapter.buildAddLiquidity({
7025
+ const dexAdapter = createDex(protocol, rpcUrl);
7026
+ const addTx = await dexAdapter.buildAddLiquidity({
7325
7027
  protocol: protocol.name,
7326
7028
  token_a: tokenA,
7327
7029
  token_b: tokenB,
@@ -7333,143 +7035,202 @@ function registerDex(parent, getOpts, makeExecutor2) {
7333
7035
  range_pct: opts.range !== void 0 ? parseFloat(opts.range) : void 0,
7334
7036
  pool: poolAddr
7335
7037
  });
7336
- const result = await executor.execute(tx);
7337
- printOutput(result, getOpts());
7038
+ process.stderr.write("Step 1/2: Adding liquidity...\n");
7039
+ const addResult = await executor.execute(addTx);
7040
+ printOutput({ step: "lp_add", ...addResult }, getOpts());
7041
+ if (addResult.status !== "confirmed" && addResult.status !== "simulated") {
7042
+ process.stderr.write("Step 2/2: Skipped \u2014 LP add did not succeed.\n");
7043
+ return;
7044
+ }
7045
+ const mintedTokenId = addResult.details?.minted_token_id ? BigInt(addResult.details.minted_token_id) : void 0;
7046
+ const iface = protocol.interface;
7047
+ if (iface === "algebra_v3" && protocol.contracts?.["farming_center"]) {
7048
+ if (!mintedTokenId) {
7049
+ process.stderr.write("Step 2/2: Skipped staking (no tokenId \u2014 run in --broadcast mode to get minted NFT)\n");
7050
+ return;
7051
+ }
7052
+ if (!poolAddr) throw new Error("--pool is required for lp farm with KittenSwap (needed for farming center)");
7053
+ process.stderr.write("Step 2/2: Staking into KittenSwap eternal farming...\n");
7054
+ const farmAdapter = createKittenSwapFarming(protocol, rpcUrl);
7055
+ const stakeTx = await farmAdapter.buildEnterFarming(mintedTokenId, poolAddr, recipient);
7056
+ const stakeResult = await executor.execute(stakeTx);
7057
+ printOutput({ step: "stake_farming", ...stakeResult }, getOpts());
7058
+ return;
7059
+ }
7060
+ if (["solidly_v2", "solidly_cl", "hybra"].includes(iface)) {
7061
+ if (!mintedTokenId && iface !== "solidly_v2") {
7062
+ process.stderr.write("Step 2/2: Skipped staking (no tokenId \u2014 run in --broadcast mode to get minted NFT)\n");
7063
+ return;
7064
+ }
7065
+ let gaugeAddr = opts.gauge;
7066
+ if (!gaugeAddr && poolAddr) {
7067
+ try {
7068
+ const gaugeAdapter2 = createGauge(protocol, rpcUrl);
7069
+ if (gaugeAdapter2.resolveGauge) {
7070
+ gaugeAddr = await gaugeAdapter2.resolveGauge(poolAddr);
7071
+ }
7072
+ } catch {
7073
+ }
7074
+ }
7075
+ if (!gaugeAddr) throw new Error("--gauge <address> is required for staking (could not auto-resolve gauge)");
7076
+ process.stderr.write("Step 2/2: Staking into gauge...\n");
7077
+ const gaugeAdapter = createGauge(protocol, rpcUrl);
7078
+ const tokenIdArg = mintedTokenId;
7079
+ const amountArg = iface === "solidly_v2" ? BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935") : 0n;
7080
+ const lpTokenArg = iface === "solidly_v2" ? poolAddr : void 0;
7081
+ const stakeTx = await gaugeAdapter.buildDeposit(gaugeAddr, amountArg, tokenIdArg, lpTokenArg);
7082
+ const stakeResult = await executor.execute(stakeTx);
7083
+ printOutput({ step: "stake_gauge", ...stakeResult }, getOpts());
7084
+ return;
7085
+ }
7086
+ if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
7087
+ process.stderr.write("Step 2/2: Merchant Moe LB hooks handle rewards automatically \u2014 no staking needed.\n");
7088
+ return;
7089
+ }
7090
+ process.stderr.write("Step 2/2: No staking adapter found for this protocol interface \u2014 liquidity added only.\n");
7338
7091
  });
7339
- dex.command("lp-remove").description("Remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--liquidity <amount>", "Liquidity amount to remove in wei").option("--recipient <address>", "Recipient address").action(async (opts) => {
7092
+ lp.command("claim").description("Claim rewards from a pool (fee or emission)").requiredOption("--protocol <protocol>", "Protocol slug").option("--pool <address>", "Pool address (required for farming/LB)").option("--gauge <address>", "Gauge contract address (required for solidly/hybra)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--bins <binIds>", "Comma-separated bin IDs (for Merchant Moe LB)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
7340
7093
  const executor = makeExecutor2();
7341
7094
  const chainName = parent.opts().chain ?? "hyperevm";
7342
7095
  const registry = Registry.loadEmbedded();
7343
7096
  const chain = registry.getChain(chainName);
7097
+ const rpcUrl = chain.effectiveRpcUrl();
7344
7098
  const protocol = registry.getProtocol(opts.protocol);
7345
- const adapter = createDex(protocol, chain.effectiveRpcUrl());
7099
+ const account = resolveAccount(opts.address);
7100
+ const iface = protocol.interface;
7101
+ if (iface === "algebra_v3" && protocol.contracts?.["farming_center"]) {
7102
+ if (!opts.pool) throw new Error("--pool is required for KittenSwap farming claim");
7103
+ if (!opts.tokenId) throw new Error("--token-id is required for KittenSwap farming claim");
7104
+ const adapter = createKittenSwapFarming(protocol, rpcUrl);
7105
+ const tx = await adapter.buildCollectRewards(
7106
+ BigInt(opts.tokenId),
7107
+ opts.pool,
7108
+ account
7109
+ );
7110
+ const result = await executor.execute(tx);
7111
+ printOutput(result, getOpts());
7112
+ return;
7113
+ }
7114
+ if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
7115
+ if (!opts.pool) throw new Error("--pool is required for Merchant Moe LB claim");
7116
+ const adapter = createMerchantMoeLB(protocol, rpcUrl);
7117
+ const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
7118
+ const tx = await adapter.buildClaimRewards(account, opts.pool, binIds);
7119
+ const result = await executor.execute(tx);
7120
+ printOutput(result, getOpts());
7121
+ return;
7122
+ }
7123
+ if (["solidly_v2", "solidly_cl", "algebra_v3", "hybra"].includes(iface)) {
7124
+ if (!opts.gauge) throw new Error("--gauge is required for gauge claim");
7125
+ const adapter = createGauge(protocol, rpcUrl);
7126
+ let tx;
7127
+ if (opts.tokenId) {
7128
+ if (!adapter.buildClaimRewardsByTokenId) throw new Error(`${protocol.name} does not support NFT-based claim`);
7129
+ tx = await adapter.buildClaimRewardsByTokenId(opts.gauge, BigInt(opts.tokenId));
7130
+ } else {
7131
+ tx = await adapter.buildClaimRewards(opts.gauge, account);
7132
+ }
7133
+ const result = await executor.execute(tx);
7134
+ printOutput(result, getOpts());
7135
+ return;
7136
+ }
7137
+ throw new Error(`No claim method found for protocol interface '${iface}'`);
7138
+ });
7139
+ lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--liquidity <amount>", "Liquidity amount to remove in wei").option("--pool <address>", "Pool address (needed to resolve gauge)").option("--gauge <address>", "Gauge contract address (for solidly/hybra unstake)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--recipient <address>", "Recipient address").action(async (opts) => {
7140
+ const executor = makeExecutor2();
7141
+ const chainName = parent.opts().chain ?? "hyperevm";
7142
+ const registry = Registry.loadEmbedded();
7143
+ const chain = registry.getChain(chainName);
7144
+ const rpcUrl = chain.effectiveRpcUrl();
7145
+ const protocol = registry.getProtocol(opts.protocol);
7146
+ const iface = protocol.interface;
7147
+ const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
7346
7148
  const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
7347
7149
  const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
7348
- const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7349
- const tx = await adapter.buildRemoveLiquidity({
7150
+ const poolAddr = opts.pool ? opts.pool : void 0;
7151
+ let didUnstake = false;
7152
+ if (iface === "algebra_v3" && protocol.contracts?.["farming_center"] && opts.tokenId && poolAddr) {
7153
+ process.stderr.write("Step 1/2: Exiting KittenSwap farming...\n");
7154
+ const farmAdapter = createKittenSwapFarming(protocol, rpcUrl);
7155
+ const exitTx = await farmAdapter.buildExitFarming(BigInt(opts.tokenId), poolAddr);
7156
+ const exitResult = await executor.execute(exitTx);
7157
+ printOutput({ step: "unstake_farming", ...exitResult }, getOpts());
7158
+ if (exitResult.status !== "confirmed" && exitResult.status !== "simulated") {
7159
+ process.stderr.write("Step 2/2: Skipped \u2014 unstake did not succeed.\n");
7160
+ return;
7161
+ }
7162
+ didUnstake = true;
7163
+ } else if (["solidly_v2", "solidly_cl", "hybra"].includes(iface)) {
7164
+ let gaugeAddr = opts.gauge;
7165
+ if (!gaugeAddr && poolAddr) {
7166
+ try {
7167
+ const gaugeAdapter = createGauge(protocol, rpcUrl);
7168
+ if (gaugeAdapter.resolveGauge) {
7169
+ gaugeAddr = await gaugeAdapter.resolveGauge(poolAddr);
7170
+ }
7171
+ } catch {
7172
+ }
7173
+ }
7174
+ if (gaugeAddr) {
7175
+ process.stderr.write("Step 1/2: Withdrawing from gauge...\n");
7176
+ const gaugeAdapter = createGauge(protocol, rpcUrl);
7177
+ const tokenId = opts.tokenId ? BigInt(opts.tokenId) : void 0;
7178
+ const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, BigInt(opts.liquidity), tokenId);
7179
+ const withdrawResult = await executor.execute(withdrawTx);
7180
+ printOutput({ step: "unstake_gauge", ...withdrawResult }, getOpts());
7181
+ if (withdrawResult.status !== "confirmed" && withdrawResult.status !== "simulated") {
7182
+ process.stderr.write("Step 2/2: Skipped \u2014 unstake did not succeed.\n");
7183
+ return;
7184
+ }
7185
+ didUnstake = true;
7186
+ }
7187
+ }
7188
+ if (!didUnstake) {
7189
+ process.stderr.write("Step 1/2: No staking detected \u2014 skipping unstake.\n");
7190
+ }
7191
+ process.stderr.write("Step 2/2: Removing liquidity...\n");
7192
+ const dexAdapter = createDex(protocol, rpcUrl);
7193
+ const removeTx = await dexAdapter.buildRemoveLiquidity({
7350
7194
  protocol: protocol.name,
7351
7195
  token_a: tokenA,
7352
7196
  token_b: tokenB,
7353
7197
  liquidity: BigInt(opts.liquidity),
7354
7198
  recipient
7355
7199
  });
7356
- const result = await executor.execute(tx);
7357
- printOutput(result, getOpts());
7200
+ const removeResult = await executor.execute(removeTx);
7201
+ printOutput({ step: "lp_remove", ...removeResult }, getOpts());
7358
7202
  });
7359
- dex.command("compare").description("Compare quotes across DEXes").requiredOption("--token-in <token>", "Input token").requiredOption("--token-out <token>", "Output token").requiredOption("--amount <amount>", "Amount in wei").action(async (opts) => {
7203
+ lp.command("positions").description("Show all LP positions across protocols").option("--protocol <protocol>", "Filter to a single protocol slug").option("--pool <address>", "Filter to a specific pool address").option("--bins <binIds>", "Comma-separated bin IDs (for Merchant Moe LB, auto-detected if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
7360
7204
  const chainName = parent.opts().chain ?? "hyperevm";
7361
7205
  const registry = Registry.loadEmbedded();
7362
7206
  const chain = registry.getChain(chainName);
7363
- const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
7364
- const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
7365
- const dexProtocols = registry.getProtocolsByCategory("dex").filter((p) => p.chain === chainName);
7207
+ const rpcUrl = chain.effectiveRpcUrl();
7208
+ const user = resolveAccount(opts.address);
7209
+ const allProtocols = registry.getProtocolsForChain(chainName);
7210
+ const protocols = opts.protocol ? [registry.getProtocol(opts.protocol)] : allProtocols;
7366
7211
  const results = [];
7367
- await Promise.all(dexProtocols.map(async (p) => {
7368
- try {
7369
- const adapter = createDex(p, chain.effectiveRpcUrl());
7370
- const q = await adapter.quote({ protocol: p.name, token_in: tokenIn, token_out: tokenOut, amount_in: BigInt(opts.amount) });
7371
- results.push({ protocol: p.name, amount_out: q.amount_out });
7372
- } catch (e) {
7373
- results.push({ protocol: p.name, amount_out: 0n, error: e instanceof Error ? e.message : String(e) });
7374
- }
7375
- }));
7376
- results.sort((a, b) => b.amount_out > a.amount_out ? 1 : -1);
7377
- printOutput({ chain: chainName, quotes: results }, getOpts());
7378
- });
7379
- }
7380
-
7381
- // src/commands/gauge.ts
7382
- import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
7383
- function resolveAccount() {
7384
- const walletAddr = process.env["DEFI_WALLET_ADDRESS"];
7385
- if (walletAddr) return walletAddr;
7386
- const privateKey = process.env["DEFI_PRIVATE_KEY"];
7387
- if (privateKey) return privateKeyToAccount2(privateKey).address;
7388
- return void 0;
7389
- }
7390
- function registerGauge(parent, getOpts, makeExecutor2) {
7391
- const gauge = parent.command("gauge").description("Gauge operations: find, deposit, withdraw, claim, earned");
7392
- gauge.command("discover").description("Find all pools with emission gauges (scans V2 + CL factories)").requiredOption("--protocol <protocol>", "Protocol slug").action(async (opts) => {
7393
- const chainName = parent.opts().chain ?? "hyperevm";
7394
- const registry = Registry.loadEmbedded();
7395
- const chain = registry.getChain(chainName);
7396
- const protocol = registry.getProtocol(opts.protocol);
7397
- const adapter = createGauge(protocol, chain.effectiveRpcUrl());
7398
- if (!adapter.discoverGaugedPools) throw new Error(`${protocol.name} does not support gauge discovery`);
7399
- const pools = await adapter.discoverGaugedPools();
7400
- printOutput(pools, getOpts());
7401
- });
7402
- gauge.command("find").description("Find gauge address for a pool via voter contract").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "Pool address").action(async (opts) => {
7403
- const chainName = parent.opts().chain ?? "hyperevm";
7404
- const registry = Registry.loadEmbedded();
7405
- const chain = registry.getChain(chainName);
7406
- const protocol = registry.getProtocol(opts.protocol);
7407
- const adapter = createGauge(protocol, chain.effectiveRpcUrl());
7408
- if (!adapter.resolveGauge) throw new Error(`${protocol.name} does not support gauge lookup`);
7409
- const gaugeAddr = await adapter.resolveGauge(opts.pool);
7410
- printOutput({ pool: opts.pool, gauge: gaugeAddr, protocol: protocol.name }, getOpts());
7411
- });
7412
- gauge.command("earned").description("Check pending rewards for a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
7413
- const chainName = parent.opts().chain ?? "hyperevm";
7414
- const registry = Registry.loadEmbedded();
7415
- const chain = registry.getChain(chainName);
7416
- const protocol = registry.getProtocol(opts.protocol);
7417
- const adapter = createGauge(protocol, chain.effectiveRpcUrl());
7418
- if (opts.tokenId) {
7419
- if (!adapter.getPendingRewardsByTokenId) throw new Error(`${protocol.name} does not support NFT rewards`);
7420
- const earned = await adapter.getPendingRewardsByTokenId(opts.gauge, BigInt(opts.tokenId));
7421
- printOutput({ gauge: opts.gauge, token_id: opts.tokenId, earned: earned.toString() }, getOpts());
7422
- } else {
7423
- const account = resolveAccount();
7424
- if (!account) throw new Error("DEFI_WALLET_ADDRESS or DEFI_PRIVATE_KEY required");
7425
- const rewards = await adapter.getPendingRewards(opts.gauge, account);
7426
- printOutput(rewards.map((r) => ({ token: r.token, amount: r.amount.toString() })), getOpts());
7427
- }
7428
- });
7429
- gauge.command("deposit").description("Deposit LP tokens or NFT into a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--amount <amount>", "LP token amount in wei (for V2 gauges)").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
7430
- const executor = makeExecutor2();
7431
- const chainName = parent.opts().chain ?? "hyperevm";
7432
- const registry = Registry.loadEmbedded();
7433
- const chain = registry.getChain(chainName);
7434
- const protocol = registry.getProtocol(opts.protocol);
7435
- const adapter = createGauge(protocol, chain.effectiveRpcUrl());
7436
- const amount = opts.amount ? BigInt(opts.amount) : 0n;
7437
- const tokenId = opts.tokenId ? BigInt(opts.tokenId) : void 0;
7438
- const tx = await adapter.buildDeposit(opts.gauge, amount, tokenId);
7439
- const result = await executor.execute(tx);
7440
- printOutput(result, getOpts());
7441
- });
7442
- gauge.command("withdraw").description("Withdraw LP tokens or NFT from a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--amount <amount>", "LP token amount in wei (for V2 gauges)").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
7443
- const executor = makeExecutor2();
7444
- const chainName = parent.opts().chain ?? "hyperevm";
7445
- const registry = Registry.loadEmbedded();
7446
- const chain = registry.getChain(chainName);
7447
- const protocol = registry.getProtocol(opts.protocol);
7448
- const adapter = createGauge(protocol, chain.effectiveRpcUrl());
7449
- const amount = opts.amount ? BigInt(opts.amount) : 0n;
7450
- const tokenId = opts.tokenId ? BigInt(opts.tokenId) : void 0;
7451
- const tx = await adapter.buildWithdraw(opts.gauge, amount, tokenId);
7452
- const result = await executor.execute(tx);
7453
- printOutput(result, getOpts());
7454
- });
7455
- gauge.command("claim").description("Claim earned rewards from a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
7456
- const executor = makeExecutor2();
7457
- const chainName = parent.opts().chain ?? "hyperevm";
7458
- const registry = Registry.loadEmbedded();
7459
- const chain = registry.getChain(chainName);
7460
- const protocol = registry.getProtocol(opts.protocol);
7461
- const adapter = createGauge(protocol, chain.effectiveRpcUrl());
7462
- if (opts.tokenId) {
7463
- if (!adapter.buildClaimRewardsByTokenId) throw new Error(`${protocol.name} does not support NFT claim`);
7464
- const tx = await adapter.buildClaimRewardsByTokenId(opts.gauge, BigInt(opts.tokenId));
7465
- const result = await executor.execute(tx);
7466
- printOutput(result, getOpts());
7467
- } else {
7468
- const account = resolveAccount();
7469
- const tx = await adapter.buildClaimRewards(opts.gauge, account);
7470
- const result = await executor.execute(tx);
7471
- printOutput(result, getOpts());
7472
- }
7212
+ await Promise.allSettled(
7213
+ protocols.map(async (protocol) => {
7214
+ try {
7215
+ if (protocol.interface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
7216
+ if (!opts.pool) return;
7217
+ const adapter = createMerchantMoeLB(protocol, rpcUrl);
7218
+ const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
7219
+ const positions = await adapter.getUserPositions(user, opts.pool, binIds);
7220
+ for (const pos of positions) {
7221
+ results.push({
7222
+ protocol: protocol.slug,
7223
+ type: "lb",
7224
+ pool: opts.pool,
7225
+ ...pos
7226
+ });
7227
+ }
7228
+ }
7229
+ } catch {
7230
+ }
7231
+ })
7232
+ );
7233
+ printOutput(results, getOpts());
7473
7234
  });
7474
7235
  }
7475
7236
 
@@ -7631,44 +7392,6 @@ function registerCdp(parent, getOpts, makeExecutor2) {
7631
7392
  });
7632
7393
  }
7633
7394
 
7634
- // src/commands/staking.ts
7635
- function registerStaking(parent, getOpts, makeExecutor2) {
7636
- const staking = parent.command("staking").description("Liquid staking: stake, unstake, info");
7637
- staking.command("stake").description("Stake tokens via liquid staking").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--amount <amount>", "Amount in wei").option("--recipient <address>", "Recipient address").action(async (opts) => {
7638
- const executor = makeExecutor2();
7639
- const chainName = parent.opts().chain ?? "hyperevm";
7640
- const registry = Registry.loadEmbedded();
7641
- const chain = registry.getChain(chainName);
7642
- const protocol = registry.getProtocol(opts.protocol);
7643
- const adapter = createLiquidStaking(protocol, chain.effectiveRpcUrl());
7644
- const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7645
- const tx = await adapter.buildStake({ protocol: protocol.name, amount: BigInt(opts.amount), recipient });
7646
- const result = await executor.execute(tx);
7647
- printOutput(result, getOpts());
7648
- });
7649
- staking.command("unstake").description("Unstake tokens").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--amount <amount>", "Amount in wei").option("--recipient <address>", "Recipient address").action(async (opts) => {
7650
- const executor = makeExecutor2();
7651
- const chainName = parent.opts().chain ?? "hyperevm";
7652
- const registry = Registry.loadEmbedded();
7653
- const chain = registry.getChain(chainName);
7654
- const protocol = registry.getProtocol(opts.protocol);
7655
- const adapter = createLiquidStaking(protocol, chain.effectiveRpcUrl());
7656
- const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7657
- const tx = await adapter.buildUnstake({ protocol: protocol.name, amount: BigInt(opts.amount), recipient });
7658
- const result = await executor.execute(tx);
7659
- printOutput(result, getOpts());
7660
- });
7661
- staking.command("info").description("Show staking info and rates").requiredOption("--protocol <protocol>", "Protocol slug").action(async (opts) => {
7662
- const chainName = parent.opts().chain ?? "hyperevm";
7663
- const registry = Registry.loadEmbedded();
7664
- const chain = registry.getChain(chainName);
7665
- const protocol = registry.getProtocol(opts.protocol);
7666
- const adapter = createLiquidStaking(protocol, chain.effectiveRpcUrl());
7667
- const info = await adapter.getInfo();
7668
- printOutput(info, getOpts());
7669
- });
7670
- }
7671
-
7672
7395
  // src/commands/vault.ts
7673
7396
  function registerVault(parent, getOpts, makeExecutor2) {
7674
7397
  const vault = parent.command("vault").description("Vault operations: deposit, withdraw, info");
@@ -9412,53 +9135,6 @@ async function runAllChains(registry, patterns, oracleThreshold, stableThreshold
9412
9135
  };
9413
9136
  }
9414
9137
 
9415
- // src/commands/arb.ts
9416
- function registerArb(parent, getOpts, makeExecutor2) {
9417
- parent.command("arb").description("Detect arbitrage opportunities across DEXes").option("--token-in <token>", "Base token (default: WHYPE)", "WHYPE").option("--token-out <token>", "Quote token (default: USDC)", "USDC").option("--amount <amount>", "Test amount in wei", "1000000000000000000").option("--execute", "Execute best arb (default: analysis only)").option("--min-profit <bps>", "Min profit in bps to execute", "10").action(async (opts) => {
9418
- const chainName = parent.opts().chain ?? "hyperevm";
9419
- const registry = Registry.loadEmbedded();
9420
- const chain = registry.getChain(chainName);
9421
- const rpcUrl = chain.effectiveRpcUrl();
9422
- const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
9423
- const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
9424
- const amountIn = BigInt(opts.amount);
9425
- const dexProtocols = registry.getProtocolsByCategory("dex").filter((p) => p.chain === chainName);
9426
- const quotes = [];
9427
- for (const p of dexProtocols) {
9428
- try {
9429
- const adapter = createDex(p, rpcUrl);
9430
- const buyQuote = await adapter.quote({ protocol: p.name, token_in: tokenIn, token_out: tokenOut, amount_in: amountIn });
9431
- if (buyQuote.amount_out === 0n) continue;
9432
- const sellQuote = await adapter.quote({ protocol: p.name, token_in: tokenOut, token_out: tokenIn, amount_in: buyQuote.amount_out });
9433
- const profitBps = Number((sellQuote.amount_out - amountIn) * 10000n / amountIn);
9434
- quotes.push({ protocol: p.name, buy: buyQuote.amount_out, sell: sellQuote.amount_out, profit_bps: profitBps });
9435
- } catch {
9436
- }
9437
- }
9438
- const opportunities = [];
9439
- for (let i = 0; i < quotes.length; i++) {
9440
- for (let j = 0; j < quotes.length; j++) {
9441
- if (i === j) continue;
9442
- const buyAmount = quotes[i].buy;
9443
- const sellAmount = quotes[j].sell;
9444
- if (sellAmount > amountIn) {
9445
- const profitBps = Number((sellAmount - amountIn) * 10000n / amountIn);
9446
- opportunities.push({ buy_on: quotes[i].protocol, sell_on: quotes[j].protocol, profit_bps: profitBps });
9447
- }
9448
- }
9449
- }
9450
- opportunities.sort((a, b) => b.profit_bps - a.profit_bps);
9451
- printOutput({
9452
- chain: chainName,
9453
- token_in: tokenIn,
9454
- token_out: tokenOut,
9455
- amount_in: amountIn,
9456
- single_dex: quotes,
9457
- cross_dex_opportunities: opportunities.slice(0, 5)
9458
- }, getOpts());
9459
- });
9460
- }
9461
-
9462
9138
  // src/commands/positions.ts
9463
9139
  import { encodeFunctionData as encodeFunctionData30, parseAbi as parseAbi34 } from "viem";
9464
9140
  var ERC20_ABI6 = parseAbi34([
@@ -10071,207 +9747,6 @@ function registerWhales(parent, getOpts) {
10071
9747
  });
10072
9748
  }
10073
9749
 
10074
- // src/commands/compare.ts
10075
- import { spawnSync } from "child_process";
10076
- function round25(x) {
10077
- return Math.round(x * 100) / 100;
10078
- }
10079
- async function fetchPerpRates() {
10080
- let result = spawnSync("perp", ["--json", "arb", "scan", "--rates"], { encoding: "utf8", timeout: 3e4 });
10081
- if (result.error || result.status !== 0) {
10082
- result = spawnSync("npx", ["-y", "perp-cli@latest", "--json", "arb", "scan", "--rates"], {
10083
- encoding: "utf8",
10084
- timeout: 6e4
10085
- });
10086
- }
10087
- if (result.error || result.status !== 0) {
10088
- throw new Error("perp-cli not found or failed");
10089
- }
10090
- let data;
10091
- try {
10092
- data = JSON.parse(result.stdout);
10093
- } catch {
10094
- throw new Error("perp JSON parse error");
10095
- }
10096
- const d = data;
10097
- const symbolsRaw = d["data"]?.["symbols"] ?? d["symbols"];
10098
- const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [];
10099
- const results = [];
10100
- for (const sym of symbols) {
10101
- const symbol = sym["symbol"] ?? "?";
10102
- const maxSpread = sym["maxSpreadAnnual"] ?? 0;
10103
- const longEx = sym["longExchange"] ?? "?";
10104
- const shortEx = sym["shortExchange"] ?? "?";
10105
- if (Math.abs(maxSpread) > 0) {
10106
- results.push({
10107
- type: "perp_funding",
10108
- asset: symbol,
10109
- apy: round25(maxSpread),
10110
- detail: `long ${longEx} / short ${shortEx}`,
10111
- risk: Math.abs(maxSpread) > 50 ? "high" : Math.abs(maxSpread) > 20 ? "medium" : "low",
10112
- source: "perp-cli"
10113
- });
10114
- }
10115
- const rates = Array.isArray(sym["rates"]) ? sym["rates"] : [];
10116
- for (const rate of rates) {
10117
- const exchange = rate["exchange"] ?? "?";
10118
- const annual = rate["annualizedPct"] ?? 0;
10119
- if (Math.abs(annual) > 1) {
10120
- results.push({
10121
- type: "perp_rate",
10122
- asset: symbol,
10123
- apy: round25(annual),
10124
- detail: exchange,
10125
- risk: Math.abs(annual) > 50 ? "high" : Math.abs(annual) > 20 ? "medium" : "low",
10126
- source: "perp-cli"
10127
- });
10128
- }
10129
- }
10130
- }
10131
- return results;
10132
- }
10133
- async function fetchLendingRates(registry, asset) {
10134
- const chainKeys = Array.from(registry.chains.keys());
10135
- const tasks = chainKeys.map(async (ck) => {
10136
- try {
10137
- const chain = registry.getChain(ck);
10138
- const chainName = chain.name.toLowerCase();
10139
- let assetAddr;
10140
- try {
10141
- assetAddr = registry.resolveToken(chainName, asset).address;
10142
- } catch {
10143
- return [];
10144
- }
10145
- const protos = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "aave_v3");
10146
- if (protos.length === 0) return [];
10147
- const rpc = chain.effectiveRpcUrl();
10148
- const rates = [];
10149
- for (const proto of protos) {
10150
- try {
10151
- const lending = createLending(proto, rpc);
10152
- const r = await lending.getRates(assetAddr);
10153
- if (r.supply_apy > 0) {
10154
- rates.push({
10155
- type: "lending_supply",
10156
- asset,
10157
- apy: round25(r.supply_apy * 100),
10158
- detail: `${r.protocol} (${chain.name})`,
10159
- risk: "low",
10160
- source: "defi-cli"
10161
- });
10162
- }
10163
- } catch {
10164
- }
10165
- }
10166
- return rates;
10167
- } catch {
10168
- return [];
10169
- }
10170
- });
10171
- const nested = await Promise.all(tasks);
10172
- return nested.flat();
10173
- }
10174
- function registerCompare(parent, getOpts) {
10175
- parent.command("compare").description("Compare all yield sources: perp funding vs lending APY vs staking").option("--asset <token>", "Token symbol to compare (e.g. USDC, ETH)", "USDC").option("--no-perps", "Exclude perp funding rates").option("--no-lending", "Exclude lending rates").option("--min-apy <pct>", "Minimum absolute APY to show", "1.0").action(async (opts) => {
10176
- try {
10177
- const registry = Registry.loadEmbedded();
10178
- const asset = opts.asset ?? "USDC";
10179
- const includePerps = opts.perps !== false;
10180
- const includeLending = opts.lending !== false;
10181
- const minApy = parseFloat(opts.minApy ?? "1.0");
10182
- const t0 = Date.now();
10183
- const opportunities = [];
10184
- if (includePerps) {
10185
- try {
10186
- const perpData = await fetchPerpRates();
10187
- for (const opp of perpData) {
10188
- const apy = Math.abs(opp["apy"] ?? 0);
10189
- if (apy >= minApy) opportunities.push(opp);
10190
- }
10191
- } catch {
10192
- }
10193
- }
10194
- if (includeLending) {
10195
- const lendingData = await fetchLendingRates(registry, asset);
10196
- for (const opp of lendingData) {
10197
- const apy = Math.abs(opp["apy"] ?? 0);
10198
- if (apy >= minApy) opportunities.push(opp);
10199
- }
10200
- }
10201
- opportunities.sort((a, b) => {
10202
- const aApy = Math.abs(a["apy"] ?? 0);
10203
- const bApy = Math.abs(b["apy"] ?? 0);
10204
- return bApy - aApy;
10205
- });
10206
- const scanMs = Date.now() - t0;
10207
- printOutput(
10208
- {
10209
- asset,
10210
- scan_duration_ms: scanMs,
10211
- total_opportunities: opportunities.length,
10212
- opportunities
10213
- },
10214
- getOpts()
10215
- );
10216
- } catch (err) {
10217
- printOutput({ error: String(err) }, getOpts());
10218
- process.exit(1);
10219
- }
10220
- });
10221
- }
10222
-
10223
- // src/commands/swap.ts
10224
- var ODOS_API = "https://api.odos.xyz";
10225
- function registerSwap(parent, getOpts, makeExecutor2) {
10226
- parent.command("swap").description("Aggregator swap: best price across all DEXes (ODOS)").requiredOption("--token-in <token>", "Input token symbol or address").requiredOption("--token-out <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--slippage <bps>", "Slippage tolerance in basis points", "50").option("--recipient <address>", "Recipient address").action(async (opts) => {
10227
- const executor = makeExecutor2();
10228
- const chainName = parent.opts().chain ?? "hyperevm";
10229
- const registry = Registry.loadEmbedded();
10230
- const chain = registry.getChain(chainName);
10231
- const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
10232
- const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
10233
- const sender = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
10234
- try {
10235
- const quoteRes = await fetch(`${ODOS_API}/sor/quote/v2`, {
10236
- method: "POST",
10237
- headers: { "Content-Type": "application/json" },
10238
- body: JSON.stringify({
10239
- chainId: chain.chain_id,
10240
- inputTokens: [{ tokenAddress: tokenIn, amount: opts.amount }],
10241
- outputTokens: [{ tokenAddress: tokenOut, proportion: 1 }],
10242
- slippageLimitPercent: parseInt(opts.slippage) / 100,
10243
- userAddr: sender
10244
- })
10245
- });
10246
- const quote = await quoteRes.json();
10247
- if (!quote.pathId) {
10248
- printOutput({ error: "No ODOS route found", quote }, getOpts());
10249
- return;
10250
- }
10251
- const assembleRes = await fetch(`${ODOS_API}/sor/assemble`, {
10252
- method: "POST",
10253
- headers: { "Content-Type": "application/json" },
10254
- body: JSON.stringify({ pathId: quote.pathId, userAddr: sender })
10255
- });
10256
- const assembled = await assembleRes.json();
10257
- if (assembled.transaction) {
10258
- const tx = {
10259
- description: `ODOS swap ${tokenIn} \u2192 ${tokenOut}`,
10260
- to: assembled.transaction.to,
10261
- data: assembled.transaction.data,
10262
- value: BigInt(assembled.transaction.value ?? 0)
10263
- };
10264
- const result = await executor.execute(tx);
10265
- printOutput({ ...result, odos_quote: quote }, getOpts());
10266
- } else {
10267
- printOutput({ error: "ODOS assembly failed", assembled }, getOpts());
10268
- }
10269
- } catch (e) {
10270
- printOutput({ error: `ODOS API error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
10271
- }
10272
- });
10273
- }
10274
-
10275
9750
  // src/commands/bridge.ts
10276
9751
  var LIFI_API = "https://li.quest/v1";
10277
9752
  var DLN_API = "https://dln.debridge.finance/v1.0/dln/order";
@@ -10497,249 +9972,208 @@ function registerBridge(parent, getOpts) {
10497
9972
  });
10498
9973
  }
10499
9974
 
10500
- // src/commands/nft.ts
10501
- function registerNft(parent, getOpts) {
10502
- const nft = parent.command("nft").description("NFT operations: collection info, ownership, balance");
10503
- nft.command("info").description("Get NFT collection info (name, symbol, total supply)").requiredOption("--collection <address>", "NFT collection contract address").action(async (opts) => {
10504
- const chainName = parent.opts().chain ?? "hyperevm";
10505
- const registry = Registry.loadEmbedded();
10506
- const chain = registry.getChain(chainName);
10507
- const nftProtocols = registry.getProtocolsByCategory("nft").filter((p) => p.chain === chainName);
10508
- const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
10509
- try {
10510
- const adapter = createNft(entry, chain.effectiveRpcUrl());
10511
- const info = await adapter.getCollectionInfo(opts.collection);
10512
- printOutput(info, getOpts());
10513
- } catch (e) {
10514
- printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
10515
- }
10516
- });
10517
- nft.command("owner").description("Check who owns a specific NFT token ID").requiredOption("--collection <address>", "NFT collection contract address").requiredOption("--token-id <id>", "Token ID").action(async (opts) => {
10518
- const chainName = parent.opts().chain ?? "hyperevm";
10519
- const registry = Registry.loadEmbedded();
10520
- const chain = registry.getChain(chainName);
10521
- const nftProtocols = registry.getProtocolsByCategory("nft").filter((p) => p.chain === chainName);
10522
- const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
10523
- try {
10524
- const adapter = createNft(entry, chain.effectiveRpcUrl());
10525
- const info = await adapter.getTokenInfo(opts.collection, BigInt(opts.tokenId));
10526
- printOutput(info, getOpts());
10527
- } catch (e) {
10528
- printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
10529
- }
10530
- });
10531
- nft.command("balance").description("Check how many NFTs an address holds in a collection").requiredOption("--collection <address>", "NFT collection contract address").requiredOption("--owner <address>", "Owner address to query").action(async (opts) => {
10532
- const chainName = parent.opts().chain ?? "hyperevm";
10533
- const registry = Registry.loadEmbedded();
10534
- const chain = registry.getChain(chainName);
10535
- const nftProtocols = registry.getProtocolsByCategory("nft").filter((p) => p.chain === chainName);
10536
- const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
10537
- try {
10538
- const adapter = createNft(entry, chain.effectiveRpcUrl());
10539
- const balance = await adapter.getBalance(opts.owner, opts.collection);
10540
- printOutput({ collection: opts.collection, owner: opts.owner, balance }, getOpts());
10541
- } catch (e) {
10542
- printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
10543
- }
10544
- });
10545
- nft.command("uri").description("Get token URI for a specific NFT").requiredOption("--collection <address>", "NFT collection contract address").requiredOption("--token-id <id>", "Token ID").action(async (opts) => {
10546
- const chainName = parent.opts().chain ?? "hyperevm";
10547
- const registry = Registry.loadEmbedded();
10548
- const chain = registry.getChain(chainName);
10549
- const nftProtocols = registry.getProtocolsByCategory("nft").filter((p) => p.chain === chainName);
10550
- const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
10551
- try {
10552
- const adapter = createNft(entry, chain.effectiveRpcUrl());
10553
- const info = await adapter.getTokenInfo(opts.collection, BigInt(opts.tokenId));
10554
- printOutput({ collection: opts.collection, token_id: opts.tokenId, token_uri: info.token_uri }, getOpts());
10555
- } catch (e) {
10556
- printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
10557
- }
9975
+ // src/commands/swap.ts
9976
+ var CHAIN_NAMES = {
9977
+ hyperevm: { kyber: "hyperevm", openocean: "hyperevm" },
9978
+ mantle: { openocean: "mantle" }
9979
+ };
9980
+ var KYBER_API = "https://aggregator-api.kyberswap.com";
9981
+ async function kyberGetQuote(chain, tokenIn, tokenOut, amountIn) {
9982
+ const params = new URLSearchParams({ tokenIn, tokenOut, amountIn });
9983
+ const url = `${KYBER_API}/${chain}/api/v1/routes?${params}`;
9984
+ const res = await fetch(url, { headers: { "x-client-id": "defi-cli" } });
9985
+ if (!res.ok) throw new Error(`KyberSwap quote failed: ${res.status} ${await res.text()}`);
9986
+ const json = await res.json();
9987
+ const data = json.data;
9988
+ if (!data?.routeSummary) throw new Error(`KyberSwap: no route found`);
9989
+ return data;
9990
+ }
9991
+ async function kyberBuildTx(chain, routeSummary, sender, recipient, slippageTolerance) {
9992
+ const url = `${KYBER_API}/${chain}/api/v1/route/build`;
9993
+ const res = await fetch(url, {
9994
+ method: "POST",
9995
+ headers: { "Content-Type": "application/json", "x-client-id": "defi-cli" },
9996
+ body: JSON.stringify({ routeSummary, sender, recipient, slippageTolerance })
10558
9997
  });
9998
+ if (!res.ok) throw new Error(`KyberSwap build failed: ${res.status} ${await res.text()}`);
9999
+ const json = await res.json();
10000
+ const data = json.data;
10001
+ if (!data) throw new Error("KyberSwap: no build data");
10002
+ return {
10003
+ to: String(data.routerAddress),
10004
+ data: String(data.data),
10005
+ value: String(data.value ?? "0x0")
10006
+ };
10559
10007
  }
10560
-
10561
- // src/commands/farm.ts
10562
- function registerFarm(parent, getOpts, makeExecutor2) {
10563
- const farm = parent.command("farm").description("LP farm operations: deposit, withdraw, claim rewards (MasterChef)");
10564
- farm.command("deposit").description("Deposit LP tokens into a MasterChef farm").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pid <pid>", "Farm pool ID").requiredOption("--amount <amount>", "LP token amount in wei").action(async (opts) => {
10565
- const executor = makeExecutor2();
10566
- const registry = Registry.loadEmbedded();
10567
- const protocol = registry.getProtocol(opts.protocol);
10568
- const chainName = parent.opts().chain;
10569
- const chain = registry.getChain(chainName ?? "hyperevm");
10570
- const rpcUrl = chain.effectiveRpcUrl();
10571
- const adapter = createMasterChef(protocol, rpcUrl);
10572
- const tx = await adapter.buildDeposit(
10573
- protocol.contracts?.["masterchef"],
10574
- BigInt(opts.amount),
10575
- BigInt(opts.pid)
10576
- );
10577
- const result = await executor.execute(tx);
10578
- printOutput(result, getOpts());
10579
- });
10580
- farm.command("withdraw").description("Withdraw LP tokens from a MasterChef farm").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pid <pid>", "Farm pool ID").requiredOption("--amount <amount>", "LP token amount in wei").action(async (opts) => {
10581
- const executor = makeExecutor2();
10582
- const registry = Registry.loadEmbedded();
10583
- const protocol = registry.getProtocol(opts.protocol);
10584
- const chainName = parent.opts().chain;
10585
- const chain = registry.getChain(chainName ?? "hyperevm");
10586
- const rpcUrl = chain.effectiveRpcUrl();
10587
- const adapter = createMasterChef(protocol, rpcUrl);
10588
- const tx = await adapter.buildWithdrawPid(
10589
- BigInt(opts.pid),
10590
- BigInt(opts.amount)
10591
- );
10592
- const result = await executor.execute(tx);
10593
- printOutput(result, getOpts());
10594
- });
10595
- farm.command("claim").description("Claim pending rewards from a MasterChef farm").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pid <pid>", "Farm pool ID").action(async (opts) => {
10596
- const executor = makeExecutor2();
10597
- const registry = Registry.loadEmbedded();
10598
- const protocol = registry.getProtocol(opts.protocol);
10599
- const chainName = parent.opts().chain;
10600
- const chain = registry.getChain(chainName ?? "hyperevm");
10601
- const rpcUrl = chain.effectiveRpcUrl();
10602
- const adapter = createMasterChef(protocol, rpcUrl);
10603
- const tx = await adapter.buildClaimRewardsPid(
10604
- BigInt(opts.pid)
10605
- );
10606
- const result = await executor.execute(tx);
10607
- printOutput(result, getOpts());
10608
- });
10609
- farm.command("info").description("Show pending rewards and farm info").requiredOption("--protocol <protocol>", "Protocol slug").option("--pid <pid>", "Farm pool ID (optional)").option("--address <address>", "Wallet address to query (defaults to DEFI_WALLET_ADDRESS env)").action(async (opts) => {
10610
- const registry = Registry.loadEmbedded();
10611
- const protocol = registry.getProtocol(opts.protocol);
10612
- const chainName = parent.opts().chain;
10613
- const chain = registry.getChain(chainName ?? "hyperevm");
10614
- const rpcUrl = chain.effectiveRpcUrl();
10615
- const adapter = createMasterChef(protocol, rpcUrl);
10616
- const walletAddress = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10617
- if (!walletAddress) {
10618
- throw new Error("--address or DEFI_WALLET_ADDRESS required");
10619
- }
10620
- const masterchef = protocol.contracts?.["masterchef"];
10621
- const rewards = await adapter.getPendingRewards(masterchef, walletAddress);
10622
- printOutput(rewards, getOpts());
10008
+ var OPENOCEAN_API = "https://open-api.openocean.finance/v4";
10009
+ async function openoceanSwap(chain, inTokenAddress, outTokenAddress, amountIn, slippagePct, account) {
10010
+ const params = new URLSearchParams({
10011
+ inTokenAddress,
10012
+ outTokenAddress,
10013
+ amount: amountIn,
10014
+ gasPrice: "0.1",
10015
+ slippage: slippagePct,
10016
+ account
10623
10017
  });
10018
+ const url = `${OPENOCEAN_API}/${chain}/swap?${params}`;
10019
+ const res = await fetch(url);
10020
+ if (!res.ok) throw new Error(`OpenOcean swap failed: ${res.status} ${await res.text()}`);
10021
+ const json = await res.json();
10022
+ const data = json.data;
10023
+ if (!data) throw new Error("OpenOcean: no swap data");
10024
+ return {
10025
+ to: String(data.to),
10026
+ data: String(data.data),
10027
+ value: String(data.value ?? "0x0"),
10028
+ outAmount: String(data.outAmount ?? "0")
10029
+ };
10624
10030
  }
10625
-
10626
- // src/commands/farming.ts
10627
- import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
10628
- function registerFarming(parent, getOpts, makeExecutor2) {
10629
- const farming = parent.command("farming").description("Algebra eternal farming operations (KittenSwap): enter, exit, collect rewards, claim, discover");
10630
- farming.command("enter").description("Enter farming: stake an NFT position to start earning rewards").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").requiredOption("--pool <address>", "Pool address").requiredOption("--token-id <id>", "NFT position token ID").option("--owner <address>", "Owner address to receive claimed rewards (defaults to DEFI_WALLET_ADDRESS or private key address)").action(async (opts) => {
10631
- const executor = makeExecutor2();
10632
- const registry = Registry.loadEmbedded();
10633
- const protocol = registry.getProtocol(opts.protocol);
10634
- const chainName = parent.opts().chain;
10635
- const chain = registry.getChain(chainName ?? "hyperevm");
10636
- const rpcUrl = chain.effectiveRpcUrl();
10637
- const adapter = createKittenSwapFarming(protocol, rpcUrl);
10638
- const owner = resolveOwner(opts.owner);
10639
- const tx = await adapter.buildEnterFarming(
10640
- BigInt(opts.tokenId),
10641
- opts.pool,
10642
- owner
10643
- );
10644
- const result = await executor.execute(tx);
10645
- printOutput(result, getOpts());
10646
- });
10647
- farming.command("exit").description("Exit farming: unstake an NFT position").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").requiredOption("--pool <address>", "Pool address").requiredOption("--token-id <id>", "NFT position token ID").action(async (opts) => {
10648
- const executor = makeExecutor2();
10649
- const registry = Registry.loadEmbedded();
10650
- const protocol = registry.getProtocol(opts.protocol);
10651
- const chainName = parent.opts().chain;
10652
- const chain = registry.getChain(chainName ?? "hyperevm");
10653
- const rpcUrl = chain.effectiveRpcUrl();
10654
- const adapter = createKittenSwapFarming(protocol, rpcUrl);
10655
- const tx = await adapter.buildExitFarming(
10656
- BigInt(opts.tokenId),
10657
- opts.pool
10658
- );
10659
- const result = await executor.execute(tx);
10660
- printOutput(result, getOpts());
10661
- });
10662
- farming.command("rewards").description("Collect + claim farming rewards for a staked position (collectRewards + claimReward multicall)").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").requiredOption("--pool <address>", "Pool address").requiredOption("--token-id <id>", "NFT position token ID").option("--owner <address>", "Owner address to receive claimed rewards (defaults to DEFI_WALLET_ADDRESS or private key address)").action(async (opts) => {
10663
- const executor = makeExecutor2();
10664
- const registry = Registry.loadEmbedded();
10665
- const protocol = registry.getProtocol(opts.protocol);
10666
- const chainName = parent.opts().chain;
10667
- const chain = registry.getChain(chainName ?? "hyperevm");
10668
- const rpcUrl = chain.effectiveRpcUrl();
10669
- const adapter = createKittenSwapFarming(protocol, rpcUrl);
10670
- const owner = resolveOwner(opts.owner);
10671
- const tx = await adapter.buildCollectRewards(
10672
- BigInt(opts.tokenId),
10673
- opts.pool,
10674
- owner
10675
- );
10676
- const result = await executor.execute(tx);
10677
- printOutput(result, getOpts());
10678
- });
10679
- farming.command("claim").description("Claim accumulated farming rewards (KITTEN + WHYPE) without changing position").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").option("--owner <address>", "Owner address to receive rewards (defaults to DEFI_WALLET_ADDRESS or private key address)").action(async (opts) => {
10031
+ var LIQD_API = "https://api.liqd.ag/v2";
10032
+ var LIQD_ROUTER = "0x744489ee3d540777a66f2cf297479745e0852f7a";
10033
+ async function liquidSwapRoute(tokenIn, tokenOut, amountIn, slippagePct) {
10034
+ const params = new URLSearchParams({ tokenIn, tokenOut, amountIn, slippage: slippagePct });
10035
+ const url = `${LIQD_API}/route?${params}`;
10036
+ const res = await fetch(url);
10037
+ if (!res.ok) throw new Error(`LiquidSwap route failed: ${res.status} ${await res.text()}`);
10038
+ const json = await res.json();
10039
+ const execution = json.execution;
10040
+ if (!execution) throw new Error("LiquidSwap: no execution data in response");
10041
+ const details = json.details;
10042
+ return {
10043
+ to: String(execution.to ?? LIQD_ROUTER),
10044
+ data: String(execution.calldata),
10045
+ value: String(execution.value ?? "0x0"),
10046
+ outAmount: String(details?.amountOut ?? json.amountOut ?? "0")
10047
+ };
10048
+ }
10049
+ function registerSwap(parent, getOpts, makeExecutor2) {
10050
+ parent.command("swap").description("Swap tokens via DEX aggregator (KyberSwap, OpenOcean, LiquidSwap)").requiredOption("--from <token>", "Input token symbol or address").requiredOption("--to <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--provider <name>", "Aggregator: kyber, openocean, liquid", "kyber").option("--slippage <bps>", "Slippage tolerance in bps", "50").action(async (opts) => {
10680
10051
  const executor = makeExecutor2();
10052
+ const chainName = parent.opts().chain ?? "hyperevm";
10681
10053
  const registry = Registry.loadEmbedded();
10682
- const protocol = registry.getProtocol(opts.protocol);
10683
- const chainName = parent.opts().chain;
10684
- const chain = registry.getChain(chainName ?? "hyperevm");
10685
- const rpcUrl = chain.effectiveRpcUrl();
10686
- const adapter = createKittenSwapFarming(protocol, rpcUrl);
10687
- const owner = resolveOwner(opts.owner);
10688
- const tx = await adapter.buildClaimReward(owner);
10689
- const result = await executor.execute(tx);
10690
- printOutput(result, getOpts());
10691
- });
10692
- farming.command("pending").description("Query pending farming rewards for a position (read-only)").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").requiredOption("--pool <address>", "Pool address").requiredOption("--token-id <id>", "NFT position token ID").action(async (opts) => {
10693
- const registry = Registry.loadEmbedded();
10694
- const protocol = registry.getProtocol(opts.protocol);
10695
- const chainName = parent.opts().chain;
10696
- const chain = registry.getChain(chainName ?? "hyperevm");
10697
- const rpcUrl = chain.effectiveRpcUrl();
10698
- const adapter = createKittenSwapFarming(protocol, rpcUrl);
10699
- const rewards = await adapter.getPendingRewards(
10700
- BigInt(opts.tokenId),
10701
- opts.pool
10702
- );
10703
- printOutput(
10704
- {
10705
- tokenId: opts.tokenId,
10706
- pool: opts.pool,
10707
- reward_kitten: rewards.reward.toString(),
10708
- bonus_reward_whype: rewards.bonusReward.toString()
10709
- },
10710
- getOpts()
10711
- );
10712
- });
10713
- farming.command("discover").description("Discover all pools with active KittenSwap farming incentives").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").action(async (opts) => {
10714
- const registry = Registry.loadEmbedded();
10715
- const protocol = registry.getProtocol(opts.protocol);
10716
- const chainName = parent.opts().chain;
10717
- const chain = registry.getChain(chainName ?? "hyperevm");
10718
- const rpcUrl = chain.effectiveRpcUrl();
10719
- const adapter = createKittenSwapFarming(protocol, rpcUrl);
10720
- const pools = await adapter.discoverFarmingPools();
10721
- const output = pools.map((p) => ({
10722
- pool: p.pool,
10723
- nonce: p.key.nonce.toString(),
10724
- total_reward: p.totalReward.toString(),
10725
- bonus_reward: p.bonusReward.toString(),
10726
- active: p.active
10727
- }));
10728
- printOutput(output, getOpts());
10054
+ const provider = opts.provider.toLowerCase();
10055
+ const slippageBps = parseInt(opts.slippage, 10);
10056
+ const fromAddr = opts.from.startsWith("0x") ? opts.from : registry.resolveToken(chainName, opts.from).address;
10057
+ const toAddr = opts.to.startsWith("0x") ? opts.to : registry.resolveToken(chainName, opts.to).address;
10058
+ const wallet = process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
10059
+ if (provider === "kyber") {
10060
+ const chainNames = CHAIN_NAMES[chainName];
10061
+ if (!chainNames?.kyber) {
10062
+ printOutput({ error: `KyberSwap: unsupported chain '${chainName}'. Supported: hyperevm` }, getOpts());
10063
+ return;
10064
+ }
10065
+ const kyberChain = chainNames.kyber;
10066
+ try {
10067
+ const quoteData = await kyberGetQuote(kyberChain, fromAddr, toAddr, opts.amount);
10068
+ const routeSummary = quoteData.routeSummary;
10069
+ const amountOut = String(routeSummary.amountOut ?? "0");
10070
+ const txData = await kyberBuildTx(
10071
+ kyberChain,
10072
+ routeSummary,
10073
+ wallet,
10074
+ wallet,
10075
+ slippageBps
10076
+ );
10077
+ const tx = {
10078
+ description: `KyberSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
10079
+ to: txData.to,
10080
+ data: txData.data,
10081
+ value: txData.value.startsWith("0x") ? BigInt(txData.value) : BigInt(txData.value || 0),
10082
+ approvals: [{ token: fromAddr, spender: txData.to, amount: BigInt(opts.amount) }]
10083
+ };
10084
+ const result = await executor.execute(tx);
10085
+ printOutput({
10086
+ provider: "kyber",
10087
+ chain: kyberChain,
10088
+ from_token: fromAddr,
10089
+ to_token: toAddr,
10090
+ amount_in: opts.amount,
10091
+ amount_out: amountOut,
10092
+ router: txData.to,
10093
+ ...result
10094
+ }, getOpts());
10095
+ } catch (e) {
10096
+ printOutput({ error: `KyberSwap error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
10097
+ }
10098
+ return;
10099
+ }
10100
+ if (provider === "openocean") {
10101
+ const chainNames = CHAIN_NAMES[chainName];
10102
+ if (!chainNames) {
10103
+ printOutput({ error: `OpenOcean: unsupported chain '${chainName}'. Supported: ${Object.keys(CHAIN_NAMES).join(", ")}` }, getOpts());
10104
+ return;
10105
+ }
10106
+ const ooChain = chainNames.openocean;
10107
+ const fromToken = opts.from.startsWith("0x") ? registry.tokens.get(chainName)?.find((t) => t.address.toLowerCase() === opts.from.toLowerCase()) : registry.tokens.get(chainName)?.find((t) => t.symbol.toLowerCase() === opts.from.toLowerCase());
10108
+ const fromDecimals = fromToken?.decimals ?? 18;
10109
+ const humanAmount = (Number(opts.amount) / 10 ** fromDecimals).toString();
10110
+ const slippagePct = (slippageBps / 100).toFixed(2);
10111
+ try {
10112
+ const swap = await openoceanSwap(
10113
+ ooChain,
10114
+ fromAddr,
10115
+ toAddr,
10116
+ humanAmount,
10117
+ slippagePct,
10118
+ wallet
10119
+ );
10120
+ const tx = {
10121
+ description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
10122
+ to: swap.to,
10123
+ data: swap.data,
10124
+ value: swap.value.startsWith("0x") ? BigInt(swap.value) : BigInt(swap.value || 0),
10125
+ approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
10126
+ };
10127
+ const result = await executor.execute(tx);
10128
+ printOutput({
10129
+ provider: "openocean",
10130
+ chain: ooChain,
10131
+ from_token: fromAddr,
10132
+ to_token: toAddr,
10133
+ amount_in: opts.amount,
10134
+ amount_out: swap.outAmount,
10135
+ router: swap.to,
10136
+ ...result
10137
+ }, getOpts());
10138
+ } catch (e) {
10139
+ printOutput({ error: `OpenOcean error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
10140
+ }
10141
+ return;
10142
+ }
10143
+ if (provider === "liquid") {
10144
+ if (chainName !== "hyperevm") {
10145
+ printOutput({ error: `LiquidSwap only supports hyperevm, got '${chainName}'` }, getOpts());
10146
+ return;
10147
+ }
10148
+ const slippagePct = (slippageBps / 100).toFixed(2);
10149
+ try {
10150
+ const route = await liquidSwapRoute(fromAddr, toAddr, opts.amount, slippagePct);
10151
+ const tx = {
10152
+ description: `LiquidSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
10153
+ to: route.to,
10154
+ data: route.data,
10155
+ value: route.value.startsWith("0x") ? BigInt(route.value) : BigInt(route.value || 0),
10156
+ approvals: [{ token: fromAddr, spender: route.to, amount: BigInt(opts.amount) }]
10157
+ };
10158
+ const result = await executor.execute(tx);
10159
+ printOutput({
10160
+ provider: "liquid",
10161
+ chain: chainName,
10162
+ from_token: fromAddr,
10163
+ to_token: toAddr,
10164
+ amount_in: opts.amount,
10165
+ amount_out: route.outAmount,
10166
+ router: route.to,
10167
+ ...result
10168
+ }, getOpts());
10169
+ } catch (e) {
10170
+ printOutput({ error: `LiquidSwap error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
10171
+ }
10172
+ return;
10173
+ }
10174
+ printOutput({ error: `Unknown provider '${opts.provider}'. Choose: kyber, openocean, liquid` }, getOpts());
10729
10175
  });
10730
10176
  }
10731
- function resolveOwner(optOwner) {
10732
- if (optOwner) return optOwner;
10733
- const walletAddr = process.env["DEFI_WALLET_ADDRESS"];
10734
- if (walletAddr) return walletAddr;
10735
- const privateKey = process.env["DEFI_PRIVATE_KEY"];
10736
- if (privateKey) {
10737
- return privateKeyToAccount3(privateKey).address;
10738
- }
10739
- throw new Error(
10740
- "--owner, DEFI_WALLET_ADDRESS, or DEFI_PRIVATE_KEY is required to resolve reward recipient"
10741
- );
10742
- }
10743
10177
 
10744
10178
  // src/commands/setup.ts
10745
10179
  import pc2 from "picocolors";
@@ -10789,8 +10223,8 @@ function isValidPrivateKey(s) {
10789
10223
  }
10790
10224
  async function deriveAddress(privateKey) {
10791
10225
  try {
10792
- const { privateKeyToAccount: privateKeyToAccount4 } = await import("viem/accounts");
10793
- const account = privateKeyToAccount4(privateKey);
10226
+ const { privateKeyToAccount: privateKeyToAccount3 } = await import("viem/accounts");
10227
+ const account = privateKeyToAccount3(privateKey);
10794
10228
  return account.address;
10795
10229
  } catch {
10796
10230
  return null;
@@ -10885,107 +10319,6 @@ function registerSetup(program2) {
10885
10319
  });
10886
10320
  }
10887
10321
 
10888
- // src/commands/lb.ts
10889
- function registerLB(parent, getOpts, makeExecutor2) {
10890
- const lb = parent.command("lb").description("Merchant Moe Liquidity Book: add/remove liquidity, rewards, positions");
10891
- lb.command("add").description("Add liquidity to a Liquidity Book pair").requiredOption("--protocol <protocol>", "Protocol slug (e.g. merchantmoe-mantle)").requiredOption("--pool <address>", "LB pair address").requiredOption("--token-x <address>", "Token X address").requiredOption("--token-y <address>", "Token Y address").requiredOption("--bin-step <step>", "Bin step of the pair").option("--amount-x <wei>", "Amount of token X in wei", "0").option("--amount-y <wei>", "Amount of token Y in wei", "0").option("--bins <N>", "Number of bins on each side of active bin", "5").option("--active-id <id>", "Active bin id (defaults to on-chain query)").option("--recipient <address>", "Recipient address").action(async (opts) => {
10892
- const executor = makeExecutor2();
10893
- const registry = Registry.loadEmbedded();
10894
- const protocol = registry.getProtocol(opts.protocol);
10895
- const chainName = parent.opts().chain;
10896
- const chain = registry.getChain(chainName ?? "mantle");
10897
- const rpcUrl = chain.effectiveRpcUrl();
10898
- const adapter = createMerchantMoeLB(protocol, rpcUrl);
10899
- const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
10900
- const tx = await adapter.buildAddLiquidity({
10901
- pool: opts.pool,
10902
- tokenX: opts.tokenX,
10903
- tokenY: opts.tokenY,
10904
- binStep: parseInt(opts.binStep),
10905
- amountX: BigInt(opts.amountX),
10906
- amountY: BigInt(opts.amountY),
10907
- numBins: parseInt(opts.bins),
10908
- activeIdDesired: opts.activeId ? parseInt(opts.activeId) : void 0,
10909
- recipient
10910
- });
10911
- const result = await executor.execute(tx);
10912
- printOutput(result, getOpts());
10913
- });
10914
- lb.command("remove").description("Remove liquidity from Liquidity Book bins").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-x <address>", "Token X address").requiredOption("--token-y <address>", "Token Y address").requiredOption("--bin-step <step>", "Bin step of the pair").requiredOption("--bins <binIds>", "Comma-separated bin IDs to remove from").requiredOption("--amounts <amounts>", "Comma-separated LB token amounts to remove per bin (wei)").option("--recipient <address>", "Recipient address").action(async (opts) => {
10915
- const executor = makeExecutor2();
10916
- const registry = Registry.loadEmbedded();
10917
- const protocol = registry.getProtocol(opts.protocol);
10918
- const adapter = createMerchantMoeLB(protocol);
10919
- const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
10920
- const binIds = opts.bins.split(",").map((s) => parseInt(s.trim()));
10921
- const amounts = opts.amounts.split(",").map((s) => BigInt(s.trim()));
10922
- const tx = await adapter.buildRemoveLiquidity({
10923
- tokenX: opts.tokenX,
10924
- tokenY: opts.tokenY,
10925
- binStep: parseInt(opts.binStep),
10926
- binIds,
10927
- amounts,
10928
- recipient
10929
- });
10930
- const result = await executor.execute(tx);
10931
- printOutput(result, getOpts());
10932
- });
10933
- lb.command("rewards").description("Show pending MOE rewards for a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "LB pair address").option("--bins <binIds>", "Comma-separated bin IDs to check (auto-detected from rewarder range if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10934
- const registry = Registry.loadEmbedded();
10935
- const protocol = registry.getProtocol(opts.protocol);
10936
- const chainName = parent.opts().chain;
10937
- const chain = registry.getChain(chainName ?? "mantle");
10938
- const rpcUrl = chain.effectiveRpcUrl();
10939
- const adapter = createMerchantMoeLB(protocol, rpcUrl);
10940
- const user = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10941
- if (!user) throw new Error("--address or DEFI_WALLET_ADDRESS required");
10942
- const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
10943
- const rewards = await adapter.getPendingRewards(user, opts.pool, binIds);
10944
- printOutput(rewards, getOpts());
10945
- });
10946
- lb.command("claim").description("Claim pending MOE rewards from a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "LB pair address").option("--bins <binIds>", "Comma-separated bin IDs to claim from (auto-detected from rewarder range if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10947
- const executor = makeExecutor2();
10948
- const registry = Registry.loadEmbedded();
10949
- const protocol = registry.getProtocol(opts.protocol);
10950
- const chainName = parent.opts().chain;
10951
- const chain = registry.getChain(chainName ?? "mantle");
10952
- const rpcUrl = chain.effectiveRpcUrl();
10953
- const adapter = createMerchantMoeLB(protocol, rpcUrl);
10954
- const user = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10955
- if (!user) throw new Error("--address or DEFI_WALLET_ADDRESS required");
10956
- const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
10957
- const tx = await adapter.buildClaimRewards(user, opts.pool, binIds);
10958
- const result = await executor.execute(tx);
10959
- printOutput(result, getOpts());
10960
- });
10961
- lb.command("discover").description("Find all rewarded LB pools on chain").requiredOption("--protocol <protocol>", "Protocol slug").option("--active-only", "Only show non-stopped pools").action(async (opts) => {
10962
- const registry = Registry.loadEmbedded();
10963
- const protocol = registry.getProtocol(opts.protocol);
10964
- const chainName = parent.opts().chain;
10965
- const chain = registry.getChain(chainName ?? "mantle");
10966
- const rpcUrl = chain.effectiveRpcUrl();
10967
- const adapter = createMerchantMoeLB(protocol, rpcUrl);
10968
- let pools = await adapter.discoverRewardedPools();
10969
- if (opts.activeOnly) {
10970
- pools = pools.filter((p) => !p.stopped);
10971
- }
10972
- printOutput(pools, getOpts());
10973
- });
10974
- lb.command("positions").description("Show user positions per bin in a LB pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "LB pair address").option("--bins <binIds>", "Comma-separated bin IDs to query (auto-detected from rewarder range or active \xB1 50 if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
10975
- const registry = Registry.loadEmbedded();
10976
- const protocol = registry.getProtocol(opts.protocol);
10977
- const chainName = parent.opts().chain;
10978
- const chain = registry.getChain(chainName ?? "mantle");
10979
- const rpcUrl = chain.effectiveRpcUrl();
10980
- const adapter = createMerchantMoeLB(protocol, rpcUrl);
10981
- const user = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
10982
- if (!user) throw new Error("--address or DEFI_WALLET_ADDRESS required");
10983
- const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
10984
- const positions = await adapter.getUserPositions(user, opts.pool, binIds);
10985
- printOutput(positions, getOpts());
10986
- });
10987
- }
10988
-
10989
10322
  // src/cli.ts
10990
10323
  var _require = createRequire(import.meta.url);
10991
10324
  var _pkg = _require("../package.json");
@@ -10999,8 +10332,8 @@ var BANNER = `
10999
10332
 
11000
10333
  2 chains \xB7 21 protocols \xB7 by HypurrQuant
11001
10334
 
11002
- Scan exploits, swap tokens, bridge assets, track whales,
11003
- compare yields \u2014 all from your terminal.
10335
+ Lending, LP provision, farming, gauges, vaults,
10336
+ yield comparison \u2014 all from your terminal.
11004
10337
  `;
11005
10338
  var program = new Command().name("defi").description("DeFi CLI \u2014 Multi-chain DeFi toolkit").version(_pkg.version).addHelpText("before", BANNER).option("--json", "Output as JSON").option("--ndjson", "Output as newline-delimited JSON").option("--fields <fields>", "Select specific output fields (comma-separated)").option("--chain <chain>", "Target chain", "hyperevm").option("--dry-run", "Dry-run mode (default, no broadcast)", true).option("--broadcast", "Actually broadcast the transaction");
11006
10339
  function getOutputMode() {
@@ -11015,37 +10348,23 @@ function makeExecutor() {
11015
10348
  }
11016
10349
  registerStatus(program, getOutputMode);
11017
10350
  registerSchema(program, getOutputMode);
11018
- registerDex(program, getOutputMode, makeExecutor);
11019
- registerGauge(program, getOutputMode, makeExecutor);
10351
+ registerLP(program, getOutputMode, makeExecutor);
11020
10352
  registerLending(program, getOutputMode, makeExecutor);
11021
10353
  registerCdp(program, getOutputMode, makeExecutor);
11022
- registerStaking(program, getOutputMode, makeExecutor);
11023
10354
  registerVault(program, getOutputMode, makeExecutor);
11024
10355
  registerYield(program, getOutputMode, makeExecutor);
11025
10356
  registerPortfolio(program, getOutputMode);
11026
10357
  registerMonitor(program, getOutputMode);
11027
10358
  registerAlert(program, getOutputMode);
11028
10359
  registerScan(program, getOutputMode);
11029
- registerArb(program, getOutputMode, makeExecutor);
11030
10360
  registerPositions(program, getOutputMode);
11031
10361
  registerPrice(program, getOutputMode);
11032
10362
  registerWallet(program, getOutputMode);
11033
10363
  registerToken(program, getOutputMode, makeExecutor);
11034
10364
  registerWhales(program, getOutputMode);
11035
- registerCompare(program, getOutputMode);
11036
- registerSwap(program, getOutputMode, makeExecutor);
11037
10365
  registerBridge(program, getOutputMode);
11038
- registerNft(program, getOutputMode);
11039
- registerFarm(program, getOutputMode, makeExecutor);
11040
- registerFarming(program, getOutputMode, makeExecutor);
11041
- registerLB(program, getOutputMode, makeExecutor);
10366
+ registerSwap(program, getOutputMode, makeExecutor);
11042
10367
  registerSetup(program);
11043
- program.command("agent").description("Agent mode: read JSON commands from stdin (for AI agents)").action(async () => {
11044
- const executor = makeExecutor();
11045
- process.stderr.write("Agent mode: reading JSON commands from stdin...\n");
11046
- process.stderr.write("Agent mode not yet fully implemented in TS port.\n");
11047
- process.exit(1);
11048
- });
11049
10368
 
11050
10369
  // src/landing.ts
11051
10370
  import pc3 from "picocolors";
@@ -11152,7 +10471,7 @@ async function showLandingPage(isJson) {
11152
10471
  console.log(" Commands:");
11153
10472
  console.log(pc3.dim(" defi status Protocol overview"));
11154
10473
  console.log(pc3.dim(" defi lending rates Compare lending APYs"));
11155
- console.log(pc3.dim(" defi dex quote Get swap quotes"));
10474
+ console.log(pc3.dim(" defi lp discover Find LP farming pools"));
11156
10475
  console.log(pc3.dim(" defi portfolio View all positions"));
11157
10476
  console.log(pc3.dim(" defi scan Exploit detection"));
11158
10477
  console.log(pc3.dim(" defi --help Full command list"));
@@ -11225,30 +10544,22 @@ async function main() {
11225
10544
  const knownSubcommands = /* @__PURE__ */ new Set([
11226
10545
  "status",
11227
10546
  "schema",
11228
- "dex",
11229
- "gauge",
10547
+ "lp",
11230
10548
  "lending",
11231
10549
  "cdp",
11232
- "staking",
11233
10550
  "vault",
11234
10551
  "yield",
11235
10552
  "portfolio",
11236
10553
  "monitor",
11237
10554
  "alert",
11238
10555
  "scan",
11239
- "arb",
11240
10556
  "positions",
11241
10557
  "price",
11242
10558
  "wallet",
11243
10559
  "token",
11244
10560
  "whales",
11245
- "compare",
11246
- "swap",
11247
10561
  "bridge",
11248
- "nft",
11249
- "farm",
11250
- "farming",
11251
- "lb",
10562
+ "swap",
11252
10563
  "agent",
11253
10564
  "setup",
11254
10565
  "init"