@aibtc/mcp-server 1.73.0 → 1.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/sbtc.service.d.ts +2 -1
- package/dist/services/sbtc.service.js +7 -2
- package/dist/services/tokens.service.js +9 -2
- package/dist/services/x402.service.js +5 -5
- package/dist/tools/inbox-x402.tools.d.ts +13 -19
- package/dist/tools/inbox-x402.tools.js +43 -33
- package/dist/utils/fee.d.ts +41 -2
- package/dist/utils/fee.js +15 -1
- package/package.json +2 -2
- package/skill/SKILL.md +1 -1
|
@@ -41,7 +41,8 @@ export declare class SbtcService {
|
|
|
41
41
|
getBalance(address: string): Promise<SbtcBalance>;
|
|
42
42
|
/**
|
|
43
43
|
* Transfer sBTC to a recipient
|
|
44
|
-
* @param fee Optional fee in micro-STX. If omitted,
|
|
44
|
+
* @param fee Optional fee in micro-STX. If omitted, the sbtc_transfer clamp
|
|
45
|
+
* is used rather than callContract's contract_call default.
|
|
45
46
|
*/
|
|
46
47
|
transfer(account: Account, recipient: string, amount: bigint, memo?: string, fee?: bigint, sponsored?: boolean): Promise<TransferResult>;
|
|
47
48
|
/**
|
|
@@ -4,6 +4,7 @@ import { getContracts, parseContractId } from "../config/index.js";
|
|
|
4
4
|
import { callContract } from "../transactions/builder.js";
|
|
5
5
|
import { sponsoredContractCall } from "../transactions/sponsor-builder.js";
|
|
6
6
|
import { createFungiblePostCondition } from "../transactions/post-conditions.js";
|
|
7
|
+
import { resolveDefaultFee } from "../utils/fee.js";
|
|
7
8
|
// ============================================================================
|
|
8
9
|
// sBTC Service
|
|
9
10
|
// ============================================================================
|
|
@@ -33,7 +34,8 @@ export class SbtcService {
|
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
36
|
* Transfer sBTC to a recipient
|
|
36
|
-
* @param fee Optional fee in micro-STX. If omitted,
|
|
37
|
+
* @param fee Optional fee in micro-STX. If omitted, the sbtc_transfer clamp
|
|
38
|
+
* is used rather than callContract's contract_call default.
|
|
37
39
|
*/
|
|
38
40
|
async transfer(account, recipient, amount, memo, fee, sponsored) {
|
|
39
41
|
const sbtcContract = this.contracts.SBTC_TOKEN;
|
|
@@ -57,7 +59,10 @@ export class SbtcService {
|
|
|
57
59
|
if (sponsored) {
|
|
58
60
|
return sponsoredContractCall(account, contractCallOptions, this.network);
|
|
59
61
|
}
|
|
60
|
-
return callContract(account,
|
|
62
|
+
return callContract(account, {
|
|
63
|
+
...contractCallOptions,
|
|
64
|
+
fee: fee ?? (await resolveDefaultFee(this.network, "sbtc_transfer")),
|
|
65
|
+
});
|
|
61
66
|
}
|
|
62
67
|
/**
|
|
63
68
|
* Get sBTC deposit instructions
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { uintCV, principalCV, noneCV, someCV, bufferCV } from "@stacks/transactions";
|
|
2
2
|
import { getHiroApi } from "./hiro-api.js";
|
|
3
|
-
import { parseContractId, getWellKnownTokens } from "../config/index.js";
|
|
3
|
+
import { parseContractId, getWellKnownTokens, getContracts } from "../config/index.js";
|
|
4
4
|
import { callContract } from "../transactions/builder.js";
|
|
5
5
|
import { createFungiblePostCondition } from "../transactions/post-conditions.js";
|
|
6
|
+
import { resolveDefaultFee } from "../utils/fee.js";
|
|
6
7
|
// ============================================================================
|
|
7
8
|
// Tokens Service (SIP-010)
|
|
8
9
|
// ============================================================================
|
|
@@ -102,7 +103,13 @@ export class TokensService {
|
|
|
102
103
|
functionName: "transfer",
|
|
103
104
|
functionArgs,
|
|
104
105
|
postConditions: [postCondition],
|
|
105
|
-
|
|
106
|
+
// A plain sBTC transfer gets the sbtc_transfer clamp, not the
|
|
107
|
+
// contract_call default that callContract would apply.
|
|
108
|
+
...(fee !== undefined
|
|
109
|
+
? { fee }
|
|
110
|
+
: contractId === getContracts(this.network).SBTC_TOKEN && {
|
|
111
|
+
fee: await resolveDefaultFee(this.network, "sbtc_transfer"),
|
|
112
|
+
}),
|
|
106
113
|
});
|
|
107
114
|
}
|
|
108
115
|
/**
|
|
@@ -705,10 +705,10 @@ export async function createApiClient(baseUrl, options) {
|
|
|
705
705
|
.willSendEq(amount)
|
|
706
706
|
.ft(contracts.SBTC_TOKEN, "sbtc-token"),
|
|
707
707
|
],
|
|
708
|
-
// Clamped
|
|
709
|
-
//
|
|
710
|
-
//
|
|
711
|
-
fee: await resolveDefaultFee(acct.network, "
|
|
708
|
+
// Clamped fee — do NOT let @stacks auto-estimate, since Hiro's
|
|
709
|
+
// contract_call tiers are polluted by outliers (observed >2000
|
|
710
|
+
// STX). The sbtc_transfer clamp caps it at 0.003 STX.
|
|
711
|
+
fee: await resolveDefaultFee(acct.network, "sbtc_transfer"),
|
|
712
712
|
});
|
|
713
713
|
}
|
|
714
714
|
else {
|
|
@@ -1062,7 +1062,7 @@ export async function checkSufficientBalance(account, amount, asset, sponsored =
|
|
|
1062
1062
|
const stxBalanceForSbtc = BigInt(stxInfoForSbtc.balance);
|
|
1063
1063
|
// Same clamped fee the interceptor actually sets on the sBTC contract
|
|
1064
1064
|
// call — so this check is exact, not a high-priority over-estimate.
|
|
1065
|
-
const estimatedSbtcFee = await resolveDefaultFee(account.network, "
|
|
1065
|
+
const estimatedSbtcFee = await resolveDefaultFee(account.network, "sbtc_transfer");
|
|
1066
1066
|
if (stxBalanceForSbtc < estimatedSbtcFee) {
|
|
1067
1067
|
const stxShortfall = estimatedSbtcFee - stxBalanceForSbtc;
|
|
1068
1068
|
throw new InsufficientBalanceError(`Insufficient STX balance to cover sBTC transfer fee: need ${formatStx(estimatedSbtcFee.toString())} estimated fee, ` +
|
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
/**
|
|
3
|
-
* Pre-flight balance check for
|
|
4
|
-
* Verifies the wallet holds enough sBTC for the message
|
|
5
|
-
*
|
|
3
|
+
* Pre-flight balance check for an sBTC inbox payment.
|
|
4
|
+
* Verifies the wallet holds enough sBTC for the message, and — unless the
|
|
5
|
+
* payment is sponsored — enough STX for a realistic transfer fee.
|
|
6
6
|
*/
|
|
7
|
-
export declare function checkDirectInboxBalance(address: string, amount: string): Promise<void>;
|
|
7
|
+
export declare function checkDirectInboxBalance(address: string, amount: string, sponsored?: boolean): Promise<void>;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* x402 inbox messaging via the `x402-stacks` client interceptor
|
|
10
|
+
* (`wrapAxiosWithPayment`). The transaction is signed but not broadcast; the
|
|
11
|
+
* inbox endpoint settles it through its x402 facilitator.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* (
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* where the *sender pays its own STX gas*. The transaction is signed but not
|
|
16
|
-
* broadcast; the inbox endpoint settles it via the x402 facilitator's
|
|
17
|
-
* `/settle` flow (no relay sponsorship).
|
|
13
|
+
* The inbox decides who pays gas. When its 402 advertises `extra.feePayer`,
|
|
14
|
+
* x402-stacks (>=2.1.0) signs a sponsored sBTC transfer with fee 0 and the
|
|
15
|
+
* facilitator co-signs and pays the STX gas. Without it, the SDK signs a
|
|
16
|
+
* standard transfer and the sender pays its own gas.
|
|
18
17
|
*
|
|
19
18
|
* Server side: `lib/inbox/x402-verify.ts` deserializes the payment tx and
|
|
20
|
-
* branches on `tx.auth.authType
|
|
21
|
-
*
|
|
22
|
-
* adds a sponsor signature or pays gas.
|
|
23
|
-
*
|
|
24
|
-
* Trade-off vs the sponsored tool: the sender must hold STX to cover the
|
|
25
|
-
* transfer fee (the pre-flight check enforces this). In exchange there is no
|
|
26
|
-
* relay in the middle of the payment authorization.
|
|
19
|
+
* branches on `tx.auth.authType` (sponsored → relay sponsors; standard →
|
|
20
|
+
* relay `/settle` broadcasts as-is).
|
|
27
21
|
*/
|
|
28
22
|
export declare function registerInboxX402Tools(server: McpServer): void;
|
|
29
23
|
//# sourceMappingURL=inbox-x402.tools.d.ts.map
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import axios, { AxiosError } from "axios";
|
|
3
3
|
import { wrapAxiosWithPayment, decodePaymentRequired, decodePaymentResponse, X402_HEADERS, } from "x402-stacks";
|
|
4
4
|
import { getAccount, NETWORK } from "../services/x402.service.js";
|
|
5
|
+
import { getSpendLimiter } from "../services/spend-limiter.js";
|
|
5
6
|
import { getSbtcService } from "../services/sbtc.service.js";
|
|
6
7
|
import { getHiroApi } from "../services/hiro-api.js";
|
|
7
8
|
import { getExplorerTxUrl } from "../config/networks.js";
|
|
@@ -18,13 +19,13 @@ const INBOX_BASE = "https://aibtc.com/api/inbox";
|
|
|
18
19
|
* We budget against `medium_priority`, capped at this ceiling, so the pre-flight
|
|
19
20
|
* reflects what the transaction will actually cost.
|
|
20
21
|
*/
|
|
21
|
-
const MAX_REALISTIC_FEE_USTX =
|
|
22
|
+
const MAX_REALISTIC_FEE_USTX = 3000n; // 0.003 STX — the sbtc_transfer fee ceiling
|
|
22
23
|
/**
|
|
23
|
-
* Pre-flight balance check for
|
|
24
|
-
* Verifies the wallet holds enough sBTC for the message
|
|
25
|
-
*
|
|
24
|
+
* Pre-flight balance check for an sBTC inbox payment.
|
|
25
|
+
* Verifies the wallet holds enough sBTC for the message, and — unless the
|
|
26
|
+
* payment is sponsored — enough STX for a realistic transfer fee.
|
|
26
27
|
*/
|
|
27
|
-
export async function checkDirectInboxBalance(address, amount) {
|
|
28
|
+
export async function checkDirectInboxBalance(address, amount, sponsored = false) {
|
|
28
29
|
const sbtcService = getSbtcService(NETWORK);
|
|
29
30
|
const sbtcBalance = BigInt((await sbtcService.getBalance(address)).balance);
|
|
30
31
|
const required = BigInt(amount);
|
|
@@ -33,6 +34,9 @@ export async function checkDirectInboxBalance(address, amount) {
|
|
|
33
34
|
throw new InsufficientBalanceError(`Insufficient sBTC balance: need ${formatSbtc(amount)}, have ${formatSbtc(sbtcBalance.toString())} (shortfall: ${formatSbtc(shortfall.toString())}). ` +
|
|
34
35
|
`Deposit more sBTC via the bridge at https://bridge.stx.eco or use a different wallet.`, "sBTC", sbtcBalance.toString(), amount, shortfall.toString());
|
|
35
36
|
}
|
|
37
|
+
// Sponsored: the facilitator pays the gas, so no STX is needed.
|
|
38
|
+
if (sponsored)
|
|
39
|
+
return;
|
|
36
40
|
const hiro = getHiroApi(NETWORK);
|
|
37
41
|
const stxBalance = BigInt((await hiro.getStxBalance(address)).balance);
|
|
38
42
|
const fees = await hiro.getMempoolFees();
|
|
@@ -53,33 +57,28 @@ export async function checkDirectInboxBalance(address, amount) {
|
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
/**
|
|
56
|
-
*
|
|
60
|
+
* x402 inbox messaging via the `x402-stacks` client interceptor
|
|
61
|
+
* (`wrapAxiosWithPayment`). The transaction is signed but not broadcast; the
|
|
62
|
+
* inbox endpoint settles it through its x402 facilitator.
|
|
57
63
|
*
|
|
58
|
-
*
|
|
59
|
-
* (
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* where the *sender pays its own STX gas*. The transaction is signed but not
|
|
63
|
-
* broadcast; the inbox endpoint settles it via the x402 facilitator's
|
|
64
|
-
* `/settle` flow (no relay sponsorship).
|
|
64
|
+
* The inbox decides who pays gas. When its 402 advertises `extra.feePayer`,
|
|
65
|
+
* x402-stacks (>=2.1.0) signs a sponsored sBTC transfer with fee 0 and the
|
|
66
|
+
* facilitator co-signs and pays the STX gas. Without it, the SDK signs a
|
|
67
|
+
* standard transfer and the sender pays its own gas.
|
|
65
68
|
*
|
|
66
69
|
* Server side: `lib/inbox/x402-verify.ts` deserializes the payment tx and
|
|
67
|
-
* branches on `tx.auth.authType
|
|
68
|
-
*
|
|
69
|
-
* adds a sponsor signature or pays gas.
|
|
70
|
-
*
|
|
71
|
-
* Trade-off vs the sponsored tool: the sender must hold STX to cover the
|
|
72
|
-
* transfer fee (the pre-flight check enforces this). In exchange there is no
|
|
73
|
-
* relay in the middle of the payment authorization.
|
|
70
|
+
* branches on `tx.auth.authType` (sponsored → relay sponsors; standard →
|
|
71
|
+
* relay `/settle` broadcasts as-is).
|
|
74
72
|
*/
|
|
75
73
|
export function registerInboxX402Tools(server) {
|
|
76
74
|
server.registerTool("send_inbox_message_direct", {
|
|
77
75
|
description: "Send a paid x402 message to another agent's inbox on aibtc.com. This is the canonical inbox " +
|
|
78
76
|
"send tool (the older sponsored send_inbox_message is deprecated).\n\n" +
|
|
79
|
-
"It signs
|
|
80
|
-
"
|
|
81
|
-
"the
|
|
82
|
-
"Requires an unlocked wallet holding sBTC (message cost)
|
|
77
|
+
"It signs an sBTC transfer with the x402-stacks client interceptor and the inbox settles it via its " +
|
|
78
|
+
"x402 facilitator. When the inbox offers sponsorship (402 `extra.feePayer`), the transfer is gasless — " +
|
|
79
|
+
"you pay only the sBTC message cost. Otherwise you also pay your own STX gas fee.\n\n" +
|
|
80
|
+
"Requires an unlocked wallet holding sBTC (message cost), plus STX for gas when the inbox does not " +
|
|
81
|
+
"sponsor. Mainnet only.",
|
|
83
82
|
inputSchema: z.object({
|
|
84
83
|
recipientBtcAddress: z
|
|
85
84
|
.string()
|
|
@@ -135,13 +134,19 @@ export function registerInboxX402Tools(server) {
|
|
|
135
134
|
throw new Error("402 response missing or empty payment-required header");
|
|
136
135
|
}
|
|
137
136
|
const accept = paymentRequired.accepts[0];
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
137
|
+
// The SDK signs a sponsored (fee 0) transfer exactly when the
|
|
138
|
+
// requirement names a fee payer — mirror its check.
|
|
139
|
+
const sponsored = typeof accept.extra?.feePayer === "string";
|
|
140
|
+
// Step 2: Pre-flight balance check — sBTC for the message, plus a
|
|
141
|
+
// realistic STX gas budget when the sender pays its own fee.
|
|
142
|
+
await checkDirectInboxBalance(account.address, accept.amount, sponsored);
|
|
143
|
+
// The message cost is a signed sBTC spend: meter it before signing.
|
|
144
|
+
const spendLimiter = getSpendLimiter();
|
|
145
|
+
await spendLimiter.check("sats", BigInt(accept.amount), account.address);
|
|
141
146
|
// Step 3: Build a payment-enabled axios client. wrapAxiosWithPayment
|
|
142
|
-
// installs the x402-stacks interceptor: on a 402 it signs
|
|
143
|
-
// (
|
|
144
|
-
//
|
|
147
|
+
// installs the x402-stacks interceptor: on a 402 it signs the sBTC
|
|
148
|
+
// transfer (sponsored or standard, per `extra.feePayer`) and retries
|
|
149
|
+
// with the payment-signature header.
|
|
145
150
|
const stacksAccount = {
|
|
146
151
|
address: account.address,
|
|
147
152
|
privateKey: account.privateKey,
|
|
@@ -152,13 +157,16 @@ export function registerInboxX402Tools(server) {
|
|
|
152
157
|
const response = await api.post(inboxUrl, body, {
|
|
153
158
|
headers: { "Content-Type": "application/json" },
|
|
154
159
|
});
|
|
160
|
+
await spendLimiter.record("sats", BigInt(accept.amount), account.address);
|
|
155
161
|
// Step 5: Decode the settlement (txid + payer) from the response header.
|
|
156
162
|
const settlement = decodePaymentResponse(response.headers[X402_HEADERS.PAYMENT_RESPONSE]);
|
|
157
163
|
const txid = settlement?.transaction;
|
|
158
164
|
const payer = settlement?.payer;
|
|
159
165
|
return createJsonResponse({
|
|
160
166
|
success: true,
|
|
161
|
-
message:
|
|
167
|
+
message: sponsored
|
|
168
|
+
? "Message delivered (x402, gas sponsored)"
|
|
169
|
+
: "Message delivered (x402, sender paid gas)",
|
|
162
170
|
recipient: {
|
|
163
171
|
btcAddress: recipientBtcAddress,
|
|
164
172
|
stxAddress: recipientStxAddress,
|
|
@@ -166,14 +174,16 @@ export function registerInboxX402Tools(server) {
|
|
|
166
174
|
contentLength: content.length,
|
|
167
175
|
inbox: response.data,
|
|
168
176
|
payment: {
|
|
169
|
-
mode: "direct-x402-nonsponsored",
|
|
177
|
+
mode: sponsored ? "x402-sponsored" : "direct-x402-nonsponsored",
|
|
170
178
|
amount: accept.amount + " sats sBTC",
|
|
171
179
|
...(payer && { payer }),
|
|
172
180
|
...(txid && {
|
|
173
181
|
txid,
|
|
174
182
|
explorer: getExplorerTxUrl(txid, NETWORK),
|
|
175
183
|
}),
|
|
176
|
-
note:
|
|
184
|
+
note: sponsored
|
|
185
|
+
? `STX gas paid by the inbox facilitator (${accept.extra?.feePayer}).`
|
|
186
|
+
: "Sender paid its own STX gas — no relay sponsorship.",
|
|
177
187
|
},
|
|
178
188
|
});
|
|
179
189
|
}
|
package/dist/utils/fee.d.ts
CHANGED
|
@@ -6,6 +6,44 @@
|
|
|
6
6
|
* fee estimates from the Stacks mempool.
|
|
7
7
|
*/
|
|
8
8
|
import type { Network } from "../config/networks.js";
|
|
9
|
+
/**
|
|
10
|
+
* Fee floor and ceiling clamps by transaction type (in micro-STX).
|
|
11
|
+
* Prevents absurd fees during mempool spikes while allowing reasonable variation.
|
|
12
|
+
*
|
|
13
|
+
* Ceilings are set conservatively:
|
|
14
|
+
* token_transfer: 3,000 uSTX (0.003 STX) — simple transfer
|
|
15
|
+
* contract_call: 50,000 uSTX (0.05 STX) — complex contract call
|
|
16
|
+
* smart_contract: 50,000 uSTX (0.05 STX) — deployment
|
|
17
|
+
* These match the x402-sponsor-relay reference implementation.
|
|
18
|
+
*
|
|
19
|
+
* sbtc_transfer is a plain sBTC `transfer` call. It is priced from Hiro's
|
|
20
|
+
* contract_call tier, whose medium estimate is skewed by heavy calls and sat
|
|
21
|
+
* at 2.5 STX, so every x402 sBTC payment paid the 0.05 STX ceiling. Mainnet
|
|
22
|
+
* sBTC transfers confirm at 500-3,000 uSTX.
|
|
23
|
+
*/
|
|
24
|
+
declare const FEE_CLAMPS: {
|
|
25
|
+
readonly token_transfer: {
|
|
26
|
+
readonly floor: 180n;
|
|
27
|
+
readonly ceiling: 3000n;
|
|
28
|
+
};
|
|
29
|
+
readonly contract_call: {
|
|
30
|
+
readonly floor: 3000n;
|
|
31
|
+
readonly ceiling: 50000n;
|
|
32
|
+
};
|
|
33
|
+
readonly sbtc_transfer: {
|
|
34
|
+
readonly floor: 500n;
|
|
35
|
+
readonly ceiling: 3000n;
|
|
36
|
+
};
|
|
37
|
+
readonly smart_contract: {
|
|
38
|
+
readonly floor: 10000n;
|
|
39
|
+
readonly ceiling: 50000n;
|
|
40
|
+
};
|
|
41
|
+
readonly all: {
|
|
42
|
+
readonly floor: 180n;
|
|
43
|
+
readonly ceiling: 50000n;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
type FeeTxType = keyof typeof FEE_CLAMPS;
|
|
9
47
|
/**
|
|
10
48
|
* Valid fee preset strings.
|
|
11
49
|
* These map to the mempool fee priorities:
|
|
@@ -37,7 +75,7 @@ export declare function isFeePreset(value: string): value is FeePreset;
|
|
|
37
75
|
* // Undefined - returns undefined (auto-estimate)
|
|
38
76
|
* await resolveFee(undefined, "mainnet") // -> undefined
|
|
39
77
|
*/
|
|
40
|
-
export declare function resolveFee(fee: string | undefined, network: Network, txType?:
|
|
78
|
+
export declare function resolveFee(fee: string | undefined, network: Network, txType?: FeeTxType): Promise<bigint | undefined>;
|
|
41
79
|
/**
|
|
42
80
|
* Resolve a default medium-priority fee for a given transaction type.
|
|
43
81
|
*
|
|
@@ -52,5 +90,6 @@ export declare function resolveFee(fee: string | undefined, network: Network, tx
|
|
|
52
90
|
* @param txType - The transaction type for ceiling/floor selection
|
|
53
91
|
* @returns Fee in micro-STX as bigint
|
|
54
92
|
*/
|
|
55
|
-
export declare function resolveDefaultFee(network: Network, txType?:
|
|
93
|
+
export declare function resolveDefaultFee(network: Network, txType?: Exclude<FeeTxType, "all">): Promise<bigint>;
|
|
94
|
+
export {};
|
|
56
95
|
//# sourceMappingURL=fee.d.ts.map
|
package/dist/utils/fee.js
CHANGED
|
@@ -15,13 +15,27 @@ import { getHiroApi } from "../services/hiro-api.js";
|
|
|
15
15
|
* contract_call: 50,000 uSTX (0.05 STX) — complex contract call
|
|
16
16
|
* smart_contract: 50,000 uSTX (0.05 STX) — deployment
|
|
17
17
|
* These match the x402-sponsor-relay reference implementation.
|
|
18
|
+
*
|
|
19
|
+
* sbtc_transfer is a plain sBTC `transfer` call. It is priced from Hiro's
|
|
20
|
+
* contract_call tier, whose medium estimate is skewed by heavy calls and sat
|
|
21
|
+
* at 2.5 STX, so every x402 sBTC payment paid the 0.05 STX ceiling. Mainnet
|
|
22
|
+
* sBTC transfers confirm at 500-3,000 uSTX.
|
|
18
23
|
*/
|
|
19
24
|
const FEE_CLAMPS = {
|
|
20
25
|
token_transfer: { floor: 180n, ceiling: 3000n },
|
|
21
26
|
contract_call: { floor: 3000n, ceiling: 50000n },
|
|
27
|
+
sbtc_transfer: { floor: 500n, ceiling: 3000n },
|
|
22
28
|
smart_contract: { floor: 10000n, ceiling: 50000n },
|
|
23
29
|
all: { floor: 180n, ceiling: 50000n }, // Widest range for aggregate fees
|
|
24
30
|
};
|
|
31
|
+
/** Hiro mempool fee tier each clamp type is estimated from. */
|
|
32
|
+
const FEE_TIER = {
|
|
33
|
+
token_transfer: "token_transfer",
|
|
34
|
+
contract_call: "contract_call",
|
|
35
|
+
sbtc_transfer: "contract_call",
|
|
36
|
+
smart_contract: "smart_contract",
|
|
37
|
+
all: "all",
|
|
38
|
+
};
|
|
25
39
|
/**
|
|
26
40
|
* Check if a string is a valid fee preset.
|
|
27
41
|
*/
|
|
@@ -77,7 +91,7 @@ export async function resolveFee(fee, network, txType = "all") {
|
|
|
77
91
|
const hiroApi = getHiroApi(network);
|
|
78
92
|
try {
|
|
79
93
|
const mempoolFees = await hiroApi.getMempoolFees();
|
|
80
|
-
const feeTier = mempoolFees[txType];
|
|
94
|
+
const feeTier = mempoolFees[FEE_TIER[txType]];
|
|
81
95
|
const priorityKey = presetToPriorityKey(fee);
|
|
82
96
|
const rawFee = BigInt(Math.ceil(feeTier[priorityKey]));
|
|
83
97
|
const clamps = FEE_CLAMPS[txType];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aibtc/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.74.0",
|
|
4
4
|
"description": "Bitcoin-native MCP server for AI agents: BTC/STX wallets, DeFi yield, sBTC peg, NFTs, and x402 payments.",
|
|
5
5
|
"mcpName": "io.github.aibtcdev/mcp-server",
|
|
6
6
|
"type": "module",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"sbtc": "^0.3.2",
|
|
75
75
|
"tslib": "^2.8.1",
|
|
76
76
|
"ws": "^8.21.0",
|
|
77
|
-
"x402-stacks": "^2.0
|
|
77
|
+
"x402-stacks": "^2.1.0",
|
|
78
78
|
"zod": "^4.6.5"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
package/skill/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Bitcoin L1 wallet for agents - check balances, send BTC, manage UTX
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: aibtcdev
|
|
7
|
-
version: 1.
|
|
7
|
+
version: 1.74.0 # x-release-please-version
|
|
8
8
|
npm: "@aibtc/mcp-server"
|
|
9
9
|
github: https://github.com/aibtcdev/aibtc-mcp-server
|
|
10
10
|
---
|