shll-skills 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -509,7 +509,18 @@ var DEFAULT_RPC = "https://bsc-dataseed1.binance.org";
509
509
  var DEFAULT_LISTING_MANAGER = "0x1f9CE85bD0FF75acc3D92eB79f1Eb472f0865071";
510
510
  var DEFAULT_LISTING_ID = "0x733e9d959da5c1745fa507df6b47537f0945012eff3ceb4b684cd4482f2bc4d3";
511
511
  var PANCAKE_V2_ROUTER = "0x10ED43C718714eb63d5aA57B78B54704E256024E";
512
+ var PANCAKE_V3_SMART_ROUTER = "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
512
513
  var WBNB = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
514
+ var VENUS_VTOKENS = {
515
+ BNB: "0xA07c5b74C9B40447a954e1466938b865b6BBea36",
516
+ // vBNB
517
+ USDT: "0xfD5840Cd36d94D7229439859C0112a4185BC0255",
518
+ // vUSDT
519
+ USDC: "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8",
520
+ // vUSDC
521
+ BUSD: "0x95c78222B3D6e262426483D42CfA53685A67Ab9D"
522
+ // vBUSD
523
+ };
513
524
  var TOKEN_REGISTRY = {
514
525
  BNB: { address: "0x0000000000000000000000000000000000000000", decimals: 18 },
515
526
  WBNB: { address: WBNB, decimals: 18 },
@@ -647,6 +658,96 @@ var GET_AMOUNTS_OUT_ABI = [{
647
658
  outputs: [{ name: "amounts", type: "uint256[]" }],
648
659
  stateMutability: "view"
649
660
  }];
661
+ var V3_EXACT_INPUT_SINGLE_ABI = [{
662
+ type: "function",
663
+ name: "exactInputSingle",
664
+ inputs: [{
665
+ name: "params",
666
+ type: "tuple",
667
+ components: [
668
+ { name: "tokenIn", type: "address" },
669
+ { name: "tokenOut", type: "address" },
670
+ { name: "fee", type: "uint24" },
671
+ { name: "recipient", type: "address" },
672
+ { name: "amountIn", type: "uint256" },
673
+ { name: "amountOutMinimum", type: "uint256" },
674
+ { name: "sqrtPriceLimitX96", type: "uint160" }
675
+ ]
676
+ }],
677
+ outputs: [{ name: "amountOut", type: "uint256" }],
678
+ stateMutability: "payable"
679
+ }];
680
+ var V3_QUOTE_ABI = [{
681
+ type: "function",
682
+ name: "quoteExactInputSingle",
683
+ inputs: [{
684
+ name: "params",
685
+ type: "tuple",
686
+ components: [
687
+ { name: "tokenIn", type: "address" },
688
+ { name: "tokenOut", type: "address" },
689
+ { name: "amountIn", type: "uint256" },
690
+ { name: "fee", type: "uint24" },
691
+ { name: "sqrtPriceLimitX96", type: "uint160" }
692
+ ]
693
+ }],
694
+ outputs: [
695
+ { name: "amountOut", type: "uint256" },
696
+ { name: "sqrtPriceX96After", type: "uint160" },
697
+ { name: "initializedTicksCrossed", type: "uint32" },
698
+ { name: "gasEstimate", type: "uint256" }
699
+ ],
700
+ stateMutability: "nonpayable"
701
+ }];
702
+ var V3_QUOTER = "0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997";
703
+ var VTOKEN_ABI = [
704
+ // mint(uint256) — supply ERC20 tokens to Venus
705
+ {
706
+ type: "function",
707
+ name: "mint",
708
+ inputs: [{ name: "mintAmount", type: "uint256" }],
709
+ outputs: [{ name: "", type: "uint256" }],
710
+ stateMutability: "nonpayable"
711
+ },
712
+ // redeemUnderlying(uint256) — redeem by underlying amount
713
+ {
714
+ type: "function",
715
+ name: "redeemUnderlying",
716
+ inputs: [{ name: "redeemAmount", type: "uint256" }],
717
+ outputs: [{ name: "", type: "uint256" }],
718
+ stateMutability: "nonpayable"
719
+ }
720
+ ];
721
+ var VTOKEN_READ_ABI = [
722
+ {
723
+ type: "function",
724
+ name: "balanceOfUnderlying",
725
+ inputs: [{ name: "owner", type: "address" }],
726
+ outputs: [{ name: "", type: "uint256" }],
727
+ stateMutability: "view"
728
+ },
729
+ {
730
+ type: "function",
731
+ name: "supplyRatePerBlock",
732
+ inputs: [],
733
+ outputs: [{ name: "", type: "uint256" }],
734
+ stateMutability: "view"
735
+ },
736
+ {
737
+ type: "function",
738
+ name: "underlying",
739
+ inputs: [],
740
+ outputs: [{ name: "", type: "address" }],
741
+ stateMutability: "view"
742
+ }
743
+ ];
744
+ var VBNB_MINT_ABI = [{
745
+ type: "function",
746
+ name: "mint",
747
+ inputs: [],
748
+ outputs: [],
749
+ stateMutability: "payable"
750
+ }];
650
751
  function toHex(s) {
651
752
  return s.startsWith("0x") ? s : `0x${s}`;
652
753
  }
@@ -698,7 +799,7 @@ function createClient(options) {
698
799
  }
699
800
  var program = new import_commander.Command();
700
801
  program.name("shll-onchain-runner").description("Execute DeFi actions securely via SHLL AgentNFA");
701
- var swapCmd = new import_commander.Command("swap").description("Swap tokens on PancakeSwap V2").requiredOption("-f, --from <token>", "Input token (symbol or 0x address, e.g. USDC, BNB)").requiredOption("-t, --to <token>", "Output token (symbol or 0x address)").requiredOption("-a, --amount <number>", "Amount to swap (human-readable, e.g. 0.5)").option("-s, --slippage <percent>", "Slippage tolerance in percent (default: 5)", "5").option("--router <address>", "DEX router address", PANCAKE_V2_ROUTER);
802
+ var swapCmd = new import_commander.Command("swap").description("Swap tokens on PancakeSwap (auto-routes V2/V3)").requiredOption("-f, --from <token>", "Input token (symbol or 0x address, e.g. USDC, BNB)").requiredOption("-t, --to <token>", "Output token (symbol or 0x address)").requiredOption("-a, --amount <number>", "Amount to swap (human-readable, e.g. 0.5)").option("-s, --slippage <percent>", "Slippage tolerance in percent (default: 5)", "5").option("--dex <mode>", "DEX routing: auto, v2, v3 (default: auto)", "auto").option("--fee <tier>", "V3 fee tier in bps (default: 2500 = 0.25%)", "2500").option("--router <address>", "DEX router address (override)");
702
803
  addSharedOptions(swapCmd);
703
804
  swapCmd.action(async (opts) => {
704
805
  try {
@@ -710,38 +811,85 @@ swapCmd.action(async (opts) => {
710
811
  const isNativeIn = fromToken.address === "0x0000000000000000000000000000000000000000";
711
812
  const amountIn = parseAmount(opts.amount, fromToken.decimals);
712
813
  const slippage = Number(opts.slippage);
713
- const router = opts.router || PANCAKE_V2_ROUTER;
814
+ const dexMode = opts.dex || "auto";
815
+ const feeTier = Number(opts.fee || "2500");
714
816
  const deadline = BigInt(Math.floor(Date.now() / 1e3) + 20 * 60);
715
817
  const vault = await client.getVault(tokenId);
716
- const pathIn = isNativeIn ? WBNB : fromToken.address;
717
- const pathOut = toToken.address === "0x0000000000000000000000000000000000000000" ? WBNB : toToken.address;
718
- let path;
719
- if (pathIn.toLowerCase() !== WBNB.toLowerCase() && pathOut.toLowerCase() !== WBNB.toLowerCase()) {
720
- path = [pathIn, WBNB, pathOut];
721
- } else {
722
- path = [pathIn, pathOut];
723
- }
724
818
  const publicClient = (0, import_viem2.createPublicClient)({ chain: import_chains2.bsc, transport: (0, import_viem2.http)(rpcUrl) });
725
- let minOut = 0n;
726
- try {
727
- const amounts = await publicClient.readContract({
728
- address: router,
729
- abi: GET_AMOUNTS_OUT_ABI,
730
- functionName: "getAmountsOut",
731
- args: [amountIn, path]
732
- });
733
- const expectedOut = amounts[amounts.length - 1];
734
- minOut = expectedOut * BigInt(100 - slippage) / 100n;
735
- output({
736
- status: "info",
737
- message: `Quote: ${amountIn.toString()} ${opts.from} \u2192 ~${expectedOut.toString()} ${opts.to} (minOut with ${slippage}% slippage: ${minOut.toString()})`
738
- });
739
- } catch {
740
- output({ status: "error", message: "Failed to get on-chain quote. The token pair may lack liquidity." });
741
- process.exit(1);
819
+ const tokenInAddr = isNativeIn ? WBNB : fromToken.address;
820
+ const tokenOutAddr = toToken.address === "0x0000000000000000000000000000000000000000" ? WBNB : toToken.address;
821
+ let v3Quote = 0n;
822
+ let v3Available = false;
823
+ if (dexMode === "auto" || dexMode === "v3") {
824
+ try {
825
+ const v3Result = await publicClient.simulateContract({
826
+ address: V3_QUOTER,
827
+ abi: V3_QUOTE_ABI,
828
+ functionName: "quoteExactInputSingle",
829
+ args: [{
830
+ tokenIn: tokenInAddr,
831
+ tokenOut: tokenOutAddr,
832
+ amountIn,
833
+ fee: feeTier,
834
+ sqrtPriceLimitX96: 0n
835
+ }]
836
+ });
837
+ v3Quote = v3Result.result[0];
838
+ v3Available = v3Quote > 0n;
839
+ } catch {
840
+ }
742
841
  }
842
+ let v2Quote = 0n;
843
+ let v2Available = false;
844
+ const v2Router = opts.router || PANCAKE_V2_ROUTER;
845
+ if (dexMode === "auto" || dexMode === "v2") {
846
+ try {
847
+ let path;
848
+ if (tokenInAddr.toLowerCase() !== WBNB.toLowerCase() && tokenOutAddr.toLowerCase() !== WBNB.toLowerCase()) {
849
+ path = [tokenInAddr, WBNB, tokenOutAddr];
850
+ } else {
851
+ path = [tokenInAddr, tokenOutAddr];
852
+ }
853
+ const amounts = await publicClient.readContract({
854
+ address: v2Router,
855
+ abi: GET_AMOUNTS_OUT_ABI,
856
+ functionName: "getAmountsOut",
857
+ args: [amountIn, path]
858
+ });
859
+ v2Quote = amounts[amounts.length - 1];
860
+ v2Available = v2Quote > 0n;
861
+ } catch {
862
+ }
863
+ }
864
+ let useV3 = false;
865
+ if (dexMode === "v3") {
866
+ if (!v3Available) {
867
+ output({ status: "error", message: "V3 pool not available for this pair/fee tier" });
868
+ process.exit(1);
869
+ }
870
+ useV3 = true;
871
+ } else if (dexMode === "v2") {
872
+ if (!v2Available) {
873
+ output({ status: "error", message: "V2 pair not available for this token pair" });
874
+ process.exit(1);
875
+ }
876
+ useV3 = false;
877
+ } else {
878
+ if (!v3Available && !v2Available) {
879
+ output({ status: "error", message: "No liquidity found on V2 or V3 for this pair" });
880
+ process.exit(1);
881
+ }
882
+ useV3 = v3Available && (!v2Available || v3Quote >= v2Quote);
883
+ }
884
+ const selectedQuote = useV3 ? v3Quote : v2Quote;
885
+ const minOut = selectedQuote * BigInt(100 - slippage) / 100n;
886
+ output({
887
+ status: "info",
888
+ message: `Route: ${useV3 ? "V3" : "V2"} | Quote: ${amountIn.toString()} ${opts.from} \u2192 ~${selectedQuote.toString()} ${opts.to}` + (v3Available && v2Available ? ` (V3: ${v3Quote.toString()}, V2: ${v2Quote.toString()})` : "") + ` | minOut: ${minOut.toString()} (${slippage}% slippage)`
889
+ });
743
890
  const actions = [];
744
891
  if (!isNativeIn) {
892
+ const router = useV3 ? PANCAKE_V3_SMART_ROUTER : v2Router;
745
893
  try {
746
894
  const currentAllowance = await publicClient.readContract({
747
895
  address: fromToken.address,
@@ -761,25 +909,52 @@ swapCmd.action(async (opts) => {
761
909
  const approveData = (0, import_viem2.encodeFunctionData)({
762
910
  abi: ERC20_ABI,
763
911
  functionName: "approve",
764
- args: [router, amountIn]
912
+ args: [useV3 ? PANCAKE_V3_SMART_ROUTER : v2Router, amountIn]
765
913
  });
766
914
  actions.push({ target: fromToken.address, value: 0n, data: approveData });
767
915
  }
768
916
  }
769
- if (isNativeIn) {
917
+ if (useV3) {
770
918
  const data = (0, import_viem2.encodeFunctionData)({
771
- abi: SWAP_EXACT_ETH_ABI,
772
- functionName: "swapExactETHForTokens",
773
- args: [minOut, path, vault, deadline]
919
+ abi: V3_EXACT_INPUT_SINGLE_ABI,
920
+ functionName: "exactInputSingle",
921
+ args: [{
922
+ tokenIn: tokenInAddr,
923
+ tokenOut: tokenOutAddr,
924
+ fee: feeTier,
925
+ recipient: vault,
926
+ amountIn,
927
+ amountOutMinimum: minOut,
928
+ sqrtPriceLimitX96: 0n
929
+ }]
774
930
  });
775
- actions.push({ target: router, value: amountIn, data });
776
- } else {
777
- const data = (0, import_viem2.encodeFunctionData)({
778
- abi: SWAP_EXACT_TOKENS_ABI,
779
- functionName: "swapExactTokensForTokens",
780
- args: [amountIn, minOut, path, vault, deadline]
931
+ actions.push({
932
+ target: PANCAKE_V3_SMART_ROUTER,
933
+ value: isNativeIn ? amountIn : 0n,
934
+ data
781
935
  });
782
- actions.push({ target: router, value: 0n, data });
936
+ } else {
937
+ let path;
938
+ if (tokenInAddr.toLowerCase() !== WBNB.toLowerCase() && tokenOutAddr.toLowerCase() !== WBNB.toLowerCase()) {
939
+ path = [tokenInAddr, WBNB, tokenOutAddr];
940
+ } else {
941
+ path = [tokenInAddr, tokenOutAddr];
942
+ }
943
+ if (isNativeIn) {
944
+ const data = (0, import_viem2.encodeFunctionData)({
945
+ abi: SWAP_EXACT_ETH_ABI,
946
+ functionName: "swapExactETHForTokens",
947
+ args: [minOut, path, vault, deadline]
948
+ });
949
+ actions.push({ target: v2Router, value: amountIn, data });
950
+ } else {
951
+ const data = (0, import_viem2.encodeFunctionData)({
952
+ abi: SWAP_EXACT_TOKENS_ABI,
953
+ functionName: "swapExactTokensForTokens",
954
+ args: [amountIn, minOut, path, vault, deadline]
955
+ });
956
+ actions.push({ target: v2Router, value: 0n, data });
957
+ }
783
958
  }
784
959
  for (const action of actions) {
785
960
  const simResult = await client.validate(tokenId, action);
@@ -796,7 +971,7 @@ swapCmd.action(async (opts) => {
796
971
  const result = await client.executeBatch(tokenId, actions, true);
797
972
  hash = result.hash;
798
973
  }
799
- output({ status: "success", hash });
974
+ output({ status: "success", hash, dex: useV3 ? "v3" : "v2" });
800
975
  } catch (error) {
801
976
  const message = error instanceof Error ? error.message : "Unknown error";
802
977
  output({ status: "error", message });
@@ -1822,6 +1997,161 @@ statusCmd.action(async (opts) => {
1822
1997
  process.exit(1);
1823
1998
  }
1824
1999
  });
2000
+ var lendCmd = new import_commander.Command("lend").description("Supply tokens to Venus Protocol to earn yield").requiredOption("-t, --token <symbol>", "Token to supply (BNB, USDT, USDC, BUSD)").requiredOption("-a, --amount <number>", "Amount to supply (human-readable)");
2001
+ addSharedOptions(lendCmd);
2002
+ lendCmd.action(async (opts) => {
2003
+ try {
2004
+ const client = createClient(opts);
2005
+ const tokenId = BigInt(opts.tokenId);
2006
+ const rpcUrl = opts.rpc || DEFAULT_RPC;
2007
+ const publicClient = (0, import_viem2.createPublicClient)({ chain: import_chains2.bsc, transport: (0, import_viem2.http)(rpcUrl) });
2008
+ const symbol = opts.token.toUpperCase();
2009
+ const vTokenAddr = VENUS_VTOKENS[symbol];
2010
+ if (!vTokenAddr) {
2011
+ output({ status: "error", message: `Unsupported token for Venus lending: ${symbol}. Supported: ${Object.keys(VENUS_VTOKENS).join(", ")}` });
2012
+ process.exit(1);
2013
+ }
2014
+ const isBNB = symbol === "BNB";
2015
+ const tokenInfo = resolveToken(symbol);
2016
+ const amount = parseAmount(opts.amount, tokenInfo.decimals);
2017
+ const vault = await client.getVault(tokenId);
2018
+ output({ status: "info", message: `Supplying ${opts.amount} ${symbol} to Venus (vToken: ${vTokenAddr})` });
2019
+ const actions = [];
2020
+ if (isBNB) {
2021
+ const data = (0, import_viem2.encodeFunctionData)({ abi: VBNB_MINT_ABI, functionName: "mint" });
2022
+ actions.push({ target: vTokenAddr, value: amount, data });
2023
+ } else {
2024
+ const currentAllowance = await publicClient.readContract({
2025
+ address: tokenInfo.address,
2026
+ abi: ERC20_ABI,
2027
+ functionName: "allowance",
2028
+ args: [vault, vTokenAddr]
2029
+ }).catch(() => 0n);
2030
+ if (currentAllowance < amount) {
2031
+ const approveData = (0, import_viem2.encodeFunctionData)({
2032
+ abi: ERC20_ABI,
2033
+ functionName: "approve",
2034
+ args: [vTokenAddr, amount]
2035
+ });
2036
+ actions.push({ target: tokenInfo.address, value: 0n, data: approveData });
2037
+ }
2038
+ const mintData = (0, import_viem2.encodeFunctionData)({
2039
+ abi: VTOKEN_ABI,
2040
+ functionName: "mint",
2041
+ args: [amount]
2042
+ });
2043
+ actions.push({ target: vTokenAddr, value: 0n, data: mintData });
2044
+ }
2045
+ for (const action of actions) {
2046
+ const simResult = await client.validate(tokenId, action);
2047
+ if (!simResult.ok) {
2048
+ output({ status: "rejected", reason: simResult.reason });
2049
+ process.exit(0);
2050
+ }
2051
+ }
2052
+ let hash;
2053
+ if (actions.length === 1) {
2054
+ const result = await client.execute(tokenId, actions[0], true);
2055
+ hash = result.hash;
2056
+ } else {
2057
+ const result = await client.executeBatch(tokenId, actions, true);
2058
+ hash = result.hash;
2059
+ }
2060
+ output({ status: "success", hash, protocol: "venus", action: "supply", token: symbol, amount: opts.amount });
2061
+ } catch (error) {
2062
+ const message = error instanceof Error ? error.message : "Unknown error";
2063
+ output({ status: "error", message });
2064
+ process.exit(1);
2065
+ }
2066
+ });
2067
+ var redeemCmd = new import_commander.Command("redeem").description("Withdraw supplied tokens from Venus Protocol").requiredOption("-t, --token <symbol>", "Token to redeem (BNB, USDT, USDC, BUSD)").requiredOption("-a, --amount <number>", "Amount of underlying to redeem (human-readable)");
2068
+ addSharedOptions(redeemCmd);
2069
+ redeemCmd.action(async (opts) => {
2070
+ try {
2071
+ const client = createClient(opts);
2072
+ const tokenId = BigInt(opts.tokenId);
2073
+ const symbol = opts.token.toUpperCase();
2074
+ const vTokenAddr = VENUS_VTOKENS[symbol];
2075
+ if (!vTokenAddr) {
2076
+ output({ status: "error", message: `Unsupported token: ${symbol}. Supported: ${Object.keys(VENUS_VTOKENS).join(", ")}` });
2077
+ process.exit(1);
2078
+ }
2079
+ const tokenInfo = resolveToken(symbol);
2080
+ const amount = parseAmount(opts.amount, tokenInfo.decimals);
2081
+ output({ status: "info", message: `Redeeming ${opts.amount} ${symbol} from Venus` });
2082
+ const data = (0, import_viem2.encodeFunctionData)({
2083
+ abi: VTOKEN_ABI,
2084
+ functionName: "redeemUnderlying",
2085
+ args: [amount]
2086
+ });
2087
+ const action = { target: vTokenAddr, value: 0n, data };
2088
+ const simResult = await client.validate(tokenId, action);
2089
+ if (!simResult.ok) {
2090
+ output({ status: "rejected", reason: simResult.reason });
2091
+ process.exit(0);
2092
+ }
2093
+ const result = await client.execute(tokenId, action, true);
2094
+ output({ status: "success", hash: result.hash, protocol: "venus", action: "redeem", token: symbol, amount: opts.amount });
2095
+ } catch (error) {
2096
+ const message = error instanceof Error ? error.message : "Unknown error";
2097
+ output({ status: "error", message });
2098
+ process.exit(1);
2099
+ }
2100
+ });
2101
+ var lendingInfoCmd = new import_commander.Command("lending-info").description("Show Venus Protocol supply balances and APY for agent vault").requiredOption("-k, --token-id <number>", "Agent NFA Token ID").option("-r, --rpc <url>", "BSC RPC URL", DEFAULT_RPC).option("--nfa-address <address>", "AgentNFA contract address", DEFAULT_NFA).option("--guard-address <address>", "PolicyGuard contract address", DEFAULT_GUARD);
2102
+ lendingInfoCmd.action(async (opts) => {
2103
+ try {
2104
+ const rpcUrl = opts.rpc || DEFAULT_RPC;
2105
+ const publicClient = (0, import_viem2.createPublicClient)({ chain: import_chains2.bsc, transport: (0, import_viem2.http)(rpcUrl) });
2106
+ const tokenId = BigInt(opts.tokenId);
2107
+ const policyClient = createPolicyClient(opts);
2108
+ const vault = await policyClient.getVault(tokenId);
2109
+ const BLOCKS_PER_YEAR = 10512000n;
2110
+ const positions = [];
2111
+ for (const [symbol, vTokenAddr] of Object.entries(VENUS_VTOKENS)) {
2112
+ try {
2113
+ const supplied = await publicClient.readContract({
2114
+ address: vTokenAddr,
2115
+ abi: VTOKEN_READ_ABI,
2116
+ functionName: "balanceOfUnderlying",
2117
+ args: [vault]
2118
+ });
2119
+ const ratePerBlock = await publicClient.readContract({
2120
+ address: vTokenAddr,
2121
+ abi: VTOKEN_READ_ABI,
2122
+ functionName: "supplyRatePerBlock"
2123
+ });
2124
+ const rateFloat = Number(ratePerBlock) / 1e18;
2125
+ const apy = (Math.pow(1 + rateFloat, Number(BLOCKS_PER_YEAR)) - 1) * 100;
2126
+ const tokenInfo = resolveToken(symbol);
2127
+ const suppliedHuman = (Number(supplied) / Math.pow(10, tokenInfo.decimals)).toFixed(6);
2128
+ positions.push({
2129
+ token: symbol,
2130
+ vToken: vTokenAddr,
2131
+ supplied: suppliedHuman,
2132
+ suppliedRaw: supplied.toString(),
2133
+ apyPercent: apy.toFixed(2),
2134
+ hasPosition: supplied > 0n
2135
+ });
2136
+ } catch {
2137
+ positions.push({ token: symbol, vToken: vTokenAddr, error: "Failed to query" });
2138
+ }
2139
+ }
2140
+ const activePositions = positions.filter((p) => p.hasPosition);
2141
+ output({
2142
+ status: "success",
2143
+ vault,
2144
+ protocol: "venus",
2145
+ positions,
2146
+ activeCount: activePositions.length,
2147
+ totalMarkets: positions.length
2148
+ });
2149
+ } catch (error) {
2150
+ const message = error instanceof Error ? error.message : "Unknown error";
2151
+ output({ status: "error", message });
2152
+ process.exit(1);
2153
+ }
2154
+ });
1825
2155
  program.addCommand(swapCmd);
1826
2156
  program.addCommand(rawCmd);
1827
2157
  program.addCommand(tokensCmd);
@@ -1840,4 +2170,7 @@ program.addCommand(genWalletCmd);
1840
2170
  program.addCommand(balanceCmd);
1841
2171
  program.addCommand(historyCmd);
1842
2172
  program.addCommand(statusCmd);
2173
+ program.addCommand(lendCmd);
2174
+ program.addCommand(redeemCmd);
2175
+ program.addCommand(lendingInfoCmd);
1843
2176
  program.parse(process.argv);