@arkade-os/sdk 0.3.0-alpha.6 → 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 (37) 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/script/base.js +16 -95
  8. package/dist/cjs/utils/arkTransaction.js +13 -0
  9. package/dist/cjs/wallet/index.js +1 -1
  10. package/dist/cjs/wallet/serviceWorker/utils.js +0 -9
  11. package/dist/cjs/wallet/serviceWorker/worker.js +14 -17
  12. package/dist/cjs/wallet/utils.js +11 -0
  13. package/dist/cjs/wallet/wallet.js +69 -51
  14. package/dist/esm/adapters/expo.js +3 -0
  15. package/dist/esm/index.js +2 -2
  16. package/dist/esm/providers/expoArk.js +200 -0
  17. package/dist/esm/providers/expoIndexer.js +157 -0
  18. package/dist/esm/providers/indexer.js +3 -1
  19. package/dist/esm/script/base.js +13 -92
  20. package/dist/esm/utils/arkTransaction.js +13 -1
  21. package/dist/esm/wallet/index.js +1 -1
  22. package/dist/esm/wallet/serviceWorker/utils.js +0 -8
  23. package/dist/esm/wallet/serviceWorker/worker.js +15 -18
  24. package/dist/esm/wallet/utils.js +8 -0
  25. package/dist/esm/wallet/wallet.js +70 -52
  26. package/dist/types/adapters/expo.d.ts +4 -0
  27. package/dist/types/index.d.ts +5 -5
  28. package/dist/types/providers/ark.d.ts +136 -2
  29. package/dist/types/providers/expoArk.d.ts +22 -0
  30. package/dist/types/providers/expoIndexer.d.ts +26 -0
  31. package/dist/types/providers/indexer.d.ts +8 -0
  32. package/dist/types/utils/arkTransaction.d.ts +3 -1
  33. package/dist/types/wallet/index.d.ts +44 -6
  34. package/dist/types/wallet/serviceWorker/utils.d.ts +0 -2
  35. package/dist/types/wallet/utils.d.ts +2 -0
  36. package/dist/types/wallet/wallet.d.ts +9 -1
  37. package/package.json +11 -2
@@ -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;
@@ -1,4 +1,6 @@
1
- import { Output, SettlementEvent } from "../providers/ark";
1
+ import { Output, SettlementEvent, ArkProvider } from "../providers/ark";
2
+ import { IndexerProvider } from "../providers/indexer";
3
+ import { OnchainProvider } from "../providers/onchain";
2
4
  import { Identity } from "../identity";
3
5
  import { RelativeTimelock } from "../script/tapscript";
4
6
  import { EncodedVtxoScript, TapLeafScript } from "../script/base";
@@ -7,19 +9,54 @@ import { StorageAdapter } from "../storage";
7
9
  /**
8
10
  * Configuration options for wallet initialization.
9
11
  *
10
- * Defines the parameters required to create and configure a wallet instance,
11
- * including identity, server URLs, and optional timelock settings.
12
- * If optional parameters are not provided, the wallet will fetch them from the
13
- * Ark server.
12
+ * Supports two configuration modes:
13
+ * 1. URL-based: Provide arkServerUrl, indexerUrl (optional), and esploraUrl
14
+ * 2. Provider-based: Provide arkProvider, indexerProvider, and onchainProvider instances
15
+ *
16
+ * At least one of the following must be provided:
17
+ * - arkServerUrl OR arkProvider
18
+ *
19
+ * The wallet will use provided URLs to create default providers if custom provider
20
+ * instances are not supplied. If optional parameters are not provided, the wallet
21
+ * will fetch configuration from the Ark server.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // URL-based configuration
26
+ * const wallet = await Wallet.create({
27
+ * identity: SingleKey.fromHex('...'),
28
+ * arkServerUrl: 'https://ark.example.com',
29
+ * esploraUrl: 'https://mempool.space/api'
30
+ * });
31
+ *
32
+ * // Provider-based configuration (e.g., for Expo/React Native)
33
+ * const wallet = await Wallet.create({
34
+ * identity: SingleKey.fromHex('...'),
35
+ * arkProvider: new ExpoArkProvider('https://ark.example.com'),
36
+ * indexerProvider: new ExpoIndexerProvider('https://ark.example.com'),
37
+ * onchainProvider: new EsploraProvider('https://mempool.space/api')
38
+ * });
39
+ * ```
14
40
  */
15
41
  export interface WalletConfig {
16
42
  identity: Identity;
17
- arkServerUrl: string;
43
+ arkServerUrl?: string;
44
+ indexerUrl?: string;
18
45
  esploraUrl?: string;
19
46
  arkServerPublicKey?: string;
20
47
  boardingTimelock?: RelativeTimelock;
21
48
  exitTimelock?: RelativeTimelock;
22
49
  storage?: StorageAdapter;
50
+ arkProvider?: ArkProvider;
51
+ indexerProvider?: IndexerProvider;
52
+ onchainProvider?: OnchainProvider;
53
+ }
54
+ /**
55
+ * Provider class constructor interface for dependency injection.
56
+ * Ensures provider classes follow the consistent constructor pattern.
57
+ */
58
+ export interface ProviderClass<T> {
59
+ new (serverUrl: string): T;
23
60
  }
