@arkade-os/sdk 0.3.0-alpha.7 → 0.3.0-alpha.8

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.
Files changed (35) hide show
  1. package/README.md +51 -0
  2. package/dist/cjs/adapters/expo.js +8 -0
  3. package/dist/cjs/index.js +2 -1
  4. package/dist/cjs/providers/expoArk.js +237 -0
  5. package/dist/cjs/providers/expoIndexer.js +194 -0
  6. package/dist/cjs/providers/indexer.js +3 -1
  7. package/dist/cjs/utils/arkTransaction.js +13 -0
  8. package/dist/cjs/wallet/index.js +1 -1
  9. package/dist/cjs/wallet/serviceWorker/utils.js +0 -9
  10. package/dist/cjs/wallet/serviceWorker/worker.js +14 -17
  11. package/dist/cjs/wallet/utils.js +11 -0
  12. package/dist/cjs/wallet/wallet.js +69 -51
  13. package/dist/esm/adapters/expo.js +3 -0
  14. package/dist/esm/index.js +2 -2
  15. package/dist/esm/providers/expoArk.js +200 -0
  16. package/dist/esm/providers/expoIndexer.js +157 -0
  17. package/dist/esm/providers/indexer.js +3 -1
  18. package/dist/esm/utils/arkTransaction.js +13 -1
  19. package/dist/esm/wallet/index.js +1 -1
  20. package/dist/esm/wallet/serviceWorker/utils.js +0 -8
  21. package/dist/esm/wallet/serviceWorker/worker.js +15 -18
  22. package/dist/esm/wallet/utils.js +8 -0
  23. package/dist/esm/wallet/wallet.js +70 -52
  24. package/dist/types/adapters/expo.d.ts +4 -0
  25. package/dist/types/index.d.ts +5 -5
  26. package/dist/types/providers/ark.d.ts +136 -2
  27. package/dist/types/providers/expoArk.d.ts +22 -0
  28. package/dist/types/providers/expoIndexer.d.ts +26 -0
  29. package/dist/types/providers/indexer.d.ts +8 -0
  30. package/dist/types/utils/arkTransaction.d.ts +3 -1
  31. package/dist/types/wallet/index.d.ts +44 -6
  32. package/dist/types/wallet/serviceWorker/utils.d.ts +0 -2
  33. package/dist/types/wallet/utils.d.ts +2 -0
  34. package/dist/types/wallet/wallet.d.ts +9 -1
  35. package/package.json +11 -2
@@ -173,6 +173,7 @@ export class RestIndexerProvider {
173
173
  scripts: data.event.scripts || [],
174
174
  newVtxos: (data.event.newVtxos || []).map(convertVtxo),
175
175
  spentVtxos: (data.event.spentVtxos || []).map(convertVtxo),
176
+ sweptVtxos: (data.event.sweptVtxos || []).map(convertVtxo),
176
177
  tx: data.event.tx,
177
178
  checkpointTxs: data.event.checkpointTxs,
178
179
  };
@@ -326,7 +327,7 @@ export class RestIndexerProvider {
326
327
  });
327
328
  if (!res.ok) {
328
329
  const errorText = await res.text();
329
- throw new Error(`Failed to unsubscribe to scripts: ${errorText}`);
330
+ console.warn(`Failed to unsubscribe to scripts: ${errorText}`);
330
331
  }
331
332
  }
332
333
  }
@@ -354,6 +355,7 @@ function convertVtxo(vtxo) {
354
355
  arkTxId: vtxo.arkTxid,
355
356
  createdAt: new Date(Number(vtxo.createdAt) * 1000),
356
357
  isUnrolled: vtxo.isUnrolled,
358
+ isSpent: vtxo.isSpent,
357
359
  };
358
360
  }
359
361
  // Unexported namespace for type guards only
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_SEQUENCE, Transaction, } from "@scure/btc-signer/transaction.js";
2
- import { CLTVMultisigTapscript, decodeTapscript } from '../script/tapscript.js';
2
+ import { CLTVMultisigTapscript, decodeTapscript, } from '../script/tapscript.js';
3
3
  import { scriptFromTapLeafScript, VtxoScript, } from '../script/base.js';
4
4
  import { P2A } from './anchor.js';
5
5
  import { hex } from "@scure/base";
@@ -103,3 +103,15 @@ const nLocktimeMinSeconds = 500000000n;
103
103
  function isSeconds(locktime) {
104
104
  return locktime >= nLocktimeMinSeconds;
105
105
  }
