@finchagentic/mcp 4.6.1 β 4.6.2
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/package.json +5 -6
- package/dist/_http-cache.js +0 -96
- package/dist/_text-search.js +0 -39
- package/dist/agent-loop.js +0 -301
- package/dist/annotations.js +0 -122
- package/dist/cli.js +0 -1391
- package/dist/clink-input.js +0 -15
- package/dist/config.js +0 -132
- package/dist/convex.js +0 -175
- package/dist/dex-pair.js +0 -54
- package/dist/enrichment-router.js +0 -315
- package/dist/index.js +0 -258
- package/dist/llm.js +0 -298
- package/dist/local-memory-file.js +0 -150
- package/dist/local-memory.js +0 -135
- package/dist/local-vault.js +0 -456
- package/dist/output-schemas.js +0 -605
- package/dist/project.js +0 -36
- package/dist/prompts.js +0 -111
- package/dist/public-url.js +0 -107
- package/dist/resources.js +0 -111
- package/dist/server.js +0 -322
- package/dist/signal-gate.js +0 -57
- package/dist/token-decimals.js +0 -26
- package/dist/token-gate.js +0 -88
- package/dist/tool-filter.js +0 -53
- package/dist/tools/_solidity-scan.js +0 -313
- package/dist/tools/agents.js +0 -441
- package/dist/tools/automation.js +0 -354
- package/dist/tools/base-mcp.js +0 -466
- package/dist/tools/base.js +0 -283
- package/dist/tools/chronicle.js +0 -268
- package/dist/tools/coder.js +0 -94
- package/dist/tools/deep-research.js +0 -1421
- package/dist/tools/defi.js +0 -292
- package/dist/tools/equity.js +0 -372
- package/dist/tools/events.js +0 -182
- package/dist/tools/github.js +0 -564
- package/dist/tools/insider.js +0 -264
- package/dist/tools/insight.js +0 -630
- package/dist/tools/market.js +0 -555
- package/dist/tools/memory.js +0 -1059
- package/dist/tools/miroshark.js +0 -350
- package/dist/tools/monitor.js +0 -319
- package/dist/tools/os.js +0 -236
- package/dist/tools/packets.js +0 -296
- package/dist/tools/research-chain.js +0 -226
- package/dist/tools/research-compare.js +0 -280
- package/dist/tools/research.js +0 -188
- package/dist/tools/rh-bridge.js +0 -148
- package/dist/tools/rh-mcp.js +0 -1448
- package/dist/tools/rh-orders.js +0 -556
- package/dist/tools/scanner.js +0 -564
- package/dist/tools/stake.js +0 -369
- package/dist/tools/vault.js +0 -1020
- package/dist/tools/wallet.js +0 -200
- package/dist/types.js +0 -2
- package/dist/wallet.js +0 -372
package/dist/tools/stake.js
DELETED
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.STAKE_TOOLS = void 0;
|
|
4
|
-
exports.parseStakeAmount = parseStakeAmount;
|
|
5
|
-
exports.handleStakeTool = handleStakeTool;
|
|
6
|
-
const ethers_1 = require("ethers");
|
|
7
|
-
const convex_js_1 = require("../convex.js");
|
|
8
|
-
const config_js_1 = require("../config.js");
|
|
9
|
-
const rh_mcp_js_1 = require("./rh-mcp.js");
|
|
10
|
-
// FINCH staking - MCP wrapper around the EXISTING app/convex staking actions
|
|
11
|
-
// (stake.ts's stakeFINCH/unstakeFINCH, stakeInternal.ts's myStakingStats/
|
|
12
|
-
// globalStats). No staking logic lives here - this only parses a
|
|
13
|
-
// human-friendly amount ("all", "max", "50%", "1m", "100k", a raw number)
|
|
14
|
-
// into wei and calls the backend, exactly like the webapp's Stake page does.
|
|
15
|
-
//
|
|
16
|
-
// IMPORTANT - custodial wallet, not the local signing wallet: unlike
|
|
17
|
-
// base_mcp_swap/rh_mcp_swap (which sign and broadcast with the LOCAL
|
|
18
|
-
// ~/.finch/wallet.json key and need no login), staking moves FINCH from the
|
|
19
|
-
// account's CUSTODIAL wallet (the one shown on the webapp's Wallet page) -
|
|
20
|
-
// stake.ts's actions resolve identity via a real logged-in session, not a
|
|
21
|
-
// wallet signature. Requires `finch login` first; there is no way around
|
|
22
|
-
// this without changing stake.ts's own session-based design, which is out
|
|
23
|
-
// of scope here. The local wallet and the custodial wallet are DIFFERENT
|
|
24
|
-
// addresses with different balances - never conflate them in output.
|
|
25
|
-
//
|
|
26
|
-
// SECURITY: no tool here ever returns a private key or seed phrase, and
|
|
27
|
-
// none ever will - the custodial key stays server-side (Turnkey-enclave /
|
|
28
|
-
// encrypted-at-rest), exactly as it already does for the webapp. "Log in,
|
|
29
|
-
// then stake" is the ceiling of what this integration does; it does not,
|
|
30
|
-
// and must not, add an export path.
|
|
31
|
-
const FINCH_CA = "0xce1981b0431fb495912cab057d2877a290199824";
|
|
32
|
-
const FINCH_DECIMALS = 18;
|
|
33
|
-
const ERC20_BALANCE_ABI = ["function balanceOf(address) view returns (uint256)"];
|
|
34
|
-
// Mirrors app/convex/stake.ts's STAKING_TIERS - fixed constants, not worth a
|
|
35
|
-
// round-trip to the backend to fetch. Keep in sync if the backend tiers change.
|
|
36
|
-
const STAKING_TIERS = {
|
|
37
|
-
7: { multiplier: 1.0, label: "Flexible" },
|
|
38
|
-
30: { multiplier: 1.5, label: "Committed" },
|
|
39
|
-
90: { multiplier: 2.0, label: "Diamond Hands" },
|
|
40
|
-
};
|
|
41
|
-
const VALID_LOCK_TIERS = Object.keys(STAKING_TIERS).map(Number);
|
|
42
|
-
function requireLogin() {
|
|
43
|
-
const token = (0, config_js_1.getSavedToken)();
|
|
44
|
-
return token ? { token } : null;
|
|
45
|
-
}
|
|
46
|
-
const NOT_LOGGED_IN_MSG = "Staking needs a logged-in session (not just the local wallet). Run `finch login` first, then retry.";
|
|
47
|
-
async function finchBalanceOf(address) {
|
|
48
|
-
const provider = await (0, rh_mcp_js_1.rhProviderAsync)();
|
|
49
|
-
const finch = new ethers_1.ethers.Contract(FINCH_CA, ERC20_BALANCE_ABI, provider);
|
|
50
|
-
return await finch.balanceOf(address);
|
|
51
|
-
}
|
|
52
|
-
// Parses "all" | "max" | "50%" | "1m" | "100k" | "100000" | "value 100000"
|
|
53
|
-
// against a live balance. Returns wei as a string, or throws with a message
|
|
54
|
-
// the caller can show verbatim.
|
|
55
|
-
function parseStakeAmount(input, balanceWei) {
|
|
56
|
-
const raw = input.trim().toLowerCase().replace(/^value\s+/, "");
|
|
57
|
-
if (raw === "all" || raw === "max") {
|
|
58
|
-
if (balanceWei <= 0n)
|
|
59
|
-
throw new Error("Wallet has 0 FINCH - nothing to stake.");
|
|
60
|
-
return balanceWei;
|
|
61
|
-
}
|
|
62
|
-
const pctMatch = raw.match(/^(\d+(?:\.\d+)?)\s*%$/);
|
|
63
|
-
if (pctMatch) {
|
|
64
|
-
const pct = parseFloat(pctMatch[1]);
|
|
65
|
-
if (!(pct > 0) || pct > 100)
|
|
66
|
-
throw new Error("Percentage must be between 0 and 100.");
|
|
67
|
-
return (balanceWei * BigInt(Math.round(pct * 100))) / 10000n;
|
|
68
|
-
}
|
|
69
|
-
const suffixMatch = raw.match(/^(\d+(?:\.\d+)?)\s*([km])$/);
|
|
70
|
-
if (suffixMatch) {
|
|
71
|
-
const n = parseFloat(suffixMatch[1]);
|
|
72
|
-
const mult = suffixMatch[2] === "m" ? 1000000 : 1000;
|
|
73
|
-
return ethers_1.ethers.parseUnits((n * mult).toString(), FINCH_DECIMALS);
|
|
74
|
-
}
|
|
75
|
-
const plainMatch = raw.match(/^(\d+(?:\.\d+)?)$/);
|
|
76
|
-
if (plainMatch) {
|
|
77
|
-
return ethers_1.ethers.parseUnits(plainMatch[1], FINCH_DECIMALS);
|
|
78
|
-
}
|
|
79
|
-
throw new Error(`Could not parse amount "${input}". Use "all", "max", a percentage like "50%", ` +
|
|
80
|
-
`a shorthand like "1m" or "100k", or a plain number like "100000".`);
|
|
81
|
-
}
|
|
82
|
-
function fmtFinch(wei) {
|
|
83
|
-
return `${Number(ethers_1.ethers.formatUnits(wei, FINCH_DECIMALS)).toLocaleString("en-US", { maximumFractionDigits: 4 })} FINCH`;
|
|
84
|
-
}
|
|
85
|
-
exports.STAKE_TOOLS = [
|
|
86
|
-
{
|
|
87
|
-
name: "stake_finch_status",
|
|
88
|
-
description: "View your FINCH staking status - custodial wallet balance available to stake, your active stakes " +
|
|
89
|
-
"(amount, lock-up remaining, accrued rewards), and pool-wide stats (total staked, daily USDG reward). " +
|
|
90
|
-
"Requires `finch login` (stakes live on your account's custodial wallet, not the local MCP signing wallet).",
|
|
91
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: "stake_finch",
|
|
95
|
-
description: "Stake FINCH from your custodial wallet (the one shown on the webapp's Wallet page) to earn USDG " +
|
|
96
|
-
"rewards. Locks for a fixed period chosen at stake time - unstake is blocked until then, and the " +
|
|
97
|
-
"tier cannot be changed mid-stake. Longer locks earn a higher reward multiplier: " +
|
|
98
|
-
"7 days = 1.0x (default) Β· 30 days = 1.5x Β· 90 days = 2.0x. " +
|
|
99
|
-
"Requires `finch login` first and `confirm: true`. Amount accepts \"all\"/\"max\" (entire " +
|
|
100
|
-
"available balance), a percentage (\"50%\"), a shorthand (\"1m\", \"100k\"), or a plain number " +
|
|
101
|
-
"(\"100000\").",
|
|
102
|
-
inputSchema: {
|
|
103
|
-
type: "object",
|
|
104
|
-
properties: {
|
|
105
|
-
amount: {
|
|
106
|
-
type: "string",
|
|
107
|
-
description: "\"all\" | \"max\" | \"50%\" | \"1m\" | \"100k\" | \"100000\"",
|
|
108
|
-
},
|
|
109
|
-
lockTier: {
|
|
110
|
-
type: "number",
|
|
111
|
-
enum: [7, 30, 90],
|
|
112
|
-
description: "Lock period in days: 7 (1.0x, default), 30 (1.5x), or 90 (2.0x). Omit for 7-day/1.0x.",
|
|
113
|
-
},
|
|
114
|
-
confirm: {
|
|
115
|
-
type: "boolean",
|
|
116
|
-
description: "Must be true to stake - this moves real FINCH out of your custodial wallet.",
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
required: ["amount", "confirm"],
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: "unstake_finch",
|
|
124
|
-
description: "Unstake FINCH back to your custodial wallet, plus any pending USDG rewards. Only works once the " +
|
|
125
|
-
"lock-up period has passed (check stake_finch_status for which stakes are unlockable). Requires " +
|
|
126
|
-
"`finch login` and `confirm: true`. Pass a specific stakeId, or \"all\" to unstake every currently " +
|
|
127
|
-
"unlockable stake in one call (each processed and reported individually).",
|
|
128
|
-
inputSchema: {
|
|
129
|
-
type: "object",
|
|
130
|
-
properties: {
|
|
131
|
-
stakeId: { type: "string", description: "A stake ID from stake_finch_status, or \"all\"" },
|
|
132
|
-
confirm: { type: "boolean", description: "Must be true to unstake." },
|
|
133
|
-
},
|
|
134
|
-
required: ["stakeId", "confirm"],
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: "claim_vested_rewards",
|
|
139
|
-
description: "Claim all your fully-vested USDG staking rewards to your custodial wallet. Daily rewards split " +
|
|
140
|
-
"50% instant / 50% vested over 3 days - this claims whatever portion has finished vesting across " +
|
|
141
|
-
"ALL your stakes in one call (check stake_finch_status for accrued totals; you'll also get a " +
|
|
142
|
-
"notification when rewards become claimable). Does nothing to the stake itself - principal stays " +
|
|
143
|
-
"staked and earning. Requires `finch login` and `confirm: true`.",
|
|
144
|
-
inputSchema: {
|
|
145
|
-
type: "object",
|
|
146
|
-
properties: {
|
|
147
|
-
confirm: { type: "boolean", description: "Must be true to claim - this moves real USDG into your custodial wallet." },
|
|
148
|
-
},
|
|
149
|
-
required: ["confirm"],
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
name: "stake_auto_restake",
|
|
154
|
-
description: "Turn auto-restake on/off for one of your stakes. When ON, the moment the lock-up ends the stake " +
|
|
155
|
-
"is automatically renewed for another full period at the SAME tier - no FINCH ever leaves the " +
|
|
156
|
-
"treasury, no gas, no confirmation needed each cycle (a background job on the Finch backend does " +
|
|
157
|
-
"this, roughly every 15 minutes). When OFF (default), you instead get a one-time notification that " +
|
|
158
|
-
"the stake is unlockable and it just sits there earning nothing until you run unstake_finch. " +
|
|
159
|
-
"Does NOT auto-compound rewards into the stake - that would require signing from your custodial " +
|
|
160
|
-
"wallet unattended, which this intentionally does not do; claim rewards yourself with " +
|
|
161
|
-
"claim_vested_rewards (you'll get a notification when they're ready). Requires `finch login`.",
|
|
162
|
-
inputSchema: {
|
|
163
|
-
type: "object",
|
|
164
|
-
properties: {
|
|
165
|
-
stakeId: { type: "string", description: "A stake ID from stake_finch_status" },
|
|
166
|
-
enabled: { type: "boolean", description: "true to turn auto-restake on, false to turn it off" },
|
|
167
|
-
},
|
|
168
|
-
required: ["stakeId", "enabled"],
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
];
|
|
172
|
-
async function handleStakeTool(name, args) {
|
|
173
|
-
if (name === "stake_finch_status") {
|
|
174
|
-
const login = requireLogin();
|
|
175
|
-
if (!login)
|
|
176
|
-
return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
|
|
177
|
-
const data = await (0, convex_js_1.callConvex)("/mcp/stake/status", "GET", undefined, "stake_finch_status");
|
|
178
|
-
if (data.error)
|
|
179
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
180
|
-
const address = data.walletAddress ?? null;
|
|
181
|
-
let balanceWei = null;
|
|
182
|
-
if (address) {
|
|
183
|
-
try {
|
|
184
|
-
balanceWei = await finchBalanceOf(address);
|
|
185
|
-
}
|
|
186
|
-
catch { /* balance is a nice-to-have, don't fail the whole status */ }
|
|
187
|
-
}
|
|
188
|
-
const my = data.myStats;
|
|
189
|
-
const global = data.global;
|
|
190
|
-
const lines = [
|
|
191
|
-
`## FINCH Staking Status`,
|
|
192
|
-
``,
|
|
193
|
-
address ? `**Custodial wallet:** \`${address}\` ${balanceWei != null ? `(${fmtFinch(balanceWei)} available to stake)` : ""}` : `_Could not resolve custodial wallet address._`,
|
|
194
|
-
``,
|
|
195
|
-
global ? `**Pool:** ${global.totalStakedHuman.toLocaleString()} FINCH staked by ${global.totalStakers} stakers Β· ${global.dailyRewardUsdg} USDG/day pool` : "",
|
|
196
|
-
`**Lock tiers:** ${VALID_LOCK_TIERS.map((t) => `${t}d/${STAKING_TIERS[t].multiplier}x (${STAKING_TIERS[t].label})`).join(" Β· ")} β pick with \`stake_finch lockTier\`, default 7d/1.0x`,
|
|
197
|
-
``,
|
|
198
|
-
my ? `**Your stakes:** ${my.activeStakes} active Β· ${my.totalStaked.toLocaleString()} FINCH staked Β· ${my.totalRewards.toFixed(4)} USDG accrued Β· ${my.canUnstakeCount} unlockable now` : "",
|
|
199
|
-
];
|
|
200
|
-
if (my?.stakes?.length) {
|
|
201
|
-
lines.push(``, `**Detail:**`);
|
|
202
|
-
for (const s of my.stakes) {
|
|
203
|
-
const unlocked = Date.now() >= s.unlockAt;
|
|
204
|
-
const tier = STAKING_TIERS[s.lockTier ?? 7] ?? STAKING_TIERS[7];
|
|
205
|
-
const autoNote = s.status === "staked" ? (s.autoRestake ? " Β· π auto-restake ON" : "") : "";
|
|
206
|
-
lines.push(`- \`${s._id}\` β ${s.amountHuman} FINCH Β· ${s.lockTier ?? 7}d/${tier.multiplier}x Β· ${s.status}${s.status === "staked" ? (unlocked ? " Β· unlockable now" : ` Β· unlocks ${new Date(s.unlockAt).toISOString().slice(0, 10)}`) : ""}${autoNote}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return { content: [{ type: "text", text: lines.filter(Boolean).join("\n") }] };
|
|
210
|
-
}
|
|
211
|
-
if (name === "stake_finch") {
|
|
212
|
-
const raw = (args ?? {});
|
|
213
|
-
const { amount, confirm } = raw;
|
|
214
|
-
// Coerce defensively - MCP clients commonly send numeric args as JSON
|
|
215
|
-
// strings, and unlike `amount` (a string by design, parsed by
|
|
216
|
-
// parseStakeAmount) or `confirm` (unambiguous boolean), a stray
|
|
217
|
-
// "30" here must not silently mismatch the numeric VALID_LOCK_TIERS
|
|
218
|
-
// check and fall through to the 7-day default with no error.
|
|
219
|
-
const lockTier = raw.lockTier === undefined || raw.lockTier === null || raw.lockTier === ""
|
|
220
|
-
? undefined
|
|
221
|
-
: Number(raw.lockTier);
|
|
222
|
-
if (!amount)
|
|
223
|
-
return { content: [{ type: "text", text: "amount is required" }], isError: true };
|
|
224
|
-
if (lockTier !== undefined && (!Number.isFinite(lockTier) || !VALID_LOCK_TIERS.includes(lockTier))) {
|
|
225
|
-
return {
|
|
226
|
-
content: [{ type: "text", text: `Invalid lockTier ${JSON.stringify(raw.lockTier)} - must be one of ${VALID_LOCK_TIERS.join(", ")} (days).` }],
|
|
227
|
-
isError: true,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
if (confirm !== true) {
|
|
231
|
-
return {
|
|
232
|
-
content: [{ type: "text", text: "Refusing to stake without confirmation - pass `confirm: true` after reviewing the amount." }],
|
|
233
|
-
isError: true,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
const login = requireLogin();
|
|
237
|
-
if (!login)
|
|
238
|
-
return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
|
|
239
|
-
const status = await (0, convex_js_1.callConvex)("/mcp/stake/status", "GET", undefined, "stake_finch_status");
|
|
240
|
-
if (status.error)
|
|
241
|
-
return { content: [{ type: "text", text: `Error: ${status.error}` }], isError: true };
|
|
242
|
-
if (!status.walletAddress)
|
|
243
|
-
return { content: [{ type: "text", text: "Could not resolve your custodial wallet address." }], isError: true };
|
|
244
|
-
let balanceWei;
|
|
245
|
-
try {
|
|
246
|
-
balanceWei = await finchBalanceOf(status.walletAddress);
|
|
247
|
-
}
|
|
248
|
-
catch (e) {
|
|
249
|
-
return { content: [{ type: "text", text: `Could not read FINCH balance: ${e?.message ?? e}` }], isError: true };
|
|
250
|
-
}
|
|
251
|
-
let amountWei;
|
|
252
|
-
try {
|
|
253
|
-
amountWei = parseStakeAmount(amount, balanceWei);
|
|
254
|
-
}
|
|
255
|
-
catch (e) {
|
|
256
|
-
return { content: [{ type: "text", text: e.message }], isError: true };
|
|
257
|
-
}
|
|
258
|
-
if (amountWei > balanceWei) {
|
|
259
|
-
return {
|
|
260
|
-
content: [{ type: "text", text: `Insufficient FINCH. Have ${fmtFinch(balanceWei)}, tried to stake ${fmtFinch(amountWei)}.` }],
|
|
261
|
-
isError: true,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
const result = await (0, convex_js_1.callConvex)("/mcp/stake/stake", "POST", { amountWei: amountWei.toString(), lockTier }, "stake_finch", 30000, true);
|
|
265
|
-
if (result.error)
|
|
266
|
-
return { content: [{ type: "text", text: `Stake failed: ${result.error}` }], isError: true };
|
|
267
|
-
const tier = STAKING_TIERS[lockTier ?? 7];
|
|
268
|
-
return {
|
|
269
|
-
content: [{
|
|
270
|
-
type: "text",
|
|
271
|
-
text: [
|
|
272
|
-
`β
Staked ${fmtFinch(amountWei)}`,
|
|
273
|
-
`Lock: ${lockTier ?? 7} days (${tier.label}, ${tier.multiplier}x rewards)`,
|
|
274
|
-
`Stake ID: \`${result.stakeId}\``,
|
|
275
|
-
`Tx: \`${result.txHash}\``,
|
|
276
|
-
`${rh_mcp_js_1.RH_EXPLORER}/tx/${result.txHash}`,
|
|
277
|
-
].join("\n"),
|
|
278
|
-
}],
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
if (name === "unstake_finch") {
|
|
282
|
-
const { stakeId, confirm } = (args ?? {});
|
|
283
|
-
if (!stakeId)
|
|
284
|
-
return { content: [{ type: "text", text: "stakeId is required (or \"all\")" }], isError: true };
|
|
285
|
-
if (confirm !== true) {
|
|
286
|
-
return {
|
|
287
|
-
content: [{ type: "text", text: "Refusing to unstake without confirmation - pass `confirm: true`." }],
|
|
288
|
-
isError: true,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const login = requireLogin();
|
|
292
|
-
if (!login)
|
|
293
|
-
return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
|
|
294
|
-
let targetIds;
|
|
295
|
-
if (stakeId.toLowerCase() === "all") {
|
|
296
|
-
const status = await (0, convex_js_1.callConvex)("/mcp/stake/status", "GET", undefined, "stake_finch_status");
|
|
297
|
-
if (status.error)
|
|
298
|
-
return { content: [{ type: "text", text: `Error: ${status.error}` }], isError: true };
|
|
299
|
-
const now = Date.now();
|
|
300
|
-
targetIds = (status.myStats?.stakes ?? [])
|
|
301
|
-
.filter((s) => s.status === "staked" && now >= s.unlockAt)
|
|
302
|
-
.map((s) => s._id);
|
|
303
|
-
if (!targetIds.length) {
|
|
304
|
-
return { content: [{ type: "text", text: "No stakes are currently unlockable." }] };
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
targetIds = [stakeId];
|
|
309
|
-
}
|
|
310
|
-
const lines = [];
|
|
311
|
-
for (const id of targetIds) {
|
|
312
|
-
const result = await (0, convex_js_1.callConvex)("/mcp/stake/unstake", "POST", { stakeId: id }, "unstake_finch", 30000, true);
|
|
313
|
-
if (result.error) {
|
|
314
|
-
lines.push(`π΄ \`${id}\`: ${result.error}`);
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
lines.push(`β
\`${id}\` unstaked β tx \`${result.txHash}\`${result.rewardsTxHash ? ` Β· rewards tx \`${result.rewardsTxHash}\`` : ""}`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
321
|
-
}
|
|
322
|
-
if (name === "claim_vested_rewards") {
|
|
323
|
-
const { confirm } = (args ?? {});
|
|
324
|
-
if (confirm !== true) {
|
|
325
|
-
return {
|
|
326
|
-
content: [{ type: "text", text: "Refusing to claim without confirmation - pass `confirm: true`." }],
|
|
327
|
-
isError: true,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
const login = requireLogin();
|
|
331
|
-
if (!login)
|
|
332
|
-
return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
|
|
333
|
-
const result = await (0, convex_js_1.callConvex)("/mcp/stake/claim-rewards", "POST", {}, "claim_vested_rewards", 30000, true);
|
|
334
|
-
if (result.error)
|
|
335
|
-
return { content: [{ type: "text", text: `Claim failed: ${result.error}` }], isError: true };
|
|
336
|
-
return {
|
|
337
|
-
content: [{
|
|
338
|
-
type: "text",
|
|
339
|
-
text: [
|
|
340
|
-
`β
Claimed ${(result.claimedAmount ?? 0).toFixed(4)} USDG vested rewards`,
|
|
341
|
-
`Tx: \`${result.txHash}\``,
|
|
342
|
-
`${rh_mcp_js_1.RH_EXPLORER}/tx/${result.txHash}`,
|
|
343
|
-
].join("\n"),
|
|
344
|
-
}],
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
if (name === "stake_auto_restake") {
|
|
348
|
-
const { stakeId, enabled } = (args ?? {});
|
|
349
|
-
if (!stakeId)
|
|
350
|
-
return { content: [{ type: "text", text: "stakeId is required" }], isError: true };
|
|
351
|
-
if (typeof enabled !== "boolean")
|
|
352
|
-
return { content: [{ type: "text", text: "enabled (true/false) is required" }], isError: true };
|
|
353
|
-
const login = requireLogin();
|
|
354
|
-
if (!login)
|
|
355
|
-
return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
|
|
356
|
-
const result = await (0, convex_js_1.callConvex)("/mcp/stake/auto-restake", "POST", { stakeId, enabled }, "stake_auto_restake");
|
|
357
|
-
if (result.error)
|
|
358
|
-
return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
|
|
359
|
-
return {
|
|
360
|
-
content: [{
|
|
361
|
-
type: "text",
|
|
362
|
-
text: enabled
|
|
363
|
-
? `π Auto-restake ON for \`${stakeId}\`. It'll renew itself for another full period at the same tier every time it unlocks - no action needed from you.`
|
|
364
|
-
: `βΈοΈ Auto-restake OFF for \`${stakeId}\`. You'll get a notification when it unlocks instead, and it'll just sit there until you run \`unstake_finch\`.`,
|
|
365
|
-
}],
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
return null;
|
|
369
|
-
}
|