@arkade-os/sdk 0.3.7 → 0.3.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/README.md +78 -1
- package/dist/cjs/identity/singleKey.js +33 -1
- package/dist/cjs/index.js +17 -2
- package/dist/cjs/intent/index.js +31 -2
- package/dist/cjs/providers/ark.js +9 -3
- package/dist/cjs/providers/indexer.js +2 -2
- package/dist/cjs/wallet/batch.js +183 -0
- package/dist/cjs/wallet/index.js +15 -0
- package/dist/cjs/wallet/serviceWorker/request.js +0 -2
- package/dist/cjs/wallet/serviceWorker/wallet.js +98 -34
- package/dist/cjs/wallet/serviceWorker/worker.js +169 -69
- package/dist/cjs/wallet/utils.js +2 -2
- package/dist/cjs/wallet/vtxo-manager.js +5 -0
- package/dist/cjs/wallet/wallet.js +399 -356
- package/dist/esm/identity/singleKey.js +31 -0
- package/dist/esm/index.js +12 -7
- package/dist/esm/intent/index.js +31 -2
- package/dist/esm/providers/ark.js +9 -3
- package/dist/esm/providers/indexer.js +2 -2
- package/dist/esm/wallet/batch.js +180 -0
- package/dist/esm/wallet/index.js +14 -0
- package/dist/esm/wallet/serviceWorker/request.js +0 -2
- package/dist/esm/wallet/serviceWorker/wallet.js +96 -33
- package/dist/esm/wallet/serviceWorker/worker.js +171 -71
- package/dist/esm/wallet/utils.js +2 -2
- package/dist/esm/wallet/vtxo-manager.js +6 -1
- package/dist/esm/wallet/wallet.js +400 -359
- package/dist/types/identity/index.d.ts +5 -3
- package/dist/types/identity/singleKey.d.ts +20 -1
- package/dist/types/index.d.ts +11 -8
- package/dist/types/intent/index.d.ts +19 -2
- package/dist/types/providers/ark.d.ts +9 -8
- package/dist/types/providers/indexer.d.ts +2 -2
- package/dist/types/wallet/batch.d.ts +87 -0
- package/dist/types/wallet/index.d.ts +75 -16
- package/dist/types/wallet/serviceWorker/request.d.ts +5 -1
- package/dist/types/wallet/serviceWorker/wallet.d.ts +46 -15
- package/dist/types/wallet/serviceWorker/worker.d.ts +6 -3
- package/dist/types/wallet/utils.d.ts +8 -3
- package/dist/types/wallet/wallet.d.ts +96 -35
- package/package.json +123 -113
|
@@ -83,4 +83,35 @@ export class SingleKey {
|
|
|
83
83
|
return signAsync(message, this.key, { prehash: false });
|
|
84
84
|
return schnorr.signAsync(message, this.key);
|
|
85
85
|
}
|
|
86
|
+
async toReadonly() {
|
|
87
|
+
return new ReadonlySingleKey(await this.compressedPublicKey());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class ReadonlySingleKey {
|
|
91
|
+
constructor(publicKey) {
|
|
92
|
+
this.publicKey = publicKey;
|
|
93
|
+
if (publicKey.length !== 33) {
|
|
94
|
+
throw new Error("Invalid public key length");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Create a ReadonlySingleKey from a compressed public key.
|
|
99
|
+
*
|
|
100
|
+
* @param publicKey - 33-byte compressed public key (02/03 prefix + 32-byte x coordinate)
|
|
101
|
+
* @returns A new ReadonlySingleKey instance
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* const pubkey = new Uint8Array(33); // your compressed public key
|
|
105
|
+
* const readonlyKey = ReadonlySingleKey.fromPublicKey(pubkey);
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
static fromPublicKey(publicKey) {
|
|
109
|
+
return new ReadonlySingleKey(publicKey);
|
|
110
|
+
}
|
|
111
|
+
xOnlyPublicKey() {
|
|
112
|
+
return Promise.resolve(this.publicKey.slice(1));
|
|
113
|
+
}
|
|
114
|
+
compressedPublicKey() {
|
|
115
|
+
return Promise.resolve(this.publicKey);
|
|
116
|
+
}
|
|
86
117
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Transaction } from './utils/transaction.js';
|
|
2
|
-
import { SingleKey } from './identity/singleKey.js';
|
|
2
|
+
import { SingleKey, ReadonlySingleKey } from './identity/singleKey.js';
|
|
3
3
|
import { ArkAddress } from './script/address.js';
|
|
4
4
|
import { VHTLC } from './script/vhtlc.js';
|
|
5
5
|
import { DefaultVtxo } from './script/default.js';
|
|
6
6
|
import { VtxoScript, TapTreeCoder, } from './script/base.js';
|
|
7
|
-
import { TxType, } from './wallet/index.js';
|
|
8
|
-
import {
|
|
7
|
+
import { TxType, isSpendable, isSubdust, isRecoverable, isExpired, } from './wallet/index.js';
|
|
8
|
+
import { Batch } from './wallet/batch.js';
|
|
9
|
+
import { Wallet, ReadonlyWallet, waitForIncomingFunds, getSequence, } from './wallet/wallet.js';
|
|
9
10
|
import { TxTree } from './tree/txTree.js';
|
|
10
11
|
import { Ramps } from './wallet/ramps.js';
|
|
11
12
|
import { isVtxoExpiringSoon, VtxoManager } from './wallet/vtxo-manager.js';
|
|
12
|
-
import { ServiceWorkerWallet } from './wallet/serviceWorker/wallet.js';
|
|
13
|
+
import { ServiceWorkerWallet, ServiceWorkerReadonlyWallet, } from './wallet/serviceWorker/wallet.js';
|
|
13
14
|
import { OnchainWallet } from './wallet/onchain.js';
|
|
14
15
|
import { setupServiceWorker } from './wallet/serviceWorker/utils.js';
|
|
15
16
|
import { Worker } from './wallet/serviceWorker/worker.js';
|
|
@@ -29,9 +30,11 @@ import { Unroll } from './wallet/unroll.js';
|
|
|
29
30
|
import { WalletRepositoryImpl } from './repositories/walletRepository.js';
|
|
30
31
|
import { ContractRepositoryImpl } from './repositories/contractRepository.js';
|
|
31
32
|
import { ArkError, maybeArkError } from './providers/errors.js';
|
|
33
|
+
import { validateVtxoTxGraph, validateConnectorsTxGraph, } from './tree/validation.js';
|
|
34
|
+
import { buildForfeitTx } from './forfeit.js';
|
|
32
35
|
export {
|
|
33
36
|
// Wallets
|
|
34
|
-
Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager,
|
|
37
|
+
Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, OnchainWallet, Ramps, VtxoManager,
|
|
35
38
|
// Providers
|
|
36
39
|
ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider,
|
|
37
40
|
// Script-related
|
|
@@ -39,7 +42,7 @@ ArkAddress, DefaultVtxo, VtxoScript, VHTLC,
|
|
|
39
42
|
// Enums
|
|
40
43
|
TxType, IndexerTxType, ChainTxType, SettlementEventType,
|
|
41
44
|
// Service Worker
|
|
42
|
-
setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response,
|
|
45
|
+
setupServiceWorker, Worker, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, Request, Response,
|
|
43
46
|
// Tapscript
|
|
44
47
|
decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder,
|
|
45
48
|
// Ark PSBT fields
|
|
@@ -59,4 +62,6 @@ TxTree,
|
|
|
59
62
|
// Anchor
|
|
60
63
|
P2A, Unroll, Transaction,
|
|
61
64
|
// Errors
|
|
62
|
-
ArkError, maybeArkError,
|
|
65
|
+
ArkError, maybeArkError,
|
|
66
|
+
// Batch session
|
|
67
|
+
Batch, validateVtxoTxGraph, validateConnectorsTxGraph, buildForfeitTx, isRecoverable, isSpendable, isSubdust, isExpired, getSequence, };
|
package/dist/esm/intent/index.js
CHANGED
|
@@ -33,12 +33,15 @@ export var Intent;
|
|
|
33
33
|
* ownership of VTXOs and UTXOs. The proof includes the message to be
|
|
34
34
|
* signed and the inputs/outputs that demonstrate ownership.
|
|
35
35
|
*
|
|
36
|
-
* @param message - The Intent message to be signed
|
|
36
|
+
* @param message - The Intent message to be signed, either raw string of Message object
|
|
37
37
|
* @param inputs - Array of transaction inputs to prove ownership of
|
|
38
38
|
* @param outputs - Optional array of transaction outputs
|
|
39
39
|
* @returns An unsigned Intent proof transaction
|
|
40
40
|
*/
|
|
41
41
|
function create(message, inputs, outputs = []) {
|
|
42
|
+
if (typeof message !== "string") {
|
|
43
|
+
message = encodeMessage(message);
|
|
44
|
+
}
|
|
42
45
|
if (inputs.length == 0)
|
|
43
46
|
throw new Error("intent proof requires at least one input");
|
|
44
47
|
if (!validateInputs(inputs))
|
|
@@ -51,6 +54,29 @@ export var Intent;
|
|
|
51
54
|
return craftToSignTx(toSpend, inputs, outputs);
|
|
52
55
|
}
|
|
53
56
|
Intent.create = create;
|
|
57
|
+
function encodeMessage(message) {
|
|
58
|
+
switch (message.type) {
|
|
59
|
+
case "register":
|
|
60
|
+
return JSON.stringify({
|
|
61
|
+
type: "register",
|
|
62
|
+
onchain_output_indexes: message.onchain_output_indexes,
|
|
63
|
+
valid_at: message.valid_at,
|
|
64
|
+
expire_at: message.expire_at,
|
|
65
|
+
cosigners_public_keys: message.cosigners_public_keys,
|
|
66
|
+
});
|
|
67
|
+
case "delete":
|
|
68
|
+
return JSON.stringify({
|
|
69
|
+
type: "delete",
|
|
70
|
+
expire_at: message.expire_at,
|
|
71
|
+
});
|
|
72
|
+
case "get-pending-tx":
|
|
73
|
+
return JSON.stringify({
|
|
74
|
+
type: "get-pending-tx",
|
|
75
|
+
expire_at: message.expire_at,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
Intent.encodeMessage = encodeMessage;
|
|
54
80
|
})(Intent || (Intent = {}));
|
|
55
81
|
const OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([OP.RETURN]);
|
|
56
82
|
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
@@ -105,9 +131,12 @@ function craftToSpendTx(message, pkScript) {
|
|
|
105
131
|
// craftToSignTx creates the transaction that will be signed for the proof
|
|
106
132
|
function craftToSignTx(toSpend, inputs, outputs) {
|
|
107
133
|
const firstInput = inputs[0];
|
|
134
|
+
const lockTime = inputs
|
|
135
|
+
.map((input) => input.sequence || 0)
|
|
136
|
+
.reduce((a, b) => Math.max(a, b), 0);
|
|
108
137
|
const tx = new Transaction({
|
|
109
138
|
version: 2,
|
|
110
|
-
lockTime
|
|
139
|
+
lockTime,
|
|
111
140
|
});
|
|
112
141
|
// add the first "toSpend" input
|
|
113
142
|
tx.addInput({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hex } from "@scure/base";
|
|
2
2
|
import { eventSourceIterator } from './utils.js';
|
|
3
3
|
import { maybeArkError } from './errors.js';
|
|
4
|
+
import { Intent } from '../intent/index.js';
|
|
4
5
|
export var SettlementEventType;
|
|
5
6
|
(function (SettlementEventType) {
|
|
6
7
|
SettlementEventType["BatchStarted"] = "batch_started";
|
|
@@ -123,7 +124,7 @@ export class RestArkProvider {
|
|
|
123
124
|
body: JSON.stringify({
|
|
124
125
|
intent: {
|
|
125
126
|
proof: intent.proof,
|
|
126
|
-
message: intent.message,
|
|
127
|
+
message: Intent.encodeMessage(intent.message),
|
|
127
128
|
},
|
|
128
129
|
}),
|
|
129
130
|
});
|
|
@@ -144,7 +145,7 @@ export class RestArkProvider {
|
|
|
144
145
|
body: JSON.stringify({
|
|
145
146
|
intent: {
|
|
146
147
|
proof: intent.proof,
|
|
147
|
-
message: intent.message,
|
|
148
|
+
message: Intent.encodeMessage(intent.message),
|
|
148
149
|
},
|
|
149
150
|
}),
|
|
150
151
|
});
|
|
@@ -324,7 +325,12 @@ export class RestArkProvider {
|
|
|
324
325
|
headers: {
|
|
325
326
|
"Content-Type": "application/json",
|
|
326
327
|
},
|
|
327
|
-
body: JSON.stringify({
|
|
328
|
+
body: JSON.stringify({
|
|
329
|
+
intent: {
|
|
330
|
+
proof: intent.proof,
|
|
331
|
+
message: Intent.encodeMessage(intent.message),
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
328
334
|
});
|
|
329
335
|
if (!response.ok) {
|
|
330
336
|
const errorText = await response.text();
|
|
@@ -361,7 +361,7 @@ function convertVtxo(vtxo) {
|
|
|
361
361
|
// Unexported namespace for type guards only
|
|
362
362
|
var Response;
|
|
363
363
|
(function (Response) {
|
|
364
|
-
function
|
|
364
|
+
function isBatchInfo(data) {
|
|
365
365
|
return (typeof data === "object" &&
|
|
366
366
|
typeof data.totalOutputAmount === "string" &&
|
|
367
367
|
typeof data.totalOutputVtxos === "number" &&
|
|
@@ -385,7 +385,7 @@ var Response;
|
|
|
385
385
|
typeof data.totalOutputAmount === "string" &&
|
|
386
386
|
typeof data.totalOutputVtxos === "number" &&
|
|
387
387
|
typeof data.batches === "object" &&
|
|
388
|
-
Object.values(data.batches).every(
|
|
388
|
+
Object.values(data.batches).every(isBatchInfo));
|
|
389
389
|
}
|
|
390
390
|
Response.isCommitmentTx = isCommitmentTx;
|
|
391
391
|
function isOutpoint(data) {
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { SettlementEventType } from '../providers/ark.js';
|
|
2
|
+
import { TxTree } from '../tree/txTree.js';
|
|
3
|
+
import { hex } from "@scure/base";
|
|
4
|
+
/**
|
|
5
|
+
* Batch namespace provides utilities for joining and processing batch session.
|
|
6
|
+
* The batch settlement process involves multiple events, this namespace provides abstractions and types to handle them.
|
|
7
|
+
* @see https://docs.arkadeos.com/learn/pillars/batch-swaps
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // use wallet handler or create a custom one
|
|
11
|
+
* const handler = wallet.createBatchHandler(intentId, inputs, musig2session);
|
|
12
|
+
*
|
|
13
|
+
* const abortController = new AbortController();
|
|
14
|
+
* // Get event stream from Ark provider
|
|
15
|
+
* const eventStream = arkProvider.getEventStream(
|
|
16
|
+
* abortController.signal,
|
|
17
|
+
* ['your-topic-1', 'your-topic-2']
|
|
18
|
+
* );
|
|
19
|
+
*
|
|
20
|
+
* // Join the batch and process events
|
|
21
|
+
* try {
|
|
22
|
+
* const commitmentTxid = await Batch.join(eventStream, handler);
|
|
23
|
+
* console.log('Batch completed with commitment:', commitmentTxid);
|
|
24
|
+
* } catch (error) {
|
|
25
|
+
* console.error('Batch processing failed:', error);
|
|
26
|
+
* } finally {
|
|
27
|
+
* abortController.abort();
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export var Batch;
|
|
32
|
+
(function (Batch) {
|
|
33
|
+
// State machine steps for batch session
|
|
34
|
+
let Step;
|
|
35
|
+
(function (Step) {
|
|
36
|
+
Step["Start"] = "start";
|
|
37
|
+
Step["BatchStarted"] = "batch_started";
|
|
38
|
+
Step["TreeSigningStarted"] = "tree_signing_started";
|
|
39
|
+
Step["TreeNoncesAggregated"] = "tree_nonces_aggregated";
|
|
40
|
+
Step["BatchFinalization"] = "batch_finalization";
|
|
41
|
+
})(Step || (Step = {}));
|
|
42
|
+
/**
|
|
43
|
+
* Start the state machine that will process the batch events and join a batch.
|
|
44
|
+
* @param eventIterator - The events stream to process.
|
|
45
|
+
* @param handler - How to react to events.
|
|
46
|
+
* @param options - Options.
|
|
47
|
+
*/
|
|
48
|
+
async function join(eventIterator, handler, options = {}) {
|
|
49
|
+
const { abortController, skipVtxoTreeSigning = false, eventCallback, } = options;
|
|
50
|
+
let step = Step.Start;
|
|
51
|
+
// keep track of tree transactions as they arrive
|
|
52
|
+
const flatVtxoTree = [];
|
|
53
|
+
const flatConnectorTree = [];
|
|
54
|
+
// once everything is collected, the TxTree objects are created
|
|
55
|
+
let vtxoTree = undefined;
|
|
56
|
+
let connectorTree = undefined;
|
|
57
|
+
for await (const event of eventIterator) {
|
|
58
|
+
if (abortController?.signal.aborted) {
|
|
59
|
+
throw new Error("canceled");
|
|
60
|
+
}
|
|
61
|
+
if (eventCallback) {
|
|
62
|
+
// don't wait for the callback to complete and ignore errors
|
|
63
|
+
eventCallback(event).catch(() => { });
|
|
64
|
+
}
|
|
65
|
+
switch (event.type) {
|
|
66
|
+
case SettlementEventType.BatchStarted: {
|
|
67
|
+
const e = event;
|
|
68
|
+
const { skip } = await handler.onBatchStarted(e);
|
|
69
|
+
if (!skip) {
|
|
70
|
+
step = Step.BatchStarted;
|
|
71
|
+
if (skipVtxoTreeSigning) {
|
|
72
|
+
// skip TxTree events and musig2 signatures and nonces
|
|
73
|
+
step = Step.TreeNoncesAggregated;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
case SettlementEventType.BatchFinalized: {
|
|
79
|
+
if (step !== Step.BatchFinalization) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (handler.onBatchFinalized) {
|
|
83
|
+
await handler.onBatchFinalized(event);
|
|
84
|
+
}
|
|
85
|
+
return event.commitmentTxid;
|
|
86
|
+
}
|
|
87
|
+
case SettlementEventType.BatchFailed: {
|
|
88
|
+
if (handler.onBatchFailed) {
|
|
89
|
+
await handler.onBatchFailed(event);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
throw new Error(event.reason);
|
|
93
|
+
}
|
|
94
|
+
case SettlementEventType.TreeTx: {
|
|
95
|
+
if (step !== Step.BatchStarted &&
|
|
96
|
+
step !== Step.TreeNoncesAggregated) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// batchIndex 0 = vtxo tree, batchIndex 1 = connector tree
|
|
100
|
+
if (event.batchIndex === 0) {
|
|
101
|
+
flatVtxoTree.push(event.chunk);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
flatConnectorTree.push(event.chunk);
|
|
105
|
+
}
|
|
106
|
+
if (handler.onTreeTxEvent) {
|
|
107
|
+
await handler.onTreeTxEvent(event);
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
case SettlementEventType.TreeSignature: {
|
|
112
|
+
if (step !== Step.TreeNoncesAggregated) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!vtxoTree) {
|
|
116
|
+
throw new Error("vtxo tree not initialized");
|
|
117
|
+
}
|
|
118
|
+
// push signature to the vtxo tree
|
|
119
|
+
const tapKeySig = hex.decode(event.signature);
|
|
120
|
+
vtxoTree.update(event.txid, (tx) => {
|
|
121
|
+
tx.updateInput(0, {
|
|
122
|
+
tapKeySig,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
if (handler.onTreeSignatureEvent) {
|
|
126
|
+
await handler.onTreeSignatureEvent(event);
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
case SettlementEventType.TreeSigningStarted: {
|
|
131
|
+
if (step !== Step.BatchStarted) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// create vtxo tree from collected chunks
|
|
135
|
+
vtxoTree = TxTree.create(flatVtxoTree);
|
|
136
|
+
const { skip } = await handler.onTreeSigningStarted(event, vtxoTree);
|
|
137
|
+
if (!skip) {
|
|
138
|
+
step = Step.TreeSigningStarted;
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
case SettlementEventType.TreeNonces: {
|
|
143
|
+
if (step !== Step.TreeSigningStarted) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const { fullySigned } = await handler.onTreeNonces(event);
|
|
147
|
+
if (fullySigned) {
|
|
148
|
+
step = Step.TreeNoncesAggregated;
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
case SettlementEventType.BatchFinalization: {
|
|
153
|
+
if (step !== Step.TreeNoncesAggregated) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// Build vtxo tree if it hasn't been built yet
|
|
157
|
+
if (!vtxoTree && flatVtxoTree.length > 0) {
|
|
158
|
+
vtxoTree = TxTree.create(flatVtxoTree);
|
|
159
|
+
}
|
|
160
|
+
if (!vtxoTree && !skipVtxoTreeSigning) {
|
|
161
|
+
throw new Error("vtxo tree not initialized");
|
|
162
|
+
}
|
|
163
|
+
// Build connector tree if we have chunks
|
|
164
|
+
if (flatConnectorTree.length > 0) {
|
|
165
|
+
connectorTree = TxTree.create(flatConnectorTree);
|
|
166
|
+
}
|
|
167
|
+
await handler.onBatchFinalization(event, vtxoTree, connectorTree);
|
|
168
|
+
step = Step.BatchFinalization;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
default:
|
|
172
|
+
// unknown event type, continue
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// iterator closed without finalization, something went wrong
|
|
177
|
+
throw new Error("event stream closed");
|
|
178
|
+
}
|
|
179
|
+
Batch.join = join;
|
|
180
|
+
})(Batch || (Batch = {}));
|
package/dist/esm/wallet/index.js
CHANGED
|
@@ -9,6 +9,20 @@ export function isSpendable(vtxo) {
|
|
|
9
9
|
export function isRecoverable(vtxo) {
|
|
10
10
|
return vtxo.virtualStatus.state === "swept" && isSpendable(vtxo);
|
|
11
11
|
}
|
|
12
|
+
export function isExpired(vtxo) {
|
|
13
|
+
if (vtxo.virtualStatus.state === "swept")
|
|
14
|
+
return true; // swept by server = expired
|
|
15
|
+
const expiry = vtxo.virtualStatus.batchExpiry;
|
|
16
|
+
if (!expiry)
|
|
17
|
+
return false;
|
|
18
|
+
// we use this as a workaround to avoid issue on regtest where expiry date is expressed in blockheight instead of timestamp
|
|
19
|
+
// if expiry, as Date, is before 2025, then we admit it's too small to be a timestamp
|
|
20
|
+
// TODO: API should return the expiry unit
|
|
21
|
+
const expireAt = new Date(expiry);
|
|
22
|
+
if (expireAt.getFullYear() < 2025)
|
|
23
|
+
return false;
|
|
24
|
+
return expiry <= Date.now();
|
|
25
|
+
}
|
|
12
26
|
export function isSubdust(vtxo, dust) {
|
|
13
27
|
return vtxo.value < dust;
|
|
14
28
|
}
|
|
@@ -11,8 +11,6 @@ export var Request;
|
|
|
11
11
|
return (message.type === "INIT_WALLET" &&
|
|
12
12
|
"arkServerUrl" in message &&
|
|
13
13
|
typeof message.arkServerUrl === "string" &&
|
|
14
|
-
"privateKey" in message &&
|
|
15
|
-
typeof message.privateKey === "string" &&
|
|
16
14
|
("arkServerPublicKey" in message
|
|
17
15
|
? message.arkServerPublicKey === undefined ||
|
|
18
16
|
typeof message.arkServerPublicKey === "string"
|
|
@@ -13,7 +13,16 @@ class UnexpectedResponseError extends Error {
|
|
|
13
13
|
this.name = "UnexpectedResponseError";
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
const createCommon = (options) => {
|
|
17
|
+
// Default to IndexedDB for service worker context
|
|
18
|
+
const storage = new IndexedDBStorageAdapter(options.dbName || DEFAULT_DB_NAME, options.dbVersion);
|
|
19
|
+
// Create repositories
|
|
20
|
+
return {
|
|
21
|
+
walletRepo: new WalletRepositoryImpl(storage),
|
|
22
|
+
contractRepo: new ContractRepositoryImpl(storage),
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export class ServiceWorkerReadonlyWallet {
|
|
17
26
|
constructor(serviceWorker, identity, walletRepository, contractRepository) {
|
|
18
27
|
this.serviceWorker = serviceWorker;
|
|
19
28
|
this.identity = identity;
|
|
@@ -21,27 +30,17 @@ export class ServiceWorkerWallet {
|
|
|
21
30
|
this.contractRepository = contractRepository;
|
|
22
31
|
}
|
|
23
32
|
static async create(options) {
|
|
24
|
-
|
|
25
|
-
const storage = new IndexedDBStorageAdapter(options.dbName || DEFAULT_DB_NAME, options.dbVersion);
|
|
26
|
-
// Create repositories
|
|
27
|
-
const walletRepo = new WalletRepositoryImpl(storage);
|
|
28
|
-
const contractRepo = new ContractRepositoryImpl(storage);
|
|
29
|
-
// Extract identity and check if it can expose private key
|
|
30
|
-
const identity = isPrivateKeyIdentity(options.identity)
|
|
31
|
-
? options.identity
|
|
32
|
-
: null;
|
|
33
|
-
if (!identity) {
|
|
34
|
-
throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose a single private key");
|
|
35
|
-
}
|
|
36
|
-
// Extract private key for service worker initialization
|
|
37
|
-
const privateKey = identity.toHex();
|
|
33
|
+
const { walletRepo, contractRepo } = createCommon(options);
|
|
38
34
|
// Create the wallet instance
|
|
39
|
-
const wallet = new
|
|
35
|
+
const wallet = new ServiceWorkerReadonlyWallet(options.serviceWorker, options.identity, walletRepo, contractRepo);
|
|
36
|
+
const publicKey = await options.identity
|
|
37
|
+
.compressedPublicKey()
|
|
38
|
+
.then(hex.encode);
|
|
40
39
|
// Initialize the service worker with the config
|
|
41
40
|
const initMessage = {
|
|
42
41
|
type: "INIT_WALLET",
|
|
43
42
|
id: getRandomId(),
|
|
44
|
-
|
|
43
|
+
key: { publicKey },
|
|
45
44
|
arkServerUrl: options.arkServerUrl,
|
|
46
45
|
arkServerPublicKey: options.arkServerPublicKey,
|
|
47
46
|
};
|
|
@@ -56,14 +55,14 @@ export class ServiceWorkerWallet {
|
|
|
56
55
|
* @example
|
|
57
56
|
* ```typescript
|
|
58
57
|
* // One-liner setup - handles everything automatically!
|
|
59
|
-
* const wallet = await
|
|
58
|
+
* const wallet = await ServiceWorkerReadonlyWallet.setup({
|
|
60
59
|
* serviceWorkerPath: '/service-worker.js',
|
|
61
60
|
* arkServerUrl: 'https://mutinynet.arkade.sh'
|
|
62
61
|
* });
|
|
63
62
|
*
|
|
64
|
-
* // With custom identity
|
|
65
|
-
* const identity =
|
|
66
|
-
* const wallet = await
|
|
63
|
+
* // With custom readonly identity
|
|
64
|
+
* const identity = ReadonlySingleKey.fromPublicKey('your_public_key_hex');
|
|
65
|
+
* const wallet = await ServiceWorkerReadonlyWallet.setup({
|
|
67
66
|
* serviceWorkerPath: '/service-worker.js',
|
|
68
67
|
* arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
69
68
|
* identity
|
|
@@ -74,7 +73,7 @@ export class ServiceWorkerWallet {
|
|
|
74
73
|
// Register and setup the service worker
|
|
75
74
|
const serviceWorker = await setupServiceWorker(options.serviceWorkerPath);
|
|
76
75
|
// Use the existing create method
|
|
77
|
-
return
|
|
76
|
+
return ServiceWorkerReadonlyWallet.create({
|
|
78
77
|
...options,
|
|
79
78
|
serviceWorker,
|
|
80
79
|
});
|
|
@@ -226,6 +225,81 @@ export class ServiceWorkerWallet {
|
|
|
226
225
|
throw new Error(`Failed to get vtxos: ${error}`);
|
|
227
226
|
}
|
|
228
227
|
}
|
|
228
|
+
async reload() {
|
|
229
|
+
const message = {
|
|
230
|
+
type: "RELOAD_WALLET",
|
|
231
|
+
id: getRandomId(),
|
|
232
|
+
};
|
|
233
|
+
const response = await this.sendMessage(message);
|
|
234
|
+
if (Response.isWalletReloaded(response)) {
|
|
235
|
+
return response.success;
|
|
236
|
+
}
|
|
237
|
+
throw new UnexpectedResponseError(response);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
241
|
+
constructor(serviceWorker, identity, walletRepository, contractRepository) {
|
|
242
|
+
super(serviceWorker, identity, walletRepository, contractRepository);
|
|
243
|
+
this.serviceWorker = serviceWorker;
|
|
244
|
+
this.identity = identity;
|
|
245
|
+
this.walletRepository = walletRepository;
|
|
246
|
+
this.contractRepository = contractRepository;
|
|
247
|
+
}
|
|
248
|
+
static async create(options) {
|
|
249
|
+
const { walletRepo, contractRepo } = createCommon(options);
|
|
250
|
+
// Extract identity and check if it can expose private key
|
|
251
|
+
const identity = isPrivateKeyIdentity(options.identity)
|
|
252
|
+
? options.identity
|
|
253
|
+
: null;
|
|
254
|
+
if (!identity) {
|
|
255
|
+
throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose a single private key");
|
|
256
|
+
}
|
|
257
|
+
// Extract private key for service worker initialization
|
|
258
|
+
const privateKey = identity.toHex();
|
|
259
|
+
// Create the wallet instance
|
|
260
|
+
const wallet = new ServiceWorkerWallet(options.serviceWorker, identity, walletRepo, contractRepo);
|
|
261
|
+
// Initialize the service worker with the config
|
|
262
|
+
const initMessage = {
|
|
263
|
+
type: "INIT_WALLET",
|
|
264
|
+
id: getRandomId(),
|
|
265
|
+
key: { privateKey },
|
|
266
|
+
arkServerUrl: options.arkServerUrl,
|
|
267
|
+
arkServerPublicKey: options.arkServerPublicKey,
|
|
268
|
+
};
|
|
269
|
+
// Initialize the service worker
|
|
270
|
+
await wallet.sendMessage(initMessage);
|
|
271
|
+
return wallet;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Simplified setup method that handles service worker registration,
|
|
275
|
+
* identity creation, and wallet initialization automatically.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```typescript
|
|
279
|
+
* // One-liner setup - handles everything automatically!
|
|
280
|
+
* const wallet = await ServiceWorkerWallet.setup({
|
|
281
|
+
* serviceWorkerPath: '/service-worker.js',
|
|
282
|
+
* arkServerUrl: 'https://mutinynet.arkade.sh'
|
|
283
|
+
* });
|
|
284
|
+
*
|
|
285
|
+
* // With custom identity
|
|
286
|
+
* const identity = SingleKey.fromHex('your_private_key_hex');
|
|
287
|
+
* const wallet = await ServiceWorkerWallet.setup({
|
|
288
|
+
* serviceWorkerPath: '/service-worker.js',
|
|
289
|
+
* arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
290
|
+
* identity
|
|
291
|
+
* });
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
static async setup(options) {
|
|
295
|
+
// Register and setup the service worker
|
|
296
|
+
const serviceWorker = await setupServiceWorker(options.serviceWorkerPath);
|
|
297
|
+
// Use the existing create method
|
|
298
|
+
return ServiceWorkerWallet.create({
|
|
299
|
+
...options,
|
|
300
|
+
serviceWorker,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
229
303
|
async sendBitcoin(params) {
|
|
230
304
|
const message = {
|
|
231
305
|
type: "SEND_BITCOIN",
|
|
@@ -283,17 +357,6 @@ export class ServiceWorkerWallet {
|
|
|
283
357
|
throw new Error(`Settlement failed: ${error}`);
|
|
284
358
|
}
|
|
285
359
|
}
|
|
286
|
-
async reload() {
|
|
287
|
-
const message = {
|
|
288
|
-
type: "RELOAD_WALLET",
|
|
289
|
-
id: getRandomId(),
|
|
290
|
-
};
|
|
291
|
-
const response = await this.sendMessage(message);
|
|
292
|
-
if (Response.isWalletReloaded(response)) {
|
|
293
|
-
return response.success;
|
|
294
|
-
}
|
|
295
|
-
throw new UnexpectedResponseError(response);
|
|
296
|
-
}
|
|
297
360
|
}
|
|
298
361
|
function getRandomId() {
|
|
299
362
|
const randomValue = crypto.getRandomValues(new Uint8Array(16));
|