bsv-mcp 0.2.9 → 0.2.10
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/index.js +91390 -27617
- package/index.ts +27 -0
- package/package.json +3 -1
- package/tools/index.ts +7 -2
- package/tools/wallet/brc100.ts +572 -0
- package/tools/wallet/createOrdinals.ts +62 -143
- package/tools/wallet/getBalance.ts +29 -25
- package/tools/wallet/purchaseListing.ts +89 -290
- package/tools/wallet/schemas.ts +7 -11
- package/tools/wallet/sendToAddress.ts +46 -47
- package/tools/wallet/tools.ts +12 -86
- package/tools/wallet/transferOrdToken.ts +137 -129
|
@@ -1,326 +1,125 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
5
|
-
type ChangeResult,
|
|
6
|
-
type ExistingListing,
|
|
7
|
-
type LocalSigner,
|
|
8
|
-
oneSatBroadcaster,
|
|
9
|
-
type Payment,
|
|
10
|
-
type PurchaseOrdListingConfig,
|
|
11
|
-
purchaseOrdListing,
|
|
12
|
-
purchaseOrdTokenListing,
|
|
13
|
-
type Royalty,
|
|
14
|
-
TokenType,
|
|
15
|
-
type TokenUtxo,
|
|
16
|
-
type Utxo,
|
|
17
|
-
} from "js-1sat-ord";
|
|
1
|
+
import type { OneSatContext } from '@1sat/actions'
|
|
2
|
+
import { purchaseBsv21, purchaseOrdinal } from '@1sat/actions'
|
|
3
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
|
|
18
5
|
import {
|
|
19
6
|
MARKET_FEE_PERCENTAGE,
|
|
20
7
|
MARKET_WALLET_ADDRESS,
|
|
21
8
|
MINIMUM_MARKET_FEE_SATOSHIS,
|
|
22
|
-
} from
|
|
23
|
-
import { purchaseListingArgsSchema } from
|
|
24
|
-
import type { Wallet } from "./wallet";
|
|
25
|
-
|
|
26
|
-
// Define types for 1Sat API response
|
|
27
|
-
interface OrdUtxo {
|
|
28
|
-
txid: string;
|
|
29
|
-
vout: number;
|
|
30
|
-
satoshis: number;
|
|
31
|
-
script: string;
|
|
32
|
-
origin?: {
|
|
33
|
-
outpoint: string;
|
|
34
|
-
data?: {
|
|
35
|
-
map?: {
|
|
36
|
-
royalties?: string;
|
|
37
|
-
[key: string]: string | number | boolean | null | undefined;
|
|
38
|
-
};
|
|
39
|
-
insc?: {
|
|
40
|
-
text?: string;
|
|
41
|
-
file?: {
|
|
42
|
-
type?: string;
|
|
43
|
-
size?: number;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
data?: {
|
|
49
|
-
list?: {
|
|
50
|
-
price: number;
|
|
51
|
-
payout: string;
|
|
52
|
-
};
|
|
53
|
-
bsv20?: {
|
|
54
|
-
amt?: string;
|
|
55
|
-
tick?: string;
|
|
56
|
-
id?: string;
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Register the purchaseListing tool
|
|
63
|
-
*
|
|
64
|
-
* This tool enables purchasing listed ordinals (NFTs or tokens) from the marketplace:
|
|
65
|
-
* 1. Parses the listing outpoint to get the txid and vout
|
|
66
|
-
* 2. Fetches the listing UTXO from the ordinals API
|
|
67
|
-
* 3. Gets the wallet's payment UTXOs (using the wallet's internal UTXO management)
|
|
68
|
-
* 4. Uses purchaseOrdListing or purchaseOrdTokenListing based on the listing type
|
|
69
|
-
* 5. For NFTs, automatically detects and processes royalty payments to original creators
|
|
70
|
-
* 6. Broadcasts the transaction
|
|
71
|
-
* 7. Returns the transaction details including success status and txid
|
|
72
|
-
*
|
|
73
|
-
* The tool supports both NFT and token listings with appropriate type-specific handling.
|
|
74
|
-
* Royalty payments are supported for NFT purchases only (based on creator-defined metadata).
|
|
75
|
-
*/
|
|
76
|
-
export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
77
|
-
// Store a reference to check if wallet is persistent
|
|
9
|
+
} from '../constants'
|
|
10
|
+
import { purchaseListingArgsSchema } from './schemas'
|
|
78
11
|
|
|
12
|
+
export function registerPurchaseListingTool(
|
|
13
|
+
server: McpServer,
|
|
14
|
+
ctx: OneSatContext | undefined,
|
|
15
|
+
) {
|
|
79
16
|
server.tool(
|
|
80
|
-
|
|
81
|
-
|
|
17
|
+
'wallet_purchaseListing',
|
|
18
|
+
'Purchases a listing from the Bitcoin SV ordinals marketplace. Supports both NFT purchases and BSV21 token purchases.',
|
|
82
19
|
{ ...purchaseListingArgsSchema.shape },
|
|
83
|
-
async ({ listingOutpoint,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
|
|
94
|
-
e,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Fetch the listing info directly from the API
|
|
100
|
-
const response = await fetch(
|
|
101
|
-
`https://ordinals.gorillapool.io/api/txos/${listingOutpoint}?script=true`,
|
|
102
|
-
);
|
|
103
|
-
if (!response.ok) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`Failed to fetch listing data: ${response.statusText}`,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const listingData = (await response.json()) as OrdUtxo;
|
|
110
|
-
|
|
111
|
-
// Check if the listing is valid and has a price
|
|
112
|
-
if (!listingData.data?.list?.price) {
|
|
113
|
-
throw new Error("Listing is either not for sale or invalid");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Check if payout is available
|
|
117
|
-
if (!listingData.data.list.payout) {
|
|
118
|
-
throw new Error("Listing doesn't have payout information");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Calculate the market fee (3% of listing price)
|
|
122
|
-
const listingPrice = listingData.data.list.price;
|
|
123
|
-
let marketFee = Math.round(listingPrice * MARKET_FEE_PERCENTAGE);
|
|
124
|
-
|
|
125
|
-
// Ensure minimum fee
|
|
126
|
-
if (marketFee < MINIMUM_MARKET_FEE_SATOSHIS) {
|
|
127
|
-
marketFee = MINIMUM_MARKET_FEE_SATOSHIS;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Parse the listing outpoint to get txid and vout
|
|
131
|
-
const [txid, voutStr] = listingOutpoint.split("_");
|
|
132
|
-
if (!txid) {
|
|
133
|
-
throw new Error("Invalid outpoint format. Expected txid_vout");
|
|
134
|
-
}
|
|
135
|
-
const vout = Number.parseInt(voutStr || "0", 10);
|
|
136
|
-
|
|
137
|
-
// Get private key from the wallet
|
|
138
|
-
const paymentPk = wallet.getPrivateKey();
|
|
139
|
-
if (!paymentPk) {
|
|
140
|
-
throw new Error("No private key available in wallet");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Get payment address
|
|
144
|
-
const paymentAddress = paymentPk.toAddress().toString();
|
|
145
|
-
|
|
146
|
-
// Get payment UTXOs from the wallet's managed UTXOs
|
|
147
|
-
const { paymentUtxos } = await wallet.getUtxos();
|
|
148
|
-
if (!paymentUtxos || paymentUtxos.length === 0) {
|
|
149
|
-
// Provide more helpful error message with instructions
|
|
150
|
-
throw new Error(
|
|
151
|
-
`No payment UTXOs available for address ${paymentAddress}.
|
|
152
|
-
Please fund this wallet address with enough BSV to cover the purchase price
|
|
153
|
-
(${listingData.data.list.price} satoshis) plus market fee (${marketFee} satoshis) and transaction fees.`,
|
|
154
|
-
);
|
|
20
|
+
async ({ listingOutpoint, listingType, tokenID, tokenAmount, description }): Promise<CallToolResult> => {
|
|
21
|
+
if (!ctx) {
|
|
22
|
+
return {
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: 'text',
|
|
26
|
+
text: 'Wallet not initialized. Please configure a wallet before purchasing.',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
isError: true,
|
|
155
30
|
}
|
|
31
|
+
}
|
|
156
32
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
to: MARKET_WALLET_ADDRESS,
|
|
161
|
-
amount: marketFee,
|
|
162
|
-
},
|
|
163
|
-
];
|
|
164
|
-
|
|
165
|
-
// Define metadata for the transaction
|
|
166
|
-
const metaData = {
|
|
167
|
-
app: "bsv-mcp",
|
|
168
|
-
type: "ord",
|
|
169
|
-
op: "purchase",
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// Create the purchase transaction based on listing type
|
|
173
|
-
let transaction: ChangeResult;
|
|
174
|
-
|
|
175
|
-
if (listingType === "token") {
|
|
176
|
-
if (!tokenProtocol) {
|
|
177
|
-
throw new Error("tokenProtocol is required for token listings");
|
|
178
|
-
}
|
|
33
|
+
try {
|
|
34
|
+
let marketplaceRate = MARKET_FEE_PERCENTAGE
|
|
179
35
|
|
|
36
|
+
if (listingType === 'token') {
|
|
180
37
|
if (!tokenID) {
|
|
181
|
-
throw new Error(
|
|
38
|
+
throw new Error('tokenID is required for token listings')
|
|
182
39
|
}
|
|
183
40
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
41
|
+
if (!tokenAmount) {
|
|
42
|
+
const response = await fetch(
|
|
43
|
+
`https://ordinals.gorillapool.io/api/txos/${listingOutpoint}?script=true`,
|
|
44
|
+
)
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
throw new Error(`Failed to fetch listing data: ${response.statusText}`)
|
|
47
|
+
}
|
|
48
|
+
const listingData = (await response.json()) as {
|
|
49
|
+
data?: { list?: { price: number }; bsv20?: { amt?: string } }
|
|
50
|
+
}
|
|
51
|
+
if (!listingData.data?.bsv20?.amt) {
|
|
52
|
+
throw new Error('Token listing does not have an amount')
|
|
53
|
+
}
|
|
54
|
+
tokenAmount = listingData.data.bsv20.amt
|
|
188
55
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
56
|
+
if (listingData.data?.list?.price) {
|
|
57
|
+
const fee = Math.round(listingData.data.list.price * MARKET_FEE_PERCENTAGE)
|
|
58
|
+
marketplaceRate = Math.max(fee, MINIMUM_MARKET_FEE_SATOSHIS) / listingData.data.list.price
|
|
59
|
+
}
|
|
192
60
|
}
|
|
193
61
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
payout: listingData.data.list.payout,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
transaction = await purchaseOrdTokenListing({
|
|
210
|
-
protocol,
|
|
211
|
-
tokenID: tokenID,
|
|
212
|
-
utxos: paymentUtxos,
|
|
213
|
-
paymentPk,
|
|
214
|
-
listingUtxo,
|
|
215
|
-
ordAddress: ordAddress,
|
|
216
|
-
additionalPayments,
|
|
217
|
-
metaData,
|
|
218
|
-
});
|
|
219
|
-
} else {
|
|
220
|
-
// Create a regular Utxo for NFT listing
|
|
221
|
-
const listingUtxo: Utxo = {
|
|
222
|
-
txid,
|
|
223
|
-
vout,
|
|
224
|
-
script: listingData.script,
|
|
225
|
-
satoshis: listingData.satoshis,
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
// Create the ExistingListing object for NFT listings
|
|
229
|
-
const listing: ExistingListing = {
|
|
230
|
-
payout: listingData.data.list.payout,
|
|
231
|
-
listingUtxo,
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// Check for royalties in the NFT origin data
|
|
235
|
-
// Royalties are only supported for NFTs, not for tokens
|
|
236
|
-
// The royalties are defined by the original creator as a JSON string
|
|
237
|
-
// in the NFT's metadata and parsed into a Royalty[] array
|
|
238
|
-
let royalties: Royalty[] = [];
|
|
239
|
-
if (listingData.origin?.data?.map?.royalties) {
|
|
240
|
-
try {
|
|
241
|
-
royalties = JSON.parse(listingData.origin.data.map.royalties);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
// Remove console.warn
|
|
62
|
+
const result = await purchaseBsv21.execute(ctx, {
|
|
63
|
+
tokenId: tokenID,
|
|
64
|
+
outpoint: listingOutpoint,
|
|
65
|
+
amount: tokenAmount,
|
|
66
|
+
marketplaceAddress: MARKET_WALLET_ADDRESS,
|
|
67
|
+
marketplaceRate,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
if (result.error) {
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: 'text', text: result.error }],
|
|
73
|
+
isError: true,
|
|
244
74
|
}
|
|
245
75
|
}
|
|
246
76
|
|
|
247
|
-
const purchaseOrdListingConfig: PurchaseOrdListingConfig = {
|
|
248
|
-
utxos: paymentUtxos,
|
|
249
|
-
paymentPk,
|
|
250
|
-
ordAddress: ordAddress,
|
|
251
|
-
listing,
|
|
252
|
-
additionalPayments,
|
|
253
|
-
metaData,
|
|
254
|
-
royalties,
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
transaction = await purchaseOrdListing(purchaseOrdListingConfig);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// After successful transaction creation, refresh the wallet's UTXOs
|
|
261
|
-
// This ensures the wallet doesn't try to reuse spent UTXOs
|
|
262
|
-
try {
|
|
263
|
-
await wallet.refreshUtxos();
|
|
264
|
-
} catch (refreshError) {
|
|
265
|
-
// Remove console.warn
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Optionally sign with identity key then broadcast the transaction
|
|
269
|
-
|
|
270
|
-
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
271
|
-
if (!disableBroadcasting) {
|
|
272
|
-
const broadcastResult = await transaction.tx.broadcast(
|
|
273
|
-
oneSatBroadcaster(),
|
|
274
|
-
);
|
|
275
|
-
// Handle broadcast response
|
|
276
|
-
const resultStatus =
|
|
277
|
-
typeof broadcastResult === "object" && "status" in broadcastResult
|
|
278
|
-
? broadcastResult.status
|
|
279
|
-
: "unknown";
|
|
280
|
-
|
|
281
|
-
const resultMessage =
|
|
282
|
-
typeof broadcastResult === "object" && "error" in broadcastResult
|
|
283
|
-
? broadcastResult.error
|
|
284
|
-
: "Transaction broadcast successful";
|
|
285
|
-
|
|
286
77
|
return {
|
|
287
78
|
content: [
|
|
288
79
|
{
|
|
289
|
-
type:
|
|
80
|
+
type: 'text',
|
|
290
81
|
text: JSON.stringify({
|
|
291
|
-
status:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
listingType: listingType,
|
|
297
|
-
tokenProtocol: tokenID ? tokenProtocol : undefined,
|
|
298
|
-
tokenID: tokenID,
|
|
299
|
-
price: listingData.data.list.price,
|
|
300
|
-
marketFee,
|
|
301
|
-
marketFeeAddress: MARKET_WALLET_ADDRESS,
|
|
302
|
-
royaltiesPaid:
|
|
303
|
-
listingType === "nft" &&
|
|
304
|
-
listingData.origin?.data?.map?.royalties
|
|
305
|
-
? JSON.parse(listingData.origin.data.map.royalties)
|
|
306
|
-
: undefined,
|
|
82
|
+
status: 'success',
|
|
83
|
+
txid: result.txid,
|
|
84
|
+
listingOutpoint,
|
|
85
|
+
listingType,
|
|
86
|
+
tokenID,
|
|
307
87
|
}),
|
|
308
88
|
},
|
|
309
89
|
],
|
|
310
|
-
}
|
|
90
|
+
}
|
|
311
91
|
}
|
|
92
|
+
|
|
93
|
+
const result = await purchaseOrdinal.execute(ctx, {
|
|
94
|
+
outpoint: listingOutpoint,
|
|
95
|
+
marketplaceAddress: MARKET_WALLET_ADDRESS,
|
|
96
|
+
marketplaceRate,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
if (result.error) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: result.error }],
|
|
102
|
+
isError: true,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
312
106
|
return {
|
|
313
107
|
content: [
|
|
314
108
|
{
|
|
315
|
-
type:
|
|
316
|
-
text:
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: JSON.stringify({
|
|
111
|
+
status: 'success',
|
|
112
|
+
txid: result.txid,
|
|
113
|
+
listingOutpoint,
|
|
114
|
+
listingType,
|
|
115
|
+
}),
|
|
317
116
|
},
|
|
318
117
|
],
|
|
319
|
-
}
|
|
118
|
+
}
|
|
320
119
|
} catch (err: unknown) {
|
|
321
|
-
const msg = err instanceof Error ? err.message : String(err)
|
|
322
|
-
return { content: [{ type:
|
|
120
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
121
|
+
return { content: [{ type: 'text', text: msg }], isError: true }
|
|
323
122
|
}
|
|
324
123
|
},
|
|
325
|
-
)
|
|
124
|
+
)
|
|
326
125
|
}
|
package/tools/wallet/schemas.ts
CHANGED
|
@@ -64,27 +64,23 @@ export const purchaseListingArgsSchema = z
|
|
|
64
64
|
listingOutpoint: z
|
|
65
65
|
.string()
|
|
66
66
|
.describe("The outpoint of the listing to purchase (txid_vout format)"),
|
|
67
|
-
ordAddress: z
|
|
68
|
-
.string()
|
|
69
|
-
.describe("The ordinal address to receive the purchased item"),
|
|
70
67
|
listingType: z
|
|
71
68
|
.enum(["nft", "token"])
|
|
72
69
|
.default("nft")
|
|
73
70
|
.describe(
|
|
74
|
-
"Type of listing: 'nft' for ordinal NFTs, 'token' for
|
|
71
|
+
"Type of listing: 'nft' for ordinal NFTs, 'token' for BSV21 tokens",
|
|
75
72
|
),
|
|
76
|
-
|
|
77
|
-
.
|
|
73
|
+
tokenID: z
|
|
74
|
+
.string()
|
|
78
75
|
.optional()
|
|
79
|
-
.default("bsv-21")
|
|
80
76
|
.describe(
|
|
81
|
-
"Token
|
|
77
|
+
"Token ID (txid_vout of deploy transaction) — required when listingType is 'token'",
|
|
82
78
|
),
|
|
83
|
-
|
|
79
|
+
tokenAmount: z
|
|
84
80
|
.string()
|
|
85
81
|
.optional()
|
|
86
82
|
.describe(
|
|
87
|
-
"
|
|
83
|
+
"Amount of tokens in the listing (as string) — required when listingType is 'token'",
|
|
88
84
|
),
|
|
89
85
|
description: z
|
|
90
86
|
.string()
|
|
@@ -92,7 +88,7 @@ export const purchaseListingArgsSchema = z
|
|
|
92
88
|
.describe("Optional description for the transaction"),
|
|
93
89
|
})
|
|
94
90
|
.describe(
|
|
95
|
-
"Schema for the wallet_purchaseListing tool arguments (purchase NFTs or tokens)
|
|
91
|
+
"Schema for the wallet_purchaseListing tool arguments (purchase NFTs or BSV21 tokens).",
|
|
96
92
|
);
|
|
97
93
|
|
|
98
94
|
// Export types
|
|
@@ -1,75 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
1
|
+
import type { OneSatContext } from '@1sat/actions'
|
|
2
|
+
import { sendBsv } from '@1sat/actions'
|
|
3
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
+
import { toSatoshi } from 'satoshi-token'
|
|
5
|
+
import { getBsvPriceWithCache } from '../bsv/getPrice'
|
|
6
|
+
import { sendToAddressArgsSchema } from './schemas'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export function registerSendToAddressTool(
|
|
9
|
+
server: McpServer,
|
|
10
|
+
ctx: OneSatContext | undefined,
|
|
11
|
+
) {
|
|
12
12
|
server.tool(
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
'wallet_sendToAddress',
|
|
14
|
+
'Sends Bitcoin SV (BSV) to a specified address. This tool supports payments in both BSV and USD amounts (with automatic conversion using current exchange rates). Transaction fees are automatically calculated and a confirmation with transaction ID is returned upon success.',
|
|
15
15
|
{ ...sendToAddressArgsSchema.shape },
|
|
16
|
-
async ({ address, amount, currency =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
async ({ address, amount, currency = 'BSV', description = 'Send to address' }) => {
|
|
17
|
+
if (!ctx) {
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: 'Wallet not initialized. Please configure a wallet before sending.',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
isError: true,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
try {
|
|
30
|
+
let satoshis: number
|
|
31
|
+
if (currency === 'USD') {
|
|
32
|
+
const bsvPriceUsd = await getBsvPriceWithCache()
|
|
33
|
+
satoshis = toSatoshi(amount / bsvPriceUsd)
|
|
30
34
|
} else {
|
|
31
|
-
|
|
32
|
-
satoshis = toSatoshi(amount);
|
|
35
|
+
satoshis = toSatoshi(amount)
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
const result = await sendBsv.execute(ctx, {
|
|
39
|
+
requests: [{ address, satoshis }],
|
|
40
|
+
})
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
satoshis,
|
|
45
|
-
outputDescription: `Payment to ${address}`,
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
});
|
|
42
|
+
if (result.error) {
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: 'text', text: result.error }],
|
|
45
|
+
isError: true,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
49
48
|
|
|
50
49
|
return {
|
|
51
50
|
content: [
|
|
52
51
|
{
|
|
53
|
-
type:
|
|
52
|
+
type: 'text',
|
|
54
53
|
text: JSON.stringify({
|
|
55
|
-
status:
|
|
56
|
-
txid:
|
|
54
|
+
status: 'success',
|
|
55
|
+
txid: result.txid,
|
|
57
56
|
satoshis,
|
|
58
57
|
}),
|
|
59
58
|
},
|
|
60
59
|
],
|
|
61
|
-
}
|
|
60
|
+
}
|
|
62
61
|
} catch (error) {
|
|
63
62
|
return {
|
|
64
63
|
content: [
|
|
65
64
|
{
|
|
66
|
-
type:
|
|
65
|
+
type: 'text',
|
|
67
66
|
text: error instanceof Error ? error.message : String(error),
|
|
68
67
|
},
|
|
69
68
|
],
|
|
70
69
|
isError: true,
|
|
71
|
-
}
|
|
70
|
+
}
|
|
72
71
|
}
|
|
73
72
|
},
|
|
74
|
-
)
|
|
73
|
+
)
|
|
75
74
|
}
|