@arkade-os/sdk 0.3.1-alpha.3 → 0.3.1-alpha.5

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 (44) hide show
  1. package/README.md +9 -26
  2. package/dist/cjs/forfeit.js +2 -5
  3. package/dist/cjs/identity/singleKey.js +4 -5
  4. package/dist/cjs/index.js +5 -4
  5. package/dist/cjs/intent/index.js +3 -8
  6. package/dist/cjs/providers/onchain.js +19 -20
  7. package/dist/cjs/repositories/walletRepository.js +64 -2
  8. package/dist/cjs/script/base.js +14 -5
  9. package/dist/cjs/utils/arkTransaction.js +3 -5
  10. package/dist/cjs/utils/transaction.js +28 -0
  11. package/dist/cjs/wallet/onchain.js +4 -4
  12. package/dist/cjs/wallet/serviceWorker/worker.js +19 -2
  13. package/dist/cjs/wallet/unroll.js +3 -4
  14. package/dist/cjs/wallet/utils.js +9 -0
  15. package/dist/cjs/wallet/wallet.js +11 -13
  16. package/dist/esm/forfeit.js +1 -4
  17. package/dist/esm/identity/singleKey.js +3 -4
  18. package/dist/esm/index.js +3 -3
  19. package/dist/esm/intent/index.js +2 -7
  20. package/dist/esm/providers/onchain.js +19 -20
  21. package/dist/esm/repositories/walletRepository.js +64 -2
  22. package/dist/esm/script/base.js +11 -2
  23. package/dist/esm/utils/arkTransaction.js +3 -5
  24. package/dist/esm/utils/transaction.js +24 -0
  25. package/dist/esm/wallet/onchain.js +3 -3
  26. package/dist/esm/wallet/serviceWorker/worker.js +21 -4
  27. package/dist/esm/wallet/unroll.js +4 -5
  28. package/dist/esm/wallet/utils.js +8 -0
  29. package/dist/esm/wallet/wallet.js +12 -14
  30. package/dist/types/forfeit.d.ts +1 -1
  31. package/dist/types/identity/index.d.ts +1 -1
  32. package/dist/types/identity/singleKey.d.ts +1 -1
  33. package/dist/types/index.d.ts +3 -3
  34. package/dist/types/intent/index.d.ts +1 -1
  35. package/dist/types/providers/onchain.d.ts +6 -2
  36. package/dist/types/repositories/walletRepository.d.ts +9 -1
  37. package/dist/types/script/base.d.ts +2 -0
  38. package/dist/types/utils/arkTransaction.d.ts +1 -3
  39. package/dist/types/utils/transaction.d.ts +13 -0
  40. package/dist/types/wallet/onchain.d.ts +1 -1
  41. package/dist/types/wallet/serviceWorker/worker.d.ts +4 -0
  42. package/dist/types/wallet/unroll.d.ts +1 -1
  43. package/dist/types/wallet/utils.d.ts +2 -1
  44. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { Transaction } from "@scure/btc-signer/transaction.js";
1
+ import { Transaction } from './utils/transaction.js';
2
2
  import { SingleKey } from './identity/singleKey.js';
3
3
  import { ArkAddress } from './script/address.js';
4
4
  import { VHTLC } from './script/vhtlc.js';
5
5
  import { DefaultVtxo } from './script/default.js';
6
- import { VtxoScript } from './script/base.js';
6
+ import { VtxoScript, TapTreeCoder, } from './script/base.js';
7
7
  import { TxType, } from './wallet/index.js';
8
8
  import { Wallet, waitForIncomingFunds } from './wallet/wallet.js';
9
9
  import { TxTree } from './tree/txTree.js';
@@ -41,7 +41,7 @@ TxType, IndexerTxType, ChainTxType, SettlementEventType,
41
41
  // Service Worker
42
42
  setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response,
43
43
  // Tapscript
44
- decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript,
44
+ decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder,
45
45
  // Ark PSBT fields
46
46
  ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness,
47
47
  // Utils
@@ -1,5 +1,6 @@
1
- import { OP, Transaction, Script, SigHash } from "@scure/btc-signer";
1
+ import { OP, Script, SigHash } from "@scure/btc-signer";
2
2
  import { schnorr } from "@noble/curves/secp256k1.js";