106
+ export function hasBoardingTxExpired(coin, boardingTimelock) {
107
+ if (!coin.status.block_time)
108
+ return false;
109
+ if (boardingTimelock.value === 0n)
110
+ return true;
111
+ if (boardingTimelock.type !== "blocks")
112
+ return false; // TODO: handle get chain tip
113
+ // validate expiry in terms of seconds
114
+ const now = BigInt(Math.floor(Date.now() / 1000));
115
+ const blockTime = BigInt(Math.floor(coin.status.block_time));
116
+ return blockTime + boardingTimelock.value <= now;
117
+ }
@@ -4,7 +4,7 @@ export var TxType;
4
4
  TxType["TxReceived"] = "RECEIVED";
5
5
  })(TxType || (TxType = {}));
6
6
  export function isSpendable(vtxo) {
7
- return vtxo.spentBy === undefined || vtxo.spentBy === "";
7
+ return !vtxo.isSpent;
8
8
  }
9
9
  export function isRecoverable(vtxo) {
10
10
  return vtxo.virtualStatus.state === "swept" && isSpendable(vtxo);
@@ -44,11 +44,3 @@ export async function setupServiceWorker(path) {
44
44
  navigator.serviceWorker.addEventListener("error", onError);
45
45
  });
46
46
  }
47
- export function extendVirtualCoin(wallet, vtxo) {
48
- return {
49
- ...vtxo,
50
- forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
51
- intentTapLeafScript: wallet.offchainTapscript.exit(),
52
- tapTree: wallet.offchainTapscript.encode(),
53
- };
54
- }
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="webworker" />
2
2
  import { SingleKey } from '../../identity/singleKey.js';
3
- import { isSpendable, isSubdust } from '../index.js';
3
+ import { isRecoverable, isSpendable, isSubdust } from '../index.js';
4
4
  import { Wallet } from '../wallet.js';
5
5
  import { Request } from './request.js';
6
6
  import { Response } from './response.js';
@@ -10,7 +10,7 @@ import { RestIndexerProvider } from '../../providers/indexer.js';
10
10
  import { hex } from "@scure/base";
11
11
  import { IndexedDBStorageAdapter } from '../../storage/indexedDB.js';
12
12
  import { WalletRepositoryImpl, } from '../../repositories/walletRepository.js';
