@1sat/wallet-toolbox 0.0.8 → 0.0.9
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/api/OneSatApi.d.ts +100 -0
- package/dist/api/OneSatApi.js +156 -0
- package/dist/api/balance/index.d.ts +38 -0
- package/dist/api/balance/index.js +82 -0
- package/dist/api/broadcast/index.d.ts +22 -0
- package/dist/api/broadcast/index.js +45 -0
- package/dist/api/constants.d.ts +21 -0
- package/dist/api/constants.js +29 -0
- package/dist/api/index.d.ts +36 -0
- package/dist/api/index.js +38 -0
- package/dist/api/inscriptions/index.d.ts +25 -0
- package/dist/api/inscriptions/index.js +50 -0
- package/dist/api/locks/index.d.ts +44 -0
- package/dist/api/locks/index.js +233 -0
- package/dist/api/ordinals/index.d.ts +87 -0
- package/dist/api/ordinals/index.js +446 -0
- package/dist/api/payments/index.d.ts +37 -0
- package/dist/api/payments/index.js +130 -0
- package/dist/api/signing/index.d.ts +32 -0
- package/dist/api/signing/index.js +41 -0
- package/dist/api/tokens/index.d.ts +88 -0
- package/dist/api/tokens/index.js +400 -0
- package/dist/cwi/chrome.d.ts +11 -0
- package/dist/cwi/chrome.js +39 -0
- package/dist/cwi/event.d.ts +11 -0
- package/dist/cwi/event.js +38 -0
- package/dist/cwi/factory.d.ts +14 -0
- package/dist/cwi/factory.js +44 -0
- package/dist/cwi/index.d.ts +11 -0
- package/dist/cwi/index.js +11 -0
- package/dist/cwi/types.d.ts +39 -0
- package/dist/cwi/types.js +39 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/indexers/CosignIndexer.js +1 -0
- package/dist/indexers/InscriptionIndexer.js +3 -4
- package/dist/indexers/LockIndexer.js +1 -0
- package/dist/indexers/OrdLockIndexer.js +1 -0
- package/dist/indexers/OriginIndexer.js +1 -1
- package/dist/indexers/index.d.ts +1 -1
- package/dist/indexers/types.d.ts +18 -0
- package/dist/services/OneSatServices.d.ts +19 -10
- package/dist/services/OneSatServices.js +201 -39
- package/dist/services/client/ChaintracksClient.d.ts +55 -13
- package/dist/services/client/ChaintracksClient.js +123 -28
- package/dist/services/client/OrdfsClient.d.ts +2 -2
- package/dist/services/client/OrdfsClient.js +4 -3
- package/dist/services/client/TxoClient.js +9 -0
- package/dist/sync/AddressManager.d.ts +85 -0
- package/dist/sync/AddressManager.js +107 -0
- package/dist/sync/SyncManager.d.ts +207 -0
- package/dist/sync/SyncManager.js +507 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +2 -0
- package/package.json +5 -4
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locks Module
|
|
3
|
+
*
|
|
4
|
+
* Functions for time-locking BSV.
|
|
5
|
+
*/
|
|
6
|
+
import { type WalletInterface, type WalletOutput } from "@bsv/sdk";
|
|
7
|
+
export interface LockBsvRequest {
|
|
8
|
+
/** Amount in satoshis to lock */
|
|
9
|
+
satoshis: number;
|
|
10
|
+
/** Block height until which to lock */
|
|
11
|
+
until: number;
|
|
12
|
+
}
|
|
13
|
+
export interface LockData {
|
|
14
|
+
/** Total locked satoshis */
|
|
15
|
+
totalLocked: number;
|
|
16
|
+
/** Unlockable satoshis (matured locks) */
|
|
17
|
+
unlockable: number;
|
|
18
|
+
/** Next unlock block height */
|
|
19
|
+
nextUnlock: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LockOperationResponse {
|
|
22
|
+
txid?: string;
|
|
23
|
+
rawtx?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* List locked outputs from the lock basket.
|
|
28
|
+
* Returns WalletOutput[] directly - use tags for metadata (lock:until:).
|
|
29
|
+
*/
|
|
30
|
+
export declare function listLocks(cwi: WalletInterface, limit?: number): Promise<WalletOutput[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Get lock data summary.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getLockData(cwi: WalletInterface, chain?: "main" | "test", wocApiKey?: string): Promise<LockData>;
|
|
35
|
+
/**
|
|
36
|
+
* Lock BSV until a block height.
|
|
37
|
+
* Derives lock address using hardcoded keyID.
|
|
38
|
+
*/
|
|
39
|
+
export declare function lockBsv(cwi: WalletInterface, requests: LockBsvRequest[]): Promise<LockOperationResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* Unlock matured BSV locks.
|
|
42
|
+
* Uses createSignature with stored keyID to sign unlock transactions.
|
|
43
|
+
*/
|
|
44
|
+
export declare function unlockBsv(cwi: WalletInterface, chain?: "main" | "test", wocApiKey?: string): Promise<LockOperationResponse>;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locks Module
|
|
3
|
+
*
|
|
4
|
+
* Functions for time-locking BSV.
|
|
5
|
+
*/
|
|
6
|
+
import { Hash, PublicKey, Script, Transaction, TransactionSignature, Utils, } from "@bsv/sdk";
|
|
7
|
+
import { LOCK_BASKET, LOCK_PREFIX, LOCK_SUFFIX, MIN_UNLOCK_SATS } from "../constants";
|
|
8
|
+
import { getChainInfo } from "../balance";
|
|
9
|
+
// Hardcoded keyID for all locks - deterministic derivation
|
|
10
|
+
const LOCK_KEY_ID = "lock";
|
|
11
|
+
/**
|
|
12
|
+
* Build lock script for time-locked BSV.
|
|
13
|
+
*/
|
|
14
|
+
function buildLockScript(address, until) {
|
|
15
|
+
const pkh = Utils.fromBase58Check(address).data;
|
|
16
|
+
return new Script()
|
|
17
|
+
.writeScript(Script.fromHex(LOCK_PREFIX))
|
|
18
|
+
.writeBin(pkh)
|
|
19
|
+
.writeNumber(until)
|
|
20
|
+
.writeScript(Script.fromHex(LOCK_SUFFIX));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* List locked outputs from the lock basket.
|
|
24
|
+
* Returns WalletOutput[] directly - use tags for metadata (lock:until:).
|
|
25
|
+
*/
|
|
26
|
+
export async function listLocks(cwi, limit = 10000) {
|
|
27
|
+
const result = await cwi.listOutputs({
|
|
28
|
+
basket: LOCK_BASKET,
|
|
29
|
+
includeTags: true,
|
|
30
|
+
limit,
|
|
31
|
+
});
|
|
32
|
+
return result.outputs;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get lock data summary.
|
|
36
|
+
*/
|
|
37
|
+
export async function getLockData(cwi, chain = "main", wocApiKey) {
|
|
38
|
+
const lockData = { totalLocked: 0, unlockable: 0, nextUnlock: 0 };
|
|
39
|
+
const chainInfo = await getChainInfo(chain, wocApiKey);
|
|
40
|
+
const currentHeight = chainInfo?.blocks || 0;
|
|
41
|
+
const outputs = await listLocks(cwi);
|
|
42
|
+
for (const o of outputs) {
|
|
43
|
+
const lockTag = o.tags?.find((t) => t.startsWith("lock:until:"));
|
|
44
|
+
if (!lockTag)
|
|
45
|
+
continue;
|
|
46
|
+
const until = parseInt(lockTag.slice(11), 10);
|
|
47
|
+
lockData.totalLocked += o.satoshis;
|
|
48
|
+
if (until <= currentHeight) {
|
|
49
|
+
lockData.unlockable += o.satoshis;
|
|
50
|
+
}
|
|
51
|
+
else if (!lockData.nextUnlock || until < lockData.nextUnlock) {
|
|
52
|
+
lockData.nextUnlock = until;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (lockData.unlockable < MIN_UNLOCK_SATS * outputs.length) {
|
|
56
|
+
lockData.unlockable = 0;
|
|
57
|
+
}
|
|
58
|
+
return lockData;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Lock BSV until a block height.
|
|
62
|
+
* Derives lock address using hardcoded keyID.
|
|
63
|
+
*/
|
|
64
|
+
export async function lockBsv(cwi, requests) {
|
|
65
|
+
try {
|
|
66
|
+
if (!requests || requests.length === 0) {
|
|
67
|
+
return { error: "no-lock-requests" };
|
|
68
|
+
}
|
|
69
|
+
// Derive lock address once (same for all locks)
|
|
70
|
+
const { publicKey } = await cwi.getPublicKey({
|
|
71
|
+
protocolID: [1, "lock"],
|
|
72
|
+
keyID: LOCK_KEY_ID,
|
|
73
|
+
counterparty: "self",
|
|
74
|
+
forSelf: true,
|
|
75
|
+
});
|
|
76
|
+
const lockAddress = PublicKey.fromString(publicKey).toAddress();
|
|
77
|
+
const outputs = [];
|
|
78
|
+
for (const req of requests) {
|
|
79
|
+
if (req.satoshis <= 0)
|
|
80
|
+
return { error: "invalid-satoshis" };
|
|
81
|
+
if (req.until <= 0)
|
|
82
|
+
return { error: "invalid-block-height" };
|
|
83
|
+
const lockingScript = buildLockScript(lockAddress, req.until);
|
|
84
|
+
outputs.push({
|
|
85
|
+
lockingScript: lockingScript.toHex(),
|
|
86
|
+
satoshis: req.satoshis,
|
|
87
|
+
outputDescription: `Lock ${req.satoshis} sats until block ${req.until}`,
|
|
88
|
+
basket: LOCK_BASKET,
|
|
89
|
+
tags: [`lock:until:${req.until}`],
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const result = await cwi.createAction({
|
|
93
|
+
description: `Lock BSV in ${requests.length} output(s)`,
|
|
94
|
+
outputs,
|
|
95
|
+
});
|
|
96
|
+
if (!result.txid) {
|
|
97
|
+
return { error: "no-txid-returned" };
|
|
98
|
+
}
|
|
99
|
+
return { txid: result.txid, rawtx: result.tx ? Utils.toHex(result.tx) : undefined };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Unlock matured BSV locks.
|
|
107
|
+
* Uses createSignature with stored keyID to sign unlock transactions.
|
|
108
|
+
*/
|
|
109
|
+
export async function unlockBsv(cwi, chain = "main", wocApiKey) {
|
|
110
|
+
try {
|
|
111
|
+
// Get current block height
|
|
112
|
+
const chainInfo = await getChainInfo(chain, wocApiKey);
|
|
113
|
+
const currentHeight = chainInfo?.blocks || 0;
|
|
114
|
+
if (currentHeight === 0) {
|
|
115
|
+
return { error: "could-not-get-block-height" };
|
|
116
|
+
}
|
|
117
|
+
// Get lock outputs from basket
|
|
118
|
+
const result = await cwi.listOutputs({
|
|
119
|
+
basket: LOCK_BASKET,
|
|
120
|
+
includeTags: true,
|
|
121
|
+
include: "locking scripts",
|
|
122
|
+
limit: 10000,
|
|
123
|
+
});
|
|
124
|
+
// Filter for matured locks
|
|
125
|
+
const maturedLocks = [];
|
|
126
|
+
for (const o of result.outputs) {
|
|
127
|
+
const untilTag = o.tags?.find((t) => t.startsWith("lock:until:"));
|
|
128
|
+
if (!untilTag)
|
|
129
|
+
continue;
|
|
130
|
+
const until = parseInt(untilTag.slice(11), 10);
|
|
131
|
+
if (until <= currentHeight) {
|
|
132
|
+
maturedLocks.push({ output: o, until });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (maturedLocks.length === 0) {
|
|
136
|
+
return { error: "no-matured-locks" };
|
|
137
|
+
}
|
|
138
|
+
// Check minimum unlock amount
|
|
139
|
+
const totalSats = maturedLocks.reduce((sum, l) => sum + l.output.satoshis, 0);
|
|
140
|
+
if (totalSats < MIN_UNLOCK_SATS * maturedLocks.length) {
|
|
141
|
+
return { error: "insufficient-unlock-amount" };
|
|
142
|
+
}
|
|
143
|
+
// Find max until value for lockTime
|
|
144
|
+
const maxUntil = Math.max(...maturedLocks.map((l) => l.until));
|
|
145
|
+
// Build createAction args
|
|
146
|
+
const createResult = await cwi.createAction({
|
|
147
|
+
description: `Unlock ${maturedLocks.length} lock(s)`,
|
|
148
|
+
inputs: maturedLocks.map((l) => ({
|
|
149
|
+
outpoint: l.output.outpoint,
|
|
150
|
+
inputDescription: "Locked BSV",
|
|
151
|
+
unlockingScriptLength: 180, // sig + pubkey + preimage estimate
|
|
152
|
+
sequenceNumber: 0, // Must be < 0xffffffff for nLockTime
|
|
153
|
+
})),
|
|
154
|
+
outputs: [
|
|
155
|
+
{
|
|
156
|
+
lockingScript: "", // Will be filled by wallet as change
|
|
157
|
+
satoshis: 0, // Will be calculated by wallet
|
|
158
|
+
outputDescription: "Unlocked BSV",
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
lockTime: maxUntil,
|
|
162
|
+
options: { signAndProcess: false },
|
|
163
|
+
});
|
|
164
|
+
if ("error" in createResult && createResult.error) {
|
|
165
|
+
return { error: String(createResult.error) };
|
|
166
|
+
}
|
|
167
|
+
if (!createResult.signableTransaction) {
|
|
168
|
+
return { error: "no-signable-transaction" };
|
|
169
|
+
}
|
|
170
|
+
// Parse transaction from BEEF
|
|
171
|
+
const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
|
|
172
|
+
// Build unlocking scripts for each input
|
|
173
|
+
const spends = {};
|
|
174
|
+
for (let i = 0; i < maturedLocks.length; i++) {
|
|
175
|
+
const lock = maturedLocks[i];
|
|
176
|
+
const input = tx.inputs[i];
|
|
177
|
+
const lockingScript = Script.fromHex(lock.output.lockingScript);
|
|
178
|
+
// Build preimage for signature
|
|
179
|
+
const preimage = TransactionSignature.format({
|
|
180
|
+
sourceTXID: input.sourceTXID,
|
|
181
|
+
sourceOutputIndex: input.sourceOutputIndex,
|
|
182
|
+
sourceSatoshis: lock.output.satoshis,
|
|
183
|
+
transactionVersion: tx.version,
|
|
184
|
+
otherInputs: tx.inputs.filter((_, idx) => idx !== i),
|
|
185
|
+
outputs: tx.outputs,
|
|
186
|
+
inputIndex: i,
|
|
187
|
+
subscript: lockingScript,
|
|
188
|
+
inputSequence: 0,
|
|
189
|
+
lockTime: tx.lockTime,
|
|
190
|
+
scope: TransactionSignature.SIGHASH_ALL |
|
|
191
|
+
TransactionSignature.SIGHASH_ANYONECANPAY |
|
|
192
|
+
TransactionSignature.SIGHASH_FORKID,
|
|
193
|
+
});
|
|
194
|
+
// Hash preimage for signing
|
|
195
|
+
const sighash = Hash.sha256(Hash.sha256(preimage));
|
|
196
|
+
// Get signature via createSignature using hardcoded keyID
|
|
197
|
+
const { signature } = await cwi.createSignature({
|
|
198
|
+
protocolID: [1, "lock"],
|
|
199
|
+
keyID: LOCK_KEY_ID,
|
|
200
|
+
counterparty: "self",
|
|
201
|
+
hashToDirectlySign: Array.from(sighash),
|
|
202
|
+
});
|
|
203
|
+
// Get public key
|
|
204
|
+
const { publicKey } = await cwi.getPublicKey({
|
|
205
|
+
protocolID: [1, "lock"],
|
|
206
|
+
keyID: LOCK_KEY_ID,
|
|
207
|
+
counterparty: "self",
|
|
208
|
+
forSelf: true,
|
|
209
|
+
});
|
|
210
|
+
// Build unlocking script: <sig> <pubkey> <preimage>
|
|
211
|
+
const unlockingScript = new Script()
|
|
212
|
+
.writeBin(signature)
|
|
213
|
+
.writeBin(Utils.toArray(publicKey, "hex"))
|
|
214
|
+
.writeBin(Array.from(preimage));
|
|
215
|
+
spends[i] = { unlockingScript: unlockingScript.toHex() };
|
|
216
|
+
}
|
|
217
|
+
// Sign and broadcast
|
|
218
|
+
const signResult = await cwi.signAction({
|
|
219
|
+
reference: createResult.signableTransaction.reference,
|
|
220
|
+
spends,
|
|
221
|
+
});
|
|
222
|
+
if ("error" in signResult) {
|
|
223
|
+
return { error: String(signResult.error) };
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
txid: signResult.txid,
|
|
227
|
+
rawtx: signResult.tx ? Utils.toHex(signResult.tx) : undefined,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
return { error: error instanceof Error ? error.message : "unknown-error" };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordinals Module
|
|
3
|
+
*
|
|
4
|
+
* Functions for managing ordinals/inscriptions.
|
|
5
|
+
* Returns WalletOutput[] directly from the SDK - no custom mapping needed.
|
|
6
|
+
*/
|
|
7
|
+
import { type WalletInterface, type WalletOutput, type CreateActionArgs, type ListOutputsArgs } from "@bsv/sdk";
|
|
8
|
+
import type { OneSatServices } from "../../services/OneSatServices";
|
|
9
|
+
export interface TransferOrdinalRequest {
|
|
10
|
+
/** Outpoint of the ordinal to transfer (txid_vout or txid.vout format) */
|
|
11
|
+
outpoint: string;
|
|
12
|
+
/** Destination address or paymail */
|
|
13
|
+
destination: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ListOrdinalRequest {
|
|
16
|
+
/** Outpoint of ordinal to list */
|
|
17
|
+
outpoint: string;
|
|
18
|
+
/** Price in satoshis */
|
|
19
|
+
price: number;
|
|
20
|
+
/** Address that receives payment on purchase (BRC-29 receive address) */
|
|
21
|
+
payAddress: string;
|
|
22
|
+
/** Address that can cancel the listing (optional - derived from CWI if not provided) */
|
|
23
|
+
cancelAddress?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface PurchaseOrdinalRequest {
|
|
26
|
+
/** Outpoint of listing to purchase */
|
|
27
|
+
outpoint: string;
|
|
28
|
+
/** Marketplace address for fees */
|
|
29
|
+
marketplaceAddress?: string;
|
|
30
|
+
/** Marketplace fee rate (0-1) */
|
|
31
|
+
marketplaceRate?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface OrdinalOperationResponse {
|
|
34
|
+
txid?: string;
|
|
35
|
+
rawtx?: string;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Derive a cancel address for an ordinal listing.
|
|
40
|
+
* Uses the outpoint as keyID with security level 1 (self-only).
|
|
41
|
+
*/
|
|
42
|
+
export declare function deriveCancelAddress(cwi: WalletInterface, outpoint: string): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* List ordinals from the 1sat basket.
|
|
45
|
+
* Returns WalletOutput[] directly - use tags for metadata (origin:, type:, name:, own:, list:).
|
|
46
|
+
*/
|
|
47
|
+
export declare function listOrdinals(cwi: WalletInterface, options?: Partial<ListOutputsArgs>): Promise<WalletOutput[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Build CreateActionArgs for transferring an ordinal.
|
|
50
|
+
* Does NOT execute - returns params for createAction.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildTransferOrdinal(cwi: WalletInterface, request: TransferOrdinalRequest): Promise<CreateActionArgs | {
|
|
53
|
+
error: string;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Build CreateActionArgs for listing an ordinal for sale.
|
|
57
|
+
* Does NOT execute - returns params for createAction.
|
|
58
|
+
* If cancelAddress is not provided, it will be derived from the CWI.
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildListOrdinal(cwi: WalletInterface, request: ListOrdinalRequest): Promise<CreateActionArgs | {
|
|
61
|
+
error: string;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Transfer an ordinal to a new address.
|
|
65
|
+
*/
|
|
66
|
+
export declare function transferOrdinal(cwi: WalletInterface, request: TransferOrdinalRequest): Promise<OrdinalOperationResponse>;
|
|
67
|
+
/**
|
|
68
|
+
* List an ordinal for sale on the global orderbook.
|
|
69
|
+
*/
|
|
70
|
+
export declare function listOrdinal(cwi: WalletInterface, request: ListOrdinalRequest): Promise<OrdinalOperationResponse>;
|
|
71
|
+
/**
|
|
72
|
+
* Cancel an ordinal listing.
|
|
73
|
+
* Uses the origin tag to recover the keyID for signing.
|
|
74
|
+
* Cancel unlock script: <sig> <pubkey> OP_1
|
|
75
|
+
*/
|
|
76
|
+
export declare function cancelListing(cwi: WalletInterface, outpoint: string): Promise<OrdinalOperationResponse>;
|
|
77
|
+
/**
|
|
78
|
+
* Purchase an ordinal from the global orderbook.
|
|
79
|
+
*
|
|
80
|
+
* Flow:
|
|
81
|
+
* 1. Fetch listing BEEF to get the locking script
|
|
82
|
+
* 2. Decode OrdLock to get price and payout
|
|
83
|
+
* 3. Build P2PKH output for buyer
|
|
84
|
+
* 4. Build payment output for seller
|
|
85
|
+
* 5. Build custom OrdLock purchase unlock (preimage only, no signature)
|
|
86
|
+
*/
|
|
87
|
+
export declare function purchaseOrdinal(cwi: WalletInterface, request: PurchaseOrdinalRequest, services?: OneSatServices): Promise<OrdinalOperationResponse>;
|