@arkade-os/sdk 0.0.16 → 0.1.0

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 CHANGED
@@ -1,7 +1,5 @@
1
- # Ark Wallet SDK
2
- The Ark Wallet SDK is a TypeScript library for building Bitcoin wallets with support for both on-chain and off-chain transactions via Ark protocol.
3
-
4
- ![v3](https://github.com/user-attachments/assets/bec6fd29-417d-46af-8216-709edc39d566)
1
+ # Arkade TypeScript SDK
2
+ The Arkade SDK is a TypeScript library for building Bitcoin wallets with support for both on-chain and off-chain transactions via the Ark protocol.
5
3
 
6
4
  ## Installation
7
5
 
@@ -92,6 +90,16 @@ console.log('History:', history)
92
90
  }
93
91
  ```
94
92
 
93
+ ### Unilateral Exit
94
+
95
+ ```typescript
96
+ // Unilateral exit all vtxos
97
+ await wallet.exit();
98
+
99
+ // Unilateral exit a specific vtxo
100
+ await wallet.exit([{ txid: vtxo.txid, vout: vtxo.vout }]);
101
+ ```
102
+
95
103
  ### Running the wallet in a service worker
96
104
 
97
105
  1. Create a service worker file
@@ -40,6 +40,23 @@ class RestArkProvider {
40
40
  spentVtxos: [...(data.spentVtxos || [])].map(convertVtxo),
41
41
  };
42
42
  }
43
+ async getRound(txid) {
44
+ const url = `${this.serverUrl}/v1/round/${txid}`;
45
+ const response = await fetch(url);
46
+ if (!response.ok) {
47
+ throw new Error(`Failed to fetch round: ${response.statusText}`);
48
+ }
49
+ const data = (await response.json());
50
+ const round = data.round;
51
+ return {
52
+ id: round.id,
53
+ start: new Date(Number(round.start) * 1000), // Convert from Unix timestamp to Date
54
+ end: new Date(Number(round.end) * 1000), // Convert from Unix timestamp to Date
55
+ vtxoTree: this.toTxTree(round.vtxoTree),
56
+ forfeitTxs: round.forfeitTxs || [],
57
+ connectors: this.toTxTree(round.connectors),
58
+ };
59
+ }
43
60
  async submitVirtualTx(psbtBase64) {
44
61
  const url = `${this.serverUrl}/v1/redeem-tx`;
45
62
  const response = await fetch(url, {
@@ -57,5 +57,17 @@ class EsploraProvider {
57
57
  }
58
58
  return response.json();
59
59
  }
60
+ async getTxStatus(txid) {
61
+ const response = await fetch(`${this.baseUrl}/tx/${txid}/status`);
62
+ if (!response.ok) {
63
+ throw new Error(`Failed to get transaction status: ${response.statusText}`);
64
+ }
65
+ const data = await response.json();
66
+ return {
67
+ confirmed: data.confirmed,
68
+ blockTime: data.block_time,
69
+ blockHeight: data.block_height,
70
+ };
71
+ }
60
72
  }
61
73
  exports.EsploraProvider = EsploraProvider;
@@ -111,6 +111,11 @@ class TxTree {
111
111
  }
112
112
  return branch;
113
113
  }
114
+ // Returns the remaining transactions to broadcast in order to exit the vtxo
115
+ async exitBranch(vtxoTxid, isTxConfirmed) {
116
+ const offchainPart = await getOffchainPart(this.branch(vtxoTxid), isTxConfirmed);
117
+ return offchainPart.map(getExitTransaction);
118
+ }
114
119
  // Helper method to find parent of a node
115
120
  findParent(node) {
116
121
  for (const level of this.tree) {
@@ -195,3 +200,32 @@ function getCosignerKeys(tx) {
195
200
  }
196
201
  return keys;
197
202
  }
203
+ async function getOffchainPart(branch, isTxConfirmed) {
204
+ let offchainPath = [...branch];
205
+ // Iterate from the end of the branch (leaf) to the beginning (root)
206
+ for (let i = branch.length - 1; i >= 0; i--) {
207
+ const node = branch[i];
208
+ // check if the transaction is confirmed on-chain
209
+ if (await isTxConfirmed(node.txid)) {
210
+ // if this is the leaf node, return empty array as everything is confirmed
211
+ if (i === branch.length - 1) {
212
+ return [];
213
+ }
214
+ // otherwise, return the unconfirmed part of the branch
215
+ return branch.slice(i + 1);
216
+ }
217
+ }
218
+ // no confirmation: everything is offchain
219
+ return offchainPath;
220
+ }
221
+ // getExitTransaction finalizes the psbt's input using the musig2 tapkey signature
222
+ function getExitTransaction(treeNode) {
223
+ const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(treeNode.tx));
224
+ const input = tx.getInput(0);
225
+ if (!input.tapKeySig)
226
+ throw new TxTreeError("missing tapkey signature");
227
+ const rawTx = btc_signer_1.RawTx.decode(tx.unsignedTx);
228
+ rawTx.witnesses = [[input.tapKeySig]];
229
+ rawTx.segwitFlag = true;
230
+ return base_1.hex.encode(btc_signer_1.RawTx.encode(rawTx));
231
+ }
@@ -72,4 +72,8 @@ var Request;
72
72
  return message.type === "GET_STATUS";
73
73
  }
74
74
  Request.isGetStatus = isGetStatus;
75
+ function isExit(message) {
76
+ return message.type === "EXIT";
77
+ }
78
+ Request.isExit = isExit;
75
79
  })(Request || (exports.Request = Request = {}));
@@ -184,4 +184,12 @@ var Response;
184
184
  };
185
185
  }
186
186
  Response.clearResponse = clearResponse;
187
+ function exitSuccess(id) {
188
+ return {
189
+ type: "EXIT_SUCCESS",
190
+ success: true,
191
+ id,
192
+ };
193
+ }
194
+ Response.exitSuccess = exitSuccess;
187
195
  })(Response || (exports.Response = Response = {}));
@@ -324,6 +324,23 @@ class ServiceWorkerWallet {
324
324
  throw new Error(`Failed to get transaction history: ${error}`);
325
325
  }
326
326
  }
327
+ async exit(outpoints) {
328
+ const message = {
329
+ type: "EXIT",
330
+ outpoints,
331
+ id: getRandomId(),
332
+ };
333
+ try {
334
+ const response = await this.sendMessage(message);
335
+ if (response.type === "EXIT_SUCCESS") {
336
+ return;
337
+ }
338
+ throw new UnexpectedResponseError(response);
339
+ }
340
+ catch (error) {
341
+ throw new Error(`Failed to exit: ${error}`);
342
+ }
343
+ }
327
344
  }
328
345
  exports.ServiceWorkerWallet = ServiceWorkerWallet;
329
346
  function getRandomId() {
@@ -387,6 +387,30 @@ class Worker {
387
387
  }
388
388
  event.source?.postMessage(response_1.Response.walletStatus(message.id, this.wallet !== undefined));
389
389
  }
390
+ async handleExit(event) {
391
+ const message = event.data;
392
+ if (!request_1.Request.isExit(message)) {
393
+ console.error("Invalid EXIT message format", message);
394
+ event.source?.postMessage(response_1.Response.error(message.id, "Invalid EXIT message format"));
395
+ return;
396
+ }
397
+ if (!this.wallet) {
398
+ console.error("Wallet not initialized");
399
+ event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
400
+ return;
401
+ }
402
+ try {
403
+ await this.wallet.exit(message.outpoints);
404
+ event.source?.postMessage(response_1.Response.exitSuccess(message.id));
405
+ }
406
+ catch (error) {
407
+ console.error("Error exiting:", error);
408
+ const errorMessage = error instanceof Error
409
+ ? error.message
410
+ : "Unknown error occurred";
411
+ event.source?.postMessage(response_1.Response.error(message.id, errorMessage));
412
+ }
413
+ }
390
414
  async handleMessage(event) {
391
415
  this.messageCallback(event);
392
416
  const message = event.data;
@@ -440,6 +464,10 @@ class Worker {
440
464
  await this.handleGetStatus(event);
441
465
  break;
442
466
  }
467
+ case "EXIT": {
468
+ await this.handleExit(event);
469
+ break;
470
+ }
443
471
  case "CLEAR": {
444
472
  await this.handleClear(event);
445
473
  break;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Wallet = void 0;
4
4
  const base_1 = require("@scure/base");
5
5
  const payment_1 = require("@scure/btc-signer/payment");
6
+ const btc_signer_1 = require("@scure/btc-signer");
6
7
  const psbt_1 = require("@scure/btc-signer/psbt");
7
8
  const transactionHistory_1 = require("../utils/transactionHistory");
8
9
  const bip21_1 = require("../utils/bip21");
@@ -19,7 +20,6 @@ const _1 = require(".");
19
20
  const base_2 = require("../script/base");
20
21
  const tapscript_1 = require("../script/tapscript");
21
22
  const psbt_2 = require("../utils/psbt");
22
- const btc_signer_1 = require("@scure/btc-signer");
23
23
  const arknote_1 = require("../arknote");
24
24
  // Wallet does not store any data and rely on the Ark and onchain providers to fetch utxos and vtxos
25
25
  class Wallet {
@@ -585,6 +585,48 @@ class Wallet {
585
585
  }
586
586
  throw new Error("Settlement failed");
587
587
  }
588
+ async exit(outpoints) {
589
+ // TODO store the exit branches in repository
590
+ // exit should not depend on the ark provider
591
+ if (!this.arkProvider) {
592
+ throw new Error("Ark provider not configured");
593
+ }
594
+ let vtxos = await this.getVtxos();
595
+ if (outpoints && outpoints.length > 0) {
596
+ vtxos = vtxos.filter((vtxo) => outpoints.some((outpoint) => vtxo.txid === outpoint.txid &&
597
+ vtxo.vout === outpoint.vout));
598
+ }
599
+ if (vtxos.length === 0) {
600
+ throw new Error("No vtxos to exit");
601
+ }
602
+ const trees = new Map();
603
+ const transactions = [];
604
+ for (const vtxo of vtxos) {
605
+ const batchTxid = vtxo.virtualStatus.batchTxID;
606
+ if (!batchTxid)
607
+ continue;
608
+ if (!trees.has(batchTxid)) {
609
+ const round = await this.arkProvider.getRound(batchTxid);
610
+ trees.set(batchTxid, round.vtxoTree);
611
+ }
612
+ const tree = trees.get(batchTxid);
613
+ if (!tree) {
614
+ throw new Error("Tree not found");
615
+ }
616
+ const exitBranch = await tree.exitBranch(vtxo.txid, async (txid) => {
617
+ const status = await this.onchainProvider.getTxStatus(txid);
618
+ return status.confirmed;
619
+ });
620
+ transactions.push(...exitBranch);
621
+ }
622
+ const broadcastedTxs = new Map();
623
+ for (const tx of transactions) {
624
+ if (broadcastedTxs.has(tx))
625
+ continue;
626
+ const txid = await this.onchainProvider.broadcastTransaction(tx);
627
+ broadcastedTxs.set(txid, true);
628
+ }
629
+ }
588
630
  // validates the vtxo tree, creates a signing session and generates the musig2 nonces
589
631
  async handleSettlementSigningEvent(event, sweepTapTreeRoot, session) {
590
632
  const vtxoTree = event.unsignedVtxoTree;
@@ -37,6 +37,23 @@ export class RestArkProvider {
37
37
  spentVtxos: [...(data.spentVtxos || [])].map(convertVtxo),
38
38
  };
39
39
  }
40
+ async getRound(txid) {
41
+ const url = `${this.serverUrl}/v1/round/${txid}`;
42
+ const response = await fetch(url);
43
+ if (!response.ok) {
44
+ throw new Error(`Failed to fetch round: ${response.statusText}`);
45
+ }
46
+ const data = (await response.json());
47
+ const round = data.round;
48
+ return {
49
+ id: round.id,
50
+ start: new Date(Number(round.start) * 1000), // Convert from Unix timestamp to Date
51
+ end: new Date(Number(round.end) * 1000), // Convert from Unix timestamp to Date
52
+ vtxoTree: this.toTxTree(round.vtxoTree),
53
+ forfeitTxs: round.forfeitTxs || [],
54
+ connectors: this.toTxTree(round.connectors),
55
+ };
56
+ }
40
57
  async submitVirtualTx(psbtBase64) {
41
58
  const url = `${this.serverUrl}/v1/redeem-tx`;
42
59
  const response = await fetch(url, {
@@ -54,4 +54,16 @@ export class EsploraProvider {
54
54
  }
55
55
  return response.json();
56
56
  }
57
+ async getTxStatus(txid) {
58
+ const response = await fetch(`${this.baseUrl}/tx/${txid}/status`);
59
+ if (!response.ok) {
60
+ throw new Error(`Failed to get transaction status: ${response.statusText}`);
61
+ }
62
+ const data = await response.json();
63
+ return {
64
+ confirmed: data.confirmed,
65
+ blockTime: data.block_time,
66
+ blockHeight: data.block_height,
67
+ };
68
+ }
57
69
  }
@@ -1,5 +1,5 @@
1
1
  import * as bip68 from "bip68";
2
- import { ScriptNum, Transaction } from "@scure/btc-signer";
2
+ import { RawTx, ScriptNum, Transaction } from "@scure/btc-signer";
3
3
  import { sha256x2 } from "@scure/btc-signer/utils";
4
4
  import { base64, hex } from "@scure/base";
5
5
  export class TxTreeError extends Error {
@@ -72,6 +72,11 @@ export class TxTree {
72
72
  }
73
73
  return branch;
74
74
  }
75
+ // Returns the remaining transactions to broadcast in order to exit the vtxo
76
+ async exitBranch(vtxoTxid, isTxConfirmed) {
77
+ const offchainPart = await getOffchainPart(this.branch(vtxoTxid), isTxConfirmed);
78
+ return offchainPart.map(getExitTransaction);
79
+ }
75
80
  // Helper method to find parent of a node
76
81
  findParent(node) {
77
82
  for (const level of this.tree) {
@@ -155,3 +160,32 @@ export function getCosignerKeys(tx) {
155
160
  }
156
161
  return keys;
157
162
  }
163
+ async function getOffchainPart(branch, isTxConfirmed) {
164
+ let offchainPath = [...branch];
165
+ // Iterate from the end of the branch (leaf) to the beginning (root)
166
+ for (let i = branch.length - 1; i >= 0; i--) {
167
+ const node = branch[i];
168
+ // check if the transaction is confirmed on-chain
169
+ if (await isTxConfirmed(node.txid)) {
170
+ // if this is the leaf node, return empty array as everything is confirmed
171
+ if (i === branch.length - 1) {
172
+ return [];
173
+ }
174
+ // otherwise, return the unconfirmed part of the branch
175
+ return branch.slice(i + 1);
176
+ }
177
+ }
178
+ // no confirmation: everything is offchain
179
+ return offchainPath;
180
+ }
181
+ // getExitTransaction finalizes the psbt's input using the musig2 tapkey signature
182
+ function getExitTransaction(treeNode) {
183
+ const tx = Transaction.fromPSBT(base64.decode(treeNode.tx));
184
+ const input = tx.getInput(0);
185
+ if (!input.tapKeySig)
186
+ throw new TxTreeError("missing tapkey signature");
187
+ const rawTx = RawTx.decode(tx.unsignedTx);
188
+ rawTx.witnesses = [[input.tapKeySig]];
189
+ rawTx.segwitFlag = true;
190
+ return hex.encode(RawTx.encode(rawTx));
191
+ }
@@ -69,4 +69,8 @@ export var Request;
69
69
  return message.type === "GET_STATUS";
70
70
  }
71
71
  Request.isGetStatus = isGetStatus;
72
+ function isExit(message) {
73
+ return message.type === "EXIT";
74
+ }
75
+ Request.isExit = isExit;
72
76
  })(Request || (Request = {}));
@@ -181,4 +181,12 @@ export var Response;
181
181
  };
182
182
  }
183
183
  Response.clearResponse = clearResponse;
184
+ function exitSuccess(id) {
185
+ return {
186
+ type: "EXIT_SUCCESS",
187
+ success: true,
188
+ id,
189
+ };
190
+ }
191
+ Response.exitSuccess = exitSuccess;
184
192
  })(Response || (Response = {}));
@@ -321,6 +321,23 @@ export class ServiceWorkerWallet {
321
321
  throw new Error(`Failed to get transaction history: ${error}`);
322
322
  }
323
323
  }
324
+ async exit(outpoints) {
325
+ const message = {
326
+ type: "EXIT",
327
+ outpoints,
328
+ id: getRandomId(),
329
+ };
330
+ try {
331
+ const response = await this.sendMessage(message);
332
+ if (response.type === "EXIT_SUCCESS") {
333
+ return;
334
+ }
335
+ throw new UnexpectedResponseError(response);
336
+ }
337
+ catch (error) {
338
+ throw new Error(`Failed to exit: ${error}`);
339
+ }
340
+ }
324
341
  }
325
342
  function getRandomId() {
326
343
  const randomValue = crypto.getRandomValues(new Uint8Array(16));
@@ -384,6 +384,30 @@ export class Worker {
384
384
  }
385
385
  event.source?.postMessage(Response.walletStatus(message.id, this.wallet !== undefined));
386
386
  }
387
+ async handleExit(event) {
388
+ const message = event.data;
389
+ if (!Request.isExit(message)) {
390
+ console.error("Invalid EXIT message format", message);
391
+ event.source?.postMessage(Response.error(message.id, "Invalid EXIT message format"));
392
+ return;
393
+ }
394
+ if (!this.wallet) {
395
+ console.error("Wallet not initialized");
396
+ event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
397
+ return;
398
+ }
399
+ try {
400
+ await this.wallet.exit(message.outpoints);
401
+ event.source?.postMessage(Response.exitSuccess(message.id));
402
+ }
403
+ catch (error) {
404
+ console.error("Error exiting:", error);
405
+ const errorMessage = error instanceof Error
406
+ ? error.message
407
+ : "Unknown error occurred";
408
+ event.source?.postMessage(Response.error(message.id, errorMessage));
409
+ }
410
+ }
387
411
  async handleMessage(event) {
388
412
  this.messageCallback(event);
389
413
  const message = event.data;
@@ -437,6 +461,10 @@ export class Worker {
437
461
  await this.handleGetStatus(event);
438
462
  break;
439
463
  }
464
+ case "EXIT": {
465
+ await this.handleExit(event);
466
+ break;
467
+ }
440
468
  case "CLEAR": {
441
469
  await this.handleClear(event);
442
470
  break;
@@ -1,5 +1,6 @@
1
1
  import { base64, hex } from "@scure/base";
2
2
  import { Address, OutScript, p2tr, tapLeafHash, } from "@scure/btc-signer/payment";
3
+ import { Transaction } from "@scure/btc-signer";
3
4
  import { TaprootControlBlock } from "@scure/btc-signer/psbt";
4
5
  import { vtxosToTxs } from '../utils/transactionHistory.js';
5
6
  import { BIP21 } from '../utils/bip21.js';
@@ -16,7 +17,6 @@ import { TxType, } from './index.js';
16
17
  import { scriptFromTapLeafScript, VtxoScript } from '../script/base.js';
17
18
  import { CSVMultisigTapscript, decodeTapscript, } from '../script/tapscript.js';
18
19
  import { createVirtualTx } from '../utils/psbt.js';
19
- import { Transaction } from "@scure/btc-signer";
20
20
  import { ArkNote } from '../arknote/index.js';
21
21
  // Wallet does not store any data and rely on the Ark and onchain providers to fetch utxos and vtxos
22
22
  export class Wallet {
@@ -582,6 +582,48 @@ export class Wallet {
582
582
  }
583
583
  throw new Error("Settlement failed");
584
584
  }
585
+ async exit(outpoints) {
586
+ // TODO store the exit branches in repository
587
+ // exit should not depend on the ark provider
588
+ if (!this.arkProvider) {
589
+ throw new Error("Ark provider not configured");
590
+ }
591
+ let vtxos = await this.getVtxos();
592
+ if (outpoints && outpoints.length > 0) {
593
+ vtxos = vtxos.filter((vtxo) => outpoints.some((outpoint) => vtxo.txid === outpoint.txid &&
594
+ vtxo.vout === outpoint.vout));
595
+ }
596
+ if (vtxos.length === 0) {
597
+ throw new Error("No vtxos to exit");
598
+ }
599
+ const trees = new Map();
600
+ const transactions = [];
601
+ for (const vtxo of vtxos) {
602
+ const batchTxid = vtxo.virtualStatus.batchTxID;
603
+ if (!batchTxid)
604
+ continue;
605
+ if (!trees.has(batchTxid)) {
606
+ const round = await this.arkProvider.getRound(batchTxid);
607
+ trees.set(batchTxid, round.vtxoTree);
608
+ }
609
+ const tree = trees.get(batchTxid);
610
+ if (!tree) {
611
+ throw new Error("Tree not found");
612
+ }
613
+ const exitBranch = await tree.exitBranch(vtxo.txid, async (txid) => {
614
+ const status = await this.onchainProvider.getTxStatus(txid);
615
+ return status.confirmed;
616
+ });
617
+ transactions.push(...exitBranch);
618
+ }
619
+ const broadcastedTxs = new Map();
620
+ for (const tx of transactions) {
621
+ if (broadcastedTxs.has(tx))
622
+ continue;
623
+ const txid = await this.onchainProvider.broadcastTransaction(tx);
624
+ broadcastedTxs.set(txid, true);
625
+ }
626
+ }
585
627
  // validates the vtxo tree, creates a signing session and generates the musig2 nonces
586
628
  async handleSettlementSigningEvent(event, sweepTapTreeRoot, session) {
587
629
  const vtxoTree = event.unsignedVtxoTree;
@@ -75,8 +75,17 @@ export interface ArkInfo {
75
75
  end: number;
76
76
  };
77
77
  }
78
+ export interface Round {
79
+ id: string;
80
+ start: Date;
81
+ end: Date;
82
+ vtxoTree: TxTree;
83
+ forfeitTxs: string[];
84
+ connectors: TxTree;
85
+ }
78
86
  export interface ArkProvider {
79
87
  getInfo(): Promise<ArkInfo>;
88
+ getRound(txid: string): Promise<Round>;
80
89
  getVirtualCoins(address: string): Promise<{
81
90
  spendableVtxos: VirtualCoin[];
82
91
  spentVtxos: VirtualCoin[];
@@ -105,6 +114,7 @@ export declare class RestArkProvider implements ArkProvider {
105
114
  spendableVtxos: VirtualCoin[];
106
115
  spentVtxos: VirtualCoin[];
107
116
  }>;
117
+ getRound(txid: string): Promise<Round>;
108
118
  submitVirtualTx(psbtBase64: string): Promise<string>;
109
119
  subscribeToEvents(callback: (event: ArkEvent) => void): Promise<() => void>;
110
120
  registerInputsForNextRound(inputs: Input[]): Promise<{
@@ -21,6 +21,11 @@ export interface OnchainProvider {
21
21
  txid: string;
22
22
  }[]>;
23
23
  getTransactions(address: string): Promise<ExplorerTransaction[]>;
24
+ getTxStatus(txid: string): Promise<{
25
+ confirmed: boolean;
26
+ blockTime?: number;
27
+ blockHeight?: number;
28
+ }>;
24
29
  }
25
30
  export declare class EsploraProvider implements OnchainProvider {
26
31
  private baseUrl;
@@ -33,4 +38,9 @@ export declare class EsploraProvider implements OnchainProvider {
33
38
  txid: string;
34
39
  }[]>;
35
40
  getTransactions(address: string): Promise<ExplorerTransaction[]>;
41
+ getTxStatus(txid: string): Promise<{
42
+ confirmed: boolean;
43
+ blockTime?: number;
44
+ blockHeight?: number;
45
+ }>;
36
46
  }
@@ -20,6 +20,7 @@ export declare class TxTree {
20
20
  children(nodeTxid: string): TreeNode[];
21
21
  numberOfNodes(): number;
22
22
  branch(vtxoTxid: string): TreeNode[];
23
+ exitBranch(vtxoTxid: string, isTxConfirmed: (txid: string) => Promise<boolean>): Promise<string[]>;
23
24
  private findParent;
24
25
  validate(): void;
25
26
  }
@@ -119,4 +119,5 @@ export interface IWallet {
119
119
  getTransactionHistory(): Promise<ArkTransaction[]>;
120
120
  sendBitcoin(params: SendBitcoinParams, zeroFee?: boolean): Promise<string>;
121
121
  settle(params?: SettleParams, eventCallback?: (event: SettlementEvent) => void): Promise<string>;
122
+ exit(outpoints?: Outpoint[]): Promise<void>;
122
123
  }
@@ -1,7 +1,7 @@
1
1
  import { NetworkName } from "../../networks";
2
- import { SettleParams, SendBitcoinParams } from "..";
2
+ import { SettleParams, SendBitcoinParams, Outpoint } from "..";
3
3
  export declare namespace Request {
4
- type Type = "INIT_WALLET" | "SETTLE" | "GET_ADDRESS" | "GET_ADDRESS_INFO" | "GET_BALANCE" | "GET_COINS" | "GET_VTXOS" | "GET_VIRTUAL_COINS" | "GET_BOARDING_UTXOS" | "SEND_BITCOIN" | "GET_TRANSACTION_HISTORY" | "GET_STATUS" | "CLEAR";
4
+ type Type = "INIT_WALLET" | "SETTLE" | "GET_ADDRESS" | "GET_ADDRESS_INFO" | "GET_BALANCE" | "GET_COINS" | "GET_VTXOS" | "GET_VIRTUAL_COINS" | "GET_BOARDING_UTXOS" | "SEND_BITCOIN" | "GET_TRANSACTION_HISTORY" | "GET_STATUS" | "CLEAR" | "EXIT";
5
5
  interface Base {
6
6
  type: Type;
7
7
  id: string;
@@ -65,4 +65,9 @@ export declare namespace Request {
65
65
  interface Clear extends Base {
66
66
  type: "CLEAR";
67
67
  }
68
+ interface Exit extends Base {
69
+ type: "EXIT";
70
+ outpoints?: Outpoint[];
71
+ }
72
+ function isExit(message: Base): message is Exit;
68
73
  }
@@ -1,7 +1,7 @@
1
1
  import { WalletBalance, Coin, VirtualCoin, ArkTransaction, AddressInfo as WalletAddressInfo, IWallet, Addresses } from "..";
2
2
  import { SettlementEvent } from "../../providers/ark";
3
3
  export declare namespace Response {
4
- type Type = "WALLET_INITIALIZED" | "SETTLE_EVENT" | "SETTLE_SUCCESS" | "ADDRESS" | "ADDRESS_INFO" | "BALANCE" | "COINS" | "VTXOS" | "VIRTUAL_COINS" | "BOARDING_UTXOS" | "SEND_BITCOIN_SUCCESS" | "TRANSACTION_HISTORY" | "WALLET_STATUS" | "ERROR" | "CLEAR_RESPONSE";
4
+ type Type = "WALLET_INITIALIZED" | "SETTLE_EVENT" | "SETTLE_SUCCESS" | "ADDRESS" | "ADDRESS_INFO" | "BALANCE" | "COINS" | "VTXOS" | "VIRTUAL_COINS" | "BOARDING_UTXOS" | "SEND_BITCOIN_SUCCESS" | "TRANSACTION_HISTORY" | "WALLET_STATUS" | "ERROR" | "CLEAR_RESPONSE" | "EXIT_SUCCESS";
5
5
  interface Base {
6
6
  type: Type;
7
7
  success: boolean;
@@ -104,4 +104,9 @@ export declare namespace Response {
104
104
  }
105
105
  function isClearResponse(response: Base): response is ClearResponse;
106
106
  function clearResponse(id: string, success: boolean): ClearResponse;
107
+ interface ExitSuccess extends Base {
108
+ type: "EXIT_SUCCESS";
109
+ success: true;
110
+ }
111
+ function exitSuccess(id: string): ExitSuccess;
107
112
  }
@@ -1,4 +1,4 @@
1
- import { IWallet, WalletBalance, SendBitcoinParams, SettleParams, AddressInfo, Coin, ArkTransaction, WalletConfig, ExtendedCoin, ExtendedVirtualCoin, Addresses } from "..";
1
+ import { IWallet, WalletBalance, SendBitcoinParams, SettleParams, AddressInfo, Coin, ArkTransaction, WalletConfig, ExtendedCoin, ExtendedVirtualCoin, Addresses, Outpoint } from "..";
2
2
  import { Response } from "./response";
3
3
  import { SettlementEvent } from "../../providers/ark";
4
4
  export declare class ServiceWorkerWallet implements IWallet {
@@ -20,4 +20,5 @@ export declare class ServiceWorkerWallet implements IWallet {
20
20
  sendBitcoin(params: SendBitcoinParams, zeroFee?: boolean): Promise<string>;
21
21
  settle(params?: SettleParams, callback?: (event: SettlementEvent) => void): Promise<string>;
22
22
  getTransactionHistory(): Promise<ArkTransaction[]>;
23
+ exit(outpoints?: Outpoint[]): Promise<void>;
23
24
  }
@@ -22,5 +22,6 @@ export declare class Worker {
22
22
  private handleGetBoardingUtxos;
23
23
  private handleGetTransactionHistory;
24
24
  private handleGetStatus;
25
+ private handleExit;
25
26
  private handleMessage;
26
27
  }
@@ -1,7 +1,7 @@
1
1
  import { ArkAddress } from "../script/address";
2
2
  import { DefaultVtxo } from "../script/default";
3
3
  import { SettlementEvent } from "../providers/ark";
4
- import { Addresses, AddressInfo, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, IWallet, SendBitcoinParams, SettleParams, WalletBalance, WalletConfig } from ".";
4
+ import { Addresses, AddressInfo, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, IWallet, Outpoint, SendBitcoinParams, SettleParams, WalletBalance, WalletConfig } from ".";
5
5
  export declare class Wallet implements IWallet {
6
6
  private identity;
7
7
  private network;
@@ -36,6 +36,7 @@ export declare class Wallet implements IWallet {
36
36
  private sendOnchain;
37
37
  private sendOffchain;
38
38
  settle(params?: SettleParams, eventCallback?: (event: SettlementEvent) => void): Promise<string>;
39
+ exit(outpoints?: Outpoint[]): Promise<void>;
39
40
  private handleSettlementSigningEvent;
40
41
  private handleSettlementSigningNoncesGeneratedEvent;
41
42
  private handleSettlementFinalizationEvent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.0.16",
3
+ "version": "0.1.0",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",