shll-skills 2.1.0 → 3.1.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/SKILL.md +23 -3
- package/dist/index.js +373 -40
- package/dist/index.mjs +373 -40
- package/package.json +1 -1
- package/src/index.ts +451 -46
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: shll-run
|
|
3
3
|
description: Execute DeFi transactions on BSC via SHLL AgentNFA. The AI handles all commands — users only need to chat.
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
author: SHLL Team
|
|
6
6
|
website: https://shll.run
|
|
7
7
|
twitter: https://twitter.com/shllrun
|
|
@@ -144,6 +144,9 @@ Examples:
|
|
|
144
144
|
- "Swap 0.1 BNB for USDC"
|
|
145
145
|
- "What's my portfolio?"
|
|
146
146
|
- "What's the price of CAKE?"
|
|
147
|
+
- "Lend 10 USDT on Venus"
|
|
148
|
+
- "How much am I earning on Venus?"
|
|
149
|
+
- "Redeem my USDT from Venus"
|
|
147
150
|
|
|
148
151
|
---
|
|
149
152
|
|
|
@@ -161,12 +164,22 @@ Examples:
|
|
|
161
164
|
### Trading
|
|
162
165
|
| Command | What it does |
|
|
163
166
|
|---------|-------------|
|
|
164
|
-
| `shll-run swap -f <FROM> -t <TO> -a <AMT> -k <ID>` | Token swap
|
|
167
|
+
| `shll-run swap -f <FROM> -t <TO> -a <AMT> -k <ID>` | Token swap (auto-routes PancakeSwap V2/V3) |
|
|
168
|
+
| `shll-run swap ... --dex v3 --fee 500` | Force V3 with 0.05% fee tier |
|
|
165
169
|
| `shll-run wrap -a <BNB> -k <ID>` | BNB -> WBNB |
|
|
166
170
|
| `shll-run unwrap -a <BNB> -k <ID>` | WBNB -> BNB |
|
|
167
171
|
| `shll-run transfer --token <SYM> -a <AMT> --to <ADDR> -k <ID>` | Send tokens from vault |
|
|
168
172
|
| `shll-run raw --target <ADDR> --data <HEX> -k <ID>` | Raw calldata |
|
|
169
173
|
|
|
174
|
+
### Lending (Venus Protocol)
|
|
175
|
+
| Command | What it does |
|
|
176
|
+
|---------|-------------|
|
|
177
|
+
| `shll-run lend -t <TOKEN> -a <AMT> -k <ID>` | Supply tokens to Venus to earn yield |
|
|
178
|
+
| `shll-run redeem -t <TOKEN> -a <AMT> -k <ID>` | Withdraw supplied tokens from Venus |
|
|
179
|
+
| `shll-run lending-info -k <ID>` | Show supply balances + APY across Venus markets |
|
|
180
|
+
|
|
181
|
+
Supported lending tokens: **BNB, USDT, USDC, BUSD**
|
|
182
|
+
|
|
170
183
|
### Market Data (read-only)
|
|
171
184
|
| Command | What it does |
|
|
172
185
|
|---------|-------------|
|
|
@@ -185,6 +198,9 @@ Examples:
|
|
|
185
198
|
|
|
186
199
|
**Supported tokens:** BNB, USDC, USDT, WBNB, CAKE, ETH, BTCB, DAI, BUSD, or any 0x address.
|
|
187
200
|
|
|
201
|
+
**Swap routing modes:** `--dex auto` (default: compares V2/V3 quotes), `--dex v2`, `--dex v3`.
|
|
202
|
+
**V3 fee tiers:** `--fee 100` (0.01%), `--fee 500` (0.05%), `--fee 2500` (0.25%, default), `--fee 10000` (1%).
|
|
203
|
+
|
|
188
204
|
---
|
|
189
205
|
|
|
190
206
|
## HOW TO EXPLAIN THINGS TO USERS
|
|
@@ -201,7 +217,11 @@ Examples:
|
|
|
201
217
|
### "What are policies?"
|
|
202
218
|
*"Policies are on-chain safety rules: how much you can spend per transaction, how often you can trade, which DEXs are allowed, etc. You can tighten these rules but never loosen them beyond the template ceiling."*
|
|
203
219
|
|
|
204
|
-
|
|
220
|
+
### "What is Venus Protocol?"
|
|
221
|
+
*"Venus is a decentralized lending protocol on BSC. When you 'lend' tokens to Venus, you deposit them into a supply pool and earn interest (APY). Other users borrow from the same pool and pay interest. You can withdraw (redeem) your tokens plus earned interest at any time. Your tokens stay on-chain in Venus smart contracts — SHLL does not hold them."*
|
|
222
|
+
|
|
223
|
+
### "Is lending safe?"
|
|
224
|
+
*"Venus is one of the most established protocols on BSC with over $1B TVL. However, DeFi lending always carries smart contract risk and market risk. Only lend amounts you're comfortable with. Your agent's DeFiGuard policy ensures only approved lending operations can be executed."*
|
|
205
225
|
|
|
206
226
|
## OUTPUT FORMAT
|
|
207
227
|
All commands output JSON:
|
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);
|