24
61
  export interface WalletBalance {
25
62
  boarding: {
@@ -73,6 +110,7 @@ export interface VirtualCoin extends Coin {
73
110
  arkTxId?: string;
74
111
  createdAt: Date;
75
112
  isUnrolled: boolean;
113
+ isSpent?: boolean;
76
114
  }
77
115
  export declare enum TxType {
78
116
  TxSent = "SENT",
@@ -1,4 +1,3 @@
1
- import { ExtendedVirtualCoin, VirtualCoin, Wallet } from "../..";
2
1
  /**
3
2
  * setupServiceWorker sets up the service worker.
4
3
  * @param path - the path to the service worker script
@@ -8,4 +7,3 @@ import { ExtendedVirtualCoin, VirtualCoin, Wallet } from "../..";
8
7
  * ```
9
8
  */
10
9
  export declare function setupServiceWorker(path: string): Promise<ServiceWorker>;
11
- export declare function extendVirtualCoin(wallet: Wallet, vtxo: VirtualCoin): ExtendedVirtualCoin;
@@ -0,0 +1,2 @@
1
+ import type { ExtendedVirtualCoin, VirtualCoin, Wallet } from "..";
2
+ export declare function extendVirtualCoin(wallet: Wallet, vtxo: VirtualCoin): ExtendedVirtualCoin;
@@ -25,13 +25,21 @@ export type IncomingFunds = {
25
25
  *
26
26
  * @example
27
27
  * ```typescript
28
- * // Create a wallet
28
+ * // Create a wallet with URL configuration
29
29
  * const wallet = await Wallet.create({
30
30
  * identity: SingleKey.fromHex('your_private_key'),
31
31
  * arkServerUrl: 'https://ark.example.com',
32
32
  * esploraUrl: 'https://mempool.space/api'
33
33
  * });
34
34
  *
35
+ * // Or with custom provider instances (e.g., for Expo/React Native)
36
+ * const wallet = await Wallet.create({
37
+ * identity: SingleKey.fromHex('your_private_key'),
38
+ * arkProvider: new ExpoArkProvider('https://ark.example.com'),
39
+ * indexerProvider: new ExpoIndexerProvider('https://ark.example.com'),
40
+ * esploraUrl: 'https://mempool.space/api'
41
+ * });
42
+ *
35
43
  * // Get addresses
36
44
  * const arkAddress = await wallet.getAddress();
37
45
  * const boardingAddress = await wallet.getBoardingAddress();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.3.0-alpha.6",
3
+ "version": "0.3.0-alpha.8",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -14,6 +14,12 @@
14
14
  "require": "./dist/cjs/index.js",
15
15
  "default": "./dist/esm/index.js"
16
16
  },
17
+ "./adapters/expo": {
18
+ "types": "./dist/types/adapters/expo.d.ts",
19
+ "import": "./dist/esm/adapters/expo.js",
20
+ "require": "./dist/cjs/adapters/expo.js",
21
+ "default": "./dist/esm/adapters/expo.js"
22
+ },
17
23
  "./adapters/localStorage": {
18
24
  "types": "./dist/types/adapters/localStorage.d.ts",
19
25
  "import": "./dist/esm/adapters/localStorage.js",
@@ -58,6 +64,7 @@
58
64
  "@types/node": "24.3.1",
59
65
  "@vitest/coverage-v8": "3.2.4",
60
66
  "esbuild": "^0.25.9",
67
+ "expo": "~52.0.47",
61
68
  "eventsource": "4.0.0",
62
69
  "glob": "11.0.3",
63
70
  "husky": "9.1.7",
@@ -78,7 +85,7 @@
78
85
  "author": "Ark Labs",
79
86
  "license": "MIT",
80
87
  "engines": {
81
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
88
+ "node": ">=20.0.0"
82
89
  },
83
90
  "scripts": {
84
91
  "build": "rimraf dist && pnpm run build:esm && node scripts/add-extensions.js && pnpm run build:cjs && pnpm run build:types && node scripts/generate-package-files.js",
@@ -101,6 +108,8 @@
101
108
  "test:watch": "vitest",
102
109
  "test:coverage": "vitest run --coverage",
103
110
  "test:sw": "pnpm run build:browser && node test/serviceWorker/serve.js",
111
+ "expo:web:install": "cd examples/expo-demo && pnpm install",
112
+ "expo:web": "cd examples/expo-demo && pnpm start",
104
113
  "format": "prettier --write src test examples",
105
114
  "lint": "prettier --check src test examples",
106
115
  "audit": "pnpm audit",