@1sat/wallet-toolbox 0.0.5 → 0.0.7
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/OneSatWallet.d.ts +46 -17
- package/dist/OneSatWallet.js +956 -0
- package/dist/errors.js +11 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +12 -93707
- package/dist/indexers/Bsv21Indexer.js +232 -0
- package/dist/indexers/CosignIndexer.js +25 -0
- package/dist/indexers/FundIndexer.js +64 -0
- package/dist/indexers/InscriptionIndexer.js +115 -0
- package/dist/indexers/LockIndexer.js +42 -0
- package/dist/indexers/MapIndexer.js +62 -0
- package/dist/indexers/OpNSIndexer.js +38 -0
- package/dist/indexers/OrdLockIndexer.js +63 -0
- package/dist/indexers/OriginIndexer.js +240 -0
- package/dist/indexers/Outpoint.js +53 -0
- package/dist/indexers/SigmaIndexer.js +133 -0
- package/dist/indexers/TransactionParser.d.ts +53 -0
- package/dist/indexers/index.js +13 -0
- package/dist/indexers/parseAddress.js +24 -0
- package/dist/indexers/types.js +18 -0
- package/dist/services/OneSatServices.d.ts +12 -4
- package/dist/services/OneSatServices.js +231 -0
- package/dist/services/client/ArcadeClient.js +107 -0
- package/dist/services/client/BaseClient.js +125 -0
- package/dist/services/client/BeefClient.js +33 -0
- package/dist/services/client/Bsv21Client.js +65 -0
- package/dist/services/client/ChaintracksClient.js +175 -0
- package/dist/services/client/OrdfsClient.js +122 -0
- package/dist/services/client/OwnerClient.js +123 -0
- package/dist/services/client/TxoClient.js +85 -0
- package/dist/services/client/index.js +8 -0
- package/dist/services/types.js +5 -0
- package/dist/signers/ReadOnlySigner.js +47 -0
- package/dist/sync/IndexedDbSyncQueue.js +355 -0
- package/dist/sync/SqliteSyncQueue.js +197 -0
- package/dist/sync/index.js +3 -0
- package/dist/sync/types.js +4 -0
- package/package.json +5 -5
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Beef, Hash, MerklePath, Transaction, Utils } from "@bsv/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Simple error class for WalletServices error responses.
|
|
4
|
+
*/
|
|
5
|
+
class ServiceError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
description;
|
|
8
|
+
isError = true;
|
|
9
|
+
constructor(code, description) {
|
|
10
|
+
super(description);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.description = description;
|
|
13
|
+
this.name = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
import { ArcadeClient, BeefClient, Bsv21Client, ChaintracksClient, OrdfsClient, OwnerClient, TxoClient, } from "./client";
|
|
17
|
+
/**
|
|
18
|
+
* WalletServices implementation for 1Sat ecosystem.
|
|
19
|
+
*
|
|
20
|
+
* Provides access to 1Sat API clients and implements the WalletServices
|
|
21
|
+
* interface required by wallet-toolbox.
|
|
22
|
+
*
|
|
23
|
+
* API Routes:
|
|
24
|
+
* - /1sat/chaintracks/* - Block headers and chain tracking
|
|
25
|
+
* - /1sat/beef/* - Raw transactions and proofs
|
|
26
|
+
* - /1sat/arcade/* - Transaction broadcasting
|
|
27
|
+
* - /1sat/bsv21/* - BSV21 token data
|
|
28
|
+
* - /1sat/txo/* - Transaction outputs
|
|
29
|
+
* - /1sat/owner/* - Address queries and sync
|
|
30
|
+
* - /1sat/ordfs/* - Content/inscription serving
|
|
31
|
+
*/
|
|
32
|
+
export class OneSatServices {
|
|
33
|
+
chain;
|
|
34
|
+
baseUrl;
|
|
35
|
+
storage;
|
|
36
|
+
// ===== API Clients =====
|
|
37
|
+
chaintracks;
|
|
38
|
+
beef;
|
|
39
|
+
arcade;
|
|
40
|
+
txo;
|
|
41
|
+
owner;
|
|
42
|
+
ordfs;
|
|
43
|
+
bsv21;
|
|
44
|
+
constructor(chain, baseUrl, storage) {
|
|
45
|
+
this.chain = chain;
|
|
46
|
+
this.baseUrl =
|
|
47
|
+
baseUrl ||
|
|
48
|
+
(chain === "main"
|
|
49
|
+
? "https://1sat.shruggr.cloud"
|
|
50
|
+
: "https://testnet.api.1sat.app");
|
|
51
|
+
this.storage = storage;
|
|
52
|
+
const opts = { timeout: 30000 };
|
|
53
|
+
this.chaintracks = new ChaintracksClient(this.baseUrl, opts);
|
|
54
|
+
this.beef = new BeefClient(this.baseUrl, opts);
|
|
55
|
+
this.arcade = new ArcadeClient(this.baseUrl, opts);
|
|
56
|
+
this.txo = new TxoClient(this.baseUrl, opts);
|
|
57
|
+
this.owner = new OwnerClient(this.baseUrl, opts);
|
|
58
|
+
this.ordfs = new OrdfsClient(this.baseUrl, opts);
|
|
59
|
+
this.bsv21 = new Bsv21Client(this.baseUrl, opts);
|
|
60
|
+
}
|
|
61
|
+
// ===== Utility Methods =====
|
|
62
|
+
/**
|
|
63
|
+
* Get list of enabled capabilities from the server
|
|
64
|
+
*/
|
|
65
|
+
async getCapabilities() {
|
|
66
|
+
const response = await fetch(`${this.baseUrl}/capabilities`);
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error(`Failed to fetch capabilities: ${response.statusText}`);
|
|
69
|
+
}
|
|
70
|
+
return response.json();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Close all client connections
|
|
74
|
+
*/
|
|
75
|
+
close() {
|
|
76
|
+
this.chaintracks.close();
|
|
77
|
+
}
|
|
78
|
+
// ===== WalletServices Interface (Required by wallet-toolbox) =====
|
|
79
|
+
async getRawTx(txid, _useNext) {
|
|
80
|
+
// This is a network-only call for the WalletServices interface.
|
|
81
|
+
// Wallet should check storage before calling this.
|
|
82
|
+
try {
|
|
83
|
+
const beefBytes = await this.beef.getBeef(txid);
|
|
84
|
+
const tx = Transaction.fromBEEF(Array.from(beefBytes));
|
|
85
|
+
return { txid, name: "1sat-api", rawTx: Array.from(tx.toBinary()) };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return {
|
|
89
|
+
txid,
|
|
90
|
+
error: new ServiceError("NETWORK_ERROR", error instanceof Error ? error.message : "Unknown error"),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async getChainTracker() {
|
|
95
|
+
return this.chaintracks;
|
|
96
|
+
}
|
|
97
|
+
async getHeaderForHeight(height) {
|
|
98
|
+
return this.chaintracks.getHeaderBytes(height);
|
|
99
|
+
}
|
|
100
|
+
async getHeight() {
|
|
101
|
+
return this.chaintracks.currentHeight();
|
|
102
|
+
}
|
|
103
|
+
async getMerklePath(txid, _useNext) {
|
|
104
|
+
try {
|
|
105
|
+
const proofBytes = await this.beef.getProof(txid);
|
|
106
|
+
const merklePath = MerklePath.fromBinary(Array.from(proofBytes));
|
|
107
|
+
return { name: "1sat-api", merklePath };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
name: "1sat-api",
|
|
112
|
+
error: new ServiceError("NETWORK_ERROR", error instanceof Error ? error.message : "Unknown error"),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async postBeef(beef, txids) {
|
|
117
|
+
const results = [];
|
|
118
|
+
for (const txid of txids) {
|
|
119
|
+
try {
|
|
120
|
+
const beefTx = beef.findTxid(txid);
|
|
121
|
+
if (!beefTx?.tx) {
|
|
122
|
+
results.push({
|
|
123
|
+
name: "1sat-api",
|
|
124
|
+
status: "error",
|
|
125
|
+
error: new ServiceError("TX_NOT_FOUND", `Transaction ${txid} not found in BEEF`),
|
|
126
|
+
txidResults: [
|
|
127
|
+
{
|
|
128
|
+
txid,
|
|
129
|
+
status: "error",
|
|
130
|
+
data: { detail: "Transaction not found in BEEF" },
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// Use Extended Format (EF) which includes source transaction data
|
|
137
|
+
const status = await this.arcade.submitTransaction(beefTx.tx.toEF());
|
|
138
|
+
if (status.txStatus === "MINED" ||
|
|
139
|
+
status.txStatus === "SEEN_ON_NETWORK" ||
|
|
140
|
+
status.txStatus === "ACCEPTED_BY_NETWORK") {
|
|
141
|
+
results.push({
|
|
142
|
+
name: "1sat-api",
|
|
143
|
+
status: "success",
|
|
144
|
+
txidResults: [{ txid: status.txid || txid, status: "success" }],
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else if (status.txStatus === "REJECTED" ||
|
|
148
|
+
status.txStatus === "DOUBLE_SPEND_ATTEMPTED") {
|
|
149
|
+
results.push({
|
|
150
|
+
name: "1sat-api",
|
|
151
|
+
status: "error",
|
|
152
|
+
error: new ServiceError(status.txStatus, status.extraInfo || "Transaction rejected"),
|
|
153
|
+
txidResults: [{ txid, status: "error", data: status }],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// Still processing - report as success since tx was accepted
|
|
158
|
+
results.push({
|
|
159
|
+
name: "1sat-api",
|
|
160
|
+
status: "success",
|
|
161
|
+
txidResults: [{ txid: status.txid || txid, status: "success" }],
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
results.push({
|
|
167
|
+
name: "1sat-api",
|
|
168
|
+
status: "error",
|
|
169
|
+
error: new ServiceError("NETWORK_ERROR", error instanceof Error ? error.message : "Unknown error"),
|
|
170
|
+
txidResults: [{ txid, status: "error" }],
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return results;
|
|
175
|
+
}
|
|
176
|
+
async getBeefForTxid(txid) {
|
|
177
|
+
const beefBytes = await this.beef.getBeef(txid);
|
|
178
|
+
return Beef.fromBinary(Array.from(beefBytes));
|
|
179
|
+
}
|
|
180
|
+
hashOutputScript(script) {
|
|
181
|
+
const scriptBin = Utils.toArray(script, "hex");
|
|
182
|
+
return Utils.toHex(Hash.hash256(scriptBin).reverse());
|
|
183
|
+
}
|
|
184
|
+
getServicesCallHistory(_reset) {
|
|
185
|
+
const emptyHistory = {
|
|
186
|
+
serviceName: "",
|
|
187
|
+
historyByProvider: {},
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
version: 1,
|
|
191
|
+
getMerklePath: { ...emptyHistory, serviceName: "getMerklePath" },
|
|
192
|
+
getRawTx: { ...emptyHistory, serviceName: "getRawTx" },
|
|
193
|
+
postBeef: { ...emptyHistory, serviceName: "postBeef" },
|
|
194
|
+
getUtxoStatus: { ...emptyHistory, serviceName: "getUtxoStatus" },
|
|
195
|
+
getStatusForTxids: { ...emptyHistory, serviceName: "getStatusForTxids" },
|
|
196
|
+
getScriptHashHistory: {
|
|
197
|
+
...emptyHistory,
|
|
198
|
+
serviceName: "getScriptHashHistory",
|
|
199
|
+
},
|
|
200
|
+
updateFiatExchangeRates: {
|
|
201
|
+
...emptyHistory,
|
|
202
|
+
serviceName: "updateFiatExchangeRates",
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// ===== WalletServices Interface (Not Yet Implemented) =====
|
|
207
|
+
async getBsvExchangeRate() {
|
|
208
|
+
throw new Error("getBsvExchangeRate not yet implemented");
|
|
209
|
+
}
|
|
210
|
+
async getFiatExchangeRate(_currency, _base) {
|
|
211
|
+
throw new Error("getFiatExchangeRate not yet implemented");
|
|
212
|
+
}
|
|
213
|
+
async getStatusForTxids(_txids, _useNext) {
|
|
214
|
+
throw new Error("getStatusForTxids not yet implemented");
|
|
215
|
+
}
|
|
216
|
+
async isUtxo(_output) {
|
|
217
|
+
throw new Error("isUtxo not yet implemented");
|
|
218
|
+
}
|
|
219
|
+
async getUtxoStatus(_output, _outputFormat, _outpoint, _useNext) {
|
|
220
|
+
throw new Error("getUtxoStatus not yet implemented");
|
|
221
|
+
}
|
|
222
|
+
async getScriptHashHistory(_hash, _useNext) {
|
|
223
|
+
throw new Error("getScriptHashHistory not yet implemented");
|
|
224
|
+
}
|
|
225
|
+
async hashToHeader(_hash) {
|
|
226
|
+
throw new Error("hashToHeader not yet implemented");
|
|
227
|
+
}
|
|
228
|
+
async nLockTimeIsFinal(_txOrLockTime) {
|
|
229
|
+
throw new Error("nLockTimeIsFinal not yet implemented");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Utils } from "@bsv/sdk";
|
|
2
|
+
import { BaseClient } from "./BaseClient";
|
|
3
|
+
/**
|
|
4
|
+
* Client for /1sat/arcade/* routes.
|
|
5
|
+
* Provides transaction broadcast and status checking.
|
|
6
|
+
*
|
|
7
|
+
* Routes:
|
|
8
|
+
* - POST /tx - Submit single transaction
|
|
9
|
+
* - POST /txs - Submit multiple transactions
|
|
10
|
+
* - GET /tx/:txid - Get transaction status
|
|
11
|
+
* - GET /policy - Get mining policy
|
|
12
|
+
* - GET /events - SSE stream of transaction events
|
|
13
|
+
*/
|
|
14
|
+
export class ArcadeClient extends BaseClient {
|
|
15
|
+
constructor(baseUrl, options = {}) {
|
|
16
|
+
super(`${baseUrl}/1sat/arcade`, options);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Submit a single transaction for broadcast
|
|
20
|
+
*/
|
|
21
|
+
async submitTransaction(rawTx, options) {
|
|
22
|
+
const bytes = rawTx instanceof Uint8Array ? rawTx : new Uint8Array(rawTx);
|
|
23
|
+
return this.request("/tx", {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "application/octet-stream",
|
|
27
|
+
...this.buildSubmitHeaders(options),
|
|
28
|
+
},
|
|
29
|
+
body: bytes,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Submit a transaction as hex string
|
|
34
|
+
*/
|
|
35
|
+
async submitTransactionHex(rawTxHex, options) {
|
|
36
|
+
return this.submitTransaction(Utils.toArray(rawTxHex, "hex"), options);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Submit multiple transactions for broadcast
|
|
40
|
+
*/
|
|
41
|
+
async submitTransactions(rawTxs, options) {
|
|
42
|
+
return this.request("/txs", {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
...this.buildSubmitHeaders(options),
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(rawTxs.map((tx) => ({
|
|
49
|
+
rawTx: Utils.toHex(tx instanceof Uint8Array ? Array.from(tx) : tx),
|
|
50
|
+
}))),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get status of a submitted transaction
|
|
55
|
+
*/
|
|
56
|
+
async getStatus(txid) {
|
|
57
|
+
return this.request(`/tx/${txid}`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get current mining policy
|
|
61
|
+
*/
|
|
62
|
+
async getPolicy() {
|
|
63
|
+
return this.request("/policy");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe to transaction status events via SSE
|
|
67
|
+
* Returns unsubscribe function
|
|
68
|
+
*/
|
|
69
|
+
subscribeEvents(callback, callbackToken) {
|
|
70
|
+
const url = callbackToken
|
|
71
|
+
? `${this.baseUrl}/events?token=${encodeURIComponent(callbackToken)}`
|
|
72
|
+
: `${this.baseUrl}/events`;
|
|
73
|
+
const eventSource = new EventSource(url);
|
|
74
|
+
eventSource.onmessage = (event) => {
|
|
75
|
+
try {
|
|
76
|
+
const status = JSON.parse(event.data);
|
|
77
|
+
callback(status);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Ignore parse errors
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
eventSource.onerror = () => {
|
|
84
|
+
eventSource.close();
|
|
85
|
+
};
|
|
86
|
+
return () => {
|
|
87
|
+
eventSource.close();
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build headers for submit requests
|
|
92
|
+
*/
|
|
93
|
+
buildSubmitHeaders(options) {
|
|
94
|
+
const headers = {};
|
|
95
|
+
if (options?.callbackUrl)
|
|
96
|
+
headers["X-CallbackUrl"] = options.callbackUrl;
|
|
97
|
+
if (options?.callbackToken)
|
|
98
|
+
headers["X-CallbackToken"] = options.callbackToken;
|
|
99
|
+
if (options?.fullStatusUpdates)
|
|
100
|
+
headers["X-FullStatusUpdates"] = "true";
|
|
101
|
+
if (options?.skipFeeValidation)
|
|
102
|
+
headers["X-SkipFeeValidation"] = "true";
|
|
103
|
+
if (options?.skipScriptValidation)
|
|
104
|
+
headers["X-SkipScriptValidation"] = "true";
|
|
105
|
+
return headers;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { HttpError } from "../../errors";
|
|
2
|
+
/**
|
|
3
|
+
* Base client with shared HTTP utilities for all 1sat-stack API clients.
|
|
4
|
+
* Provides timeout handling, error parsing, and request helpers.
|
|
5
|
+
*/
|
|
6
|
+
export class BaseClient {
|
|
7
|
+
baseUrl;
|
|
8
|
+
timeout;
|
|
9
|
+
fetchFn;
|
|
10
|
+
constructor(baseUrl, options = {}) {
|
|
11
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
12
|
+
this.timeout = options.timeout ?? 30000;
|
|
13
|
+
this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Make a JSON request and parse the response
|
|
17
|
+
*/
|
|
18
|
+
async request(path, init) {
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
21
|
+
try {
|
|
22
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
23
|
+
...init,
|
|
24
|
+
signal: controller.signal,
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw await this.parseError(response);
|
|
28
|
+
}
|
|
29
|
+
// Handle empty responses
|
|
30
|
+
const text = await response.text();
|
|
31
|
+
if (!text) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return JSON.parse(text);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
clearTimeout(timeoutId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Make a request and return raw binary data
|
|
42
|
+
*/
|
|
43
|
+
async requestBinary(path, init) {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
46
|
+
try {
|
|
47
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
48
|
+
...init,
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw await this.parseError(response);
|
|
53
|
+
}
|
|
54
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
55
|
+
return new Uint8Array(arrayBuffer);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Make a request and return the raw Response object
|
|
63
|
+
* Useful for streaming responses
|
|
64
|
+
*/
|
|
65
|
+
async requestRaw(path, init) {
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
68
|
+
try {
|
|
69
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
70
|
+
...init,
|
|
71
|
+
signal: controller.signal,
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw await this.parseError(response);
|
|
75
|
+
}
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse error response into HttpError
|
|
84
|
+
*/
|
|
85
|
+
async parseError(response) {
|
|
86
|
+
try {
|
|
87
|
+
const data = await response.json();
|
|
88
|
+
const message = data.message || data.error || data.detail || response.statusText;
|
|
89
|
+
return new HttpError(response.status, message);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
try {
|
|
93
|
+
const text = await response.text();
|
|
94
|
+
return new HttpError(response.status, text || response.statusText);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return new HttpError(response.status, response.statusText);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build query string from options object
|
|
103
|
+
*/
|
|
104
|
+
buildQueryString(params) {
|
|
105
|
+
const entries = [];
|
|
106
|
+
for (const [key, value] of Object.entries(params)) {
|
|
107
|
+
if (value === undefined)
|
|
108
|
+
continue;
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
if (value.length > 0) {
|
|
111
|
+
entries.push(`${key}=${encodeURIComponent(value.join(","))}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (typeof value === "boolean") {
|
|
115
|
+
if (value) {
|
|
116
|
+
entries.push(`${key}=true`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
entries.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return entries.length > 0 ? `?${entries.join("&")}` : "";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BaseClient } from "./BaseClient";
|
|
2
|
+
/**
|
|
3
|
+
* Client for /1sat/beef/* routes.
|
|
4
|
+
* Provides BEEF data, raw transactions, and merkle proofs.
|
|
5
|
+
*
|
|
6
|
+
* Routes:
|
|
7
|
+
* - GET /:txid - Get BEEF for transaction
|
|
8
|
+
* - GET /:txid/raw - Get raw transaction bytes
|
|
9
|
+
* - GET /:txid/proof - Get merkle proof
|
|
10
|
+
*/
|
|
11
|
+
export class BeefClient extends BaseClient {
|
|
12
|
+
constructor(baseUrl, options = {}) {
|
|
13
|
+
super(`${baseUrl}/1sat/beef`, options);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get BEEF (Background Evaluation Extended Format) for a transaction
|
|
17
|
+
*/
|
|
18
|
+
async getBeef(txid) {
|
|
19
|
+
return this.requestBinary(`/${txid}`);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get raw transaction bytes
|
|
23
|
+
*/
|
|
24
|
+
async getRawTx(txid) {
|
|
25
|
+
return this.requestBinary(`/${txid}/tx`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get merkle proof bytes for a mined transaction
|
|
29
|
+
*/
|
|
30
|
+
async getProof(txid) {
|
|
31
|
+
return this.requestBinary(`/${txid}/proof`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { BaseClient } from "./BaseClient";
|
|
2
|
+
/**
|
|
3
|
+
* Client for /1sat/bsv21/* routes.
|
|
4
|
+
* Provides BSV21 token queries.
|
|
5
|
+
*
|
|
6
|
+
* Routes:
|
|
7
|
+
* - GET /:tokenId - Get token details
|
|
8
|
+
* - GET /:tokenId/blk/:height - Get token data at block height
|
|
9
|
+
* - GET /:tokenId/tx/:txid - Get token data for transaction
|
|
10
|
+
* - GET /:tokenId/:lockType/:address/balance - Get token balance
|
|
11
|
+
* - GET /:tokenId/:lockType/:address/unspent - Get unspent token UTXOs
|
|
12
|
+
* - GET /:tokenId/:lockType/:address/history - Get token transaction history
|
|
13
|
+
*/
|
|
14
|
+
export class Bsv21Client extends BaseClient {
|
|
15
|
+
cache = new Map();
|
|
16
|
+
constructor(baseUrl, options = {}) {
|
|
17
|
+
super(`${baseUrl}/1sat/bsv21`, options);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get token details (deploy data).
|
|
21
|
+
* Results are cached since token details are immutable.
|
|
22
|
+
*/
|
|
23
|
+
async getTokenDetails(tokenId) {
|
|
24
|
+
const cached = this.cache.get(tokenId);
|
|
25
|
+
if (cached)
|
|
26
|
+
return cached;
|
|
27
|
+
const details = await this.request(`/${tokenId}`);
|
|
28
|
+
this.cache.set(tokenId, details);
|
|
29
|
+
return details;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get token transaction data for a specific txid
|
|
33
|
+
*/
|
|
34
|
+
async getTokenByTxid(tokenId, txid) {
|
|
35
|
+
return this.request(`/${tokenId}/tx/${txid}`);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get token balance for an address
|
|
39
|
+
* @param tokenId - Token ID (outpoint of deploy tx)
|
|
40
|
+
* @param lockType - Lock type (e.g., 'p2pkh', 'ordlock')
|
|
41
|
+
* @param address - Address to check
|
|
42
|
+
*/
|
|
43
|
+
async getBalance(tokenId, lockType, address) {
|
|
44
|
+
const data = await this.request(`/${tokenId}/${lockType}/${address}/balance`);
|
|
45
|
+
return BigInt(data.balance);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get unspent token UTXOs for an address
|
|
49
|
+
*/
|
|
50
|
+
async getUnspent(tokenId, lockType, address) {
|
|
51
|
+
return this.request(`/${tokenId}/${lockType}/${address}/unspent`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get token transaction history for an address
|
|
55
|
+
*/
|
|
56
|
+
async getHistory(tokenId, lockType, address) {
|
|
57
|
+
return this.request(`/${tokenId}/${lockType}/${address}/history`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Clear the token details cache
|
|
61
|
+
*/
|
|
62
|
+
clearCache() {
|
|
63
|
+
this.cache.clear();
|
|
64
|
+
}
|
|
65
|
+
}
|