bsv-mcp 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/package.json +1 -1
- package/tools/ordinals/index.ts +2 -2
- package/tools/ordinals/marketListings.ts +91 -19
- package/tools/ordinals/{bsv20MarketSales.ts → marketSales.ts} +58 -22
- package/tools/wallet/purchaseListing.ts +85 -24
- package/tools/wallet/schemas.ts +12 -0
- package/tools/wallet/tools.ts +0 -5
package/README.md
CHANGED
|
@@ -154,7 +154,8 @@ Wallet tools provide core BSV wallet functionality:
|
|
|
154
154
|
| `wallet_decrypt` | Decrypts data using a specified protocol and key |
|
|
155
155
|
| `wallet_getAddress` | Returns a BSV address for the current wallet or a derived path |
|
|
156
156
|
| `wallet_sendToAddress` | Sends BSV to a specified address (supports BSV or USD amounts) |
|
|
157
|
-
| `wallet_purchaseListing` | Purchases
|
|
157
|
+
| `wallet_purchaseListing` | Purchases NFTs or BSV-20/BSV-21 tokens from marketplace listings |
|
|
158
|
+
| `wallet_createOrdinals` | Creates and inscribes ordinals on the BSV blockchain |
|
|
158
159
|
|
|
159
160
|
### BSV Tools
|
|
160
161
|
|
|
@@ -173,8 +174,8 @@ Tools for working with ordinals (NFTs) on BSV:
|
|
|
173
174
|
|-----------|-------------|
|
|
174
175
|
| `ordinals_getInscription` | Retrieves detailed information about a specific inscription |
|
|
175
176
|
| `ordinals_searchInscriptions` | Searches for inscriptions based on various criteria |
|
|
176
|
-
| `ordinals_marketListings` | Retrieves
|
|
177
|
-
| `
|
|
177
|
+
| `ordinals_marketListings` | Retrieves market listings for NFTs, BSV-20, and BSV-21 tokens with unified interface |
|
|
178
|
+
| `ordinals_marketSales` | Gets information about BSV-20 and BSV-21 token market sales |
|
|
178
179
|
| `ordinals_getBsv20ById` | Retrieves details about a specific BSV20 token by ID |
|
|
179
180
|
|
|
180
181
|
### Utility Tools
|
|
@@ -193,11 +194,15 @@ Once connected, you can use natural language to interact with Bitcoin SV through
|
|
|
193
194
|
- "Get my Bitcoin SV address"
|
|
194
195
|
- "Send 0.01 BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
|
|
195
196
|
- "Send $5 USD worth of BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
|
|
197
|
+
- "Purchase this NFT listing: txid_vout"
|
|
198
|
+
- "Purchase this BSV-20 token listing: txid_vout"
|
|
196
199
|
|
|
197
200
|
### Ordinals (NFTs)
|
|
198
201
|
- "Show me information about the NFT with outpoint 6a89047af2cfac96da17d51ae8eb62c5f1d982be2bc4ba0d0cd2084b7ffed325_0"
|
|
199
202
|
- "Search for Pixel Zoide NFTs"
|
|
200
203
|
- "Show me the current marketplace listings for BSV NFTs"
|
|
204
|
+
- "Show me BSV-20 token listings for ticker PEPE"
|
|
205
|
+
- "Get recent BSV-20 token sales"
|
|
201
206
|
|
|
202
207
|
### Blockchain Operations
|
|
203
208
|
- "What is the current BSV price?"
|
|
@@ -232,6 +237,17 @@ tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
|
|
|
232
237
|
|
|
233
238
|
For Cursor, check the Cursor MCP logs in Settings → Extensions → Model Context Protocol.
|
|
234
239
|
|
|
240
|
+
## Recent Updates
|
|
241
|
+
|
|
242
|
+
### Enhanced Marketplace Tools
|
|
243
|
+
- **Unified Market Listings**: The `ordinals_marketListings` tool now supports NFTs, BSV-20, and BSV-21 tokens through a single interface with appropriate filtering.
|
|
244
|
+
- **Improved Market Sales**: The `ordinals_marketSales` tool (renamed from `ordinals_bsv20MarketSales`) now supports both BSV-20 and BSV-21 token sales.
|
|
245
|
+
- **Token Purchases**: The `wallet_purchaseListing` tool has been enhanced to support purchasing both NFTs and BSV-20/BSV-21 tokens.
|
|
246
|
+
|
|
247
|
+
### API Refinements
|
|
248
|
+
- Standardized endpoint structure for interactions with the GorillaPool API.
|
|
249
|
+
- Improved parameter handling for different token types and marketplace actions.
|
|
250
|
+
|
|
235
251
|
## Development
|
|
236
252
|
|
|
237
253
|
This project was created using `bun init` in bun v1.2.9. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
package/package.json
CHANGED
package/tools/ordinals/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { registerBsv20MarketSalesTool } from "./bsv20MarketSales";
|
|
3
2
|
import { registerGetBsv20ByIdTool } from "./getBsv20ById";
|
|
4
3
|
import { registerGetInscriptionTool } from "./getInscription";
|
|
5
4
|
import { registerMarketListingsTool } from "./marketListings";
|
|
5
|
+
import { registerMarketSalesTool } from "./marketSales";
|
|
6
6
|
import { registerSearchInscriptionsTool } from "./searchInscriptions";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -14,6 +14,6 @@ export function registerOrdinalsTools(server: McpServer): void {
|
|
|
14
14
|
registerGetInscriptionTool(server);
|
|
15
15
|
registerSearchInscriptionsTool(server);
|
|
16
16
|
registerMarketListingsTool(server);
|
|
17
|
-
|
|
17
|
+
registerMarketSalesTool(server);
|
|
18
18
|
registerGetBsv20ByIdTool(server);
|
|
19
19
|
}
|
|
@@ -2,8 +2,9 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Enhanced schema for unified market listings arguments
|
|
6
6
|
export const marketListingsArgsSchema = z.object({
|
|
7
|
+
// Common parameters
|
|
7
8
|
limit: z
|
|
8
9
|
.number()
|
|
9
10
|
.int()
|
|
@@ -12,29 +13,47 @@ export const marketListingsArgsSchema = z.object({
|
|
|
12
13
|
.default(20)
|
|
13
14
|
.describe("Number of results (1-100, default 20)"),
|
|
14
15
|
offset: z.number().int().min(0).default(0).describe("Pagination offset"),
|
|
15
|
-
sort: z
|
|
16
|
-
.enum(["recent", "price", "num"])
|
|
17
|
-
.default("recent")
|
|
18
|
-
.describe("Sort method (recent, price, or num)"),
|
|
19
16
|
dir: z
|
|
20
17
|
.enum(["asc", "desc"])
|
|
21
18
|
.default("desc")
|
|
22
19
|
.describe("Sort direction (asc or desc)"),
|
|
23
20
|
address: z.string().optional().describe("Bitcoin address"),
|
|
21
|
+
|
|
22
|
+
// NFT-specific parameters
|
|
24
23
|
origin: z.string().optional().describe("Origin outpoint"),
|
|
25
24
|
mime: z.string().optional().describe("MIME type filter"),
|
|
26
25
|
num: z.string().optional().describe("Inscription number"),
|
|
26
|
+
|
|
27
|
+
// General market parameters
|
|
27
28
|
minPrice: z.number().optional().describe("Minimum price in satoshis"),
|
|
28
29
|
maxPrice: z.number().optional().describe("Maximum price in satoshis"),
|
|
30
|
+
|
|
31
|
+
// Token-specific parameters
|
|
32
|
+
tokenType: z
|
|
33
|
+
.enum(["nft", "bsv20", "bsv21", "all"])
|
|
34
|
+
.default("all")
|
|
35
|
+
.describe("Type of token to search for (nft, bsv20, bsv21, or all)"),
|
|
36
|
+
sort: z
|
|
37
|
+
.enum(["recent", "price", "num", "height", "price_per_token"])
|
|
38
|
+
.default("recent")
|
|
39
|
+
.describe("Sort method (recent, price, num, height, price_per_token)"),
|
|
40
|
+
bsv20Type: z
|
|
41
|
+
.enum(["v1", "v2", "all"])
|
|
42
|
+
.default("all")
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("BSV20 token type (v1, v2, or all)"),
|
|
45
|
+
id: z.string().optional().describe("Token ID in outpoint format"),
|
|
46
|
+
tick: z.string().optional().describe("Token ticker symbol"),
|
|
47
|
+
pending: z.boolean().default(false).optional().describe("Include pending sales"),
|
|
29
48
|
});
|
|
30
49
|
|
|
31
50
|
export type MarketListingsArgs = z.infer<typeof marketListingsArgsSchema>;
|
|
32
51
|
|
|
33
|
-
//
|
|
52
|
+
// Unified response type that covers all token types
|
|
34
53
|
interface MarketListingResponse {
|
|
35
54
|
results: Array<{
|
|
36
55
|
outpoint: string;
|
|
37
|
-
origin
|
|
56
|
+
origin?: {
|
|
38
57
|
outpoint: string;
|
|
39
58
|
data?: {
|
|
40
59
|
insc?: {
|
|
@@ -51,16 +70,26 @@ interface MarketListingResponse {
|
|
|
51
70
|
price?: number;
|
|
52
71
|
payout?: string;
|
|
53
72
|
sale?: boolean;
|
|
73
|
+
price_per_token?: number;
|
|
74
|
+
};
|
|
75
|
+
bsv20?: {
|
|
76
|
+
id?: string;
|
|
77
|
+
tick?: string;
|
|
78
|
+
sym?: string;
|
|
79
|
+
amt?: string;
|
|
80
|
+
op?: string;
|
|
54
81
|
};
|
|
55
82
|
};
|
|
56
83
|
satoshis?: number;
|
|
84
|
+
height?: number;
|
|
57
85
|
[key: string]: unknown;
|
|
58
86
|
}>;
|
|
59
87
|
total: number;
|
|
60
88
|
}
|
|
61
89
|
|
|
62
90
|
/**
|
|
63
|
-
* Register the Ordinals market listings tool
|
|
91
|
+
* Register the unified Ordinals market listings tool
|
|
92
|
+
* Handles NFTs, BSV20, and BSV21 tokens
|
|
64
93
|
*/
|
|
65
94
|
export function registerMarketListingsTool(server: McpServer): void {
|
|
66
95
|
server.tool(
|
|
@@ -84,25 +113,68 @@ export function registerMarketListingsTool(server: McpServer): void {
|
|
|
84
113
|
num,
|
|
85
114
|
minPrice,
|
|
86
115
|
maxPrice,
|
|
116
|
+
tokenType,
|
|
117
|
+
bsv20Type,
|
|
118
|
+
id,
|
|
119
|
+
tick,
|
|
120
|
+
pending,
|
|
87
121
|
} = args;
|
|
88
122
|
|
|
123
|
+
// Determine the API endpoint based on tokenType
|
|
124
|
+
let baseUrl = "https://ordinals.gorillapool.io/api";
|
|
125
|
+
let useTokenParams = false;
|
|
126
|
+
|
|
127
|
+
if (tokenType === "bsv20") {
|
|
128
|
+
baseUrl += "/bsv20/market";
|
|
129
|
+
useTokenParams = true;
|
|
130
|
+
} else if (tokenType === "bsv21") {
|
|
131
|
+
baseUrl += "/bsv21/market"; // Assuming BSV21 endpoint follows similar pattern
|
|
132
|
+
useTokenParams = true;
|
|
133
|
+
} else if (tokenType === "nft" || tokenType === "all") {
|
|
134
|
+
baseUrl += "/market";
|
|
135
|
+
}
|
|
136
|
+
|
|
89
137
|
// Build the URL with query parameters
|
|
90
|
-
const url = new URL(
|
|
138
|
+
const url = new URL(baseUrl);
|
|
91
139
|
url.searchParams.append("limit", limit.toString());
|
|
92
140
|
url.searchParams.append("offset", offset.toString());
|
|
93
|
-
url.searchParams.append("sort", sort);
|
|
94
141
|
url.searchParams.append("dir", dir);
|
|
95
|
-
|
|
142
|
+
|
|
143
|
+
// Add sort parameter based on token type
|
|
144
|
+
if (useTokenParams) {
|
|
145
|
+
// BSV20/BSV21 specific sort options
|
|
146
|
+
if (sort === "height" || sort === "price" || sort === "price_per_token") {
|
|
147
|
+
url.searchParams.append("sort", sort);
|
|
148
|
+
}
|
|
149
|
+
// Add token-specific parameters
|
|
150
|
+
if (bsv20Type && bsv20Type !== "all") {
|
|
151
|
+
url.searchParams.append("type", bsv20Type);
|
|
152
|
+
}
|
|
153
|
+
if (id) url.searchParams.append("id", id);
|
|
154
|
+
if (tick) url.searchParams.append("tick", tick);
|
|
155
|
+
if (pending !== undefined) url.searchParams.append("pending", pending.toString());
|
|
156
|
+
// For BSV20/21, min/max price parameters have slightly different names
|
|
157
|
+
if (minPrice !== undefined) url.searchParams.append("min_price", minPrice.toString());
|
|
158
|
+
if (maxPrice !== undefined) url.searchParams.append("max_price", maxPrice.toString());
|
|
159
|
+
} else {
|
|
160
|
+
// NFT specific sort options
|
|
161
|
+
if (sort === "recent" || sort === "price" || sort === "num") {
|
|
162
|
+
url.searchParams.append("sort", sort);
|
|
163
|
+
}
|
|
164
|
+
// Add NFT-specific parameters
|
|
165
|
+
if (origin) url.searchParams.append("origin", origin);
|
|
166
|
+
if (mime) url.searchParams.append("mime", mime);
|
|
167
|
+
if (num) url.searchParams.append("num", num);
|
|
168
|
+
// For NFTs, min/max price parameters use short names
|
|
169
|
+
if (minPrice !== undefined) url.searchParams.append("min", minPrice.toString());
|
|
170
|
+
if (maxPrice !== undefined) url.searchParams.append("max", maxPrice.toString());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Add common parameters
|
|
96
174
|
if (address) url.searchParams.append("address", address);
|
|
97
|
-
if (origin) url.searchParams.append("origin", origin);
|
|
98
|
-
if (mime) url.searchParams.append("mime", mime);
|
|
99
|
-
if (num) url.searchParams.append("num", num);
|
|
100
|
-
if (minPrice !== undefined)
|
|
101
|
-
url.searchParams.append("min", minPrice.toString());
|
|
102
|
-
if (maxPrice !== undefined)
|
|
103
|
-
url.searchParams.append("max", maxPrice.toString());
|
|
104
175
|
|
|
105
|
-
//
|
|
176
|
+
// Make the fetch request
|
|
177
|
+
console.log(`Fetching market listings from: ${url.toString()}`);
|
|
106
178
|
const response = await fetch(url.toString());
|
|
107
179
|
|
|
108
180
|
if (!response.ok) {
|
|
@@ -2,8 +2,9 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
// Schema for
|
|
6
|
-
export const
|
|
5
|
+
// Schema for unified market sales arguments
|
|
6
|
+
export const marketSalesArgsSchema = z.object({
|
|
7
|
+
// Common parameters
|
|
7
8
|
limit: z
|
|
8
9
|
.number()
|
|
9
10
|
.int()
|
|
@@ -16,20 +17,27 @@ export const bsv20MarketSalesArgsSchema = z.object({
|
|
|
16
17
|
.enum(["asc", "desc"])
|
|
17
18
|
.default("desc")
|
|
18
19
|
.describe("Sort direction (asc or desc)"),
|
|
19
|
-
|
|
20
|
+
address: z.string().optional().describe("Bitcoin address"),
|
|
21
|
+
|
|
22
|
+
// Token-specific parameters
|
|
23
|
+
tokenType: z
|
|
24
|
+
.enum(["bsv20", "bsv21", "all"])
|
|
25
|
+
.default("bsv20")
|
|
26
|
+
.describe("Type of token to search for (bsv20, bsv21, or all)"),
|
|
27
|
+
bsv20Type: z
|
|
20
28
|
.enum(["v1", "v2", "all"])
|
|
21
29
|
.default("all")
|
|
22
|
-
.
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("BSV20 token type (v1, v2, or all)"),
|
|
23
32
|
id: z.string().optional().describe("Token ID in outpoint format"),
|
|
24
33
|
tick: z.string().optional().describe("Token ticker symbol"),
|
|
25
|
-
pending: z.boolean().default(false).describe("Include pending sales"),
|
|
26
|
-
address: z.string().optional().describe("Bitcoin address"),
|
|
34
|
+
pending: z.boolean().default(false).optional().describe("Include pending sales"),
|
|
27
35
|
});
|
|
28
36
|
|
|
29
|
-
export type
|
|
37
|
+
export type MarketSalesArgs = z.infer<typeof marketSalesArgsSchema>;
|
|
30
38
|
|
|
31
|
-
//
|
|
32
|
-
interface
|
|
39
|
+
// Unified response type for token sales
|
|
40
|
+
interface MarketSaleResponse {
|
|
33
41
|
results: Array<{
|
|
34
42
|
outpoint: string;
|
|
35
43
|
data?: {
|
|
@@ -48,42 +56,70 @@ interface Bsv20SaleResponse {
|
|
|
48
56
|
};
|
|
49
57
|
satoshis?: number;
|
|
50
58
|
height?: number;
|
|
59
|
+
owner?: string;
|
|
60
|
+
spend?: string;
|
|
61
|
+
spendHeight?: number;
|
|
62
|
+
spendIdx?: string;
|
|
51
63
|
[key: string]: unknown;
|
|
52
64
|
}>;
|
|
53
65
|
total: number;
|
|
54
66
|
}
|
|
55
67
|
|
|
56
68
|
/**
|
|
57
|
-
* Register the
|
|
69
|
+
* Register the unified market sales tool
|
|
70
|
+
* Handles BSV20 and BSV21 token sales
|
|
58
71
|
*/
|
|
59
|
-
export function
|
|
72
|
+
export function registerMarketSalesTool(server: McpServer): void {
|
|
60
73
|
server.tool(
|
|
61
|
-
"
|
|
74
|
+
"ordinals_marketSales",
|
|
62
75
|
{
|
|
63
|
-
args:
|
|
76
|
+
args: marketSalesArgsSchema,
|
|
64
77
|
},
|
|
65
78
|
async (
|
|
66
|
-
{ args }: { args:
|
|
79
|
+
{ args }: { args: MarketSalesArgs },
|
|
67
80
|
extra: RequestHandlerExtra,
|
|
68
81
|
) => {
|
|
69
82
|
try {
|
|
70
|
-
const {
|
|
83
|
+
const {
|
|
84
|
+
limit,
|
|
85
|
+
offset,
|
|
86
|
+
dir,
|
|
87
|
+
tokenType,
|
|
88
|
+
bsv20Type,
|
|
89
|
+
id,
|
|
90
|
+
tick,
|
|
91
|
+
pending,
|
|
92
|
+
address
|
|
93
|
+
} = args;
|
|
94
|
+
|
|
95
|
+
// Determine the API endpoint based on tokenType
|
|
96
|
+
let baseUrl = "https://ordinals.gorillapool.io/api";
|
|
97
|
+
|
|
98
|
+
if (tokenType === "bsv21") {
|
|
99
|
+
baseUrl += "/bsv21/market/sales";
|
|
100
|
+
} else {
|
|
101
|
+
// Default to BSV20 for "bsv20" or "all"
|
|
102
|
+
baseUrl += "/bsv20/market/sales";
|
|
103
|
+
}
|
|
71
104
|
|
|
72
105
|
// Build the URL with query parameters
|
|
73
|
-
const url = new URL(
|
|
74
|
-
"https://ordinals.gorillapool.io/api/bsv20/market/sales",
|
|
75
|
-
);
|
|
106
|
+
const url = new URL(baseUrl);
|
|
76
107
|
url.searchParams.append("limit", limit.toString());
|
|
77
108
|
url.searchParams.append("offset", offset.toString());
|
|
78
109
|
url.searchParams.append("dir", dir);
|
|
79
|
-
|
|
80
|
-
|
|
110
|
+
|
|
111
|
+
// Add type parameter for bsv20 only if specified
|
|
112
|
+
if (bsv20Type && bsv20Type !== "all") {
|
|
113
|
+
url.searchParams.append("type", bsv20Type);
|
|
114
|
+
}
|
|
81
115
|
|
|
82
116
|
if (id) url.searchParams.append("id", id);
|
|
83
117
|
if (tick) url.searchParams.append("tick", tick);
|
|
118
|
+
if (pending !== undefined) url.searchParams.append("pending", pending.toString());
|
|
84
119
|
if (address) url.searchParams.append("address", address);
|
|
85
120
|
|
|
86
|
-
// Fetch
|
|
121
|
+
// Fetch market sales from GorillaPool API
|
|
122
|
+
console.log(`Fetching market sales from: ${url.toString()}`);
|
|
87
123
|
const response = await fetch(url.toString());
|
|
88
124
|
|
|
89
125
|
if (!response.ok) {
|
|
@@ -92,7 +128,7 @@ export function registerBsv20MarketSalesTool(server: McpServer): void {
|
|
|
92
128
|
);
|
|
93
129
|
}
|
|
94
130
|
|
|
95
|
-
const data = (await response.json()) as
|
|
131
|
+
const data = (await response.json()) as MarketSaleResponse;
|
|
96
132
|
|
|
97
133
|
return {
|
|
98
134
|
content: [
|
|
@@ -3,11 +3,15 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
4
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import {
|
|
6
|
+
type ChangeResult,
|
|
6
7
|
type ExistingListing,
|
|
7
8
|
type Payment,
|
|
9
|
+
type TokenUtxo,
|
|
8
10
|
type Utxo,
|
|
11
|
+
TokenType,
|
|
9
12
|
oneSatBroadcaster,
|
|
10
13
|
purchaseOrdListing,
|
|
14
|
+
purchaseOrdTokenListing,
|
|
11
15
|
} from "js-1sat-ord";
|
|
12
16
|
import type { z } from "zod";
|
|
13
17
|
import {
|
|
@@ -29,6 +33,11 @@ interface ListingResponse {
|
|
|
29
33
|
price: number;
|
|
30
34
|
payout: string;
|
|
31
35
|
};
|
|
36
|
+
bsv20?: {
|
|
37
|
+
amt?: string;
|
|
38
|
+
tick?: string;
|
|
39
|
+
id?: string;
|
|
40
|
+
};
|
|
32
41
|
};
|
|
33
42
|
}
|
|
34
43
|
|
|
@@ -39,7 +48,7 @@ interface ListingResponse {
|
|
|
39
48
|
* 1. Parses the listing outpoint to get the txid and vout
|
|
40
49
|
* 2. Fetches the listing UTXO from the ordinals API
|
|
41
50
|
* 3. Gets the wallet's payment UTXOs (using the wallet's internal UTXO management)
|
|
42
|
-
* 4. Uses purchaseOrdListing
|
|
51
|
+
* 4. Uses purchaseOrdListing or purchaseOrdTokenListing based on the listing type
|
|
43
52
|
* 5. Broadcasts the transaction
|
|
44
53
|
* 6. Returns the transaction details
|
|
45
54
|
*/
|
|
@@ -56,6 +65,7 @@ export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
|
56
65
|
): Promise<CallToolResult> => {
|
|
57
66
|
try {
|
|
58
67
|
console.log(`Attempting to purchase listing: ${args.listingOutpoint}`);
|
|
68
|
+
console.log(`Listing type: ${args.listingType}`);
|
|
59
69
|
console.log("Using wallet instance:", wallet);
|
|
60
70
|
console.log("Wallet has UTXOs:", await wallet.getUtxos());
|
|
61
71
|
|
|
@@ -102,20 +112,6 @@ export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
|
102
112
|
}
|
|
103
113
|
const vout = Number.parseInt(voutStr || "0", 10);
|
|
104
114
|
|
|
105
|
-
// Create listing UTXO object in the format required by js-1sat-ord
|
|
106
|
-
const listingUtxo: Utxo = {
|
|
107
|
-
txid,
|
|
108
|
-
vout,
|
|
109
|
-
script: listingData.script,
|
|
110
|
-
satoshis: listingData.satoshis,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Create the ExistingListing object
|
|
114
|
-
const listing: ExistingListing = {
|
|
115
|
-
payout: listingData.data.list.payout,
|
|
116
|
-
listingUtxo,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
115
|
// Get private key from the wallet
|
|
120
116
|
const paymentPk = wallet.getPrivateKey();
|
|
121
117
|
if (!paymentPk) {
|
|
@@ -152,15 +148,77 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
152
148
|
op: "purchase",
|
|
153
149
|
};
|
|
154
150
|
|
|
155
|
-
// Create the purchase transaction
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
// Create the purchase transaction based on listing type
|
|
152
|
+
let transaction: ChangeResult;
|
|
153
|
+
|
|
154
|
+
if (args.listingType === "token") {
|
|
155
|
+
if (!args.tokenProtocol) {
|
|
156
|
+
throw new Error("tokenProtocol is required for token listings");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!args.tokenID) {
|
|
160
|
+
throw new Error("tokenID is required for token listings");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Validate token data from the listing
|
|
164
|
+
if (!listingData.data.bsv20) {
|
|
165
|
+
throw new Error("This is not a valid BSV-20 token listing");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// For BSV-20, the amount should be included in the listing data
|
|
169
|
+
if (!listingData.data.bsv20.amt) {
|
|
170
|
+
throw new Error("Token listing doesn't have an amount specified");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Convert the token protocol to the enum type expected by js-1sat-ord
|
|
174
|
+
const protocol = args.tokenProtocol === "bsv-20"
|
|
175
|
+
? TokenType.BSV20
|
|
176
|
+
: TokenType.BSV21;
|
|
177
|
+
|
|
178
|
+
// Create a TokenUtxo with the required fields
|
|
179
|
+
const listingUtxo: TokenUtxo = {
|
|
180
|
+
txid,
|
|
181
|
+
vout,
|
|
182
|
+
script: listingData.script,
|
|
183
|
+
satoshis: 1, // TokenUtxo's satoshis must be exactly 1
|
|
184
|
+
amt: listingData.data.bsv20.amt,
|
|
185
|
+
id: args.tokenID,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
transaction = await purchaseOrdTokenListing({
|
|
189
|
+
protocol,
|
|
190
|
+
tokenID: args.tokenID,
|
|
191
|
+
utxos: paymentUtxos,
|
|
192
|
+
paymentPk,
|
|
193
|
+
listingUtxo,
|
|
194
|
+
ordAddress: args.ordAddress,
|
|
195
|
+
additionalPayments,
|
|
196
|
+
metaData,
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
// Create a regular Utxo for NFT listing
|
|
200
|
+
const listingUtxo: Utxo = {
|
|
201
|
+
txid,
|
|
202
|
+
vout,
|
|
203
|
+
script: listingData.script,
|
|
204
|
+
satoshis: listingData.satoshis,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Create the ExistingListing object for NFT listings
|
|
208
|
+
const listing: ExistingListing = {
|
|
209
|
+
payout: listingData.data.list.payout,
|
|
210
|
+
listingUtxo,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
transaction = await purchaseOrdListing({
|
|
214
|
+
utxos: paymentUtxos,
|
|
215
|
+
paymentPk,
|
|
216
|
+
ordAddress: args.ordAddress,
|
|
217
|
+
listing,
|
|
218
|
+
additionalPayments,
|
|
219
|
+
metaData,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
164
222
|
|
|
165
223
|
// After successful transaction creation, refresh the wallet's UTXOs
|
|
166
224
|
// This ensures the wallet doesn't try to reuse spent UTXOs
|
|
@@ -199,6 +257,9 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
199
257
|
txid: transaction.tx.id("hex"),
|
|
200
258
|
listingOutpoint: args.listingOutpoint,
|
|
201
259
|
destinationAddress: args.ordAddress,
|
|
260
|
+
listingType: args.listingType,
|
|
261
|
+
tokenProtocol: args.tokenID ? args.tokenProtocol : undefined,
|
|
262
|
+
tokenID: args.tokenID,
|
|
202
263
|
price: listingData.data.list.price,
|
|
203
264
|
marketFee,
|
|
204
265
|
marketFeeAddress: MARKET_WALLET_ADDRESS,
|
package/tools/wallet/schemas.ts
CHANGED
|
@@ -279,6 +279,18 @@ export const purchaseListingArgsSchema = z.object({
|
|
|
279
279
|
.string()
|
|
280
280
|
.optional()
|
|
281
281
|
.describe("Optional description for the transaction"),
|
|
282
|
+
listingType: z
|
|
283
|
+
.enum(["nft", "token"])
|
|
284
|
+
.default("nft")
|
|
285
|
+
.describe("Type of listing: 'nft' for ordinal NFTs, 'token' for BSV-20 tokens"),
|
|
286
|
+
tokenProtocol: z
|
|
287
|
+
.enum(["bsv-20", "bsv-21"])
|
|
288
|
+
.optional()
|
|
289
|
+
.describe("Token protocol for token listings (required when listingType is 'token')"),
|
|
290
|
+
tokenID: z
|
|
291
|
+
.string()
|
|
292
|
+
.optional()
|
|
293
|
+
.describe("Token ID for BSV-21 tokens or ticker for BSV-20 tokens (required when listingType is 'token')"),
|
|
282
294
|
});
|
|
283
295
|
|
|
284
296
|
export type PurchaseListingArgs = z.infer<typeof purchaseListingArgsSchema>;
|
package/tools/wallet/tools.ts
CHANGED
|
@@ -40,8 +40,6 @@ import type { createOrdinalsArgsSchema } from "./createOrdinals";
|
|
|
40
40
|
import { registerGetAddressTool } from "./getAddress";
|
|
41
41
|
import { registerPurchaseListingTool } from "./purchaseListing";
|
|
42
42
|
import { registerSendToAddressTool } from "./sendToAddress";
|
|
43
|
-
import { registerSendOrdinalsTool } from "./sendOrdinals";
|
|
44
|
-
import type { sendOrdinalsArgsSchema } from "./sendOrdinals";
|
|
45
43
|
|
|
46
44
|
// Define mapping from tool names to argument schemas
|
|
47
45
|
type ToolArgSchemas = {
|
|
@@ -74,7 +72,6 @@ type ToolArgSchemas = {
|
|
|
74
72
|
wallet_sendToAddress: typeof sendToAddressArgsSchema;
|
|
75
73
|
wallet_purchaseListing: typeof purchaseListingArgsSchema;
|
|
76
74
|
wallet_createOrdinals: typeof createOrdinalsArgsSchema;
|
|
77
|
-
wallet_sendOrdinals: typeof sendOrdinalsArgsSchema;
|
|
78
75
|
};
|
|
79
76
|
|
|
80
77
|
// Define a type for the handler function with proper argument types
|
|
@@ -210,8 +207,6 @@ export function registerWalletTools(
|
|
|
210
207
|
// Register ordinals extension tools
|
|
211
208
|
// Register the wallet_createOrdinals tool
|
|
212
209
|
registerCreateOrdinalsTool(server, wallet);
|
|
213
|
-
// Register the wallet_sendOrdinals tool
|
|
214
|
-
registerSendOrdinalsTool(server, wallet);
|
|
215
210
|
|
|
216
211
|
return handlers;
|
|
217
212
|
}
|