@aibtc/mcp-server 1.70.1 → 1.72.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/README.md +45 -8
- package/dist/bridge/index.js +4 -4
- package/dist/config/contracts.d.ts +4 -4
- package/dist/config/contracts.js +2 -2
- package/dist/config/networks.js +4 -1
- package/dist/endpoints/registry.d.ts +8 -26
- package/dist/endpoints/registry.js +7 -932
- package/dist/index.js +118 -42
- package/dist/services/stacking.service.d.ts +144 -45
- package/dist/services/stacking.service.js +416 -123
- package/dist/services/x402-discovery.service.d.ts +81 -0
- package/dist/services/x402-discovery.service.js +170 -0
- package/dist/tools/arxiv-research.tools.d.ts +1 -1
- package/dist/tools/arxiv-research.tools.js +6 -6
- package/dist/tools/at-stake-legion.tools.d.ts +1 -1
- package/dist/tools/at-stake-legion.tools.js +14 -14
- package/dist/tools/at-stake.tools.d.ts +1 -1
- package/dist/tools/at-stake.tools.js +17 -17
- package/dist/tools/bitcoin.tools.d.ts +1 -1
- package/dist/tools/bitcoin.tools.js +12 -12
- package/dist/tools/bitflow.tools.d.ts +1 -1
- package/dist/tools/bitflow.tools.js +21 -21
- package/dist/tools/bns.tools.d.ts +1 -1
- package/dist/tools/bns.tools.js +18 -18
- package/dist/tools/bounty-hint.d.ts +1 -1
- package/dist/tools/bounty-scanner.tools.d.ts +1 -1
- package/dist/tools/bounty-scanner.tools.js +20 -20
- package/dist/tools/child-inscription.tools.d.ts +1 -1
- package/dist/tools/child-inscription.tools.js +6 -6
- package/dist/tools/competition.tools.d.ts +1 -1
- package/dist/tools/competition.tools.js +4 -4
- package/dist/tools/contract.tools.d.ts +1 -1
- package/dist/tools/contract.tools.js +8 -8
- package/dist/tools/credentials.tools.d.ts +1 -1
- package/dist/tools/credentials.tools.js +8 -8
- package/dist/tools/defi.tools.d.ts +1 -1
- package/dist/tools/defi.tools.js +21 -21
- package/dist/tools/dual-stacking.tools.d.ts +1 -1
- package/dist/tools/dual-stacking.tools.js +7 -7
- package/dist/tools/earning.tools.d.ts +1 -1
- package/dist/tools/earning.tools.js +5 -5
- package/dist/tools/endpoint.tools.d.ts +1 -1
- package/dist/tools/endpoint.tools.js +92 -49
- package/dist/tools/identity.tools.d.ts +1 -1
- package/dist/tools/identity.tools.js +19 -19
- package/dist/tools/inbox-x402.tools.d.ts +1 -1
- package/dist/tools/inbox-x402.tools.js +2 -2
- package/dist/tools/inbox.tools.d.ts +1 -1
- package/dist/tools/inbox.tools.js +2 -2
- package/dist/tools/index.d.ts +6 -3
- package/dist/tools/index.js +69 -58
- package/dist/tools/inference-marketplace.tools.d.ts +1 -1
- package/dist/tools/inference-marketplace.tools.js +10 -10
- package/dist/tools/jingswap.tools.d.ts +1 -1
- package/dist/tools/jingswap.tools.js +30 -30
- package/dist/tools/legion.tools.d.ts +1 -1
- package/dist/tools/legion.tools.js +20 -20
- package/dist/tools/lightning.tools.d.ts +1 -1
- package/dist/tools/lightning.tools.js +14 -14
- package/dist/tools/mempool.tools.d.ts +1 -1
- package/dist/tools/mempool.tools.js +5 -5
- package/dist/tools/news.tools.d.ts +1 -1
- package/dist/tools/news.tools.js +2 -1
- package/dist/tools/nft.tools.d.ts +1 -1
- package/dist/tools/nft.tools.js +12 -12
- package/dist/tools/nonce.tools.d.ts +1 -1
- package/dist/tools/nonce.tools.js +8 -8
- package/dist/tools/nostr.tools.d.ts +1 -1
- package/dist/tools/nostr.tools.js +12 -12
- package/dist/tools/openrouter.tools.d.ts +1 -1
- package/dist/tools/openrouter.tools.js +4 -4
- package/dist/tools/ordinals-marketplace.tools.d.ts +1 -1
- package/dist/tools/ordinals-marketplace.tools.js +10 -10
- package/dist/tools/ordinals-p2p.tools.d.ts +1 -1
- package/dist/tools/ordinals-p2p.tools.js +18 -18
- package/dist/tools/ordinals.tools.d.ts +1 -1
- package/dist/tools/ordinals.tools.js +8 -8
- package/dist/tools/pillar-direct.tools.d.ts +1 -1
- package/dist/tools/pillar-direct.tools.js +59 -38
- package/dist/tools/pillar.tools.d.ts +1 -1
- package/dist/tools/pillar.tools.js +28 -26
- package/dist/tools/profiles.d.ts +51 -0
- package/dist/tools/profiles.js +111 -0
- package/dist/tools/psbt.tools.d.ts +1 -1
- package/dist/tools/psbt.tools.js +8 -8
- package/dist/tools/query.tools.d.ts +1 -1
- package/dist/tools/query.tools.js +12 -12
- package/dist/tools/relay-diagnostic.tools.d.ts +1 -1
- package/dist/tools/relay-diagnostic.tools.js +3 -3
- package/dist/tools/reputation.tools.d.ts +1 -1
- package/dist/tools/reputation.tools.js +22 -22
- package/dist/tools/runes.tools.d.ts +1 -1
- package/dist/tools/runes.tools.js +18 -18
- package/dist/tools/sbtc.tools.d.ts +1 -1
- package/dist/tools/sbtc.tools.js +14 -33
- package/dist/tools/scaffold.tools.d.ts +1 -1
- package/dist/tools/scaffold.tools.js +4 -4
- package/dist/tools/settings.tools.d.ts +1 -1
- package/dist/tools/settings.tools.js +9 -9
- package/dist/tools/signing.tools.d.ts +1 -1
- package/dist/tools/signing.tools.js +20 -20
- package/dist/tools/skill-mappings.js +4 -0
- package/dist/tools/souldinals.tools.d.ts +1 -1
- package/dist/tools/souldinals.tools.js +10 -10
- package/dist/tools/stacking-lottery.tools.d.ts +1 -1
- package/dist/tools/stacking-lottery.tools.js +11 -11
- package/dist/tools/stacking.tools.d.ts +1 -1
- package/dist/tools/stacking.tools.js +358 -50
- package/dist/tools/stacks-market.tools.d.ts +1 -1
- package/dist/tools/stacks-market.tools.js +22 -22
- package/dist/tools/styx.tools.d.ts +1 -1
- package/dist/tools/styx.tools.js +11 -11
- package/dist/tools/taproot-multisig.tools.d.ts +1 -1
- package/dist/tools/taproot-multisig.tools.js +5 -5
- package/dist/tools/tenero.tools.d.ts +1 -1
- package/dist/tools/tenero.tools.js +22 -22
- package/dist/tools/tokens.tools.d.ts +1 -1
- package/dist/tools/tokens.tools.js +10 -10
- package/dist/tools/transfer.tools.d.ts +1 -1
- package/dist/tools/transfer.tools.js +4 -4
- package/dist/tools/wallet-management.tools.d.ts +1 -1
- package/dist/tools/wallet-management.tools.js +16 -16
- package/dist/tools/wallet.tools.d.ts +1 -1
- package/dist/tools/wallet.tools.js +2 -2
- package/dist/tools/yield-dashboard.tools.d.ts +1 -1
- package/dist/tools/yield-dashboard.tools.js +18 -27
- package/dist/tools/yield-hunter.tools.d.ts +1 -1
- package/dist/tools/yield-hunter.tools.js +6 -6
- package/dist/utils/bitcoin.d.ts +8 -0
- package/dist/utils/bitcoin.js +24 -0
- package/dist/utils/fee.js +1 -1
- package/package.json +8 -7
- package/skill/SKILL.md +1 -1
- package/skill/references/stacks-defi.md +1 -1
|
@@ -1,107 +1,415 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { getAccount, getWalletAddress, NETWORK } from "../services/x402.service.js";
|
|
3
|
-
import { getStackingService } from "../services/stacking.service.js";
|
|
3
|
+
import { MAX_STAKE_CYCLES, buildPayoutCalldata, getStackingService, } from "../services/stacking.service.js";
|
|
4
4
|
import { getExplorerTxUrl } from "../config/networks.js";
|
|
5
5
|
import { createJsonResponse, createErrorResponse } from "../utils/index.js";
|
|
6
|
+
import { btcAddressToPoxAddr } from "../utils/bitcoin.js";
|
|
7
|
+
const POX5_NOTE = "PoX-5: every stake names a signer manager contract; rewards are paid in sBTC through that manager.";
|
|
8
|
+
function stx(ustx) {
|
|
9
|
+
const whole = ustx / 1000000n;
|
|
10
|
+
const frac = (ustx % 1000000n).toString().padStart(6, "0");
|
|
11
|
+
return `${whole}.${frac} STX`;
|
|
12
|
+
}
|
|
13
|
+
function poxSummary(pox) {
|
|
14
|
+
return {
|
|
15
|
+
contract: pox.contractId,
|
|
16
|
+
burnHeight: pox.burnHeight,
|
|
17
|
+
rewardCycle: pox.rewardCycle,
|
|
18
|
+
nextCycleStartHeight: pox.nextCycleStartHeight,
|
|
19
|
+
preparePhaseStartHeight: pox.preparePhaseStartHeight,
|
|
20
|
+
inPreparePhase: pox.inPreparePhase,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function stakerSummary(info) {
|
|
24
|
+
return {
|
|
25
|
+
signerManager: info.signerManager,
|
|
26
|
+
amountUstx: info.amountUstx.toString(),
|
|
27
|
+
amount: stx(info.amountUstx),
|
|
28
|
+
firstRewardCycle: info.firstRewardCycle,
|
|
29
|
+
numCycles: info.numCycles,
|
|
30
|
+
unlockCycle: info.unlockCycle,
|
|
31
|
+
unlockBurnHeight: info.unlockBurnHeight,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const payoutInputs = {
|
|
35
|
+
btcRewardAddress: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Optional Bitcoin address to receive rewards. Encoded as `{ pox-addr, max-fee }` signer calldata, " +
|
|
39
|
+
"the shape reference signer managers (e.g. Xverse, Fast Pool) use to pay rewards to BTC via an " +
|
|
40
|
+
"sBTC withdrawal. Some managers require it (PlanBetter), some ignore it (native-pool). " +
|
|
41
|
+
"Omit to receive sBTC where the manager supports that."),
|
|
42
|
+
maxWithdrawalFeeSats: z
|
|
43
|
+
.number()
|
|
44
|
+
.int()
|
|
45
|
+
.nonnegative()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Max sBTC withdrawal fee (sats) per payout when btcRewardAddress is set. Default 3000."),
|
|
48
|
+
signerCalldataHex: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Advanced: raw signer calldata (hex, max 500 bytes) for managers with a custom format. " +
|
|
52
|
+
"Cannot be combined with btcRewardAddress."),
|
|
53
|
+
};
|
|
54
|
+
function resolveCalldata(input) {
|
|
55
|
+
if (input.btcRewardAddress && input.signerCalldataHex) {
|
|
56
|
+
throw new Error("Pass either btcRewardAddress or signerCalldataHex, not both.");
|
|
57
|
+
}
|
|
58
|
+
if (input.btcRewardAddress) {
|
|
59
|
+
return buildPayoutCalldata(btcAddressToPoxAddr(input.btcRewardAddress, NETWORK), BigInt(input.maxWithdrawalFeeSats ?? 3000));
|
|
60
|
+
}
|
|
61
|
+
if (input.signerCalldataHex) {
|
|
62
|
+
const hex = input.signerCalldataHex.replace(/^0x/, "");
|
|
63
|
+
if (!/^([0-9a-fA-F]{2})*$/.test(hex)) {
|
|
64
|
+
throw new Error("signerCalldataHex must be an even-length hex string.");
|
|
65
|
+
}
|
|
66
|
+
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
6
70
|
export function registerStackingTools(server) {
|
|
7
|
-
//
|
|
71
|
+
// --- get_pox_info ---
|
|
8
72
|
server.registerTool("get_pox_info", {
|
|
9
|
-
description: "
|
|
73
|
+
description: "Current PoX-5 state: reward cycle, burn height, next cycle start, and whether the prepare " +
|
|
74
|
+
"phase is active (stake, extend and unstake are refused during it).",
|
|
10
75
|
}, async () => {
|
|
11
76
|
try {
|
|
12
|
-
const
|
|
13
|
-
const poxInfo = await stackingService.getPoxInfo();
|
|
77
|
+
const pox = await getStackingService(NETWORK).getPoxState();
|
|
14
78
|
return createJsonResponse({
|
|
15
79
|
network: NETWORK,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
80
|
+
...poxSummary(pox),
|
|
81
|
+
firstBurnHeight: pox.firstBurnHeight,
|
|
82
|
+
rewardCycleLength: pox.rewardCycleLength,
|
|
83
|
+
prepareCycleLength: pox.prepareCycleLength,
|
|
84
|
+
signerSetMinUstx: pox.signerSetMinUstx.toString(),
|
|
85
|
+
totalLiquidSupplyUstx: pox.totalLiquidSupplyUstx.toString(),
|
|
86
|
+
note: POX5_NOTE,
|
|
23
87
|
});
|
|
24
88
|
}
|
|
25
89
|
catch (error) {
|
|
26
90
|
return createErrorResponse(error);
|
|
27
91
|
}
|
|
28
92
|
});
|
|
29
|
-
//
|
|
93
|
+
// --- get_stacking_status ---
|
|
30
94
|
server.registerTool("get_stacking_status", {
|
|
31
|
-
description: "
|
|
32
|
-
|
|
95
|
+
description: "PoX-5 staking status for an address: locked amount, signer manager, lock period, unlock " +
|
|
96
|
+
"cycle and burn height, any protocol bond membership, and locked/unlocked STX balance.",
|
|
97
|
+
inputSchema: z.object({
|
|
33
98
|
address: z
|
|
34
99
|
.string()
|
|
35
100
|
.optional()
|
|
36
|
-
.describe("
|
|
37
|
-
},
|
|
101
|
+
.describe("Stacks address to check. Uses the active wallet if omitted."),
|
|
102
|
+
}),
|
|
38
103
|
}, async ({ address }) => {
|
|
39
104
|
try {
|
|
40
|
-
const stackingService = getStackingService(NETWORK);
|
|
41
105
|
const walletAddress = address || (await getWalletAddress());
|
|
42
|
-
const status = await
|
|
106
|
+
const status = await getStackingService(NETWORK).getStakingStatus(walletAddress);
|
|
43
107
|
return createJsonResponse({
|
|
44
108
|
address: walletAddress,
|
|
45
109
|
network: NETWORK,
|
|
46
|
-
|
|
110
|
+
staking: status.staking !== null,
|
|
111
|
+
stake: status.staking ? stakerSummary(status.staking) : null,
|
|
112
|
+
bond: status.bond
|
|
113
|
+
? {
|
|
114
|
+
bondIndex: status.bond.bondIndex,
|
|
115
|
+
signerManager: status.bond.signerManager,
|
|
116
|
+
amountUstx: status.bond.amountUstx.toString(),
|
|
117
|
+
amountSats: status.bond.amountSats.toString(),
|
|
118
|
+
isL1Lock: status.bond.isL1Lock,
|
|
119
|
+
}
|
|
120
|
+
: null,
|
|
121
|
+
balance: {
|
|
122
|
+
totalUstx: status.account.balanceUstx.toString(),
|
|
123
|
+
lockedUstx: status.account.lockedUstx.toString(),
|
|
124
|
+
unlockedUstx: status.account.unlockedUstx.toString(),
|
|
125
|
+
burnchainUnlockHeight: status.account.burnchainUnlockHeight,
|
|
126
|
+
},
|
|
127
|
+
pox: poxSummary(status.pox),
|
|
47
128
|
});
|
|
48
129
|
}
|
|
49
130
|
catch (error) {
|
|
50
131
|
return createErrorResponse(error);
|
|
51
132
|
}
|
|
52
133
|
});
|
|
53
|
-
//
|
|
134
|
+
// --- list_stacking_signers ---
|
|
135
|
+
server.registerTool("list_stacking_signers", {
|
|
136
|
+
description: "List PoX-5 signer managers in the signer set for a reward cycle (default: next cycle), with " +
|
|
137
|
+
"the STX delegated to each. A signer manager is what stack_stx stakes with. Only signers with " +
|
|
138
|
+
"at least the signer-set minimum delegated appear here; other registered managers can still be " +
|
|
139
|
+
"staked with by contract id. Set withPayoutInfo to see how each manager pays stakers (slower).",
|
|
140
|
+
inputSchema: z.object({
|
|
141
|
+
rewardCycle: z
|
|
142
|
+
.number()
|
|
143
|
+
.int()
|
|
144
|
+
.positive()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("Reward cycle to list. Defaults to the next cycle (the one a new stake joins)."),
|
|
147
|
+
withPayoutInfo: z
|
|
148
|
+
.boolean()
|
|
149
|
+
.optional()
|
|
150
|
+
.default(false)
|
|
151
|
+
.describe("Also read each manager's claim path (one extra request per signer)."),
|
|
152
|
+
}),
|
|
153
|
+
}, async ({ rewardCycle, withPayoutInfo }) => {
|
|
154
|
+
try {
|
|
155
|
+
const service = getStackingService(NETWORK);
|
|
156
|
+
const pox = await service.getPoxState();
|
|
157
|
+
const cycle = rewardCycle ?? pox.rewardCycle + 1;
|
|
158
|
+
const signers = (await service.getSignerSet(cycle)).sort((a, b) => b.delegatedUstx > a.delegatedUstx ? 1 : b.delegatedUstx < a.delegatedUstx ? -1 : 0);
|
|
159
|
+
const rows = [];
|
|
160
|
+
for (const s of signers) {
|
|
161
|
+
rows.push({
|
|
162
|
+
signerManager: s.signerManager,
|
|
163
|
+
delegatedUstx: s.delegatedUstx.toString(),
|
|
164
|
+
delegated: stx(s.delegatedUstx),
|
|
165
|
+
...(withPayoutInfo && { stakerClaim: await service.getClaimStyle(s.signerManager) }),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return createJsonResponse({
|
|
169
|
+
network: NETWORK,
|
|
170
|
+
rewardCycle: cycle,
|
|
171
|
+
count: rows.length,
|
|
172
|
+
signers: rows,
|
|
173
|
+
...(withPayoutInfo && {
|
|
174
|
+
stakerClaimKey: {
|
|
175
|
+
"staker-arg": "on-chain; claim_stacking_rewards works",
|
|
176
|
+
caller: "on-chain; claim_stacking_rewards works (paid to the caller)",
|
|
177
|
+
none: "no on-chain staker claim; the manager pays off-chain or not at all",
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
note: "Managers can restrict who may stake (allowlists, minimums, required payout calldata). " +
|
|
181
|
+
"Check a manager's own terms before staking; a refused stake aborts on chain.",
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return createErrorResponse(error);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// --- stack_stx ---
|
|
54
189
|
server.registerTool("stack_stx", {
|
|
55
|
-
description: `Lock STX
|
|
190
|
+
description: `Lock STX in PoX-5 with a signer manager to earn sBTC rewards.
|
|
56
191
|
|
|
57
|
-
|
|
58
|
-
inputSchema: {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
192
|
+
The lock starts next reward cycle and lasts numCycles cycles (1-${MAX_STAKE_CYCLES}). Pick a signer manager with list_stacking_signers. Refused during the prepare phase and if the address is already staking (use extend_stacking). Locking is not a transfer: the transaction carries a staking post-condition for exactly the locked amount.`,
|
|
193
|
+
inputSchema: z.object({
|
|
194
|
+
signerManager: z
|
|
195
|
+
.string()
|
|
196
|
+
.describe("Signer manager contract id, e.g. SP8HK160YD5GHXP69VGA0TC7AQJ1X4CDW3XVERSE.xverse-signer-manager-2"),
|
|
197
|
+
amount: z.string().describe("Amount to lock, in micro-STX"),
|
|
198
|
+
numCycles: z
|
|
199
|
+
.number()
|
|
200
|
+
.int()
|
|
201
|
+
.min(1)
|
|
202
|
+
.max(MAX_STAKE_CYCLES)
|
|
203
|
+
.describe(`Reward cycles to lock (1-${MAX_STAKE_CYCLES}; one cycle is about two weeks)`),
|
|
204
|
+
...payoutInputs,
|
|
205
|
+
}),
|
|
206
|
+
}, async ({ signerManager, amount, numCycles, ...payout }) => {
|
|
66
207
|
try {
|
|
67
|
-
const stackingService = getStackingService(NETWORK);
|
|
68
208
|
const account = await getAccount();
|
|
69
|
-
const result = await
|
|
209
|
+
const result = await getStackingService(NETWORK).stake(account, {
|
|
210
|
+
signerManager,
|
|
211
|
+
amountUstx: BigInt(amount),
|
|
212
|
+
numCycles,
|
|
213
|
+
signerCalldata: resolveCalldata(payout),
|
|
214
|
+
});
|
|
70
215
|
return createJsonResponse({
|
|
71
216
|
success: true,
|
|
72
217
|
txid: result.txid,
|
|
73
|
-
stacker: account.address,
|
|
74
|
-
amount,
|
|
75
|
-
lockPeriod,
|
|
76
|
-
startBurnHeight,
|
|
77
|
-
network: NETWORK,
|
|
78
218
|
explorerUrl: getExplorerTxUrl(result.txid, NETWORK),
|
|
219
|
+
staker: account.address,
|
|
220
|
+
signerManager,
|
|
221
|
+
amountUstx: amount,
|
|
222
|
+
amount: stx(BigInt(amount)),
|
|
223
|
+
firstRewardCycle: result.pox.rewardCycle + 1,
|
|
224
|
+
numCycles,
|
|
225
|
+
unlockCycle: result.unlockCycle,
|
|
226
|
+
unlockBurnHeight: result.unlockBurnHeight,
|
|
227
|
+
rewardPayout: payout.btcRewardAddress
|
|
228
|
+
? `BTC to ${payout.btcRewardAddress} (if the manager supports it)`
|
|
229
|
+
: payout.signerCalldataHex
|
|
230
|
+
? "custom signer calldata"
|
|
231
|
+
: "sBTC (if the manager supports it)",
|
|
232
|
+
network: NETWORK,
|
|
79
233
|
});
|
|
80
234
|
}
|
|
81
235
|
catch (error) {
|
|
82
236
|
return createErrorResponse(error);
|
|
83
237
|
}
|
|
84
238
|
});
|
|
85
|
-
//
|
|
239
|
+
// --- extend_stacking ---
|
|
86
240
|
server.registerTool("extend_stacking", {
|
|
87
|
-
description:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
241
|
+
description: `Update an existing PoX-5 stake: extend the lock, add STX, switch signer manager, or change payout calldata (pox-5 stake-update).
|
|
242
|
+
|
|
243
|
+
Any combination in one call. Increasing locks more of the address's unlocked STX. Refused during the prepare phase. The lock may not run more than ${MAX_STAKE_CYCLES} cycles past the next cycle.`,
|
|
244
|
+
inputSchema: z.object({
|
|
245
|
+
cyclesToExtend: z
|
|
246
|
+
.number()
|
|
247
|
+
.int()
|
|
248
|
+
.nonnegative()
|
|
249
|
+
.optional()
|
|
250
|
+
.default(0)
|
|
251
|
+
.describe("Cycles to add to the current unlock cycle"),
|
|
252
|
+
amountIncrease: z
|
|
253
|
+
.string()
|
|
254
|
+
.optional()
|
|
255
|
+
.default("0")
|
|
256
|
+
.describe("Additional micro-STX to lock"),
|
|
257
|
+
signerManager: z
|
|
258
|
+
.string()
|
|
259
|
+
.optional()
|
|
260
|
+
.describe("New signer manager contract id. Defaults to the current one."),
|
|
261
|
+
...payoutInputs,
|
|
262
|
+
}),
|
|
263
|
+
}, async ({ cyclesToExtend, amountIncrease, signerManager, ...payout }) => {
|
|
94
264
|
try {
|
|
95
|
-
const stackingService = getStackingService(NETWORK);
|
|
96
265
|
const account = await getAccount();
|
|
97
|
-
const result = await
|
|
266
|
+
const result = await getStackingService(NETWORK).updateStake(account, {
|
|
267
|
+
signerManager,
|
|
268
|
+
cyclesToExtend,
|
|
269
|
+
amountIncreaseUstx: BigInt(amountIncrease),
|
|
270
|
+
signerCalldata: resolveCalldata(payout),
|
|
271
|
+
});
|
|
98
272
|
return createJsonResponse({
|
|
99
273
|
success: true,
|
|
100
274
|
txid: result.txid,
|
|
101
|
-
|
|
102
|
-
|
|
275
|
+
explorerUrl: getExplorerTxUrl(result.txid, NETWORK),
|
|
276
|
+
staker: account.address,
|
|
277
|
+
previous: stakerSummary(result.previous),
|
|
278
|
+
signerManager: result.signerManager,
|
|
279
|
+
newAmountUstx: result.newAmountUstx.toString(),
|
|
280
|
+
newAmount: stx(result.newAmountUstx),
|
|
281
|
+
unlockCycle: result.unlockCycle,
|
|
282
|
+
unlockBurnHeight: result.unlockBurnHeight,
|
|
103
283
|
network: NETWORK,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
return createErrorResponse(error);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
// --- unstake_stx ---
|
|
291
|
+
server.registerTool("unstake_stx", {
|
|
292
|
+
description: "Stop a PoX-5 stake early (pox-5 unstake). The STX stays locked through the current reward " +
|
|
293
|
+
"cycle and unlocks at the start of the next one. Refused during the prepare phase.",
|
|
294
|
+
inputSchema: z.object({}),
|
|
295
|
+
}, async () => {
|
|
296
|
+
try {
|
|
297
|
+
const account = await getAccount();
|
|
298
|
+
const result = await getStackingService(NETWORK).unstake(account);
|
|
299
|
+
return createJsonResponse({
|
|
300
|
+
success: true,
|
|
301
|
+
txid: result.txid,
|
|
104
302
|
explorerUrl: getExplorerTxUrl(result.txid, NETWORK),
|
|
303
|
+
staker: account.address,
|
|
304
|
+
previous: stakerSummary(result.previous),
|
|
305
|
+
unlockCycle: result.unlockCycle,
|
|
306
|
+
unlockBurnHeight: result.unlockBurnHeight,
|
|
307
|
+
network: NETWORK,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
return createErrorResponse(error);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
// --- get_stacking_rewards ---
|
|
315
|
+
server.registerTool("get_stacking_rewards", {
|
|
316
|
+
description: "PoX-5 sBTC rewards an address has earned from its signer manager for one reward cycle and not " +
|
|
317
|
+
"yet claimed (before the manager's fees), whether the manager still has to pull them from pox-5, " +
|
|
318
|
+
"and whether the manager supports an on-chain staker claim.",
|
|
319
|
+
inputSchema: z.object({
|
|
320
|
+
rewardCycle: z.number().int().nonnegative().describe("Reward cycle to check"),
|
|
321
|
+
address: z
|
|
322
|
+
.string()
|
|
323
|
+
.optional()
|
|
324
|
+
.describe("Stacks address. Uses the active wallet if omitted."),
|
|
325
|
+
signerManager: z
|
|
326
|
+
.string()
|
|
327
|
+
.optional()
|
|
328
|
+
.describe("Signer manager to check. Defaults to the address's current signer manager."),
|
|
329
|
+
}),
|
|
330
|
+
}, async ({ rewardCycle, address, signerManager }) => {
|
|
331
|
+
try {
|
|
332
|
+
const service = getStackingService(NETWORK);
|
|
333
|
+
const staker = address || (await getWalletAddress());
|
|
334
|
+
const manager = signerManager ?? (await service.getStakerInfo(staker))?.signerManager;
|
|
335
|
+
if (!manager) {
|
|
336
|
+
throw new Error(`${staker} is not currently staking; pass signerManager to check rewards from a past signer.`);
|
|
337
|
+
}
|
|
338
|
+
const [earnedSats, unpulledSats, claimStyle] = await Promise.all([
|
|
339
|
+
service.getStakerUnclaimedRewards(manager, rewardCycle, staker),
|
|
340
|
+
service.getSignerUnpulledRewards(manager, rewardCycle),
|
|
341
|
+
service.getClaimStyle(manager),
|
|
342
|
+
]);
|
|
343
|
+
return createJsonResponse({
|
|
344
|
+
address: staker,
|
|
345
|
+
network: NETWORK,
|
|
346
|
+
rewardCycle,
|
|
347
|
+
signerManager: manager,
|
|
348
|
+
unclaimedSatsBeforeFees: earnedSats.toString(),
|
|
349
|
+
managerUnpulledSats: unpulledSats.toString(),
|
|
350
|
+
stakerClaim: claimStyle,
|
|
351
|
+
next: earnedSats === 0n
|
|
352
|
+
? "Nothing to claim for this cycle."
|
|
353
|
+
: claimStyle === "none"
|
|
354
|
+
? "This manager has no on-chain staker claim; it pays stakers off-chain."
|
|
355
|
+
: "Call claim_stacking_rewards with this rewardCycle.",
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
return createErrorResponse(error);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
// --- claim_stacking_rewards ---
|
|
363
|
+
server.registerTool("claim_stacking_rewards", {
|
|
364
|
+
description: "Claim PoX-5 sBTC rewards for one reward cycle through the staker's signer manager. If the " +
|
|
365
|
+
"manager has not yet pulled that cycle's rewards from pox-5, this first sends the manager's " +
|
|
366
|
+
"permissionless claim-rewards (a second transaction, paid by this wallet), then the staker " +
|
|
367
|
+
"claim. Rewards arrive as sBTC, or as a BTC withdrawal if a payout address was set when " +
|
|
368
|
+
"staking. Not available for managers that pay off-chain.",
|
|
369
|
+
inputSchema: z.object({
|
|
370
|
+
rewardCycle: z.number().int().nonnegative().describe("Reward cycle to claim"),
|
|
371
|
+
signerManager: z
|
|
372
|
+
.string()
|
|
373
|
+
.optional()
|
|
374
|
+
.describe("Signer manager to claim from. Defaults to the wallet's current signer manager."),
|
|
375
|
+
}),
|
|
376
|
+
}, async ({ rewardCycle, signerManager }) => {
|
|
377
|
+
try {
|
|
378
|
+
const service = getStackingService(NETWORK);
|
|
379
|
+
const account = await getAccount();
|
|
380
|
+
const manager = signerManager ?? (await service.getStakerInfo(account.address))?.signerManager;
|
|
381
|
+
if (!manager) {
|
|
382
|
+
throw new Error(`${account.address} is not currently staking; pass signerManager to claim from a past signer.`);
|
|
383
|
+
}
|
|
384
|
+
const [earnedSats, unpulledSats, claimStyle] = await Promise.all([
|
|
385
|
+
service.getStakerUnclaimedRewards(manager, rewardCycle, account.address),
|
|
386
|
+
service.getSignerUnpulledRewards(manager, rewardCycle),
|
|
387
|
+
service.getClaimStyle(manager),
|
|
388
|
+
]);
|
|
389
|
+
if (claimStyle === "none") {
|
|
390
|
+
throw new Error(`${manager} has no on-chain staker claim (claim-staker-rewards); it pays stakers off-chain.`);
|
|
391
|
+
}
|
|
392
|
+
if (earnedSats === 0n) {
|
|
393
|
+
throw new Error(`No unclaimed rewards for ${account.address} from ${manager} in cycle ${rewardCycle}.`);
|
|
394
|
+
}
|
|
395
|
+
let pull = null;
|
|
396
|
+
if (unpulledSats > 0n) {
|
|
397
|
+
const pulled = await service.pullSignerRewards(account, manager, rewardCycle, unpulledSats);
|
|
398
|
+
pull = { txid: pulled.txid, explorerUrl: getExplorerTxUrl(pulled.txid, NETWORK) };
|
|
399
|
+
}
|
|
400
|
+
const claim = await service.claimStakerRewards(account, manager, rewardCycle, claimStyle);
|
|
401
|
+
return createJsonResponse({
|
|
402
|
+
success: true,
|
|
403
|
+
network: NETWORK,
|
|
404
|
+
rewardCycle,
|
|
405
|
+
signerManager: manager,
|
|
406
|
+
unclaimedSatsBeforeFees: earnedSats.toString(),
|
|
407
|
+
managerPull: pull,
|
|
408
|
+
txid: claim.txid,
|
|
409
|
+
explorerUrl: getExplorerTxUrl(claim.txid, NETWORK),
|
|
410
|
+
note: pull
|
|
411
|
+
? "Two transactions were sent in nonce order: the manager's pull, then the staker claim."
|
|
412
|
+
: undefined,
|
|
105
413
|
});
|
|
106
414
|
}
|
|
107
415
|
catch (error) {
|
|
@@ -17,6 +17,6 @@
|
|
|
17
17
|
* - stacks_market_redeem — Redeem winning shares after market resolution
|
|
18
18
|
* - stacks_market_get_position — Check YES/NO share balances for any address
|
|
19
19
|
*/
|
|
20
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
20
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
21
21
|
export declare function registerStacksMarketTools(server: McpServer): void;
|
|
22
22
|
//# sourceMappingURL=stacks-market.tools.d.ts.map
|
|
@@ -85,7 +85,7 @@ export function registerStacksMarketTools(server) {
|
|
|
85
85
|
Returns a paginated list of markets with optional filtering by status, category, or featured flag.
|
|
86
86
|
|
|
87
87
|
Note: Stacks Market is only available on mainnet.`,
|
|
88
|
-
inputSchema: {
|
|
88
|
+
inputSchema: z.object({
|
|
89
89
|
limit: z
|
|
90
90
|
.number()
|
|
91
91
|
.optional()
|
|
@@ -103,7 +103,7 @@ Note: Stacks Market is only available on mainnet.`,
|
|
|
103
103
|
.boolean()
|
|
104
104
|
.optional()
|
|
105
105
|
.describe("When true, return only featured markets"),
|
|
106
|
-
},
|
|
106
|
+
}),
|
|
107
107
|
}, async ({ limit, status, category, featured }) => {
|
|
108
108
|
try {
|
|
109
109
|
if (NETWORK !== "mainnet") {
|
|
@@ -137,14 +137,14 @@ Note: Stacks Market is only available on mainnet.`,
|
|
|
137
137
|
Searches across market titles and descriptions.
|
|
138
138
|
|
|
139
139
|
Note: Stacks Market is only available on mainnet.`,
|
|
140
|
-
inputSchema: {
|
|
140
|
+
inputSchema: z.object({
|
|
141
141
|
query: z.string().describe("Search keyword or phrase"),
|
|
142
142
|
limit: z
|
|
143
143
|
.number()
|
|
144
144
|
.optional()
|
|
145
145
|
.default(20)
|
|
146
146
|
.describe("Maximum number of results to return (default 20)"),
|
|
147
|
-
},
|
|
147
|
+
}),
|
|
148
148
|
}, async ({ query, limit }) => {
|
|
149
149
|
try {
|
|
150
150
|
if (NETWORK !== "mainnet") {
|
|
@@ -172,11 +172,11 @@ Note: Stacks Market is only available on mainnet.`,
|
|
|
172
172
|
Market IDs are epoch millisecond timestamps (uint) visible in market URLs.
|
|
173
173
|
|
|
174
174
|
Note: Stacks Market is only available on mainnet.`,
|
|
175
|
-
inputSchema: {
|
|
175
|
+
inputSchema: z.object({
|
|
176
176
|
market_id: z
|
|
177
177
|
.string()
|
|
178
178
|
.describe("Market ID (epoch ms timestamp, e.g. '1710000000000')"),
|
|
179
|
-
},
|
|
179
|
+
}),
|
|
180
180
|
}, async ({ market_id }) => {
|
|
181
181
|
try {
|
|
182
182
|
if (NETWORK !== "mainnet") {
|
|
@@ -202,7 +202,7 @@ Returns the cost in micro-STX to buy the requested number of shares.
|
|
|
202
202
|
Side: 0 = YES, 1 = NO.
|
|
203
203
|
|
|
204
204
|
Note: Stacks Market is only available on mainnet.`,
|
|
205
|
-
inputSchema: {
|
|
205
|
+
inputSchema: z.object({
|
|
206
206
|
market_id: z
|
|
207
207
|
.string()
|
|
208
208
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -214,7 +214,7 @@ Note: Stacks Market is only available on mainnet.`,
|
|
|
214
214
|
.min(0)
|
|
215
215
|
.max(1)
|
|
216
216
|
.describe("0 = YES shares, 1 = NO shares"),
|
|
217
|
-
},
|
|
217
|
+
}),
|
|
218
218
|
}, async ({ market_id, shares, side }) => {
|
|
219
219
|
try {
|
|
220
220
|
if (NETWORK !== "mainnet") {
|
|
@@ -252,7 +252,7 @@ Returns the proceeds in micro-STX for selling the requested number of shares.
|
|
|
252
252
|
Side: 0 = YES, 1 = NO.
|
|
253
253
|
|
|
254
254
|
Note: Stacks Market is only available on mainnet.`,
|
|
255
|
-
inputSchema: {
|
|
255
|
+
inputSchema: z.object({
|
|
256
256
|
market_id: z
|
|
257
257
|
.string()
|
|
258
258
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -264,7 +264,7 @@ Note: Stacks Market is only available on mainnet.`,
|
|
|
264
264
|
.min(0)
|
|
265
265
|
.max(1)
|
|
266
266
|
.describe("0 = YES shares, 1 = NO shares"),
|
|
267
|
-
},
|
|
267
|
+
}),
|
|
268
268
|
}, async ({ market_id, shares, side }) => {
|
|
269
269
|
try {
|
|
270
270
|
if (NETWORK !== "mainnet") {
|
|
@@ -302,7 +302,7 @@ Uses the buy-yes-auto function with slippage protection via a max-cost cap.
|
|
|
302
302
|
The transaction will fail if the cost exceeds max_cost_ustx.
|
|
303
303
|
|
|
304
304
|
Requires an unlocked wallet. Only available on mainnet.`,
|
|
305
|
-
inputSchema: {
|
|
305
|
+
inputSchema: z.object({
|
|
306
306
|
market_id: z
|
|
307
307
|
.string()
|
|
308
308
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -315,7 +315,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
|
|
|
315
315
|
max_cost_ustx: z
|
|
316
316
|
.string()
|
|
317
317
|
.describe("Maximum cost in micro-STX you are willing to pay (slippage protection)"),
|
|
318
|
-
},
|
|
318
|
+
}),
|
|
319
319
|
}, async ({ market_id, shares, target_cap_ustx, max_cost_ustx }) => {
|
|
320
320
|
try {
|
|
321
321
|
if (NETWORK !== "mainnet") {
|
|
@@ -362,7 +362,7 @@ Uses the buy-no-auto function with slippage protection via a max-cost cap.
|
|
|
362
362
|
The transaction will fail if the cost exceeds max_cost_ustx.
|
|
363
363
|
|
|
364
364
|
Requires an unlocked wallet. Only available on mainnet.`,
|
|
365
|
-
inputSchema: {
|
|
365
|
+
inputSchema: z.object({
|
|
366
366
|
market_id: z
|
|
367
367
|
.string()
|
|
368
368
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -375,7 +375,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
|
|
|
375
375
|
max_cost_ustx: z
|
|
376
376
|
.string()
|
|
377
377
|
.describe("Maximum cost in micro-STX you are willing to pay (slippage protection)"),
|
|
378
|
-
},
|
|
378
|
+
}),
|
|
379
379
|
}, async ({ market_id, shares, target_cap_ustx, max_cost_ustx }) => {
|
|
380
380
|
try {
|
|
381
381
|
if (NETWORK !== "mainnet") {
|
|
@@ -422,7 +422,7 @@ Uses the sell-yes-auto function with a minimum proceeds guard.
|
|
|
422
422
|
The transaction will fail if proceeds fall below min_proceeds_ustx.
|
|
423
423
|
|
|
424
424
|
Requires an unlocked wallet. Only available on mainnet.`,
|
|
425
|
-
inputSchema: {
|
|
425
|
+
inputSchema: z.object({
|
|
426
426
|
market_id: z
|
|
427
427
|
.string()
|
|
428
428
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -432,7 +432,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
|
|
|
432
432
|
min_proceeds_ustx: z
|
|
433
433
|
.string()
|
|
434
434
|
.describe("Minimum acceptable proceeds in micro-STX (slippage protection)"),
|
|
435
|
-
},
|
|
435
|
+
}),
|
|
436
436
|
}, async ({ market_id, shares, min_proceeds_ustx }) => {
|
|
437
437
|
try {
|
|
438
438
|
if (NETWORK !== "mainnet") {
|
|
@@ -478,7 +478,7 @@ Uses the sell-no-auto function with a minimum proceeds guard.
|
|
|
478
478
|
The transaction will fail if proceeds fall below min_proceeds_ustx.
|
|
479
479
|
|
|
480
480
|
Requires an unlocked wallet. Only available on mainnet.`,
|
|
481
|
-
inputSchema: {
|
|
481
|
+
inputSchema: z.object({
|
|
482
482
|
market_id: z
|
|
483
483
|
.string()
|
|
484
484
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -488,7 +488,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
|
|
|
488
488
|
min_proceeds_ustx: z
|
|
489
489
|
.string()
|
|
490
490
|
.describe("Minimum acceptable proceeds in micro-STX (slippage protection)"),
|
|
491
|
-
},
|
|
491
|
+
}),
|
|
492
492
|
}, async ({ market_id, shares, min_proceeds_ustx }) => {
|
|
493
493
|
try {
|
|
494
494
|
if (NETWORK !== "mainnet") {
|
|
@@ -533,11 +533,11 @@ Requires an unlocked wallet. Only available on mainnet.`,
|
|
|
533
533
|
Call this after the market has been resolved to claim STX for the winning side shares you hold.
|
|
534
534
|
|
|
535
535
|
Requires an unlocked wallet. Only available on mainnet.`,
|
|
536
|
-
inputSchema: {
|
|
536
|
+
inputSchema: z.object({
|
|
537
537
|
market_id: z
|
|
538
538
|
.string()
|
|
539
539
|
.describe("Market ID (epoch ms timestamp) of the resolved market"),
|
|
540
|
-
},
|
|
540
|
+
}),
|
|
541
541
|
}, async ({ market_id }) => {
|
|
542
542
|
try {
|
|
543
543
|
if (NETWORK !== "mainnet") {
|
|
@@ -577,7 +577,7 @@ If no address is provided, uses the currently configured wallet address.
|
|
|
577
577
|
No wallet required when an address is explicitly supplied.
|
|
578
578
|
|
|
579
579
|
Note: Stacks Market is only available on mainnet.`,
|
|
580
|
-
inputSchema: {
|
|
580
|
+
inputSchema: z.object({
|
|
581
581
|
market_id: z
|
|
582
582
|
.string()
|
|
583
583
|
.describe("Market ID (epoch ms timestamp)"),
|
|
@@ -585,7 +585,7 @@ Note: Stacks Market is only available on mainnet.`,
|
|
|
585
585
|
.string()
|
|
586
586
|
.optional()
|
|
587
587
|
.describe("Stacks address to check position for. Uses configured wallet if not provided."),
|
|
588
|
-
},
|
|
588
|
+
}),
|
|
589
589
|
}, async ({ market_id, address }) => {
|
|
590
590
|
try {
|
|
591
591
|
if (NETWORK !== "mainnet") {
|
|
@@ -15,6 +15,6 @@
|
|
|
15
15
|
* - styx_status: Deposit status by ID or txid
|
|
16
16
|
* - styx_history: Deposit history for a Stacks address
|
|
17
17
|
*/
|
|
18
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
18
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
19
19
|
export declare function registerStyxTools(server: McpServer): void;
|
|
20
20
|
//# sourceMappingURL=styx.tools.d.ts.map
|