@1sat/wallet-toolbox 0.0.6 → 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 +40 -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 -93764
- 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/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,175 @@
|
|
|
1
|
+
import { BaseClient } from "./BaseClient";
|
|
2
|
+
/**
|
|
3
|
+
* Helper to read a 32-bit unsigned integer from little-endian bytes
|
|
4
|
+
*/
|
|
5
|
+
function readUint32LE(data, offset) {
|
|
6
|
+
return ((data[offset] |
|
|
7
|
+
(data[offset + 1] << 8) |
|
|
8
|
+
(data[offset + 2] << 16) |
|
|
9
|
+
(data[offset + 3] << 24)) >>>
|
|
10
|
+
0);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Convert bytes to hex string (little-endian)
|
|
14
|
+
*/
|
|
15
|
+
function toHexLE(data) {
|
|
16
|
+
return Array.from(data)
|
|
17
|
+
.reverse()
|
|
18
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
19
|
+
.join("");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* SHA256 hash
|
|
23
|
+
*/
|
|
24
|
+
async function sha256(data) {
|
|
25
|
+
const buf = await crypto.subtle.digest("SHA-256", data);
|
|
26
|
+
return new Uint8Array(buf);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Double SHA256 hash (used for block header hash)
|
|
30
|
+
*/
|
|
31
|
+
async function doubleSha256(data) {
|
|
32
|
+
return sha256(await sha256(data));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parse raw 80-byte header into BlockHeader object
|
|
36
|
+
*/
|
|
37
|
+
async function parseHeader(data, height) {
|
|
38
|
+
const version = readUint32LE(data, 0);
|
|
39
|
+
const prevHash = toHexLE(data.slice(4, 36));
|
|
40
|
+
const merkleRoot = toHexLE(data.slice(36, 68));
|
|
41
|
+
const time = readUint32LE(data, 68);
|
|
42
|
+
const bits = readUint32LE(data, 72);
|
|
43
|
+
const nonce = readUint32LE(data, 76);
|
|
44
|
+
const hash = toHexLE(await doubleSha256(data));
|
|
45
|
+
return { height, hash, version, prevHash, merkleRoot, time, bits, nonce };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Client for /1sat/chaintracks/* routes.
|
|
49
|
+
* Provides block header data and implements ChainTracker interface.
|
|
50
|
+
*
|
|
51
|
+
* Routes:
|
|
52
|
+
* - GET /tip - Get chain tip
|
|
53
|
+
* - GET /tip/stream - SSE stream of new blocks
|
|
54
|
+
* - GET /height - Get current height
|
|
55
|
+
* - GET /network - Get network type
|
|
56
|
+
* - GET /headers?height=N&count=M - Get raw header bytes
|
|
57
|
+
* - GET /header/height/:height - Get header by height
|
|
58
|
+
* - GET /header/hash/:hash - Get header by hash
|
|
59
|
+
*/
|
|
60
|
+
export class ChaintracksClient extends BaseClient {
|
|
61
|
+
eventSource = null;
|
|
62
|
+
subscribers = new Set();
|
|
63
|
+
constructor(baseUrl, options = {}) {
|
|
64
|
+
super(`${baseUrl}/1sat/chaintracks`, options);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get current blockchain height (ChainTracker interface)
|
|
68
|
+
*/
|
|
69
|
+
async currentHeight() {
|
|
70
|
+
const tip = await this.getTip();
|
|
71
|
+
return tip.height;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate merkle root for a given height (ChainTracker interface)
|
|
75
|
+
*/
|
|
76
|
+
async isValidRootForHeight(root, height) {
|
|
77
|
+
try {
|
|
78
|
+
const header = await this.getHeaderByHeight(height);
|
|
79
|
+
const isValid = header.merkleRoot === root;
|
|
80
|
+
return isValid;
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
console.error(`isValidRootForHeight(${height}) failed:`, e);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the network type (main or test)
|
|
89
|
+
*/
|
|
90
|
+
async getNetwork() {
|
|
91
|
+
const data = await this.request("/network");
|
|
92
|
+
return data.network;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get the current chain tip
|
|
96
|
+
*/
|
|
97
|
+
async getTip() {
|
|
98
|
+
return this.request("/tip");
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get block header by height
|
|
102
|
+
*/
|
|
103
|
+
async getHeaderByHeight(height) {
|
|
104
|
+
return this.request(`/header/height/${height}`);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get block header by hash
|
|
108
|
+
*/
|
|
109
|
+
async getHeaderByHash(hash) {
|
|
110
|
+
return this.request(`/header/hash/${hash}`);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get multiple headers as parsed BlockHeader objects
|
|
114
|
+
*/
|
|
115
|
+
async getHeaders(height, count) {
|
|
116
|
+
const data = await this.requestBinary(`/headers?height=${height}&count=${count}`);
|
|
117
|
+
if (data.length % 80 !== 0) {
|
|
118
|
+
throw new Error(`Invalid response length: ${data.length} bytes`);
|
|
119
|
+
}
|
|
120
|
+
const headers = [];
|
|
121
|
+
for (let i = 0; i < data.length; i += 80) {
|
|
122
|
+
headers.push(await parseHeader(data.slice(i, i + 80), height + i / 80));
|
|
123
|
+
}
|
|
124
|
+
return headers;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get raw header bytes for one or more headers
|
|
128
|
+
*/
|
|
129
|
+
async getHeaderBytes(height, count = 1) {
|
|
130
|
+
const data = await this.requestBinary(`/headers?height=${height}&count=${count}`);
|
|
131
|
+
return Array.from(data);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Subscribe to new block notifications via SSE
|
|
135
|
+
* Returns unsubscribe function
|
|
136
|
+
*/
|
|
137
|
+
subscribe(callback) {
|
|
138
|
+
this.subscribers.add(callback);
|
|
139
|
+
if (!this.eventSource) {
|
|
140
|
+
this.eventSource = new EventSource(`${this.baseUrl}/tip/stream`);
|
|
141
|
+
this.eventSource.onmessage = (event) => {
|
|
142
|
+
try {
|
|
143
|
+
const header = JSON.parse(event.data);
|
|
144
|
+
for (const cb of this.subscribers) {
|
|
145
|
+
cb(header);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Ignore parse errors (e.g., keepalive messages)
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
this.eventSource.onerror = () => {
|
|
153
|
+
this.eventSource?.close();
|
|
154
|
+
this.eventSource = null;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return () => {
|
|
158
|
+
this.subscribers.delete(callback);
|
|
159
|
+
if (this.subscribers.size === 0 && this.eventSource) {
|
|
160
|
+
this.eventSource.close();
|
|
161
|
+
this.eventSource = null;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Close all connections
|
|
167
|
+
*/
|
|
168
|
+
close() {
|
|
169
|
+
if (this.eventSource) {
|
|
170
|
+
this.eventSource.close();
|
|
171
|
+
this.eventSource = null;
|
|
172
|
+
}
|
|
173
|
+
this.subscribers.clear();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { BaseClient } from "./BaseClient";
|
|
2
|
+
/**
|
|
3
|
+
* Client for ordfs routes.
|
|
4
|
+
* Provides inscription content and metadata.
|
|
5
|
+
*
|
|
6
|
+
* Content is served from /1sat/content (e.g., https://api.1sat.app/1sat/content/:outpoint)
|
|
7
|
+
* API routes use /1sat/ordfs (e.g., https://api.1sat.app/1sat/ordfs/metadata/:outpoint)
|
|
8
|
+
*/
|
|
9
|
+
export class OrdfsClient extends BaseClient {
|
|
10
|
+
contentBaseUrl;
|
|
11
|
+
constructor(baseUrl, options = {}) {
|
|
12
|
+
super(`${baseUrl}/1sat/ordfs`, options);
|
|
13
|
+
this.contentBaseUrl = `${baseUrl.replace(/\/$/, "")}/1sat/content`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get metadata for an inscription
|
|
17
|
+
* @param outpoint - Outpoint (txid_vout) or txid
|
|
18
|
+
* @param seq - Optional sequence number (-1 for latest)
|
|
19
|
+
*/
|
|
20
|
+
async getMetadata(outpoint, seq) {
|
|
21
|
+
const path = seq !== undefined ? `${outpoint}:${seq}` : outpoint;
|
|
22
|
+
return this.request(`/metadata/${path}`);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get inscription content with full response headers
|
|
26
|
+
* @param outpoint - Outpoint (txid_vout) or txid
|
|
27
|
+
* @param options - Content request options
|
|
28
|
+
*/
|
|
29
|
+
async getContent(outpoint, options = {}) {
|
|
30
|
+
const url = this.getContentUrl(outpoint, options);
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
33
|
+
try {
|
|
34
|
+
const response = await this.fetchFn(url, {
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`Failed to fetch content: ${response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
const headers = this.parseResponseHeaders(response);
|
|
41
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
42
|
+
return {
|
|
43
|
+
data: new Uint8Array(arrayBuffer),
|
|
44
|
+
headers,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
clearTimeout(timeoutId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Preview base64-encoded HTML content
|
|
53
|
+
* @param b64HtmlData - Base64-encoded HTML
|
|
54
|
+
*/
|
|
55
|
+
async previewHtml(b64HtmlData) {
|
|
56
|
+
const response = await this.requestRaw(`/preview/${b64HtmlData}`);
|
|
57
|
+
return response.text();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Preview content by posting it directly
|
|
61
|
+
* @param content - Content to preview
|
|
62
|
+
* @param contentType - Content type header
|
|
63
|
+
*/
|
|
64
|
+
async previewContent(content, contentType) {
|
|
65
|
+
return this.requestBinary("/preview", {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "Content-Type": contentType },
|
|
68
|
+
body: content,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the URL for fetching inscription content directly.
|
|
73
|
+
* Useful for displaying in img/video tags.
|
|
74
|
+
* @param outpoint - Outpoint (txid_vout) or txid
|
|
75
|
+
* @param options - Content request options
|
|
76
|
+
*/
|
|
77
|
+
getContentUrl(outpoint, options = {}) {
|
|
78
|
+
let path = outpoint;
|
|
79
|
+
if (options.seq !== undefined) {
|
|
80
|
+
path = `${outpoint}:${options.seq}`;
|
|
81
|
+
}
|
|
82
|
+
const queryParams = this.buildQueryString({
|
|
83
|
+
map: options.map,
|
|
84
|
+
parent: options.parent,
|
|
85
|
+
raw: options.raw,
|
|
86
|
+
});
|
|
87
|
+
return `${this.contentBaseUrl}/${path}${queryParams}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Parse response headers into structured object
|
|
91
|
+
*/
|
|
92
|
+
parseResponseHeaders(response) {
|
|
93
|
+
const headers = {
|
|
94
|
+
contentType: response.headers.get("content-type") || "application/octet-stream",
|
|
95
|
+
};
|
|
96
|
+
const outpoint = response.headers.get("x-outpoint");
|
|
97
|
+
if (outpoint)
|
|
98
|
+
headers.outpoint = outpoint;
|
|
99
|
+
const origin = response.headers.get("x-origin");
|
|
100
|
+
if (origin)
|
|
101
|
+
headers.origin = origin;
|
|
102
|
+
const seq = response.headers.get("x-ord-seq");
|
|
103
|
+
if (seq)
|
|
104
|
+
headers.sequence = Number.parseInt(seq, 10);
|
|
105
|
+
const cacheControl = response.headers.get("cache-control");
|
|
106
|
+
if (cacheControl)
|
|
107
|
+
headers.cacheControl = cacheControl;
|
|
108
|
+
const mapData = response.headers.get("x-map");
|
|
109
|
+
if (mapData) {
|
|
110
|
+
try {
|
|
111
|
+
headers.map = JSON.parse(mapData);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Invalid JSON, skip
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const parent = response.headers.get("x-parent");
|
|
118
|
+
if (parent)
|
|
119
|
+
headers.parent = parent;
|
|
120
|
+
return headers;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { BaseClient } from "./BaseClient";
|
|
2
|
+
/**
|
|
3
|
+
* Client for /1sat/owner/* routes.
|
|
4
|
+
* Provides owner (address) queries and sync.
|
|
5
|
+
*
|
|
6
|
+
* Routes:
|
|
7
|
+
* - GET /:owner/txos - Get TXOs for owner
|
|
8
|
+
* - GET /:owner/balance - Get balance
|
|
9
|
+
* - GET /sync?owner=... - SSE stream of outputs for sync (supports multiple owners)
|
|
10
|
+
*/
|
|
11
|
+
export class OwnerClient extends BaseClient {
|
|
12
|
+
constructor(baseUrl, options = {}) {
|
|
13
|
+
super(`${baseUrl}/1sat/owner`, options);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get TXOs owned by an address/owner
|
|
17
|
+
*/
|
|
18
|
+
async getTxos(owner, opts) {
|
|
19
|
+
const qs = this.buildQueryString({
|
|
20
|
+
tags: opts?.tags,
|
|
21
|
+
from: opts?.from,
|
|
22
|
+
limit: opts?.limit,
|
|
23
|
+
rev: opts?.rev,
|
|
24
|
+
unspent: opts?.unspent,
|
|
25
|
+
refresh: opts?.refresh,
|
|
26
|
+
});
|
|
27
|
+
return this.request(`/${owner}/txos${qs}`);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get balance for an address/owner
|
|
31
|
+
*/
|
|
32
|
+
async getBalance(owner) {
|
|
33
|
+
return this.request(`/${owner}/balance`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Sync outputs for owner(s) via SSE stream.
|
|
37
|
+
* The server merges results from all owners in score order.
|
|
38
|
+
*
|
|
39
|
+
* @param owners - Array of addresses/owners to sync
|
|
40
|
+
* @param onOutput - Callback for each output
|
|
41
|
+
* @param from - Starting score (for pagination/resumption)
|
|
42
|
+
* @param onDone - Callback when sync completes (client should retry after delay)
|
|
43
|
+
* @param onError - Callback for errors
|
|
44
|
+
* @returns Unsubscribe function
|
|
45
|
+
*/
|
|
46
|
+
sync(owners, onOutput, from, onDone, onError) {
|
|
47
|
+
const params = new URLSearchParams();
|
|
48
|
+
for (const owner of owners) {
|
|
49
|
+
params.append("owner", owner);
|
|
50
|
+
}
|
|
51
|
+
if (from !== undefined) {
|
|
52
|
+
params.set("from", String(from));
|
|
53
|
+
}
|
|
54
|
+
const url = `${this.baseUrl}/sync?${params.toString()}`;
|
|
55
|
+
const eventSource = new EventSource(url);
|
|
56
|
+
eventSource.onmessage = (event) => {
|
|
57
|
+
try {
|
|
58
|
+
const output = JSON.parse(event.data);
|
|
59
|
+
onOutput(output);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
eventSource.addEventListener("done", () => {
|
|
66
|
+
eventSource.close();
|
|
67
|
+
onDone?.();
|
|
68
|
+
});
|
|
69
|
+
eventSource.onerror = () => {
|
|
70
|
+
eventSource.close();
|
|
71
|
+
onError?.(new Error("SSE connection error"));
|
|
72
|
+
};
|
|
73
|
+
return () => {
|
|
74
|
+
eventSource.close();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sync outputs as an async iterator.
|
|
79
|
+
* Yields SyncOutput objects until the stream is done.
|
|
80
|
+
*/
|
|
81
|
+
async *syncIterator(owners, from) {
|
|
82
|
+
const outputs = [];
|
|
83
|
+
let done = false;
|
|
84
|
+
let error = null;
|
|
85
|
+
let resolve = null;
|
|
86
|
+
const unsubscribe = this.sync(owners, (output) => {
|
|
87
|
+
outputs.push(output);
|
|
88
|
+
resolve?.();
|
|
89
|
+
}, from, () => {
|
|
90
|
+
done = true;
|
|
91
|
+
resolve?.();
|
|
92
|
+
}, (e) => {
|
|
93
|
+
error = e;
|
|
94
|
+
resolve?.();
|
|
95
|
+
});
|
|
96
|
+
try {
|
|
97
|
+
while (!done && !error) {
|
|
98
|
+
if (outputs.length > 0) {
|
|
99
|
+
const output = outputs.shift();
|
|
100
|
+
if (output)
|
|
101
|
+
yield output;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
await new Promise((r) => {
|
|
105
|
+
resolve = r;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Yield remaining outputs
|
|
110
|
+
while (outputs.length > 0) {
|
|
111
|
+
const output = outputs.shift();
|
|
112
|
+
if (output)
|
|
113
|
+
yield output;
|
|
114
|
+
}
|
|
115
|
+
if (error) {
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
unsubscribe();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { BaseClient } from "./BaseClient";
|
|
2
|
+
/**
|
|
3
|
+
* Client for /1sat/txo/* routes.
|
|
4
|
+
* Provides TXO (transaction output) lookup and search.
|
|
5
|
+
*
|
|
6
|
+
* Routes:
|
|
7
|
+
* - GET /:outpoint - Get single TXO (outpoint pattern-matched)
|
|
8
|
+
* - GET /:outpoint/spend - Get spend info
|
|
9
|
+
* - POST /outpoints - Get multiple TXOs
|
|
10
|
+
* - POST /spends - Get multiple spends
|
|
11
|
+
* - GET /tx/:txid - Get all TXOs for a transaction
|
|
12
|
+
* - GET /search?key=... - Search by key(s)
|
|
13
|
+
*/
|
|
14
|
+
export class TxoClient extends BaseClient {
|
|
15
|
+
constructor(baseUrl, options = {}) {
|
|
16
|
+
super(`${baseUrl}/1sat/txo`, options);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get a single TXO by outpoint
|
|
20
|
+
*/
|
|
21
|
+
async get(outpoint, opts) {
|
|
22
|
+
const qs = this.buildQueryString({
|
|
23
|
+
tags: opts?.tags,
|
|
24
|
+
});
|
|
25
|
+
return this.request(`/${outpoint}${qs}`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get multiple TXOs by outpoints
|
|
29
|
+
*/
|
|
30
|
+
async getBatch(outpoints, opts) {
|
|
31
|
+
const qs = this.buildQueryString({
|
|
32
|
+
tags: opts?.tags,
|
|
33
|
+
});
|
|
34
|
+
return this.request(`/outpoints${qs}`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: { "Content-Type": "application/json" },
|
|
37
|
+
body: JSON.stringify(outpoints),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get spend info for an outpoint
|
|
42
|
+
*/
|
|
43
|
+
async getSpend(outpoint) {
|
|
44
|
+
const resp = await this.request(`/${outpoint}/spend`);
|
|
45
|
+
return resp.spendTxid;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get spend info for multiple outpoints
|
|
49
|
+
*/
|
|
50
|
+
async getSpends(outpoints) {
|
|
51
|
+
const resp = await this.request("/spends", {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
body: JSON.stringify(outpoints),
|
|
55
|
+
});
|
|
56
|
+
return resp.map((r) => r.spendTxid);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get all TXOs for a transaction
|
|
60
|
+
*/
|
|
61
|
+
async getByTxid(txid, opts) {
|
|
62
|
+
const qs = this.buildQueryString({
|
|
63
|
+
tags: opts?.tags,
|
|
64
|
+
});
|
|
65
|
+
return this.request(`/tx/${txid}${qs}`);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Search TXOs by key(s)
|
|
69
|
+
*/
|
|
70
|
+
async search(keys, opts) {
|
|
71
|
+
const qs = this.buildQueryString({
|
|
72
|
+
key: Array.isArray(keys) ? keys : [keys],
|
|
73
|
+
tags: opts?.tags,
|
|
74
|
+
from: opts?.from,
|
|
75
|
+
limit: opts?.limit,
|
|
76
|
+
rev: opts?.rev,
|
|
77
|
+
unspent: opts?.unspent,
|
|
78
|
+
sats: opts?.sats,
|
|
79
|
+
spend: opts?.spend,
|
|
80
|
+
events: opts?.events,
|
|
81
|
+
block: opts?.block,
|
|
82
|
+
});
|
|
83
|
+
return this.request(`/search${qs}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { BaseClient } from "./BaseClient";
|
|
2
|
+
export { ChaintracksClient } from "./ChaintracksClient";
|
|
3
|
+
export { BeefClient } from "./BeefClient";
|
|
4
|
+
export { ArcadeClient } from "./ArcadeClient";
|
|
5
|
+
export { TxoClient } from "./TxoClient";
|
|
6
|
+
export { OwnerClient } from "./OwnerClient";
|
|
7
|
+
export { OrdfsClient } from "./OrdfsClient";
|
|
8
|
+
export { Bsv21Client } from "./Bsv21Client";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PublicKey, } from "@bsv/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* A mock PrivateKey that only supports toPublicKey().
|
|
4
|
+
* All other operations throw.
|
|
5
|
+
*/
|
|
6
|
+
class ReadOnlyPrivateKey {
|
|
7
|
+
publicKey;
|
|
8
|
+
constructor(publicKeyHex) {
|
|
9
|
+
this.publicKey = PublicKey.fromString(publicKeyHex);
|
|
10
|
+
}
|
|
11
|
+
toPublicKey() {
|
|
12
|
+
return this.publicKey;
|
|
13
|
+
}
|
|
14
|
+
toString() {
|
|
15
|
+
return this.publicKey.toString();
|
|
16
|
+
}
|
|
17
|
+
toHex() {
|
|
18
|
+
return this.publicKey.toString();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A read-only KeyDeriver that exposes an identity key but throws on any signing/derivation operation.
|
|
23
|
+
* Used when the wallet is instantiated with only a public key.
|
|
24
|
+
*/
|
|
25
|
+
export class ReadOnlySigner {
|
|
26
|
+
identityKey;
|
|
27
|
+
rootKey;
|
|
28
|
+
constructor(identityPublicKey) {
|
|
29
|
+
this.identityKey = identityPublicKey;
|
|
30
|
+
this.rootKey = new ReadOnlyPrivateKey(identityPublicKey);
|
|
31
|
+
}
|
|
32
|
+
derivePrivateKey(_protocolID, _keyID, _counterparty) {
|
|
33
|
+
throw new Error("Cannot derive private key in read-only mode");
|
|
34
|
+
}
|
|
35
|
+
derivePublicKey(_protocolID, _keyID, _counterparty, _forSelf) {
|
|
36
|
+
throw new Error("Cannot derive public key in read-only mode");
|
|
37
|
+
}
|
|
38
|
+
deriveSymmetricKey(_protocolID, _keyID, _counterparty) {
|
|
39
|
+
throw new Error("Cannot derive symmetric key in read-only mode");
|
|
40
|
+
}
|
|
41
|
+
revealCounterpartySecret(_counterparty) {
|
|
42
|
+
throw new Error("Cannot reveal secrets in read-only mode");
|
|
43
|
+
}
|
|
44
|
+
revealSpecificSecret(_counterparty, _protocolID, _keyID) {
|
|
45
|
+
throw new Error("Cannot reveal secrets in read-only mode");
|
|
46
|
+
}
|
|
47
|
+
}
|