shll-skills 6.0.1 → 6.0.3
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 +4 -4
- package/dist/{chunk-5GV6AGSA.mjs → chunk-TH3ENDDM.mjs} +82 -85
- package/dist/index.js +82 -90
- package/dist/index.mjs +4 -9
- package/dist/mcp.js +84 -94
- package/dist/mcp.mjs +6 -13
- package/package.json +2 -2
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 and users only need to chat.
|
|
4
|
-
version: 6.0.
|
|
4
|
+
version: 6.0.2
|
|
5
5
|
author: SHLL Team
|
|
6
6
|
website: https://shll.run
|
|
7
7
|
twitter: https://twitter.com/shllrun
|
|
@@ -68,7 +68,7 @@ On-chain guardrails:
|
|
|
68
68
|
- Spending limits, cooldowns, whitelist rules, and protocol rules are enforced on-chain.
|
|
69
69
|
- Raw calldata is blocked if the recipient cannot be decoded safely.
|
|
70
70
|
|
|
71
|
-
## Current Critical Constraints (v6.0.
|
|
71
|
+
## Current Critical Constraints (v6.0.2)
|
|
72
72
|
|
|
73
73
|
1. `init` command is disabled. Do not use it.
|
|
74
74
|
2. Raw calldata remains high risk; rely on strict recipient safety checks.
|
|
@@ -146,11 +146,10 @@ Write commands include:
|
|
|
146
146
|
- `raw`
|
|
147
147
|
- `lend`
|
|
148
148
|
- `redeem`
|
|
149
|
-
- `config`
|
|
150
149
|
- `four_buy`
|
|
151
150
|
- `four_sell`
|
|
152
151
|
|
|
153
|
-
Read-only commands
|
|
152
|
+
Read-only commands (no confirmation needed): `config`, `policies`, `status`, `history`, `portfolio`, `price`, `tokens`, `search`, `balance`, `four_info`.
|
|
154
153
|
|
|
155
154
|
## CLI Commands
|
|
156
155
|
|
|
@@ -187,6 +186,7 @@ If `-l/--listing` is omitted, `setup-guide` auto-selects an active listing from
|
|
|
187
186
|
|
|
188
187
|
### Read-only and audit
|
|
189
188
|
|
|
189
|
+
- `shll-run config -k <tokenId>` (view-only; modify via web console)
|
|
190
190
|
- `shll-run portfolio -k <tokenId>`
|
|
191
191
|
- `shll-run price --token <symbolOrAddress>`
|
|
192
192
|
- `shll-run search --query <text>`
|
|
@@ -6,7 +6,7 @@ var MEV_PROTECTED_RPC = "https://bscrpc.pancakeswap.finance";
|
|
|
6
6
|
var DEFAULT_LISTING_MANAGER = "0x1f9CE85bD0FF75acc3D92eB79f1Eb472f0865071";
|
|
7
7
|
var DEFAULT_LISTING_ID = "0x64083b44e38db02749e6e16bf84ce6c19146cc42a108e53324e11f250b15a0b7";
|
|
8
8
|
var DEFAULT_INDEXER = "https://indexer-mainnet.shll.run";
|
|
9
|
-
var SKILL_VERSION = "6.0.
|
|
9
|
+
var SKILL_VERSION = "6.0.2";
|
|
10
10
|
var BINDINGS_UPDATED_AT = "2026-03-06";
|
|
11
11
|
var PANCAKE_V2_ROUTER = "0x10ED43C718714eb63d5aA57B78B54704E256024E";
|
|
12
12
|
var PANCAKE_V3_SMART_ROUTER = "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
|
|
@@ -170,16 +170,11 @@ var LISTING_MANAGER_ABI = [
|
|
|
170
170
|
}
|
|
171
171
|
];
|
|
172
172
|
var SPENDING_LIMIT_ABI = [
|
|
173
|
-
{ type: "function", name: "setLimits", inputs: [{ name: "instanceId", type: "uint256" }, { name: "maxPerTx", type: "uint256" }, { name: "maxPerDay", type: "uint256" }, { name: "maxSlippageBps", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
174
173
|
{ type: "function", name: "instanceLimits", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "maxPerTx", type: "uint256" }, { name: "maxPerDay", type: "uint256" }, { name: "maxSlippageBps", type: "uint256" }], stateMutability: "view" },
|
|
175
174
|
{ type: "function", name: "tokenRestrictionEnabled", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "view" },
|
|
176
|
-
{ type: "function", name: "getTokenList", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "address[]" }], stateMutability: "view" }
|
|
177
|
-
{ type: "function", name: "addToken", inputs: [{ name: "instanceId", type: "uint256" }, { name: "token", type: "address" }], outputs: [], stateMutability: "nonpayable" },
|
|
178
|
-
{ type: "function", name: "removeToken", inputs: [{ name: "instanceId", type: "uint256" }, { name: "token", type: "address" }], outputs: [], stateMutability: "nonpayable" },
|
|
179
|
-
{ type: "function", name: "setTokenRestriction", inputs: [{ name: "instanceId", type: "uint256" }, { name: "enabled", type: "bool" }], outputs: [], stateMutability: "nonpayable" }
|
|
175
|
+
{ type: "function", name: "getTokenList", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "address[]" }], stateMutability: "view" }
|
|
180
176
|
];
|
|
181
177
|
var COOLDOWN_ABI = [
|
|
182
|
-
{ type: "function", name: "setCooldown", inputs: [{ name: "instanceId", type: "uint256" }, { name: "seconds_", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
183
178
|
{ type: "function", name: "cooldownSeconds", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" }
|
|
184
179
|
];
|
|
185
180
|
var FOUR_MEME_HELPER_ABI = [
|
|
@@ -293,7 +288,7 @@ async function resolveTokenAsync(publicClient, input) {
|
|
|
293
288
|
}
|
|
294
289
|
|
|
295
290
|
// src/shared/clients.ts
|
|
296
|
-
import { createPublicClient as createPublicClient2,
|
|
291
|
+
import { createPublicClient as createPublicClient2, http as http2 } from "viem";
|
|
297
292
|
import { privateKeyToAccount as privateKeyToAccount2, generatePrivateKey } from "viem/accounts";
|
|
298
293
|
import { bsc as bsc2 } from "viem/chains";
|
|
299
294
|
|
|
@@ -823,14 +818,6 @@ function createClients(rpcUrl) {
|
|
|
823
818
|
});
|
|
824
819
|
return { account, publicClient, policyClient, rpc: writeRpc };
|
|
825
820
|
}
|
|
826
|
-
function createWallet(rpcUrl) {
|
|
827
|
-
const privateKey = process.env.RUNNER_PRIVATE_KEY;
|
|
828
|
-
if (!privateKey) throw new Error("RUNNER_PRIVATE_KEY environment variable is required");
|
|
829
|
-
const rpc = rpcUrl || DEFAULT_RPC;
|
|
830
|
-
const account = privateKeyToAccount2(toHex(privateKey));
|
|
831
|
-
const walletClient = createWalletClient2({ account, chain: bsc2, transport: http2(rpc) });
|
|
832
|
-
return { account, walletClient };
|
|
833
|
-
}
|
|
834
821
|
function createReadOnlyClient(rpcUrl) {
|
|
835
822
|
const rpc = rpcUrl || process.env.SHLL_RPC || DEFAULT_RPC;
|
|
836
823
|
return createPublicClient2({
|
|
@@ -990,10 +977,14 @@ function checkActionRecipientSafety(action, vault) {
|
|
|
990
977
|
function policyRejectionHelp(reason, tokenId) {
|
|
991
978
|
const consoleUrl = agentConsoleUrl(tokenId);
|
|
992
979
|
const r = reason ?? "";
|
|
980
|
+
if (r.includes("Function not allowed"))
|
|
981
|
+
return { explanation: "The contract method selector is not enabled by the active DeFi policy configuration.", action: "Enable the required protocol pack or selector in Console > Safety, or use a supported transaction path.", consoleUrl };
|
|
993
982
|
if (r.includes("Approve spender not allowed"))
|
|
994
983
|
return { explanation: "The DEX router address is not in the approved spender whitelist.", action: "Contact the Agent owner to approve this router, or use a different DEX.", consoleUrl };
|
|
995
984
|
if (r.includes("Target not allowed"))
|
|
996
985
|
return { explanation: "The contract address is not in the DeFi target whitelist.", action: "Enable the corresponding DeFi Pack in Console > Safety, or contact the Agent owner.", consoleUrl };
|
|
986
|
+
if (r.includes("Receiver must be vault"))
|
|
987
|
+
return { explanation: "Swap output recipient must be the agent vault address enforced by ReceiverGuard.", action: "Retry with recipient/to set to the vault address, or use a supported flow that routes proceeds back to the vault.", consoleUrl };
|
|
997
988
|
if (r.includes("Token not in whitelist"))
|
|
998
989
|
return { explanation: "Token restriction is ON and this token is not whitelisted.", action: `Add the token to the whitelist or disable token restriction at: ${consoleUrl}`, consoleUrl };
|
|
999
990
|
if (r.includes("Exceeds per-tx limit"))
|
|
@@ -1241,13 +1232,18 @@ function ensureRecipientSafe(result) {
|
|
|
1241
1232
|
}
|
|
1242
1233
|
}
|
|
1243
1234
|
async function validateActionsOrThrow(policyClient, tokenId, actions) {
|
|
1244
|
-
for (const action of actions) {
|
|
1235
|
+
for (const [index, action] of actions.entries()) {
|
|
1245
1236
|
const sim = await policyClient.validate(tokenId, action);
|
|
1246
1237
|
if (!sim.ok) {
|
|
1247
1238
|
throw new SkillError(
|
|
1248
1239
|
"POLICY_REJECTED",
|
|
1249
1240
|
"Policy rejected transaction",
|
|
1250
1241
|
{
|
|
1242
|
+
failedActionIndex: index,
|
|
1243
|
+
failedActionTarget: action.target,
|
|
1244
|
+
failedActionSelector: getActionSelector(action),
|
|
1245
|
+
failedActionFunction: getActionFunctionName(action),
|
|
1246
|
+
failedActionValue: action.value.toString(),
|
|
1251
1247
|
reason: sim.reason,
|
|
1252
1248
|
...policyRejectionHelp(sim.reason, tokenId.toString())
|
|
1253
1249
|
},
|
|
@@ -1267,6 +1263,37 @@ async function executeActions(policyClient, tokenId, actions) {
|
|
|
1267
1263
|
const result = await policyClient.executeBatch(tokenId, actions, true);
|
|
1268
1264
|
return { hash: result.hash };
|
|
1269
1265
|
}
|
|
1266
|
+
function getActionSelector(action) {
|
|
1267
|
+
return action.data && action.data.length >= 10 ? action.data.slice(0, 10) : "0x";
|
|
1268
|
+
}
|
|
1269
|
+
function getActionFunctionName(action) {
|
|
1270
|
+
if (action.data === "0x") {
|
|
1271
|
+
return "nativeTransfer";
|
|
1272
|
+
}
|
|
1273
|
+
const candidates = [
|
|
1274
|
+
ERC20_ABI,
|
|
1275
|
+
WBNB_ABI,
|
|
1276
|
+
VTOKEN_ABI,
|
|
1277
|
+
VBNB_MINT_ABI,
|
|
1278
|
+
SWAP_EXACT_ETH_ABI,
|
|
1279
|
+
SWAP_EXACT_TOKENS_ABI,
|
|
1280
|
+
SWAP_EXACT_TOKENS_FOR_ETH_ABI,
|
|
1281
|
+
SWAP_EXACT_ETH_FOR_TOKENS_FEE_ABI,
|
|
1282
|
+
SWAP_EXACT_TOKENS_FOR_TOKENS_FEE_ABI,
|
|
1283
|
+
SWAP_EXACT_TOKENS_FOR_ETH_FEE_ABI,
|
|
1284
|
+
V3_EXACT_INPUT_SINGLE_ABI,
|
|
1285
|
+
V3_EXACT_INPUT_ABI,
|
|
1286
|
+
FOUR_MEME_V1_ABI,
|
|
1287
|
+
FOUR_MEME_V2_ABI
|
|
1288
|
+
];
|
|
1289
|
+
for (const abi of candidates) {
|
|
1290
|
+
const decoded = tryDecodeCalldata(abi, action.data);
|
|
1291
|
+
if (decoded?.functionName) {
|
|
1292
|
+
return decoded.functionName;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return "unknown";
|
|
1296
|
+
}
|
|
1270
1297
|
|
|
1271
1298
|
// src/services/info.ts
|
|
1272
1299
|
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
@@ -1705,25 +1732,6 @@ async function executeSwap(input) {
|
|
|
1705
1732
|
args: [minOut, path, vault, deadline]
|
|
1706
1733
|
})
|
|
1707
1734
|
});
|
|
1708
|
-
} else if (isNativeOut) {
|
|
1709
|
-
actions.push({
|
|
1710
|
-
target: tokenIn.address,
|
|
1711
|
-
value: 0n,
|
|
1712
|
-
data: encodeFunctionData2({
|
|
1713
|
-
abi: ERC20_ABI,
|
|
1714
|
-
functionName: "approve",
|
|
1715
|
-
args: [v2Router, amountIn]
|
|
1716
|
-
})
|
|
1717
|
-
});
|
|
1718
|
-
actions.push({
|
|
1719
|
-
target: v2Router,
|
|
1720
|
-
value: 0n,
|
|
1721
|
-
data: encodeFunctionData2({
|
|
1722
|
-
abi: SWAP_EXACT_TOKENS_FOR_ETH_ABI,
|
|
1723
|
-
functionName: "swapExactTokensForETH",
|
|
1724
|
-
args: [amountIn, minOut, path, vault, deadline]
|
|
1725
|
-
})
|
|
1726
|
-
});
|
|
1727
1735
|
} else {
|
|
1728
1736
|
actions.push({
|
|
1729
1737
|
target: tokenIn.address,
|
|
@@ -1738,8 +1746,8 @@ async function executeSwap(input) {
|
|
|
1738
1746
|
target: v2Router,
|
|
1739
1747
|
value: 0n,
|
|
1740
1748
|
data: encodeFunctionData2({
|
|
1741
|
-
abi:
|
|
1742
|
-
functionName: "
|
|
1749
|
+
abi: SWAP_EXACT_TOKENS_FOR_TOKENS_FEE_ABI,
|
|
1750
|
+
functionName: "swapExactTokensForTokensSupportingFeeOnTransferTokens",
|
|
1743
1751
|
args: [amountIn, minOut, path, vault, deadline]
|
|
1744
1752
|
})
|
|
1745
1753
|
});
|
|
@@ -2417,59 +2425,48 @@ async function getHistory(tokenIdRaw, limit) {
|
|
|
2417
2425
|
recentPolicyRejections: rejections.length
|
|
2418
2426
|
};
|
|
2419
2427
|
}
|
|
2420
|
-
async function
|
|
2421
|
-
if (!options.txLimit && !options.dailyLimit && !options.cooldown) {
|
|
2422
|
-
throw new SkillError("INVALID_INPUT", "Must specify at least one policy config option");
|
|
2423
|
-
}
|
|
2428
|
+
async function getPolicyConfigGuidance(tokenIdRaw, rpcUrl) {
|
|
2424
2429
|
const tokenId = parseTokenId(tokenIdRaw);
|
|
2425
|
-
const { publicClient, policyClient
|
|
2426
|
-
await ensureAccess(tokenId, rpc, publicClient);
|
|
2427
|
-
const { walletClient } = createWallet(rpc);
|
|
2430
|
+
const { publicClient, policyClient } = createClients(rpcUrl);
|
|
2428
2431
|
const policies = await policyClient.getPolicies(tokenId);
|
|
2429
|
-
const
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2432
|
+
const consoleUrl = agentConsoleUrl(tokenId);
|
|
2433
|
+
const currentConfig = {};
|
|
2434
|
+
const spendingPolicy = policies.find((p) => p.policyTypeName === "spending_limit");
|
|
2435
|
+
if (spendingPolicy) {
|
|
2436
|
+
try {
|
|
2437
|
+
const [maxPerTx, maxPerDay, maxSlippageBps] = await publicClient.readContract({
|
|
2438
|
+
address: spendingPolicy.address,
|
|
2439
|
+
abi: SPENDING_LIMIT_ABI,
|
|
2440
|
+
functionName: "instanceLimits",
|
|
2441
|
+
args: [tokenId]
|
|
2442
|
+
});
|
|
2443
|
+
currentConfig.spendingLimit = {
|
|
2444
|
+
maxPerTx: (Number(maxPerTx) / 1e18).toFixed(4) + " BNB",
|
|
2445
|
+
maxPerDay: (Number(maxPerDay) / 1e18).toFixed(4) + " BNB",
|
|
2446
|
+
slippageBps: maxSlippageBps.toString()
|
|
2447
|
+
};
|
|
2448
|
+
} catch {
|
|
2434
2449
|
}
|
|
2435
|
-
const current = await publicClient.readContract({
|
|
2436
|
-
address: spendingPolicy.address,
|
|
2437
|
-
abi: SPENDING_LIMIT_ABI,
|
|
2438
|
-
functionName: "instanceLimits",
|
|
2439
|
-
args: [tokenId]
|
|
2440
|
-
});
|
|
2441
|
-
const [curMaxPerTx, curMaxPerDay, curSlippage] = current;
|
|
2442
|
-
const hash = await walletClient.writeContract({
|
|
2443
|
-
address: spendingPolicy.address,
|
|
2444
|
-
abi: SPENDING_LIMIT_ABI,
|
|
2445
|
-
functionName: "setLimits",
|
|
2446
|
-
args: [
|
|
2447
|
-
tokenId,
|
|
2448
|
-
options.txLimit ? parseAmount(options.txLimit, 18) : curMaxPerTx,
|
|
2449
|
-
options.dailyLimit ? parseAmount(options.dailyLimit, 18) : curMaxPerDay,
|
|
2450
|
-
curSlippage
|
|
2451
|
-
]
|
|
2452
|
-
});
|
|
2453
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2454
|
-
results.push(`SpendingLimit updated: ${hash}`);
|
|
2455
2450
|
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2451
|
+
const cooldownPolicy = policies.find((p) => p.policyTypeName === "cooldown");
|
|
2452
|
+
if (cooldownPolicy) {
|
|
2453
|
+
try {
|
|
2454
|
+
const cooldown = await publicClient.readContract({
|
|
2455
|
+
address: cooldownPolicy.address,
|
|
2456
|
+
abi: COOLDOWN_ABI,
|
|
2457
|
+
functionName: "cooldownSeconds",
|
|
2458
|
+
args: [tokenId]
|
|
2459
|
+
});
|
|
2460
|
+
currentConfig.cooldown = { seconds: Number(cooldown) };
|
|
2461
|
+
} catch {
|
|
2460
2462
|
}
|
|
2461
|
-
const hash = await walletClient.writeContract({
|
|
2462
|
-
address: cooldownPolicy.address,
|
|
2463
|
-
abi: COOLDOWN_ABI,
|
|
2464
|
-
functionName: "setCooldown",
|
|
2465
|
-
args: [tokenId, BigInt(options.cooldown)]
|
|
2466
|
-
});
|
|
2467
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2468
|
-
results.push(`Cooldown updated: ${hash}`);
|
|
2469
2463
|
}
|
|
2470
2464
|
return {
|
|
2471
|
-
status: "
|
|
2472
|
-
|
|
2465
|
+
status: "read_only",
|
|
2466
|
+
tokenId: tokenIdRaw,
|
|
2467
|
+
currentConfig,
|
|
2468
|
+
message: "Policy modification via Skills is disabled. Please use the web console to adjust your security settings.",
|
|
2469
|
+
consoleUrl
|
|
2473
2470
|
};
|
|
2474
2471
|
}
|
|
2475
2472
|
function dedupeStrings(values) {
|
|
@@ -2507,5 +2504,5 @@ export {
|
|
|
2507
2504
|
readTokenRestriction,
|
|
2508
2505
|
getStatusOverview,
|
|
2509
2506
|
getHistory,
|
|
2510
|
-
|
|
2507
|
+
getPolicyConfigGuidance
|
|
2511
2508
|
};
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var MEV_PROTECTED_RPC = "https://bscrpc.pancakeswap.finance";
|
|
|
12
12
|
var DEFAULT_LISTING_MANAGER = "0x1f9CE85bD0FF75acc3D92eB79f1Eb472f0865071";
|
|
13
13
|
var DEFAULT_LISTING_ID = "0x64083b44e38db02749e6e16bf84ce6c19146cc42a108e53324e11f250b15a0b7";
|
|
14
14
|
var DEFAULT_INDEXER = "https://indexer-mainnet.shll.run";
|
|
15
|
-
var SKILL_VERSION = "6.0.
|
|
15
|
+
var SKILL_VERSION = "6.0.2";
|
|
16
16
|
var BINDINGS_UPDATED_AT = "2026-03-06";
|
|
17
17
|
var PANCAKE_V2_ROUTER = "0x10ED43C718714eb63d5aA57B78B54704E256024E";
|
|
18
18
|
var PANCAKE_V3_SMART_ROUTER = "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
|
|
@@ -176,16 +176,11 @@ var LISTING_MANAGER_ABI = [
|
|
|
176
176
|
}
|
|
177
177
|
];
|
|
178
178
|
var SPENDING_LIMIT_ABI = [
|
|
179
|
-
{ type: "function", name: "setLimits", inputs: [{ name: "instanceId", type: "uint256" }, { name: "maxPerTx", type: "uint256" }, { name: "maxPerDay", type: "uint256" }, { name: "maxSlippageBps", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
180
179
|
{ type: "function", name: "instanceLimits", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "maxPerTx", type: "uint256" }, { name: "maxPerDay", type: "uint256" }, { name: "maxSlippageBps", type: "uint256" }], stateMutability: "view" },
|
|
181
180
|
{ type: "function", name: "tokenRestrictionEnabled", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "view" },
|
|
182
|
-
{ type: "function", name: "getTokenList", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "address[]" }], stateMutability: "view" }
|
|
183
|
-
{ type: "function", name: "addToken", inputs: [{ name: "instanceId", type: "uint256" }, { name: "token", type: "address" }], outputs: [], stateMutability: "nonpayable" },
|
|
184
|
-
{ type: "function", name: "removeToken", inputs: [{ name: "instanceId", type: "uint256" }, { name: "token", type: "address" }], outputs: [], stateMutability: "nonpayable" },
|
|
185
|
-
{ type: "function", name: "setTokenRestriction", inputs: [{ name: "instanceId", type: "uint256" }, { name: "enabled", type: "bool" }], outputs: [], stateMutability: "nonpayable" }
|
|
181
|
+
{ type: "function", name: "getTokenList", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "address[]" }], stateMutability: "view" }
|
|
186
182
|
];
|
|
187
183
|
var COOLDOWN_ABI = [
|
|
188
|
-
{ type: "function", name: "setCooldown", inputs: [{ name: "instanceId", type: "uint256" }, { name: "seconds_", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
189
184
|
{ type: "function", name: "cooldownSeconds", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" }
|
|
190
185
|
];
|
|
191
186
|
var FOUR_MEME_HELPER_ABI = [
|
|
@@ -825,14 +820,6 @@ function createClients(rpcUrl) {
|
|
|
825
820
|
});
|
|
826
821
|
return { account, publicClient, policyClient, rpc: writeRpc };
|
|
827
822
|
}
|
|
828
|
-
function createWallet(rpcUrl) {
|
|
829
|
-
const privateKey = process.env.RUNNER_PRIVATE_KEY;
|
|
830
|
-
if (!privateKey) throw new Error("RUNNER_PRIVATE_KEY environment variable is required");
|
|
831
|
-
const rpc = rpcUrl || DEFAULT_RPC;
|
|
832
|
-
const account = (0, import_accounts2.privateKeyToAccount)(toHex(privateKey));
|
|
833
|
-
const walletClient = (0, import_viem2.createWalletClient)({ account, chain: import_chains2.bsc, transport: (0, import_viem2.http)(rpc) });
|
|
834
|
-
return { account, walletClient };
|
|
835
|
-
}
|
|
836
823
|
function createReadOnlyClient(rpcUrl) {
|
|
837
824
|
const rpc = rpcUrl || process.env.SHLL_RPC || DEFAULT_RPC;
|
|
838
825
|
return (0, import_viem2.createPublicClient)({
|
|
@@ -992,10 +979,14 @@ function checkActionRecipientSafety(action, vault) {
|
|
|
992
979
|
function policyRejectionHelp(reason, tokenId) {
|
|
993
980
|
const consoleUrl = agentConsoleUrl(tokenId);
|
|
994
981
|
const r = reason ?? "";
|
|
982
|
+
if (r.includes("Function not allowed"))
|
|
983
|
+
return { explanation: "The contract method selector is not enabled by the active DeFi policy configuration.", action: "Enable the required protocol pack or selector in Console > Safety, or use a supported transaction path.", consoleUrl };
|
|
995
984
|
if (r.includes("Approve spender not allowed"))
|
|
996
985
|
return { explanation: "The DEX router address is not in the approved spender whitelist.", action: "Contact the Agent owner to approve this router, or use a different DEX.", consoleUrl };
|
|
997
986
|
if (r.includes("Target not allowed"))
|
|
998
987
|
return { explanation: "The contract address is not in the DeFi target whitelist.", action: "Enable the corresponding DeFi Pack in Console > Safety, or contact the Agent owner.", consoleUrl };
|
|
988
|
+
if (r.includes("Receiver must be vault"))
|
|
989
|
+
return { explanation: "Swap output recipient must be the agent vault address enforced by ReceiverGuard.", action: "Retry with recipient/to set to the vault address, or use a supported flow that routes proceeds back to the vault.", consoleUrl };
|
|
999
990
|
if (r.includes("Token not in whitelist"))
|
|
1000
991
|
return { explanation: "Token restriction is ON and this token is not whitelisted.", action: `Add the token to the whitelist or disable token restriction at: ${consoleUrl}`, consoleUrl };
|
|
1001
992
|
if (r.includes("Exceeds per-tx limit"))
|
|
@@ -1262,13 +1253,18 @@ function ensureRecipientSafe(result) {
|
|
|
1262
1253
|
}
|
|
1263
1254
|
}
|
|
1264
1255
|
async function validateActionsOrThrow(policyClient, tokenId, actions) {
|
|
1265
|
-
for (const action of actions) {
|
|
1256
|
+
for (const [index, action] of actions.entries()) {
|
|
1266
1257
|
const sim = await policyClient.validate(tokenId, action);
|
|
1267
1258
|
if (!sim.ok) {
|
|
1268
1259
|
throw new SkillError(
|
|
1269
1260
|
"POLICY_REJECTED",
|
|
1270
1261
|
"Policy rejected transaction",
|
|
1271
1262
|
{
|
|
1263
|
+
failedActionIndex: index,
|
|
1264
|
+
failedActionTarget: action.target,
|
|
1265
|
+
failedActionSelector: getActionSelector(action),
|
|
1266
|
+
failedActionFunction: getActionFunctionName(action),
|
|
1267
|
+
failedActionValue: action.value.toString(),
|
|
1272
1268
|
reason: sim.reason,
|
|
1273
1269
|
...policyRejectionHelp(sim.reason, tokenId.toString())
|
|
1274
1270
|
},
|
|
@@ -1288,6 +1284,37 @@ async function executeActions(policyClient, tokenId, actions) {
|
|
|
1288
1284
|
const result = await policyClient.executeBatch(tokenId, actions, true);
|
|
1289
1285
|
return { hash: result.hash };
|
|
1290
1286
|
}
|
|
1287
|
+
function getActionSelector(action) {
|
|
1288
|
+
return action.data && action.data.length >= 10 ? action.data.slice(0, 10) : "0x";
|
|
1289
|
+
}
|
|
1290
|
+
function getActionFunctionName(action) {
|
|
1291
|
+
if (action.data === "0x") {
|
|
1292
|
+
return "nativeTransfer";
|
|
1293
|
+
}
|
|
1294
|
+
const candidates = [
|
|
1295
|
+
ERC20_ABI,
|
|
1296
|
+
WBNB_ABI,
|
|
1297
|
+
VTOKEN_ABI,
|
|
1298
|
+
VBNB_MINT_ABI,
|
|
1299
|
+
SWAP_EXACT_ETH_ABI,
|
|
1300
|
+
SWAP_EXACT_TOKENS_ABI,
|
|
1301
|
+
SWAP_EXACT_TOKENS_FOR_ETH_ABI,
|
|
1302
|
+
SWAP_EXACT_ETH_FOR_TOKENS_FEE_ABI,
|
|
1303
|
+
SWAP_EXACT_TOKENS_FOR_TOKENS_FEE_ABI,
|
|
1304
|
+
SWAP_EXACT_TOKENS_FOR_ETH_FEE_ABI,
|
|
1305
|
+
V3_EXACT_INPUT_SINGLE_ABI,
|
|
1306
|
+
V3_EXACT_INPUT_ABI,
|
|
1307
|
+
FOUR_MEME_V1_ABI,
|
|
1308
|
+
FOUR_MEME_V2_ABI
|
|
1309
|
+
];
|
|
1310
|
+
for (const abi of candidates) {
|
|
1311
|
+
const decoded = tryDecodeCalldata(abi, action.data);
|
|
1312
|
+
if (decoded?.functionName) {
|
|
1313
|
+
return decoded.functionName;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return "unknown";
|
|
1317
|
+
}
|
|
1291
1318
|
|
|
1292
1319
|
// src/services/info.ts
|
|
1293
1320
|
var import_viem5 = require("viem");
|
|
@@ -1729,25 +1756,6 @@ async function executeSwap(input) {
|
|
|
1729
1756
|
args: [minOut, path, vault, deadline]
|
|
1730
1757
|
})
|
|
1731
1758
|
});
|
|
1732
|
-
} else if (isNativeOut) {
|
|
1733
|
-
actions.push({
|
|
1734
|
-
target: tokenIn.address,
|
|
1735
|
-
value: 0n,
|
|
1736
|
-
data: (0, import_viem7.encodeFunctionData)({
|
|
1737
|
-
abi: ERC20_ABI,
|
|
1738
|
-
functionName: "approve",
|
|
1739
|
-
args: [v2Router, amountIn]
|
|
1740
|
-
})
|
|
1741
|
-
});
|
|
1742
|
-
actions.push({
|
|
1743
|
-
target: v2Router,
|
|
1744
|
-
value: 0n,
|
|
1745
|
-
data: (0, import_viem7.encodeFunctionData)({
|
|
1746
|
-
abi: SWAP_EXACT_TOKENS_FOR_ETH_ABI,
|
|
1747
|
-
functionName: "swapExactTokensForETH",
|
|
1748
|
-
args: [amountIn, minOut, path, vault, deadline]
|
|
1749
|
-
})
|
|
1750
|
-
});
|
|
1751
1759
|
} else {
|
|
1752
1760
|
actions.push({
|
|
1753
1761
|
target: tokenIn.address,
|
|
@@ -1762,8 +1770,8 @@ async function executeSwap(input) {
|
|
|
1762
1770
|
target: v2Router,
|
|
1763
1771
|
value: 0n,
|
|
1764
1772
|
data: (0, import_viem7.encodeFunctionData)({
|
|
1765
|
-
abi:
|
|
1766
|
-
functionName: "
|
|
1773
|
+
abi: SWAP_EXACT_TOKENS_FOR_TOKENS_FEE_ABI,
|
|
1774
|
+
functionName: "swapExactTokensForTokensSupportingFeeOnTransferTokens",
|
|
1767
1775
|
args: [amountIn, minOut, path, vault, deadline]
|
|
1768
1776
|
})
|
|
1769
1777
|
});
|
|
@@ -2360,59 +2368,48 @@ async function getHistory(tokenIdRaw, limit) {
|
|
|
2360
2368
|
recentPolicyRejections: rejections.length
|
|
2361
2369
|
};
|
|
2362
2370
|
}
|
|
2363
|
-
async function
|
|
2364
|
-
if (!options.txLimit && !options.dailyLimit && !options.cooldown) {
|
|
2365
|
-
throw new SkillError("INVALID_INPUT", "Must specify at least one policy config option");
|
|
2366
|
-
}
|
|
2371
|
+
async function getPolicyConfigGuidance(tokenIdRaw, rpcUrl) {
|
|
2367
2372
|
const tokenId = parseTokenId(tokenIdRaw);
|
|
2368
|
-
const { publicClient, policyClient
|
|
2369
|
-
await ensureAccess(tokenId, rpc, publicClient);
|
|
2370
|
-
const { walletClient } = createWallet(rpc);
|
|
2373
|
+
const { publicClient, policyClient } = createClients(rpcUrl);
|
|
2371
2374
|
const policies = await policyClient.getPolicies(tokenId);
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2375
|
+
const consoleUrl = agentConsoleUrl(tokenId);
|
|
2376
|
+
const currentConfig = {};
|
|
2377
|
+
const spendingPolicy = policies.find((p) => p.policyTypeName === "spending_limit");
|
|
2378
|
+
if (spendingPolicy) {
|
|
2379
|
+
try {
|
|
2380
|
+
const [maxPerTx, maxPerDay, maxSlippageBps] = await publicClient.readContract({
|
|
2381
|
+
address: spendingPolicy.address,
|
|
2382
|
+
abi: SPENDING_LIMIT_ABI,
|
|
2383
|
+
functionName: "instanceLimits",
|
|
2384
|
+
args: [tokenId]
|
|
2385
|
+
});
|
|
2386
|
+
currentConfig.spendingLimit = {
|
|
2387
|
+
maxPerTx: (Number(maxPerTx) / 1e18).toFixed(4) + " BNB",
|
|
2388
|
+
maxPerDay: (Number(maxPerDay) / 1e18).toFixed(4) + " BNB",
|
|
2389
|
+
slippageBps: maxSlippageBps.toString()
|
|
2390
|
+
};
|
|
2391
|
+
} catch {
|
|
2377
2392
|
}
|
|
2378
|
-
const current = await publicClient.readContract({
|
|
2379
|
-
address: spendingPolicy.address,
|
|
2380
|
-
abi: SPENDING_LIMIT_ABI,
|
|
2381
|
-
functionName: "instanceLimits",
|
|
2382
|
-
args: [tokenId]
|
|
2383
|
-
});
|
|
2384
|
-
const [curMaxPerTx, curMaxPerDay, curSlippage] = current;
|
|
2385
|
-
const hash = await walletClient.writeContract({
|
|
2386
|
-
address: spendingPolicy.address,
|
|
2387
|
-
abi: SPENDING_LIMIT_ABI,
|
|
2388
|
-
functionName: "setLimits",
|
|
2389
|
-
args: [
|
|
2390
|
-
tokenId,
|
|
2391
|
-
options.txLimit ? parseAmount(options.txLimit, 18) : curMaxPerTx,
|
|
2392
|
-
options.dailyLimit ? parseAmount(options.dailyLimit, 18) : curMaxPerDay,
|
|
2393
|
-
curSlippage
|
|
2394
|
-
]
|
|
2395
|
-
});
|
|
2396
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2397
|
-
results.push(`SpendingLimit updated: ${hash}`);
|
|
2398
2393
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2394
|
+
const cooldownPolicy = policies.find((p) => p.policyTypeName === "cooldown");
|
|
2395
|
+
if (cooldownPolicy) {
|
|
2396
|
+
try {
|
|
2397
|
+
const cooldown = await publicClient.readContract({
|
|
2398
|
+
address: cooldownPolicy.address,
|
|
2399
|
+
abi: COOLDOWN_ABI,
|
|
2400
|
+
functionName: "cooldownSeconds",
|
|
2401
|
+
args: [tokenId]
|
|
2402
|
+
});
|
|
2403
|
+
currentConfig.cooldown = { seconds: Number(cooldown) };
|
|
2404
|
+
} catch {
|
|
2403
2405
|
}
|
|
2404
|
-
const hash = await walletClient.writeContract({
|
|
2405
|
-
address: cooldownPolicy.address,
|
|
2406
|
-
abi: COOLDOWN_ABI,
|
|
2407
|
-
functionName: "setCooldown",
|
|
2408
|
-
args: [tokenId, BigInt(options.cooldown)]
|
|
2409
|
-
});
|
|
2410
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2411
|
-
results.push(`Cooldown updated: ${hash}`);
|
|
2412
2406
|
}
|
|
2413
2407
|
return {
|
|
2414
|
-
status: "
|
|
2415
|
-
|
|
2408
|
+
status: "read_only",
|
|
2409
|
+
tokenId: tokenIdRaw,
|
|
2410
|
+
currentConfig,
|
|
2411
|
+
message: "Policy modification via Skills is disabled. Please use the web console to adjust your security settings.",
|
|
2412
|
+
consoleUrl
|
|
2416
2413
|
};
|
|
2417
2414
|
}
|
|
2418
2415
|
function dedupeStrings(values) {
|
|
@@ -2582,15 +2579,10 @@ function registerAgentCommands(program2) {
|
|
|
2582
2579
|
process.exit(1);
|
|
2583
2580
|
}
|
|
2584
2581
|
});
|
|
2585
|
-
const configCmd = new import_commander5.Command("config").description("
|
|
2582
|
+
const configCmd = new import_commander5.Command("config").description("View current risk parameters (modify via web console)");
|
|
2586
2583
|
addSharedOptions(configCmd).action(async (opts) => {
|
|
2587
2584
|
try {
|
|
2588
|
-
output(await
|
|
2589
|
-
txLimit: opts.txLimit,
|
|
2590
|
-
dailyLimit: opts.dailyLimit,
|
|
2591
|
-
cooldown: opts.cooldown,
|
|
2592
|
-
rpcUrl: opts.rpc
|
|
2593
|
-
}));
|
|
2585
|
+
output(await getPolicyConfigGuidance(opts.tokenId, opts.rpc));
|
|
2594
2586
|
} catch (error) {
|
|
2595
2587
|
outputError(error);
|
|
2596
2588
|
process.exit(1);
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getBalance,
|
|
12
12
|
getFourMemeInfo,
|
|
13
13
|
getHistory,
|
|
14
|
+
getPolicyConfigGuidance,
|
|
14
15
|
getPolicySummary,
|
|
15
16
|
getPortfolio,
|
|
16
17
|
getPrice,
|
|
@@ -25,9 +26,8 @@ import {
|
|
|
25
26
|
toErrorPayload,
|
|
26
27
|
transferFromVault,
|
|
27
28
|
unwrapWbnb,
|
|
28
|
-
updateRiskConfig,
|
|
29
29
|
wrapBnb
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-TH3ENDDM.mjs";
|
|
31
31
|
|
|
32
32
|
// src/index.ts
|
|
33
33
|
import { Command as Command9 } from "commander";
|
|
@@ -226,15 +226,10 @@ function registerAgentCommands(program2) {
|
|
|
226
226
|
process.exit(1);
|
|
227
227
|
}
|
|
228
228
|
});
|
|
229
|
-
const configCmd = new Command5("config").description("
|
|
229
|
+
const configCmd = new Command5("config").description("View current risk parameters (modify via web console)");
|
|
230
230
|
addSharedOptions(configCmd).action(async (opts) => {
|
|
231
231
|
try {
|
|
232
|
-
output(await
|
|
233
|
-
txLimit: opts.txLimit,
|
|
234
|
-
dailyLimit: opts.dailyLimit,
|
|
235
|
-
cooldown: opts.cooldown,
|
|
236
|
-
rpcUrl: opts.rpc
|
|
237
|
-
}));
|
|
232
|
+
output(await getPolicyConfigGuidance(opts.tokenId, opts.rpc));
|
|
238
233
|
} catch (error) {
|
|
239
234
|
outputError(error);
|
|
240
235
|
process.exit(1);
|
package/dist/mcp.js
CHANGED
|
@@ -12,7 +12,7 @@ var MEV_PROTECTED_RPC = "https://bscrpc.pancakeswap.finance";
|
|
|
12
12
|
var DEFAULT_LISTING_MANAGER = "0x1f9CE85bD0FF75acc3D92eB79f1Eb472f0865071";
|
|
13
13
|
var DEFAULT_LISTING_ID = "0x64083b44e38db02749e6e16bf84ce6c19146cc42a108e53324e11f250b15a0b7";
|
|
14
14
|
var DEFAULT_INDEXER = "https://indexer-mainnet.shll.run";
|
|
15
|
-
var SKILL_VERSION = "6.0.
|
|
15
|
+
var SKILL_VERSION = "6.0.2";
|
|
16
16
|
var PANCAKE_V2_ROUTER = "0x10ED43C718714eb63d5aA57B78B54704E256024E";
|
|
17
17
|
var PANCAKE_V3_SMART_ROUTER = "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
|
|
18
18
|
var V3_QUOTER = "0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997";
|
|
@@ -175,16 +175,11 @@ var LISTING_MANAGER_ABI = [
|
|
|
175
175
|
}
|
|
176
176
|
];
|
|
177
177
|
var SPENDING_LIMIT_ABI = [
|
|
178
|
-
{ type: "function", name: "setLimits", inputs: [{ name: "instanceId", type: "uint256" }, { name: "maxPerTx", type: "uint256" }, { name: "maxPerDay", type: "uint256" }, { name: "maxSlippageBps", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
179
178
|
{ type: "function", name: "instanceLimits", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "maxPerTx", type: "uint256" }, { name: "maxPerDay", type: "uint256" }, { name: "maxSlippageBps", type: "uint256" }], stateMutability: "view" },
|
|
180
179
|
{ type: "function", name: "tokenRestrictionEnabled", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "view" },
|
|
181
|
-
{ type: "function", name: "getTokenList", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "address[]" }], stateMutability: "view" }
|
|
182
|
-
{ type: "function", name: "addToken", inputs: [{ name: "instanceId", type: "uint256" }, { name: "token", type: "address" }], outputs: [], stateMutability: "nonpayable" },
|
|
183
|
-
{ type: "function", name: "removeToken", inputs: [{ name: "instanceId", type: "uint256" }, { name: "token", type: "address" }], outputs: [], stateMutability: "nonpayable" },
|
|
184
|
-
{ type: "function", name: "setTokenRestriction", inputs: [{ name: "instanceId", type: "uint256" }, { name: "enabled", type: "bool" }], outputs: [], stateMutability: "nonpayable" }
|
|
180
|
+
{ type: "function", name: "getTokenList", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "address[]" }], stateMutability: "view" }
|
|
185
181
|
];
|
|
186
182
|
var COOLDOWN_ABI = [
|
|
187
|
-
{ type: "function", name: "setCooldown", inputs: [{ name: "instanceId", type: "uint256" }, { name: "seconds_", type: "uint256" }], outputs: [], stateMutability: "nonpayable" },
|
|
188
183
|
{ type: "function", name: "cooldownSeconds", inputs: [{ name: "instanceId", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" }
|
|
189
184
|
];
|
|
190
185
|
var FOUR_MEME_HELPER_ABI = [
|
|
@@ -824,14 +819,6 @@ function createClients(rpcUrl) {
|
|
|
824
819
|
});
|
|
825
820
|
return { account, publicClient, policyClient, rpc: writeRpc };
|
|
826
821
|
}
|
|
827
|
-
function createWallet(rpcUrl) {
|
|
828
|
-
const privateKey = process.env.RUNNER_PRIVATE_KEY;
|
|
829
|
-
if (!privateKey) throw new Error("RUNNER_PRIVATE_KEY environment variable is required");
|
|
830
|
-
const rpc = rpcUrl || DEFAULT_RPC;
|
|
831
|
-
const account = (0, import_accounts2.privateKeyToAccount)(toHex(privateKey));
|
|
832
|
-
const walletClient = (0, import_viem2.createWalletClient)({ account, chain: import_chains2.bsc, transport: (0, import_viem2.http)(rpc) });
|
|
833
|
-
return { account, walletClient };
|
|
834
|
-
}
|
|
835
822
|
function createReadOnlyClient(rpcUrl) {
|
|
836
823
|
const rpc = rpcUrl || process.env.SHLL_RPC || DEFAULT_RPC;
|
|
837
824
|
return (0, import_viem2.createPublicClient)({
|
|
@@ -991,10 +978,14 @@ function checkActionRecipientSafety(action, vault) {
|
|
|
991
978
|
function policyRejectionHelp(reason, tokenId) {
|
|
992
979
|
const consoleUrl = agentConsoleUrl(tokenId);
|
|
993
980
|
const r = reason ?? "";
|
|
981
|
+
if (r.includes("Function not allowed"))
|
|
982
|
+
return { explanation: "The contract method selector is not enabled by the active DeFi policy configuration.", action: "Enable the required protocol pack or selector in Console > Safety, or use a supported transaction path.", consoleUrl };
|
|
994
983
|
if (r.includes("Approve spender not allowed"))
|
|
995
984
|
return { explanation: "The DEX router address is not in the approved spender whitelist.", action: "Contact the Agent owner to approve this router, or use a different DEX.", consoleUrl };
|
|
996
985
|
if (r.includes("Target not allowed"))
|
|
997
986
|
return { explanation: "The contract address is not in the DeFi target whitelist.", action: "Enable the corresponding DeFi Pack in Console > Safety, or contact the Agent owner.", consoleUrl };
|
|
987
|
+
if (r.includes("Receiver must be vault"))
|
|
988
|
+
return { explanation: "Swap output recipient must be the agent vault address enforced by ReceiverGuard.", action: "Retry with recipient/to set to the vault address, or use a supported flow that routes proceeds back to the vault.", consoleUrl };
|
|
998
989
|
if (r.includes("Token not in whitelist"))
|
|
999
990
|
return { explanation: "Token restriction is ON and this token is not whitelisted.", action: `Add the token to the whitelist or disable token restriction at: ${consoleUrl}`, consoleUrl };
|
|
1000
991
|
if (r.includes("Exceeds per-tx limit"))
|
|
@@ -1242,13 +1233,18 @@ function ensureRecipientSafe(result) {
|
|
|
1242
1233
|
}
|
|
1243
1234
|
}
|
|
1244
1235
|
async function validateActionsOrThrow(policyClient, tokenId, actions) {
|
|
1245
|
-
for (const action of actions) {
|
|
1236
|
+
for (const [index, action] of actions.entries()) {
|
|
1246
1237
|
const sim = await policyClient.validate(tokenId, action);
|
|
1247
1238
|
if (!sim.ok) {
|
|
1248
1239
|
throw new SkillError(
|
|
1249
1240
|
"POLICY_REJECTED",
|
|
1250
1241
|
"Policy rejected transaction",
|
|
1251
1242
|
{
|
|
1243
|
+
failedActionIndex: index,
|
|
1244
|
+
failedActionTarget: action.target,
|
|
1245
|
+
failedActionSelector: getActionSelector(action),
|
|
1246
|
+
failedActionFunction: getActionFunctionName(action),
|
|
1247
|
+
failedActionValue: action.value.toString(),
|
|
1252
1248
|
reason: sim.reason,
|
|
1253
1249
|
...policyRejectionHelp(sim.reason, tokenId.toString())
|
|
1254
1250
|
},
|
|
@@ -1268,6 +1264,37 @@ async function executeActions(policyClient, tokenId, actions) {
|
|
|
1268
1264
|
const result = await policyClient.executeBatch(tokenId, actions, true);
|
|
1269
1265
|
return { hash: result.hash };
|
|
1270
1266
|
}
|
|
1267
|
+
function getActionSelector(action) {
|
|
1268
|
+
return action.data && action.data.length >= 10 ? action.data.slice(0, 10) : "0x";
|
|
1269
|
+
}
|
|
1270
|
+
function getActionFunctionName(action) {
|
|
1271
|
+
if (action.data === "0x") {
|
|
1272
|
+
return "nativeTransfer";
|
|
1273
|
+
}
|
|
1274
|
+
const candidates = [
|
|
1275
|
+
ERC20_ABI,
|
|
1276
|
+
WBNB_ABI,
|
|
1277
|
+
VTOKEN_ABI,
|
|
1278
|
+
VBNB_MINT_ABI,
|
|
1279
|
+
SWAP_EXACT_ETH_ABI,
|
|
1280
|
+
SWAP_EXACT_TOKENS_ABI,
|
|
1281
|
+
SWAP_EXACT_TOKENS_FOR_ETH_ABI,
|
|
1282
|
+
SWAP_EXACT_ETH_FOR_TOKENS_FEE_ABI,
|
|
1283
|
+
SWAP_EXACT_TOKENS_FOR_TOKENS_FEE_ABI,
|
|
1284
|
+
SWAP_EXACT_TOKENS_FOR_ETH_FEE_ABI,
|
|
1285
|
+
V3_EXACT_INPUT_SINGLE_ABI,
|
|
1286
|
+
V3_EXACT_INPUT_ABI,
|
|
1287
|
+
FOUR_MEME_V1_ABI,
|
|
1288
|
+
FOUR_MEME_V2_ABI
|
|
1289
|
+
];
|
|
1290
|
+
for (const abi of candidates) {
|
|
1291
|
+
const decoded = tryDecodeCalldata(abi, action.data);
|
|
1292
|
+
if (decoded?.functionName) {
|
|
1293
|
+
return decoded.functionName;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return "unknown";
|
|
1297
|
+
}
|
|
1271
1298
|
|
|
1272
1299
|
// src/services/info.ts
|
|
1273
1300
|
var import_viem5 = require("viem");
|
|
@@ -1709,25 +1736,6 @@ async function executeSwap(input) {
|
|
|
1709
1736
|
args: [minOut, path, vault, deadline]
|
|
1710
1737
|
})
|
|
1711
1738
|
});
|
|
1712
|
-
} else if (isNativeOut) {
|
|
1713
|
-
actions.push({
|
|
1714
|
-
target: tokenIn.address,
|
|
1715
|
-
value: 0n,
|
|
1716
|
-
data: (0, import_viem7.encodeFunctionData)({
|
|
1717
|
-
abi: ERC20_ABI,
|
|
1718
|
-
functionName: "approve",
|
|
1719
|
-
args: [v2Router, amountIn]
|
|
1720
|
-
})
|
|
1721
|
-
});
|
|
1722
|
-
actions.push({
|
|
1723
|
-
target: v2Router,
|
|
1724
|
-
value: 0n,
|
|
1725
|
-
data: (0, import_viem7.encodeFunctionData)({
|
|
1726
|
-
abi: SWAP_EXACT_TOKENS_FOR_ETH_ABI,
|
|
1727
|
-
functionName: "swapExactTokensForETH",
|
|
1728
|
-
args: [amountIn, minOut, path, vault, deadline]
|
|
1729
|
-
})
|
|
1730
|
-
});
|
|
1731
1739
|
} else {
|
|
1732
1740
|
actions.push({
|
|
1733
1741
|
target: tokenIn.address,
|
|
@@ -1742,8 +1750,8 @@ async function executeSwap(input) {
|
|
|
1742
1750
|
target: v2Router,
|
|
1743
1751
|
value: 0n,
|
|
1744
1752
|
data: (0, import_viem7.encodeFunctionData)({
|
|
1745
|
-
abi:
|
|
1746
|
-
functionName: "
|
|
1753
|
+
abi: SWAP_EXACT_TOKENS_FOR_TOKENS_FEE_ABI,
|
|
1754
|
+
functionName: "swapExactTokensForTokensSupportingFeeOnTransferTokens",
|
|
1747
1755
|
args: [amountIn, minOut, path, vault, deadline]
|
|
1748
1756
|
})
|
|
1749
1757
|
});
|
|
@@ -2421,59 +2429,48 @@ async function getHistory(tokenIdRaw, limit) {
|
|
|
2421
2429
|
recentPolicyRejections: rejections.length
|
|
2422
2430
|
};
|
|
2423
2431
|
}
|
|
2424
|
-
async function
|
|
2425
|
-
if (!options.txLimit && !options.dailyLimit && !options.cooldown) {
|
|
2426
|
-
throw new SkillError("INVALID_INPUT", "Must specify at least one policy config option");
|
|
2427
|
-
}
|
|
2432
|
+
async function getPolicyConfigGuidance(tokenIdRaw, rpcUrl) {
|
|
2428
2433
|
const tokenId = parseTokenId(tokenIdRaw);
|
|
2429
|
-
const { publicClient, policyClient
|
|
2430
|
-
await ensureAccess(tokenId, rpc, publicClient);
|
|
2431
|
-
const { walletClient } = createWallet(rpc);
|
|
2434
|
+
const { publicClient, policyClient } = createClients(rpcUrl);
|
|
2432
2435
|
const policies = await policyClient.getPolicies(tokenId);
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2436
|
+
const consoleUrl = agentConsoleUrl(tokenId);
|
|
2437
|
+
const currentConfig = {};
|
|
2438
|
+
const spendingPolicy = policies.find((p) => p.policyTypeName === "spending_limit");
|
|
2439
|
+
if (spendingPolicy) {
|
|
2440
|
+
try {
|
|
2441
|
+
const [maxPerTx, maxPerDay, maxSlippageBps] = await publicClient.readContract({
|
|
2442
|
+
address: spendingPolicy.address,
|
|
2443
|
+
abi: SPENDING_LIMIT_ABI,
|
|
2444
|
+
functionName: "instanceLimits",
|
|
2445
|
+
args: [tokenId]
|
|
2446
|
+
});
|
|
2447
|
+
currentConfig.spendingLimit = {
|
|
2448
|
+
maxPerTx: (Number(maxPerTx) / 1e18).toFixed(4) + " BNB",
|
|
2449
|
+
maxPerDay: (Number(maxPerDay) / 1e18).toFixed(4) + " BNB",
|
|
2450
|
+
slippageBps: maxSlippageBps.toString()
|
|
2451
|
+
};
|
|
2452
|
+
} catch {
|
|
2438
2453
|
}
|
|
2439
|
-
const current = await publicClient.readContract({
|
|
2440
|
-
address: spendingPolicy.address,
|
|
2441
|
-
abi: SPENDING_LIMIT_ABI,
|
|
2442
|
-
functionName: "instanceLimits",
|
|
2443
|
-
args: [tokenId]
|
|
2444
|
-
});
|
|
2445
|
-
const [curMaxPerTx, curMaxPerDay, curSlippage] = current;
|
|
2446
|
-
const hash = await walletClient.writeContract({
|
|
2447
|
-
address: spendingPolicy.address,
|
|
2448
|
-
abi: SPENDING_LIMIT_ABI,
|
|
2449
|
-
functionName: "setLimits",
|
|
2450
|
-
args: [
|
|
2451
|
-
tokenId,
|
|
2452
|
-
options.txLimit ? parseAmount(options.txLimit, 18) : curMaxPerTx,
|
|
2453
|
-
options.dailyLimit ? parseAmount(options.dailyLimit, 18) : curMaxPerDay,
|
|
2454
|
-
curSlippage
|
|
2455
|
-
]
|
|
2456
|
-
});
|
|
2457
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2458
|
-
results.push(`SpendingLimit updated: ${hash}`);
|
|
2459
2454
|
}
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2455
|
+
const cooldownPolicy = policies.find((p) => p.policyTypeName === "cooldown");
|
|
2456
|
+
if (cooldownPolicy) {
|
|
2457
|
+
try {
|
|
2458
|
+
const cooldown = await publicClient.readContract({
|
|
2459
|
+
address: cooldownPolicy.address,
|
|
2460
|
+
abi: COOLDOWN_ABI,
|
|
2461
|
+
functionName: "cooldownSeconds",
|
|
2462
|
+
args: [tokenId]
|
|
2463
|
+
});
|
|
2464
|
+
currentConfig.cooldown = { seconds: Number(cooldown) };
|
|
2465
|
+
} catch {
|
|
2464
2466
|
}
|
|
2465
|
-
const hash = await walletClient.writeContract({
|
|
2466
|
-
address: cooldownPolicy.address,
|
|
2467
|
-
abi: COOLDOWN_ABI,
|
|
2468
|
-
functionName: "setCooldown",
|
|
2469
|
-
args: [tokenId, BigInt(options.cooldown)]
|
|
2470
|
-
});
|
|
2471
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2472
|
-
results.push(`Cooldown updated: ${hash}`);
|
|
2473
2467
|
}
|
|
2474
2468
|
return {
|
|
2475
|
-
status: "
|
|
2476
|
-
|
|
2469
|
+
status: "read_only",
|
|
2470
|
+
tokenId: tokenIdRaw,
|
|
2471
|
+
currentConfig,
|
|
2472
|
+
message: "Policy modification via Skills is disabled. Please use the web console to adjust your security settings.",
|
|
2473
|
+
consoleUrl
|
|
2477
2474
|
};
|
|
2478
2475
|
}
|
|
2479
2476
|
function dedupeStrings(values) {
|
|
@@ -2750,20 +2747,13 @@ function registerAgentTools(server2) {
|
|
|
2750
2747
|
);
|
|
2751
2748
|
server2.tool(
|
|
2752
2749
|
"config",
|
|
2753
|
-
"
|
|
2750
|
+
"View current risk parameters and get a link to the web console for modifications",
|
|
2754
2751
|
{
|
|
2755
|
-
token_id: CommonSchemas.tokenId.describe("Agent Token ID")
|
|
2756
|
-
tx_limit: import_zod5.z.string().optional().describe("Max BNB per tx"),
|
|
2757
|
-
daily_limit: import_zod5.z.string().optional().describe("Max BNB per day"),
|
|
2758
|
-
cooldown: import_zod5.z.string().optional().describe("Seconds between tx")
|
|
2752
|
+
token_id: CommonSchemas.tokenId.describe("Agent Token ID")
|
|
2759
2753
|
},
|
|
2760
|
-
async ({ token_id
|
|
2754
|
+
async ({ token_id }) => {
|
|
2761
2755
|
try {
|
|
2762
|
-
return asToolResult5(await
|
|
2763
|
-
txLimit: tx_limit,
|
|
2764
|
-
dailyLimit: daily_limit,
|
|
2765
|
-
cooldown
|
|
2766
|
-
}));
|
|
2756
|
+
return asToolResult5(await getPolicyConfigGuidance(token_id));
|
|
2767
2757
|
} catch (error) {
|
|
2768
2758
|
return formatMcpError(error);
|
|
2769
2759
|
}
|
package/dist/mcp.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getBalance,
|
|
12
12
|
getFourMemeInfo,
|
|
13
13
|
getHistory,
|
|
14
|
+
getPolicyConfigGuidance,
|
|
14
15
|
getPolicySummary,
|
|
15
16
|
getPortfolio,
|
|
16
17
|
getPrice,
|
|
@@ -24,9 +25,8 @@ import {
|
|
|
24
25
|
sellFourMeme,
|
|
25
26
|
transferFromVault,
|
|
26
27
|
unwrapWbnb,
|
|
27
|
-
updateRiskConfig,
|
|
28
28
|
wrapBnb
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-TH3ENDDM.mjs";
|
|
30
30
|
|
|
31
31
|
// src/mcp.ts
|
|
32
32
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -303,20 +303,13 @@ function registerAgentTools(server2) {
|
|
|
303
303
|
);
|
|
304
304
|
server2.tool(
|
|
305
305
|
"config",
|
|
306
|
-
"
|
|
306
|
+
"View current risk parameters and get a link to the web console for modifications",
|
|
307
307
|
{
|
|
308
|
-
token_id: CommonSchemas.tokenId.describe("Agent Token ID")
|
|
309
|
-
tx_limit: z4.string().optional().describe("Max BNB per tx"),
|
|
310
|
-
daily_limit: z4.string().optional().describe("Max BNB per day"),
|
|
311
|
-
cooldown: z4.string().optional().describe("Seconds between tx")
|
|
308
|
+
token_id: CommonSchemas.tokenId.describe("Agent Token ID")
|
|
312
309
|
},
|
|
313
|
-
async ({ token_id
|
|
310
|
+
async ({ token_id }) => {
|
|
314
311
|
try {
|
|
315
|
-
return asToolResult5(await
|
|
316
|
-
txLimit: tx_limit,
|
|
317
|
-
dailyLimit: daily_limit,
|
|
318
|
-
cooldown
|
|
319
|
-
}));
|
|
312
|
+
return asToolResult5(await getPolicyConfigGuidance(token_id));
|
|
320
313
|
} catch (error) {
|
|
321
314
|
return formatMcpError(error);
|
|
322
315
|
}
|
package/package.json
CHANGED