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 +373 -40
- package/dist/index.mjs +373 -40
- package/package.json +1 -1
- package/src/index.ts +451 -46
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("--
|
|
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
|
|
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
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
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: [
|
|
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 (
|
|
917
|
+
if (useV3) {
|
|
770
918
|
const data = (0, import_viem2.encodeFunctionData)({
|
|
771
|
-
abi:
|
|
772
|
-
functionName: "
|
|
773
|
-
args: [
|
|
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({
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
-
|
|
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);
|