13
- import { extendVirtualCoin } from './utils.js';
13
+ import { extendVirtualCoin } from '../utils.js';
14
14
  /**
15
15
  * Worker is a class letting to interact with ServiceWorkerWallet from the client
16
16
  * it aims to be run in a service worker context
@@ -74,6 +74,8 @@ export class Worker {
74
74
  this.incomingFundsSubscription();
75
75
  // Clear storage - this replaces vtxoRepository.close()
76
76
  await this.storage.clear();
77
+ // Reset in-memory caches by recreating the repository
78
+ this.walletRepository = new WalletRepositoryImpl(this.storage);
77
79
  this.wallet = undefined;
78
80
  this.arkProvider = undefined;
79
81
  this.indexerProvider = undefined;
@@ -102,9 +104,6 @@ export class Worker {
102
104
  const txs = await this.wallet.getTransactionHistory();
103
105
  if (txs)
104
106
  await this.walletRepository.saveTransactions(address, txs);
105
- // stop previous subscriptions if any
106
- if (this.incomingFundsSubscription)
107
- this.incomingFundsSubscription();
108
107
  // subscribe for incoming funds and notify all clients when new funds arrive
109
108
  this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
110
109
  if (funds.type === "vtxo") {
@@ -124,7 +123,7 @@ export class Worker {
124
123
  // notify all clients about the vtxo update
125
124
  this.sendMessageToAllClients("VTXO_UPDATE", JSON.stringify({ newVtxos, spentVtxos }));
126
125
  }
127
- if (funds.type === "utxo" && funds.coins.length > 0) {
126
+ if (funds.type === "utxo") {
128
127
  // notify all clients about the utxo update
129
128
  this.sendMessageToAllClients("UTXO_UPDATE", JSON.stringify(funds.coins));
130
129
  }
@@ -355,17 +354,16 @@ export class Worker {
355
354
  if (!message.filter?.withRecoverable) {
356
355
  if (!this.wallet)
357
356
  throw new Error("Wallet not initialized");
358
- // exclude subdust is we don't want recoverable
359
- const dustAmount = this.wallet?.dustAmount;
360
- vtxos =
361
- dustAmount == null
362
- ? vtxos
363
- : vtxos.filter((v) => !isSubdust(v, dustAmount));
364
- }
365
- if (message.filter?.withRecoverable) {
366
- // get also swept and spendable vtxos
367
- const sweptVtxos = await this.getSweptVtxos();
368
- vtxos.push(...sweptVtxos.filter(isSpendable));
357
+ // exclude subdust and recoverable if we don't want recoverable
358
+ const notSubdust = (v) => {
359
+ const dustAmount = this.wallet?.dustAmount;
360
+ return dustAmount == null
361
+ ? true
362
+ : !isSubdust(v, dustAmount);
363
+ };
364
+ vtxos = vtxos
365
+ .filter(notSubdust)
366
+ .filter((v) => !isRecoverable(v));
369
367
  }
370
368
  event.source?.postMessage(Response.vtxos(message.id, vtxos));
371
369
  }
@@ -526,7 +524,6 @@ export class Worker {
526
524
  }
527
525
  async handleReloadWallet(event) {
528
526
  const message = event.data;
529
- console.log("RELOAD_WALLET message received", message);
530
527
  if (!Request.isReloadWallet(message)) {
531
528
  console.error("Invalid RELOAD_WALLET message format", message);
532
529
  event.source?.postMessage(Response.error(message.id, "Invalid RELOAD_WALLET message format"));
@@ -0,0 +1,8 @@
1
+ export function extendVirtualCoin(wallet, vtxo) {
2
+ return {
3
+ ...vtxo,
4
+ forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
5
+ intentTapLeafScript: wallet.offchainTapscript.exit(),
6
+ tapTree: wallet.offchainTapscript.encode(),
7
+ };
8
+ }
@@ -15,7 +15,7 @@ import { isRecoverable, isSpendable, isSubdust, TxType, } from './index.js';
15
15
  import { sha256, sha256x2 } from "@scure/btc-signer/utils.js";
16
16
  import { VtxoScript } from '../script/base.js';
17
17
  import { CSVMultisigTapscript } from '../script/tapscript.js';
18
- import { buildOffchainTx } from '../utils/arkTransaction.js';
18
+ import { buildOffchainTx, hasBoardingTxExpired } from '../utils/arkTransaction.js';
19
19
  import { ArkNote } from '../arknote/index.js';
20
20
  import { BIP322 } from '../bip322/index.js';
21
21
  import { RestIndexerProvider } from '../providers/indexer.js';
@@ -23,7 +23,7 @@ import { TxTree } from '../tree/txTree.js';
23
23
  import { InMemoryStorageAdapter } from '../storage/inMemory.js';
24
24
  import { WalletRepositoryImpl, } from '../repositories/walletRepository.js';
25
25
  import { ContractRepositoryImpl, } from '../repositories/contractRepository.js';
26
- import { extendVirtualCoin } from './serviceWorker/utils.js';
26
+ import { extendVirtualCoin } from './utils.js';
27
27
  /**
28
28
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
29
29
  * The wallet does not store any data locally and relies on Ark and onchain
@@ -31,13 +31,21 @@ import { extendVirtualCoin } from './serviceWorker/utils.js';
31
31
  *
32
32
  * @example
33
33
  * ```typescript
34
- * // Create a wallet
34
+ * // Create a wallet with URL configuration
35
35
  * const wallet = await Wallet.create({
36
36
  * identity: SingleKey.fromHex('your_private_key'),
37
37
  * arkServerUrl: 'https://ark.example.com',
38
38
  * esploraUrl: 'https://mempool.space/api'
39
39
  * });
40
40
  *
41
+ * // Or with custom provider instances (e.g., for Expo/React Native)
42
+ * const wallet = await Wallet.create({
43
+ * identity: SingleKey.fromHex('your_private_key'),
44
+ * arkProvider: new ExpoArkProvider('https://ark.example.com'),
45
+ * indexerProvider: new ExpoIndexerProvider('https://ark.example.com'),
46
+ * esploraUrl: 'https://mempool.space/api'
47
+ * });
48
+ *
41
49
  * // Get addresses
42
50
  * const arkAddress = await wallet.getAddress();
43
51
  * const boardingAddress = await wallet.getBoardingAddress();
@@ -71,11 +79,29 @@ export class Wallet {
71
79
  if (!pubkey) {
72
80
  throw new Error("Invalid configured public key");
73
81
  }
74
- const arkProvider = new RestArkProvider(config.arkServerUrl);
75
- const indexerProvider = new RestIndexerProvider(config.arkServerUrl);
82
+ // Use provided arkProvider instance or create a new one from arkServerUrl
83
+ const arkProvider = config.arkProvider ||
84
+ (() => {
85
+ if (!config.arkServerUrl) {
86
+ throw new Error("Either arkProvider or arkServerUrl must be provided");
87
+ }
88
+ return new RestArkProvider(config.arkServerUrl);
89
+ })();
90
+ // Extract arkServerUrl from provider if not explicitly provided
91
+ const arkServerUrl = config.arkServerUrl || arkProvider.serverUrl;
92
+ if (!arkServerUrl) {
93
+ throw new Error("Could not determine arkServerUrl from provider");
94
+ }
95
+ // Use provided indexerProvider instance or create a new one
96
+ // indexerUrl defaults to arkServerUrl if not provided
97
+ const indexerUrl = config.indexerUrl || arkServerUrl;
98
+ const indexerProvider = config.indexerProvider || new RestIndexerProvider(indexerUrl);
76
99
  const info = await arkProvider.getInfo();
77
100
  const network = getNetwork(info.network);
78
- const onchainProvider = new EsploraProvider(config.esploraUrl || ESPLORA_URL[info.network]);
101
+ // Extract esploraUrl from provider if not explicitly provided
102
+ const esploraUrl = config.esploraUrl || ESPLORA_URL[info.network];
103
+ // Use provided onchainProvider instance or create a new one
104
+ const onchainProvider = config.onchainProvider || new EsploraProvider(esploraUrl);
79
105
  const exitTimelock = {
80
106
  value: info.unilateralExitDelay,
81
107
  type: info.unilateralExitDelay < 512n ? "blocks" : "seconds",
@@ -99,8 +125,14 @@ export class Wallet {
99
125
  // Save tapscripts
100
126
  const offchainTapscript = bareVtxoTapscript;
101
127
  // the serverUnrollScript is the one used to create output scripts of the checkpoint transactions
102
- const rawCheckpointExitClosure = hex.decode(info.checkpointExitClosure);
103
- const serverUnrollScript = CSVMultisigTapscript.decode(rawCheckpointExitClosure);
128
+ let serverUnrollScript;
129
+ try {
130
+ const raw = hex.decode(info.checkpointExitClosure);
131
+ serverUnrollScript = CSVMultisigTapscript.decode(raw);
132
+ }
133
+ catch (e) {
134
+ throw new Error("Invalid checkpointExitClosure from server");
135
+ }
104
136
  // parse the server forfeit address
105
137
  // server is expecting funds to be sent to this address
106
138
  const forfeitAddress = Address(network).decode(info.forfeitAddress);
@@ -171,40 +203,24 @@ export class Wallet {
171
203
  // if (cachedVtxos.length) return cachedVtxos;
172
204
  // For now, always fetch fresh data from provider and update cache
173
205
  // In future, we can add cache invalidation logic based on timestamps
174
- const spendableVtxos = await this.getVirtualCoins(filter);
175
- const encodedOffchainTapscript = this.offchainTapscript.encode();
176
- const forfeit = this.offchainTapscript.forfeit();
177
- const exit = this.offchainTapscript.exit();
178
- const extendedVtxos = spendableVtxos.map((vtxo) => ({
179
- ...vtxo,
180
- forfeitTapLeafScript: forfeit,
181
- intentTapLeafScript: exit,
182
- tapTree: encodedOffchainTapscript,
183
- }));
206
+ const vtxos = await this.getVirtualCoins(filter);
207
+ const extendedVtxos = vtxos.map((vtxo) => extendVirtualCoin(this, vtxo));
184
208
  // Update cache with fresh data
185
209
  await this.walletRepository.saveVtxos(address, extendedVtxos);
186
210
  return extendedVtxos;
187
211
  }
188
212
  async getVirtualCoins(filter = { withRecoverable: true, withUnrolled: false }) {
189
213
  const scripts = [hex.encode(this.offchainTapscript.pkScript)];
190
- const response = await this.indexerProvider.getVtxos({
191
- scripts,
192
- spendableOnly: true,
193
- });
194
- const vtxos = response.vtxos;
195
- if (filter.withRecoverable) {
196
- const response = await this.indexerProvider.getVtxos({
197
- scripts,
198
- recoverableOnly: true,
199
- });
200
- vtxos.push(...response.vtxos);
214
+ const response = await this.indexerProvider.getVtxos({ scripts });
215
+ const allVtxos = response.vtxos;
216
+ let vtxos = allVtxos.filter(isSpendable);
217
+ // all recoverable vtxos are spendable by definition
218
+ if (!filter.withRecoverable) {
219
+ vtxos = vtxos.filter((vtxo) => !isRecoverable(vtxo));
201
220
  }
202
221
  if (filter.withUnrolled) {
203
- const response = await this.indexerProvider.getVtxos({
204
- scripts,
205
- spentOnly: true,
206
- });
207
- vtxos.push(...response.vtxos.filter((vtxo) => vtxo.isUnrolled));
222
+ const spentVtxos = allVtxos.filter((vtxo) => !isSpendable(vtxo));
223
+ vtxos.push(...spentVtxos.filter((vtxo) => vtxo.isUnrolled));
208
224
  }
209
225
  return vtxos;
210
226
  }
@@ -242,10 +258,10 @@ export class Wallet {
242
258
  return txs;
243
259
  }
244
260
  async getBoardingTxs() {
245
- const boardingAddress = await this.getBoardingAddress();
246
- const txs = await this.onchainProvider.getTransactions(boardingAddress);
247
261
  const utxos = [];
248
262
  const commitmentsToIgnore = new Set();
263
+ const boardingAddress = await this.getBoardingAddress();
264
+ const txs = await this.onchainProvider.getTransactions(boardingAddress);
249
265
  for (const tx of txs) {
250
266
  for (let i = 0; i < tx.vout.length; i++) {
251
267
  const vout = tx.vout[i];
@@ -386,13 +402,15 @@ export class Wallet {
386
402
  }
387
403
  }
388
404
  }
389
- // if no params are provided, use all boarding and offchain utxos as inputs
405
+ // if no params are provided, use all non expired boarding utxos and offchain vtxos as inputs
390
406
  // and send all to the offchain address
391
407
  if (!params) {
392
408
  let amount = 0;
393
- const boardingUtxos = await this.getBoardingUtxos();
409
+ const exitScript = CSVMultisigTapscript.decode(hex.decode(this.boardingTapscript.exitScript));
410
+ const boardingTimelock = exitScript.params.timelock;
411
+ const boardingUtxos = (await this.getBoardingUtxos()).filter((utxo) => !hasBoardingTxExpired(utxo, boardingTimelock));
394
412
  amount += boardingUtxos.reduce((sum, input) => sum + input.value, 0);
395
- const vtxos = await this.getVtxos();
413
+ const vtxos = await this.getVtxos({ withRecoverable: true });
396
414
  amount += vtxos.reduce((sum, input) => sum + input.value, 0);
397
415
  const inputs = [...boardingUtxos, ...vtxos];
398
416
  if (inputs.length === 0) {
@@ -602,22 +620,22 @@ export class Wallet {
602
620
  let onchainStopFunc;
603
621
  let indexerStopFunc;
604
622
  if (this.onchainProvider && boardingAddress) {
623
+ const findVoutOnTx = (tx) => {
624
+ return tx.vout.findIndex((v) => v.scriptpubkey_address === boardingAddress);
625
+ };
605
626
  onchainStopFunc = await this.onchainProvider.watchAddresses([boardingAddress], (txs) => {
627
+ // find all utxos belonging to our boarding address
606
628
  const coins = txs
629
+ // filter txs where address is in output
630
+ .filter((tx) => findVoutOnTx(tx) !== -1)
631
+ // return utxo as Coin
607
632
  .map((tx) => {
608
- const vout = tx.vout.findIndex((v) => v.scriptpubkey_address === boardingAddress);
609
- if (vout === -1) {
610
- console.warn(`No vout found for address ${boardingAddress} in transaction ${tx.txid}`);
611
- return null;
612
- }
613
- return {
614
- txid: tx.txid,
615
- vout,
616
- value: Number(tx.vout[vout].value),
617
- status: tx.status,
618
- };
619
- })
620
- .filter((coin) => coin !== null);
633
+ const { txid, status } = tx;
634
+ const vout = findVoutOnTx(tx);
635
+ const value = Number(tx.vout[vout].value);
636
+ return { txid, vout, value, status };
637
+ });
638
+ // and notify via callback
621
639
  eventCallback({
622
640
  type: "utxo",
623
641
  coins,
@@ -0,0 +1,4 @@
1
+ export { ExpoArkProvider } from "../providers/expoArk";
2
+ export { ExpoIndexerProvider } from "../providers/expoIndexer";
3
+ export type { ArkProvider } from "../providers/ark";
4
+ export type { IndexerProvider } from "../providers/indexer";
@@ -5,7 +5,7 @@ import { ArkAddress } from "./script/address";
5
5
  import { VHTLC } from "./script/vhtlc";
6
6
  import { DefaultVtxo } from "./script/default";
7
7
  import { VtxoScript, EncodedVtxoScript, TapLeafScript } from "./script/base";
8
- import { TxType, IWallet, WalletConfig, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, GetVtxosFilter, TapLeaves } from "./wallet";
8
+ import { TxType, IWallet, WalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, GetVtxosFilter, TapLeaves } from "./wallet";
9
9
  import { Wallet, waitForIncomingFunds, IncomingFunds } from "./wallet/wallet";
10
10
  import { TxTree, TxTreeNode } from "./tree/txTree";
11
11
  import { SignerSession, TreeNonces, TreePartialSigs } from "./tree/signingSession";
@@ -19,17 +19,17 @@ import { Response } from "./wallet/serviceWorker/response";
19
19
  import { ESPLORA_URL, EsploraProvider, OnchainProvider, ExplorerTransaction } from "./providers/onchain";
20
20
  import { RestArkProvider, ArkProvider, SettlementEvent, SettlementEventType, ArkInfo, Intent, Output, TxNotification, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesAggregatedEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, MarketHour } from "./providers/ark";
21
21
  import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CSVMultisigTapscript, decodeTapscript, MultisigTapscript, TapscriptType, ArkTapscript, RelativeTimelock } from "./script/tapscript";
22
- import { buildOffchainTx, ArkTxInput, OffchainTx } from "./utils/arkTransaction";
22
+ import { hasBoardingTxExpired, buildOffchainTx, ArkTxInput, OffchainTx } from "./utils/arkTransaction";
23
23
  import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry } from "./utils/unknownFields";
24
24
  import { BIP322 } from "./bip322";
25
25
  import { ArkNote } from "./arknote";
26
26
  import { networks, Network, NetworkName } from "./networks";
27
- import { RestIndexerProvider, IndexerProvider, IndexerTxType, ChainTxType, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, VtxoChain, Tx, Vtxo, PaginationOptions, SubscriptionResponse } from "./providers/indexer";
27
+ import { RestIndexerProvider, IndexerProvider, IndexerTxType, ChainTxType, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, VtxoChain, Tx, Vtxo, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent } from "./providers/indexer";
28
28
  import { Nonces } from "./musig2/nonces";
29
29
  import { PartialSig } from "./musig2/sign";
30
30
  import { AnchorBumper, P2A } from "./utils/anchor";
31
31
  import { Unroll } from "./wallet/unroll";
32
32
  import { WalletRepositoryImpl } from "./repositories/walletRepository";
33
33
  import { ContractRepositoryImpl } from "./repositories/contractRepository";
34
- export { Wallet, SingleKey, OnchainWallet, Ramps, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, waitForIncomingFunds, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, BIP322, TxTree, P2A, Unroll, Transaction, };
35
- export type { Identity, IWallet, WalletConfig, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, IndexerProvider, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, ArkInfo, Intent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesAggregatedEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, MarketHour, PaginationOptions, SubscriptionResponse, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, };
34
+ export { Wallet, SingleKey, OnchainWallet, Ramps, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, waitForIncomingFunds, hasBoardingTxExpired, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, BIP322, TxTree, P2A, Unroll, Transaction, };
35
+ export type { Identity, IWallet, WalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, IndexerProvider, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, ArkInfo, Intent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesAggregatedEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, MarketHour, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, };
@@ -150,7 +150,141 @@ export declare class RestArkProvider implements ArkProvider {
150
150
  commitmentTx?: TxNotification;
151
151
  arkTx?: TxNotification;
152
152
  }>;
153
- private parseSettlementEvent;
154
- private parseTransactionNotification;
153
+ protected parseSettlementEvent(data: ProtoTypes.GetEventStreamResponse): SettlementEvent | null;
154
+ protected parseTransactionNotification(data: ProtoTypes.GetTransactionsStreamResponse): {
155
+ commitmentTx?: TxNotification;
156
+ arkTx?: TxNotification;
157
+ } | null;
158
+ }
159
+ declare namespace ProtoTypes {
160
+ interface BatchStartedEvent {
161
+ id: string;
162
+ intentIdHashes: string[];
163
+ batchExpiry: number;
164
+ }
165
+ interface BatchFailed {
166
+ id: string;
167
+ reason: string;
168
+ }
169
+ export interface BatchFinalizationEvent {
170
+ id: string;
171
+ commitmentTx: string;
172
+ }
173
+ interface BatchFinalizedEvent {
174
+ id: string;
175
+ commitmentTxid: string;
176
+ }
177
+ interface TreeSigningStartedEvent {
178
+ id: string;
179
+ cosignersPubkeys: string[];
180
+ unsignedCommitmentTx: string;
181
+ }
182
+ interface TreeNoncesAggregatedEvent {
183
+ id: string;
184
+ treeNonces: string;
185
+ }
186
+ interface TreeTxEvent {
187
+ id: string;
188
+ topic: string[];
189
+ batchIndex: number;
190
+ txid: string;
191
+ tx: string;
192
+ children: Record<string, string>;
193
+ }
194
+ interface TreeSignatureEvent {
195
+ id: string;
196
+ topic: string[];
197
+ batchIndex: number;
198
+ txid: string;
199
+ signature: string;
200
+ }
201
+ interface Heartbeat {
202
+ }
203
+ export interface VtxoData {
204
+ outpoint: {
205
+ txid: string;
206
+ vout: number;
207
+ };
208
+ amount: string;
209
+ script: string;
210
+ createdAt: string;
211
+ expiresAt: string | null;
212
+ commitmentTxids: string[];
213
+ isPreconfirmed: boolean;
214
+ isSwept: boolean;
215
+ isUnrolled: boolean;
216
+ isSpent: boolean;
217
+ spentBy: string;
218
+ settledBy?: string;
219
+ arkTxid?: string;
220
+ }
221
+ export interface GetEventStreamResponse {
222
+ batchStarted?: BatchStartedEvent;
223
+ batchFailed?: BatchFailed;
224
+ batchFinalization?: BatchFinalizationEvent;
225
+ batchFinalized?: BatchFinalizedEvent;
226
+ treeSigningStarted?: TreeSigningStartedEvent;
227
+ treeNoncesAggregated?: TreeNoncesAggregatedEvent;
228
+ treeTx?: TreeTxEvent;
229
+ treeSignature?: TreeSignatureEvent;
230
+ heartbeat?: Heartbeat;
231
+ }
232
+ export interface GetTransactionsStreamResponse {
233
+ commitmentTx?: {
234
+ txid: string;
235
+ tx: string;
236
+ spentVtxos: VtxoData[];
237
+ spendableVtxos: VtxoData[];
238
+ checkpointTxs?: Record<string, {
239
+ txid: string;
240
+ tx: string;
241
+ }>;
242
+ };
243
+ arkTx?: {
244
+ txid: string;
245
+ tx: string;
246
+ spentVtxos: VtxoData[];
247
+ spendableVtxos: VtxoData[];
248
+ checkpointTxs?: Record<string, {
249
+ txid: string;
250
+ tx: string;
251
+ }>;
252
+ };
253
+ heartbeat?: Heartbeat;
254
+ }
255
+ export interface EventData {
256
+ batchStarted?: BatchStartedEvent;
257
+ batchFailed?: BatchFailed;
258
+ batchFinalization?: BatchFinalizationEvent;
259
+ batchFinalized?: BatchFinalizedEvent;
260
+ treeSigningStarted?: TreeSigningStartedEvent;
261
+ treeNoncesAggregated?: TreeNoncesAggregatedEvent;
262
+ treeTx?: TreeTxEvent;
263
+ treeSignature?: TreeSignatureEvent;
264
+ }
265
+ export interface TransactionData {
266
+ commitmentTx?: {
267
+ txid: string;
268
+ tx: string;
269
+ spentVtxos: VtxoData[];
270
+ spendableVtxos: VtxoData[];
271
+ checkpointTxs?: Record<string, {
272
+ txid: string;
273
+ tx: string;
274
+ }>;
275
+ };
276
+ arkTx?: {
277
+ txid: string;
278
+ tx: string;
279
+ spentVtxos: VtxoData[];
280
+ spendableVtxos: VtxoData[];
281
+ checkpointTxs?: Record<string, {
282
+ txid: string;
283
+ tx: string;
284
+ }>;
285
+ };
286
+ }
287
+ export {};
155
288
  }
156
289
  export declare function isFetchTimeoutError(err: any): boolean;
290
+ export {};
@@ -0,0 +1,22 @@
1
+ import { RestArkProvider, SettlementEvent, TxNotification } from "./ark";
2
+ /**
3
+ * Expo-compatible Ark provider implementation using expo/fetch for SSE support.
4
+ * This provider works specifically in React Native/Expo environments where
5
+ * standard EventSource is not available but expo/fetch provides SSE capabilities.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ExpoArkProvider } from '@arkade-os/sdk/providers/expo';
10
+ *
11
+ * const provider = new ExpoArkProvider('https://ark.example.com');
12
+ * const info = await provider.getInfo();
13
+ * ```
14
+ */
15
+ export declare class ExpoArkProvider extends RestArkProvider {
16
+ constructor(serverUrl: string);
17
+ getEventStream(signal: AbortSignal, topics: string[]): AsyncIterableIterator<SettlementEvent>;
18
+ getTransactionsStream(signal: AbortSignal): AsyncIterableIterator<{
19
+ commitmentTx?: TxNotification;
20
+ arkTx?: TxNotification;
21
+ }>;
22
+ }
@@ -0,0 +1,26 @@
1
+ import { RestIndexerProvider } from "./indexer";
2
+ /**
3
+ * Expo-compatible Indexer provider implementation using expo/fetch for streaming support.
4
+ * This provider works specifically in React Native/Expo environments where
5
+ * standard fetch streaming may not work properly but expo/fetch provides streaming capabilities.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo';
10
+ *
11
+ * const provider = new ExpoIndexerProvider('https://indexer.example.com');
12
+ * const vtxos = await provider.getVtxos({ scripts: ['script1'] });
13
+ * ```
14
+ */
15
+ export declare class ExpoIndexerProvider extends RestIndexerProvider {
16
+ constructor(serverUrl: string);
17
+ getSubscription(subscriptionId: string, abortSignal: AbortSignal): AsyncGenerator<{
18
+ txid: any;
19
+ scripts: any;
20
+ newVtxos: any;
21
+ spentVtxos: any;
22
+ sweptVtxos: any;
23
+ tx: any;
24
+ checkpointTxs: any;
25
+ }, void, unknown>;
26
+ }
@@ -80,12 +80,19 @@ export interface SubscriptionResponse {
80
80
  scripts: string[];
81
81
  newVtxos: VirtualCoin[];
82
82
  spentVtxos: VirtualCoin[];
83
+ sweptVtxos: VirtualCoin[];
83
84
  tx?: string;
84
85
  checkpointTxs?: Record<string, {
85
86
  txid: string;
86
87
  tx: string;
87
88
  }>;
88
89
  }