3
+ import { Transaction } from '../utils/transaction.js';
3
4
  /**
4
5
  * Intent proof implementation for Bitcoin message signing.
5
6
  *
@@ -84,9 +85,6 @@ function craftToSpendTx(message, pkScript) {
84
85
  const messageHash = hashMessage(message);
85
86
  const tx = new Transaction({
86
87
  version: 0,
87
- allowUnknownOutputs: true,
88
- allowUnknown: true,
89
- allowUnknownInputs: true,
90
88
  });
91
89
  // add input with zero hash and max index
92
90
  tx.addInput({
@@ -109,9 +107,6 @@ function craftToSignTx(toSpend, inputs, outputs) {
109
107
  const firstInput = inputs[0];
110
108
  const tx = new Transaction({
111
109
  version: 2,
112
- allowUnknownOutputs: outputs.length === 0,
113
- allowUnknown: true,
114
- allowUnknownInputs: true,
115
110
  lockTime: 0,
116
111
  });
117
112
  // add the first "toSpend" input
@@ -18,9 +18,10 @@ export const ESPLORA_URL = {
18
18
  * ```
19
19
  */
20
20
  export class EsploraProvider {
21
- constructor(baseUrl) {
21
+ constructor(baseUrl, opts) {
22
22
  this.baseUrl = baseUrl;
23
- this.polling = false;
23
+ this.pollingInterval = opts?.pollingInterval ?? 15000;
24
+ this.forcePolling = opts?.forcePolling ?? false;
24
25
  }
25
26
  async getCoins(address) {
26
27
  const response = await fetch(`${this.baseUrl}/address/${address}/utxo`);
@@ -91,13 +92,9 @@ export class EsploraProvider {
91
92
  let intervalId = null;
92
93
  const wsUrl = this.baseUrl.replace(/^http(s)?:/, "ws$1:") + "/v1/ws";
93
94
  const poll = async () => {
94
- if (this.polling)
95
- return;
96
- this.polling = true;
97
- // websocket is not reliable, so we will fallback to polling
98
- const pollingInterval = 5000; // 5 seconds
99
- const getAllTxs = () => {
100
- return Promise.all(addresses.map((address) => this.getTransactions(address))).then((txArrays) => txArrays.flat());
95
+ const getAllTxs = async () => {
96
+ const txArrays = await Promise.all(addresses.map((address) => this.getTransactions(address)));
97
+ return txArrays.flat();
101
98
  };
102
99
  // initial fetch to get existing transactions
103
100
  const initialTxs = await getAllTxs();
@@ -122,9 +119,19 @@ export class EsploraProvider {
122
119
  catch (error) {
123
120
  console.error("Error in polling mechanism:", error);
124
121
  }
125
- }, pollingInterval);
122
+ }, this.pollingInterval);
126
123
  };
127
124
  let ws = null;
125
+ const stopFunc = () => {
126
+ if (ws)
127
+ ws.close();
128
+ if (intervalId)
129
+ clearInterval(intervalId);
130
+ };
131
+ if (this.forcePolling) {
132
+ await poll();
133
+ return stopFunc;
134
+ }
128
135
  try {
129
136
  ws = new WebSocket(wsUrl);
130
137
  ws.addEventListener("open", () => {
@@ -171,13 +178,6 @@ export class EsploraProvider {
171
178
  // if websocket is not available, fallback to polling
172
179
  await poll();
173
180
  }
174
- const stopFunc = () => {
175
- if (ws && ws.readyState === WebSocket.OPEN)
176
- ws.close();
177
- if (intervalId)
178
- clearInterval(intervalId);
179
- this.polling = false;
180
- };
181
181
  return stopFunc;
182
182
  }
183
183
  async getChainTip() {
@@ -245,8 +245,7 @@ const isExplorerTransaction = (tx) => {
245
245
  return (typeof tx.txid === "string" &&
246
246
  Array.isArray(tx.vout) &&
247
247
  tx.vout.every((vout) => typeof vout.scriptpubkey_address === "string" &&
248
- typeof vout.value === "string") &&
248
+ typeof vout.value === "number") &&
249
249
  typeof tx.status === "object" &&
250
- typeof tx.status.confirmed === "boolean" &&
251
- typeof tx.status.block_time === "number");
250
+ typeof tx.status.confirmed === "boolean");
252
251
  };
@@ -12,7 +12,14 @@ const serializeVtxo = (v) => ({
12
12
  tapTree: toHex(v.tapTree),
13
13
  forfeitTapLeafScript: serializeTapLeaf(v.forfeitTapLeafScript),
14
14
  intentTapLeafScript: serializeTapLeaf(v.intentTapLeafScript),
15
- extraWitness: v.extraWitness?.map((w) => toHex(w)),
15
+ extraWitness: v.extraWitness?.map(toHex),
16
+ });
17
+ const serializeUtxo = (u) => ({
18
+ ...u,
19
+ tapTree: toHex(u.tapTree),
20
+ forfeitTapLeafScript: serializeTapLeaf(u.forfeitTapLeafScript),
21
+ intentTapLeafScript: serializeTapLeaf(u.intentTapLeafScript),
22
+ extraWitness: u.extraWitness?.map(toHex),
16
23
  });
17
24
  const deserializeTapLeaf = (t) => {
18
25
  const cb = TaprootControlBlock.decode(fromHex(t.cb));
@@ -24,13 +31,21 @@ const deserializeVtxo = (o) => ({
24
31
  tapTree: fromHex(o.tapTree),
25
32
  forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
26
33
  intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
27
- extraWitness: o.extraWitness?.map((w) => fromHex(w)),
34
+ extraWitness: o.extraWitness?.map(fromHex),
35
+ });
36
+ const deserializeUtxo = (o) => ({
37
+ ...o,
38
+ tapTree: fromHex(o.tapTree),
39
+ forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
40
+ intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
41
+ extraWitness: o.extraWitness?.map(fromHex),
28
42
  });
29
43
  export class WalletRepositoryImpl {
30
44
  constructor(storage) {
31
45
  this.storage = storage;
32
46
  this.cache = {
33
47
  vtxos: new Map(),
48
+ utxos: new Map(),
34
49
  transactions: new Map(),
35
50
  walletState: null,
36
51
  initialized: new Set(),
@@ -83,6 +98,53 @@ export class WalletRepositoryImpl {
83
98
  this.cache.vtxos.set(address, []);
84
99
  await this.storage.removeItem(`vtxos:${address}`);
85
100
  }
101
+ async getUtxos(address) {
102
+ const cacheKey = `utxos:${address}`;
103
+ if (this.cache.utxos.has(address)) {
104
+ return this.cache.utxos.get(address);
105
+ }
106
+ const stored = await this.storage.getItem(cacheKey);
107
+ if (!stored) {
108
+ this.cache.utxos.set(address, []);
109
+ return [];
110
+ }
111
+ try {
112
+ const parsed = JSON.parse(stored);
113
+ const utxos = parsed.map(deserializeUtxo);
114
+ this.cache.utxos.set(address, utxos.slice());
115
+ return utxos.slice();
116
+ }
117
+ catch (error) {
118
+ console.error(`Failed to parse UTXOs for address ${address}:`, error);
119
+ this.cache.utxos.set(address, []);
120
+ return [];
121
+ }
122
+ }
123
+ async saveUtxos(address, utxos) {
124
+ const storedUtxos = await this.getUtxos(address);
125
+ utxos.forEach((utxo) => {
126
+ const existing = storedUtxos.findIndex((u) => u.txid === utxo.txid && u.vout === utxo.vout);
127
+ if (existing !== -1) {
128
+ storedUtxos[existing] = utxo;
129
+ }
130
+ else {
131
+ storedUtxos.push(utxo);
132
+ }
133
+ });
134
+ this.cache.utxos.set(address, storedUtxos.slice());
135
+ await this.storage.setItem(`utxos:${address}`, JSON.stringify(storedUtxos.map(serializeUtxo)));
136
+ }
137
+ async removeUtxo(address, utxoId) {
138
+ const utxos = await this.getUtxos(address);
139
+ const [txid, vout] = utxoId.split(":");
140
+ const filtered = utxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
141
+ this.cache.utxos.set(address, filtered.slice());
142
+ await this.storage.setItem(`utxos:${address}`, JSON.stringify(filtered.map(serializeUtxo)));
143
+ }
144
+ async clearUtxos(address) {
145
+ this.cache.utxos.set(address, []);
146
+ await this.storage.removeItem(`utxos:${address}`);
147
+ }
86
148
  async getTransactionHistory(address) {
87
149
  const cacheKey = `tx:${address}`;
88
150
  if (this.cache.transactions.has(address)) {
@@ -4,7 +4,7 @@ import { PSBTOutput } from "@scure/btc-signer/psbt.js";
4
4
  import { hex } from "@scure/base";
5
5
  import { ArkAddress } from './address.js';
6
6
  import { ConditionCSVMultisigTapscript, CSVMultisigTapscript, } from './tapscript.js';
7
- const TapTreeCoder = PSBTOutput.tapTree[2];
7
+ export const TapTreeCoder = PSBTOutput.tapTree[2];
8
8
  export function scriptFromTapLeafScript(leaf) {
9
9
  return leaf[1].subarray(0, leaf[1].length - 1); // remove the version byte
10
10
  }
@@ -24,7 +24,16 @@ export class VtxoScript {
24
24
  }
25
25
  constructor(scripts) {
26
26
  this.scripts = scripts;
27
- const tapTree = taprootListToTree(scripts.map((script) => ({ script, leafVersion: TAP_LEAF_VERSION })));
27
+ // reverse the scripts if the number of scripts is odd
28
+ // this is to be compatible with arkd algorithm computing taproot tree from list of tapscripts
29
+ // the scripts must be reversed only HERE while we compute the tweaked public key
30
+ // but the original order should be preserved while encoding as taptree
31
+ // note: .slice().reverse() is used instead of .reverse() to avoid mutating the original array
32
+ const list = scripts.length % 2 !== 0 ? scripts.slice().reverse() : scripts;
33
+ const tapTree = taprootListToTree(list.map((script) => ({
34
+ script,
35
+ leafVersion: TAP_LEAF_VERSION,
36
+ })));
28
37
  const payment = p2tr(TAPROOT_UNSPENDABLE_KEY, tapTree, undefined, true);
29
38
  if (!payment.tapLeafScript ||
30
39
  payment.tapLeafScript.length !== scripts.length) {
@@ -1,11 +1,12 @@
1
1
  import { schnorr } from "@noble/curves/secp256k1.js";
2
2
  import { hex } from "@scure/base";
3
- import { DEFAULT_SEQUENCE, Transaction, SigHash } from "@scure/btc-signer";
3
+ import { DEFAULT_SEQUENCE, SigHash } from "@scure/btc-signer";
4
4
  import { tapLeafHash } from "@scure/btc-signer/payment.js";
5
5
  import { CLTVMultisigTapscript, decodeTapscript, } from '../script/tapscript.js';
6
6
  import { scriptFromTapLeafScript, VtxoScript, } from '../script/base.js';
7
7
  import { P2A } from './anchor.js';
8
8
  import { setArkPsbtField, VtxoTaprootTree } from './unknownFields.js';
9
+ import { Transaction } from './transaction.js';
9
10
  /**
10
11
  * Builds an offchain transaction with checkpoint transactions.
11
12
  *
@@ -45,8 +46,6 @@ function buildVirtualTx(inputs, outputs) {
45
46
  }
46
47
  const tx = new Transaction({
47
48
  version: 3,
48
- allowUnknown: true,
49
- allowUnknownOutputs: true,
50
49
  lockTime: Number(lockTime),
51
50
  });
52
51
  for (const [i, input] of inputs.entries()) {
@@ -71,8 +70,7 @@ function buildVirtualTx(inputs, outputs) {
71
70
  }
72
71
  function buildCheckpointTx(vtxo, serverUnrollScript) {
73
72
  // create the checkpoint vtxo script from collaborative closure
74
- const collaborativeClosure = decodeTapscript(vtxo.checkpointTapLeafScript ??
75
- scriptFromTapLeafScript(vtxo.tapLeafScript));
73
+ const collaborativeClosure = decodeTapscript(scriptFromTapLeafScript(vtxo.tapLeafScript));
76
74
  // create the checkpoint vtxo script combining collaborative closure and server unroll script
77
75
  const checkpointVtxoScript = new VtxoScript([
78
76
  serverUnrollScript.script,
@@ -0,0 +1,24 @@
1
+ import { Transaction as BtcSignerTransaction } from "@scure/btc-signer";
2
+ /**
3
+ * Transaction is a wrapper around the @scure/btc-signer Transaction class.
4
+ * It adds the Ark protocol specific options to the transaction.
5
+ */
6
+ export class Transaction extends BtcSignerTransaction {
7
+ constructor(opts) {
8
+ super(withArkOpts(opts));
9
+ }
10
+ static fromPSBT(psbt_, opts) {
11
+ return BtcSignerTransaction.fromPSBT(psbt_, withArkOpts(opts));
12
+ }
13
+ static fromRaw(raw, opts) {
14
+ return BtcSignerTransaction.fromRaw(raw, withArkOpts(opts));
15
+ }
16
+ }
17
+ Transaction.ARK_TX_OPTS = {
18
+ allowUnknown: true,
19
+ allowUnknownOutputs: true,
20
+ allowUnknownInputs: true,
21
+ };
22
+ function withArkOpts(opts) {
23
+ return { ...Transaction.ARK_TX_OPTS, ...opts };
24
+ }
@@ -1,8 +1,9 @@
1
- import { Transaction, p2tr } from "@scure/btc-signer";
1
+ import { p2tr } from "@scure/btc-signer";
2
2
  import { getNetwork } from '../networks.js';
3
3
  import { ESPLORA_URL, EsploraProvider, } from '../providers/onchain.js';
4
4
  import { findP2AOutput, P2A } from '../utils/anchor.js';
5
5
  import { TxWeightEstimator } from '../utils/txSizeEstimator.js';
6
+ import { Transaction } from '../utils/transaction.js';
6
7
  /**
7
8
  * Onchain Bitcoin wallet implementation for traditional Bitcoin transactions.
8
9
  *
@@ -104,9 +105,8 @@ export class OnchainWallet {
104
105
  async bumpP2A(parent) {
105
106
  const parentVsize = parent.vsize;
106
107
  let child = new Transaction({
107
- allowUnknownInputs: true,
108
- allowLegacyWitnessUtxo: true,
109
108
  version: 3,
109
+ allowLegacyWitnessUtxo: true,
110
110
  });
111
111
  child.addInput(findP2AOutput(parent)); // throws if not found
112
112
  const childVsize = TxWeightEstimator.create()
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="webworker" />
2
2
  import { SingleKey } from '../../identity/singleKey.js';
3
- import { isRecoverable, 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 { extendCoin, extendVirtualCoin } from '../utils.js';
14
14
  import { DEFAULT_DB_NAME } from './utils.js';
15
15
  /**
16
16
  * Worker is a class letting to interact with ServiceWorkerWallet from the client
@@ -57,6 +57,15 @@ export class Worker {
57
57
  spent: allVtxos.filter((vtxo) => !isSpendable(vtxo)),
58
58
  };
59
59
  }
60
+ /**
61
+ * Get all boarding utxos from wallet repository
62
+ */
63
+ async getAllBoardingUtxos() {
64
+ if (!this.wallet)
65
+ return [];
66
+ const address = await this.wallet.getBoardingAddress();
67
+ return await this.walletRepository.getUtxos(address);
68
+ }
60
69
  async start(withServiceWorkerUpdate = true) {
61
70
  self.addEventListener("message", async (event) => {
62
71
  await this.handleMessage(event);
@@ -130,6 +139,14 @@ export class Worker {
130
139
  this.sendMessageToAllClients(Response.vtxoUpdate(newVtxos, spentVtxos));
131
140
  }
132
141
  if (funds.type === "utxo") {
142
+ const newUtxos = funds.coins.map((utxo) => extendCoin(this.wallet, utxo));
143
+ if (newUtxos.length === 0) {
144
+ this.sendMessageToAllClients(Response.utxoUpdate([]));
145
+ return;
146
+ }
147
+ const boardingAddress = await this.wallet?.getBoardingAddress();
148
+ // save utxos using unified repository
149
+ await this.walletRepository.saveUtxos(boardingAddress, newUtxos);
133
150
  // notify all clients about the utxo update
134
151
  this.sendMessageToAllClients(Response.utxoUpdate(funds.coins));
135
152
  }
@@ -288,7 +305,7 @@ export class Worker {
288
305
  }
289
306
  try {
290
307
  const [boardingUtxos, spendableVtxos, sweptVtxos] = await Promise.all([
291
- this.wallet.getBoardingUtxos(),
308
+ this.getAllBoardingUtxos(),
292
309
  this.getSpendableVtxos(),
293
310
  this.getSweptVtxos(),
294
311
  ]);
@@ -393,7 +410,7 @@ export class Worker {
393
410
  return;
394
411
  }
395
412
  try {
396
- const boardingUtxos = await this.wallet.getBoardingUtxos();
413
+ const boardingUtxos = await this.getAllBoardingUtxos();
397
414
  event.source?.postMessage(Response.boardingUtxos(message.id, boardingUtxos));
398
415
  }
399
416
  catch (error) {
@@ -1,9 +1,10 @@
1
1
  import { base64, hex } from "@scure/base";
2
- import { SigHash, Transaction, TaprootControlBlock } from "@scure/btc-signer";
2
+ import { SigHash, TaprootControlBlock } from "@scure/btc-signer";
3
3
  import { ChainTxType } from '../providers/indexer.js';
4
4
  import { VtxoScript } from '../script/base.js';
5
5
  import { TxWeightEstimator } from '../utils/txSizeEstimator.js';
6
6
  import { Wallet } from './wallet.js';
7
+ import { Transaction } from '../utils/transaction.js';
7
8
  export var Unroll;
8
9
  (function (Unroll) {
9
10
  let StepType;
@@ -102,9 +103,7 @@ export var Unroll;
102
103
  if (virtualTxs.txs.length === 0) {
103
104
  throw new Error(`Tx ${nextTxToBroadcast.txid} not found`);
104
105
  }
105
- const tx = Transaction.fromPSBT(base64.decode(virtualTxs.txs[0]), {
106
- allowUnknownInputs: true,
107
- });
106
+ const tx = Transaction.fromPSBT(base64.decode(virtualTxs.txs[0]));
108
107
  // finalize the tree transaction
109
108
  if (nextTxToBroadcast.type === ChainTxType.TREE) {
110
109
  const input = tx.getInput(0);
@@ -197,7 +196,7 @@ export var Unroll;
197
196
  });
198
197
  txWeightEstimator.addTapscriptInput(64, spendingLeaf[1].length, TaprootControlBlock.encode(spendingLeaf[0]).length);
199
198
  }
200
- const tx = new Transaction({ allowUnknownInputs: true, version: 2 });
199
+ const tx = new Transaction({ version: 2 });
201
200
  for (const input of inputs) {
202
201
  tx.addInput(input);
203
202
  }
@@ -6,3 +6,11 @@ export function extendVirtualCoin(wallet, vtxo) {
6
6
  tapTree: wallet.offchainTapscript.encode(),
7
7
  };
8
8
  }
9
+ export function extendCoin(wallet, utxo) {
10
+ return {
11
+ ...utxo,
12
+ forfeitTapLeafScript: wallet.boardingTapscript.forfeit(),
13
+ intentTapLeafScript: wallet.boardingTapscript.exit(),
14
+ tapTree: wallet.boardingTapscript.encode(),
15
+ };
16
+ }
@@ -24,7 +24,7 @@ import { ConditionWitness, VtxoTaprootTree } from '../utils/unknownFields.js';
24
24
  import { InMemoryStorageAdapter } from '../storage/inMemory.js';
25
25
  import { WalletRepositoryImpl, } from '../repositories/walletRepository.js';
26
26
  import { ContractRepositoryImpl, } from '../repositories/contractRepository.js';
27
- import { extendVirtualCoin } from './utils.js';
27
+ import { extendCoin, extendVirtualCoin } from './utils.js';
28
28
  /**
29
29
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
30
30
  * The wallet does not store any data locally and relies on Ark and onchain
@@ -333,15 +333,12 @@ export class Wallet {
333
333
  async getBoardingUtxos() {
334
334
  const boardingAddress = await this.getBoardingAddress();
335
335
  const boardingUtxos = await this.onchainProvider.getCoins(boardingAddress);
336
- const encodedBoardingTapscript = this.boardingTapscript.encode();
337
- const forfeit = this.boardingTapscript.forfeit();
338
- const exit = this.boardingTapscript.exit();
339
- return boardingUtxos.map((utxo) => ({
340
- ...utxo,
341
- forfeitTapLeafScript: forfeit,
342
- intentTapLeafScript: exit,
343
- tapTree: encodedBoardingTapscript,
344
- }));
336
+ const utxos = boardingUtxos.map((utxo) => {
337
+ return extendCoin(this, utxo);
338
+ });
339
+ // Save boardingUtxos using unified repository
340
+ await this.walletRepository.saveUtxos(boardingAddress, utxos);
341
+ return utxos;
345
342
  }
346
343
  async sendBitcoin(params) {
347
344
  if (params.amount <= 0) {
@@ -761,8 +758,6 @@ export class Wallet {
761
758
  const vtxo = vtxos.find((vtxo) => vtxo.txid === input.txid && vtxo.vout === input.vout);
762
759
  // boarding utxo, we need to sign the settlement tx
763
760
  if (!vtxo) {
764
- hasBoardingUtxos = true;
765
- const inputIndexes = [];
766
761
  for (let i = 0; i < settlementPsbt.inputsLength; i++) {
767
762
  const settlementInput = settlementPsbt.getInput(i);
768
763
  if (!settlementInput.txid ||
@@ -778,9 +773,12 @@ export class Wallet {
778
773
  settlementPsbt.updateInput(i, {
779
774
  tapLeafScript: [input.forfeitTapLeafScript],
780
775
  });
781
- inputIndexes.push(i);
776
+ settlementPsbt = await this.identity.sign(settlementPsbt, [
777
+ i,
778
+ ]);
779
+ hasBoardingUtxos = true;
780
+ break;
782
781
  }
783
- settlementPsbt = await this.identity.sign(settlementPsbt, inputIndexes);
784
782
  continue;
785
783
  }
786
784
  if (isRecoverable(vtxo) || isSubdust(vtxo, this.dustAmount)) {
@@ -1,3 +1,3 @@
1
- import { Transaction } from "@scure/btc-signer";
1
+ import { Transaction } from "./utils/transaction";
2
2
  import { TransactionInputUpdate } from "@scure/btc-signer/psbt.js";
3
3
  export declare function buildForfeitTx(inputs: TransactionInputUpdate[], forfeitPkScript: Uint8Array, txLocktime?: number): Transaction;
@@ -1,4 +1,4 @@
1
- import { Transaction } from "@scure/btc-signer/transaction.js";
1
+ import { Transaction } from "../utils/transaction";
2
2
  import { SignerSession } from "../tree/signingSession";
3
3
  export interface Identity {
4
4
  signerSession(): SignerSession;
@@ -1,5 +1,5 @@
1
- import { Transaction } from "@scure/btc-signer/transaction.js";
2
1
  import { Identity } from ".";
2
+ import { Transaction } from "../utils/transaction";
3
3
  import { SignerSession } from "../tree/signingSession";
4
4
  /**
5
5
  * In-memory single key implementation for Bitcoin transaction signing.
@@ -1,10 +1,10 @@
1
- import { Transaction } from "@scure/btc-signer/transaction.js";
1
+ import { Transaction } from "./utils/transaction";
2
2
  import { SingleKey } from "./identity/singleKey";
3
3
  import { Identity } from "./identity";
4
4
  import { ArkAddress } from "./script/address";
5
5
  import { VHTLC } from "./script/vhtlc";
6
6
  import { DefaultVtxo } from "./script/default";
7
- import { VtxoScript, EncodedVtxoScript, TapLeafScript } from "./script/base";
7
+ import { VtxoScript, EncodedVtxoScript, TapLeafScript, TapTreeCoder } from "./script/base";
8
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";
@@ -33,5 +33,5 @@ import { Unroll } from "./wallet/unroll";
33
33
  import { WalletRepositoryImpl } from "./repositories/walletRepository";
34
34
  import { ContractRepositoryImpl } from "./repositories/contractRepository";
35
35
  import { ArkError, maybeArkError } from "./providers/errors";
36
- export { Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager, 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, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, Intent, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, };
36
+ export { Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, Intent, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, };
37
37
  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, SignedIntent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, };
@@ -1,5 +1,5 @@
1
- import { Transaction } from "@scure/btc-signer";
2
1
  import { TransactionInput, TransactionOutput } from "@scure/btc-signer/psbt.js";
2
+ import { Transaction } from "../utils/transaction";
3
3
  /**
4
4
  * Intent proof implementation for Bitcoin message signing.
5
5
  *
@@ -49,8 +49,12 @@ export interface OnchainProvider {
49
49
  */
50
50
  export declare class EsploraProvider implements OnchainProvider {
51
51
  private baseUrl;
52
- private polling;
53
- constructor(baseUrl: string);
52
+ readonly pollingInterval: number;
53
+ readonly forcePolling: boolean;
54
+ constructor(baseUrl: string, opts?: {
55
+ pollingInterval?: number;
56
+ forcePolling?: boolean;
57
+ });
54
58
  getCoins(address: string): Promise<Coin[]>;
55
59
  getFeeRate(): Promise<number | undefined>;
56
60
  broadcastTransaction(...txs: string[]): Promise<string>;
@@ -1,5 +1,5 @@
1
1
  import { StorageAdapter } from "../storage";
2
- import { ArkTransaction, ExtendedVirtualCoin } from "../wallet";
2
+ import { ArkTransaction, ExtendedCoin, ExtendedVirtualCoin } from "../wallet";
3
3
  export interface WalletState {
4
4
  lastSyncTime?: number;
5
5
  settings?: Record<string, any>;
@@ -9,6 +9,10 @@ export interface WalletRepository {
9
9
  saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
10
10
  removeVtxo(address: string, vtxoId: string): Promise<void>;
11
11
  clearVtxos(address: string): Promise<void>;
12
+ getUtxos(address: string): Promise<ExtendedCoin[]>;
13
+ saveUtxos(address: string, utxos: ExtendedCoin[]): Promise<void>;
14
+ removeUtxo(address: string, utxoId: string): Promise<void>;
15
+ clearUtxos(address: string): Promise<void>;
12
16
  getTransactionHistory(address: string): Promise<ArkTransaction[]>;
13
17
  saveTransactions(address: string, txs: ArkTransaction[]): Promise<void>;
14
18
  clearTransactions(address: string): Promise<void>;
@@ -23,6 +27,10 @@ export declare class WalletRepositoryImpl implements WalletRepository {
23
27
  saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
24
28
  removeVtxo(address: string, vtxoId: string): Promise<void>;
25
29
  clearVtxos(address: string): Promise<void>;
30
+ getUtxos(address: string): Promise<ExtendedCoin[]>;
31
+ saveUtxos(address: string, utxos: ExtendedCoin[]): Promise<void>;
32
+ removeUtxo(address: string, utxoId: string): Promise<void>;
33
+ clearUtxos(address: string): Promise<void>;
26
34
  getTransactionHistory(address: string): Promise<ArkTransaction[]>;
27
35
  saveTransactions(address: string, txs: ArkTransaction[]): Promise<void>;
28
36
  clearTransactions(address: string): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { NETWORK } from "@scure/btc-signer";
2
+ import { PSBTOutput } from "@scure/btc-signer/psbt.js";
2
3
  import { Bytes } from "@scure/btc-signer/utils.js";
3
4
  import { ArkAddress } from "./address";
4
5
  import { ConditionCSVMultisigTapscript, CSVMultisigTapscript } from "./tapscript";
@@ -10,6 +11,7 @@ export type TapLeafScript = [
10
11
  },
11
12
  Bytes
12
13
  ];
14
+ export declare const TapTreeCoder: (typeof PSBTOutput.tapTree)[2];
13
15
  export declare function scriptFromTapLeafScript(leaf: TapLeafScript): Bytes;
14
16
  /**
15
17
  * VtxoScript is a script that contains a list of tapleaf scripts.
@@ -1,13 +1,11 @@
1
- import { Transaction } from "@scure/btc-signer";
2
- import { Bytes } from "@scure/btc-signer/utils.js";
3
1
  import { TransactionOutput } from "@scure/btc-signer/psbt.js";
4
2
  import { ExtendedCoin, VirtualCoin } from "../wallet";
5
3
  import { RelativeTimelock } from "../script/tapscript";
6
4
  import { EncodedVtxoScript, TapLeafScript } from "../script/base";
7
5
  import { CSVMultisigTapscript } from "../script/tapscript";
6
+ import { Transaction } from "./transaction";
8
7
  export type ArkTxInput = {
9
8
  tapLeafScript: TapLeafScript;
10
- checkpointTapLeafScript?: Bytes;
11
9
  } & EncodedVtxoScript & Pick<VirtualCoin, "txid" | "vout" | "value">;
12
10
  export type OffchainTx = {
13
11
  arkTx: Transaction;
@@ -0,0 +1,13 @@
1
+ import { Transaction as BtcSignerTransaction } from "@scure/btc-signer";
2
+ import { TxOpts } from "@scure/btc-signer/transaction";
3
+ import { Bytes } from "@scure/btc-signer/utils";
4
+ /**
5
+ * Transaction is a wrapper around the @scure/btc-signer Transaction class.
6
+ * It adds the Ark protocol specific options to the transaction.
7
+ */
8
+ export declare class Transaction extends BtcSignerTransaction {
9
+ static ARK_TX_OPTS: TxOpts;
10
+ constructor(opts?: TxOpts);
11
+ static fromPSBT(psbt_: Bytes, opts?: TxOpts): Transaction;
12
+ static fromRaw(raw: Bytes, opts?: TxOpts): Transaction;
13
+ }
@@ -1,10 +1,10 @@
1
- import { Transaction } from "@scure/btc-signer";
2
1
  import { P2TR } from "@scure/btc-signer/payment.js";
3
2
  import { Coin, SendBitcoinParams } from ".";
4
3
  import { Identity } from "../identity";
5
4
  import { Network, NetworkName } from "../networks";
6
5
  import { OnchainProvider } from "../providers/onchain";
7
6
  import { AnchorBumper } from "../utils/anchor";
7
+ import { Transaction } from "../utils/transaction";
8
8
  /**
9
9
  * Onchain Bitcoin wallet implementation for traditional Bitcoin transactions.
10
10
  *