90
+ export interface SubscriptionHeartbeat {
91
+ type: "heartbeat";
92
+ }
93
+ export interface SubscriptionEvent extends SubscriptionResponse {
94
+ type: "event";
95
+ }
89
96
  export interface IndexerProvider {
90
97
  getVtxoTree(batchOutpoint: Outpoint, opts?: PaginationOptions): Promise<{
91
98
  vtxoTree: Tx[];
@@ -163,6 +170,7 @@ export declare class RestIndexerProvider implements IndexerProvider {
163
170
  scripts: any;
164
171
  newVtxos: any;
165
172
  spentVtxos: any;
173
+ sweptVtxos: any;
166
174
  tx: any;
167
175
  checkpointTxs: any;
168
176
  }, void, unknown>;
@@ -1,5 +1,6 @@
1
1
  import { Transaction } from "@scure/btc-signer/transaction.js";
2
- import { VirtualCoin } from "../wallet";
2
+ import { ExtendedCoin, VirtualCoin } from "../wallet";
3
+ import { RelativeTimelock } from "../script/tapscript";
3
4
  import { EncodedVtxoScript, TapLeafScript } from "../script/base";
4
5
  import { CSVMultisigTapscript } from "../script/tapscript";
5
6
  import { TransactionOutput } from "@scure/btc-signer/psbt.js";
@@ -25,3 +26,4 @@ export type OffchainTx = {
25
26
  * @returns Object containing the virtual transaction and checkpoint transactions
26
27
  */
27
28
  export declare function buildOffchainTx(inputs: ArkTxInput[], outputs: TransactionOutput[], serverUnrollScript: CSVMultisigTapscript.Type): OffchainTx;
29
+ export declare function hasBoardingTxExpired(coin: ExtendedCoin, boardingTimelock: RelativeTimelock): boolean;