@arkade-os/sdk 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +156 -174
  2. package/dist/cjs/arknote/index.js +61 -58
  3. package/dist/cjs/bip322/errors.js +13 -0
  4. package/dist/cjs/bip322/index.js +178 -0
  5. package/dist/cjs/forfeit.js +14 -25
  6. package/dist/cjs/identity/singleKey.js +68 -0
  7. package/dist/cjs/index.js +41 -17
  8. package/dist/cjs/providers/ark.js +253 -317
  9. package/dist/cjs/providers/indexer.js +525 -0
  10. package/dist/cjs/providers/onchain.js +193 -15
  11. package/dist/cjs/script/address.js +48 -17
  12. package/dist/cjs/script/base.js +120 -3
  13. package/dist/cjs/script/default.js +18 -4
  14. package/dist/cjs/script/tapscript.js +46 -14
  15. package/dist/cjs/script/vhtlc.js +27 -7
  16. package/dist/cjs/tree/signingSession.js +63 -106
  17. package/dist/cjs/tree/txTree.js +193 -0
  18. package/dist/cjs/tree/validation.js +79 -155
  19. package/dist/cjs/utils/anchor.js +35 -0
  20. package/dist/cjs/utils/arkTransaction.js +108 -0
  21. package/dist/cjs/utils/transactionHistory.js +84 -72
  22. package/dist/cjs/utils/txSizeEstimator.js +12 -0
  23. package/dist/cjs/utils/unknownFields.js +211 -0
  24. package/dist/cjs/wallet/index.js +12 -0
  25. package/dist/cjs/wallet/onchain.js +201 -0
  26. package/dist/cjs/wallet/ramps.js +95 -0
  27. package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +32 -0
  28. package/dist/cjs/wallet/serviceWorker/request.js +15 -12
  29. package/dist/cjs/wallet/serviceWorker/response.js +22 -27
  30. package/dist/cjs/wallet/serviceWorker/utils.js +8 -0
  31. package/dist/cjs/wallet/serviceWorker/wallet.js +58 -34
  32. package/dist/cjs/wallet/serviceWorker/worker.js +117 -108
  33. package/dist/cjs/wallet/unroll.js +270 -0
  34. package/dist/cjs/wallet/wallet.js +701 -454
  35. package/dist/esm/arknote/index.js +61 -57
  36. package/dist/esm/bip322/errors.js +9 -0
  37. package/dist/esm/bip322/index.js +174 -0
  38. package/dist/esm/forfeit.js +15 -26
  39. package/dist/esm/identity/singleKey.js +64 -0
  40. package/dist/esm/index.js +30 -12
  41. package/dist/esm/providers/ark.js +252 -317
  42. package/dist/esm/providers/indexer.js +521 -0
  43. package/dist/esm/providers/onchain.js +193 -15
  44. package/dist/esm/script/address.js +48 -17
  45. package/dist/esm/script/base.js +120 -3
  46. package/dist/esm/script/default.js +18 -4
  47. package/dist/esm/script/tapscript.js +46 -14
  48. package/dist/esm/script/vhtlc.js +27 -7
  49. package/dist/esm/tree/signingSession.js +65 -108
  50. package/dist/esm/tree/txTree.js +189 -0
  51. package/dist/esm/tree/validation.js +75 -152
  52. package/dist/esm/utils/anchor.js +31 -0
  53. package/dist/esm/utils/arkTransaction.js +105 -0
  54. package/dist/esm/utils/transactionHistory.js +84 -72
  55. package/dist/esm/utils/txSizeEstimator.js +12 -0
  56. package/dist/esm/utils/unknownFields.js +173 -0
  57. package/dist/esm/wallet/index.js +9 -0
  58. package/dist/esm/wallet/onchain.js +196 -0
  59. package/dist/esm/wallet/ramps.js +91 -0
  60. package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +32 -0
  61. package/dist/esm/wallet/serviceWorker/request.js +15 -12
  62. package/dist/esm/wallet/serviceWorker/response.js +22 -27
  63. package/dist/esm/wallet/serviceWorker/utils.js +8 -0
  64. package/dist/esm/wallet/serviceWorker/wallet.js +59 -35
  65. package/dist/esm/wallet/serviceWorker/worker.js +117 -108
  66. package/dist/esm/wallet/unroll.js +267 -0
  67. package/dist/esm/wallet/wallet.js +674 -461
  68. package/dist/types/arknote/index.d.ts +40 -13
  69. package/dist/types/bip322/errors.d.ts +6 -0
  70. package/dist/types/bip322/index.d.ts +57 -0
  71. package/dist/types/forfeit.d.ts +2 -14
  72. package/dist/types/identity/singleKey.d.ts +27 -0
  73. package/dist/types/index.d.ts +23 -12
  74. package/dist/types/providers/ark.d.ts +114 -95
  75. package/dist/types/providers/indexer.d.ts +186 -0
  76. package/dist/types/providers/onchain.d.ts +41 -11
  77. package/dist/types/script/address.d.ts +26 -2
  78. package/dist/types/script/base.d.ts +13 -3
  79. package/dist/types/script/default.d.ts +22 -0
  80. package/dist/types/script/tapscript.d.ts +61 -5
  81. package/dist/types/script/vhtlc.d.ts +27 -0
  82. package/dist/types/tree/signingSession.d.ts +5 -5
  83. package/dist/types/tree/txTree.d.ts +28 -0
  84. package/dist/types/tree/validation.d.ts +15 -22
  85. package/dist/types/utils/anchor.d.ts +19 -0
  86. package/dist/types/utils/arkTransaction.d.ts +27 -0
  87. package/dist/types/utils/transactionHistory.d.ts +7 -1
  88. package/dist/types/utils/txSizeEstimator.d.ts +3 -0
  89. package/dist/types/utils/unknownFields.d.ts +83 -0
  90. package/dist/types/wallet/index.d.ts +51 -50
  91. package/dist/types/wallet/onchain.d.ts +49 -0
  92. package/dist/types/wallet/ramps.d.ts +32 -0
  93. package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +2 -0
  94. package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +2 -0
  95. package/dist/types/wallet/serviceWorker/request.d.ts +14 -16
  96. package/dist/types/wallet/serviceWorker/response.d.ts +17 -19
  97. package/dist/types/wallet/serviceWorker/utils.d.ts +8 -0
  98. package/dist/types/wallet/serviceWorker/wallet.d.ts +36 -8
  99. package/dist/types/wallet/serviceWorker/worker.d.ts +7 -3
  100. package/dist/types/wallet/unroll.d.ts +102 -0
  101. package/dist/types/wallet/wallet.d.ts +71 -25
  102. package/package.json +14 -15
  103. package/dist/cjs/identity/inMemoryKey.js +0 -40
  104. package/dist/cjs/tree/vtxoTree.js +0 -231
  105. package/dist/cjs/utils/coinselect.js +0 -73
  106. package/dist/cjs/utils/psbt.js +0 -137
  107. package/dist/esm/identity/inMemoryKey.js +0 -36
  108. package/dist/esm/tree/vtxoTree.js +0 -191
  109. package/dist/esm/utils/coinselect.js +0 -69
  110. package/dist/esm/utils/psbt.js +0 -131
  111. package/dist/types/identity/inMemoryKey.d.ts +0 -12
  112. package/dist/types/tree/vtxoTree.d.ts +0 -33
  113. package/dist/types/utils/coinselect.d.ts +0 -21
  114. package/dist/types/utils/psbt.d.ts +0 -11
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Unroll = void 0;
4
+ const btc_signer_1 = require("@scure/btc-signer");
5
+ const indexer_1 = require("../providers/indexer");
6
+ const base_1 = require("@scure/base");
7
+ const base_2 = require("../script/base");
8
+ const psbt_1 = require("@scure/btc-signer/psbt");
9
+ const txSizeEstimator_1 = require("../utils/txSizeEstimator");
10
+ const wallet_1 = require("./wallet");
11
+ var Unroll;
12
+ (function (Unroll) {
13
+ let StepType;
14
+ (function (StepType) {
15
+ StepType[StepType["UNROLL"] = 0] = "UNROLL";
16
+ StepType[StepType["WAIT"] = 1] = "WAIT";
17
+ StepType[StepType["DONE"] = 2] = "DONE";
18
+ })(StepType = Unroll.StepType || (Unroll.StepType = {}));
19
+ /**
20
+ * Manages the unrolling process of a VTXO back to the Bitcoin blockchain.
21
+ *
22
+ * The Session class implements an async iterator that processes the unrolling steps:
23
+ * 1. **WAIT**: Waits for a transaction to be confirmed onchain (if it's in mempool)
24
+ * 2. **UNROLL**: Broadcasts the next transaction in the chain to the blockchain
25
+ * 3. **DONE**: Indicates the unrolling process is complete
26
+ *
27
+ * The unrolling process works by traversing the transaction chain from the root (most recent)
28
+ * to the leaf (oldest), broadcasting each transaction that isn't already onchain.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const session = await Unroll.Session.create(vtxoOutpoint, bumper, explorer, indexer);
33
+ *
34
+ * // iterate over the steps
35
+ * for await (const doneStep of session) {
36
+ * switch (doneStep.type) {
37
+ * case Unroll.StepType.WAIT:
38
+ * console.log(`Transaction ${doneStep.txid} confirmed`);
39
+ * break;
40
+ * case Unroll.StepType.UNROLL:
41
+ * console.log(`Broadcasting transaction ${doneStep.tx.id}`);
42
+ * break;
43
+ * case Unroll.StepType.DONE:
44
+ * console.log(`Unrolling complete for VTXO ${doneStep.vtxoTxid}`);
45
+ * break;
46
+ * }
47
+ * }
48
+ * ```
49
+ **/
50
+ class Session {
51
+ constructor(toUnroll, bumper, explorer, indexer) {
52
+ this.toUnroll = toUnroll;
53
+ this.bumper = bumper;
54
+ this.explorer = explorer;
55
+ this.indexer = indexer;
56
+ }
57
+ static async create(toUnroll, bumper, explorer, indexer) {
58
+ const { chain } = await indexer.getVtxoChain(toUnroll);
59
+ return new Session({ ...toUnroll, chain }, bumper, explorer, indexer);
60
+ }
61
+ /**
62
+ * Get the next step to be executed
63
+ * @returns The next step to be executed + the function to execute it
64
+ */
65
+ async next() {
66
+ let nextTxToBroadcast;
67
+ const chain = this.toUnroll.chain;
68
+ // Iterate through the chain from the end (root) to the beginning (leaf)
69
+ for (let i = chain.length - 1; i >= 0; i--) {
70
+ const chainTx = chain[i];
71
+ // Skip commitment transactions as they are always onchain
72
+ if (chainTx.type === indexer_1.ChainTxType.COMMITMENT ||
73
+ chainTx.type === indexer_1.ChainTxType.UNSPECIFIED) {
74
+ continue;
75
+ }
76
+ try {
77
+ // Check if the transaction is confirmed onchain
78
+ const txInfo = await this.explorer.getTxStatus(chainTx.txid);
79
+ // If found but not confirmed, it means the tx is in the mempool
80
+ // An unilateral exit is running, we must wait for it to be confirmed
81
+ if (!txInfo.confirmed) {
82
+ return {
83
+ type: StepType.WAIT,
84
+ txid: chainTx.txid,
85
+ do: doWait(this.explorer, chainTx.txid),
86
+ };
87
+ }
88
+ }
89
+ catch (e) {
90
+ // If the tx is not found, it's offchain, let's break
91
+ nextTxToBroadcast = chainTx;
92
+ break;
93
+ }
94
+ }
95
+ if (!nextTxToBroadcast) {
96
+ return {
97
+ type: StepType.DONE,
98
+ vtxoTxid: this.toUnroll.txid,
99
+ do: () => Promise.resolve(),
100
+ };
101
+ }
102
+ // Get the virtual transaction data
103
+ const virtualTxs = await this.indexer.getVirtualTxs([
104
+ nextTxToBroadcast.txid,
105
+ ]);
106
+ if (virtualTxs.txs.length === 0) {
107
+ throw new Error(`Tx ${nextTxToBroadcast.txid} not found`);
108
+ }
109
+ const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(virtualTxs.txs[0]), {
110
+ allowUnknownInputs: true,
111
+ });
112
+ // finalize the tree transaction
113
+ if (nextTxToBroadcast.type === indexer_1.ChainTxType.TREE) {
114
+ const input = tx.getInput(0);
115
+ if (!input) {
116
+ throw new Error("Input not found");
117
+ }
118
+ const tapKeySig = input.tapKeySig;
119
+ if (!tapKeySig) {
120
+ throw new Error("Tap key sig not found");
121
+ }
122
+ tx.updateInput(0, {
123
+ finalScriptWitness: [tapKeySig],
124
+ });
125
+ }
126
+ else {
127
+ // finalize ark transaction
128
+ tx.finalize();
129
+ }
130
+ return {
131
+ type: StepType.UNROLL,
132
+ tx,
133
+ do: doUnroll(this.bumper, this.explorer, tx),
134
+ };
135
+ }
136
+ /**
137
+ * Iterate over the steps to be executed and execute them
138
+ * @returns An async iterator over the executed steps
139
+ */
140
+ async *[Symbol.asyncIterator]() {
141
+ let lastStep;
142
+ do {
143
+ if (lastStep !== undefined) {
144
+ // wait 1 second before trying the next step in order to give time to the
145
+ // explorer to update the tx status
146
+ await sleep(1000);
147
+ }
148
+ const step = await this.next();
149
+ await step.do();
150
+ yield step;
151
+ lastStep = step.type;
152
+ } while (lastStep !== StepType.DONE);
153
+ }
154
+ }
155
+ Unroll.Session = Session;
156
+ /**
157
+ * Complete the unroll of a VTXO by broadcasting the transaction that spends the CSV path.
158
+ * @param wallet the wallet owning the VTXO(s)
159
+ * @param vtxoTxids the txids of the VTXO(s) to complete unroll
160
+ * @param outputAddress the address to send the unrolled funds to
161
+ * @throws if the VTXO(s) are not fully unrolled, if the txids are not found, if the tx is not confirmed, if no exit path is found or not available
162
+ * @returns the txid of the transaction spending the unrolled funds
163
+ */
164
+ async function completeUnroll(wallet, vtxoTxids, outputAddress) {
165
+ const chainTip = await wallet.onchainProvider.getChainTip();
166
+ let vtxos = await wallet.getVtxos({ withUnrolled: true });
167
+ vtxos = vtxos.filter((vtxo) => vtxoTxids.includes(vtxo.txid));
168
+ if (vtxos.length === 0) {
169
+ throw new Error("No vtxos to complete unroll");
170
+ }
171
+ const inputs = [];
172
+ let totalAmount = 0n;
173
+ const txWeightEstimator = txSizeEstimator_1.TxWeightEstimator.create();
174
+ for (const vtxo of vtxos) {
175
+ if (!vtxo.isUnrolled) {
176
+ throw new Error(`Vtxo ${vtxo.txid}:${vtxo.vout} is not fully unrolled, use unroll first`);
177
+ }
178
+ const txStatus = await wallet.onchainProvider.getTxStatus(vtxo.txid);
179
+ if (!txStatus.confirmed) {
180
+ throw new Error(`tx ${vtxo.txid} is not confirmed`);
181
+ }
182
+ const exit = availableExitPath({ height: txStatus.blockHeight, time: txStatus.blockTime }, chainTip, vtxo);
183
+ if (!exit) {
184
+ throw new Error(`no available exit path found for vtxo ${vtxo.txid}:${vtxo.vout}`);
185
+ }
186
+ const spendingLeaf = base_2.VtxoScript.decode(vtxo.tapTree).findLeaf(base_1.hex.encode(exit.script));
187
+ if (!spendingLeaf) {
188
+ throw new Error(`spending leaf not found for vtxo ${vtxo.txid}:${vtxo.vout}`);
189
+ }
190
+ totalAmount += BigInt(vtxo.value);
191
+ inputs.push({
192
+ txid: vtxo.txid,
193
+ index: vtxo.vout,
194
+ tapLeafScript: [spendingLeaf],
195
+ sequence: 0xffffffff - 1,
196
+ witnessUtxo: {
197
+ amount: BigInt(vtxo.value),
198
+ script: base_2.VtxoScript.decode(vtxo.tapTree).pkScript,
199
+ },
200
+ sighashType: btc_signer_1.SigHash.DEFAULT,
201
+ });
202
+ txWeightEstimator.addTapscriptInput(64, spendingLeaf[1].length, psbt_1.TaprootControlBlock.encode(spendingLeaf[0]).length);
203
+ }
204
+ const tx = new btc_signer_1.Transaction({ allowUnknownInputs: true, version: 2 });
205
+ for (const input of inputs) {
206
+ tx.addInput(input);
207
+ }
208
+ txWeightEstimator.addP2TROutput();
209
+ let feeRate = await wallet.onchainProvider.getFeeRate();
210
+ if (!feeRate || feeRate < wallet_1.Wallet.MIN_FEE_RATE) {
211
+ feeRate = wallet_1.Wallet.MIN_FEE_RATE;
212
+ }
213
+ const feeAmount = txWeightEstimator.vsize().fee(BigInt(feeRate));
214
+ if (feeAmount > totalAmount) {
215
+ throw new Error("fee amount is greater than the total amount");
216
+ }
217
+ tx.addOutputAddress(outputAddress, totalAmount - feeAmount);
218
+ const signedTx = await wallet.identity.sign(tx);
219
+ signedTx.finalize();
220
+ await wallet.onchainProvider.broadcastTransaction(signedTx.hex);
221
+ return signedTx.id;
222
+ }
223
+ Unroll.completeUnroll = completeUnroll;
224
+ })(Unroll || (exports.Unroll = Unroll = {}));
225
+ function sleep(ms) {
226
+ return new Promise((resolve) => setTimeout(resolve, ms));
227
+ }
228
+ function doUnroll(bumper, onchainProvider, tx) {
229
+ return async () => {
230
+ const [parent, child] = await bumper.bumpP2A(tx);
231
+ await onchainProvider.broadcastTransaction(parent, child);
232
+ };
233
+ }
234
+ function doWait(onchainProvider, txid) {
235
+ return () => {
236
+ return new Promise((resolve, reject) => {
237
+ const interval = setInterval(async () => {
238
+ try {
239
+ const txInfo = await onchainProvider.getTxStatus(txid);
240
+ if (txInfo.confirmed) {
241
+ clearInterval(interval);
242
+ resolve();
243
+ }
244
+ }
245
+ catch (e) {
246
+ clearInterval(interval);
247
+ reject(e);
248
+ }
249
+ }, 5000);
250
+ });
251
+ };
252
+ }
253
+ function availableExitPath(confirmedAt, current, vtxo) {
254
+ const exits = base_2.VtxoScript.decode(vtxo.tapTree).exitPaths();
255
+ for (const exit of exits) {
256
+ if (exit.params.timelock.type === "blocks") {
257
+ if (current.height >=
258
+ confirmedAt.height + Number(exit.params.timelock.value)) {
259
+ return exit;
260
+ }
261
+ }
262
+ else {
263
+ if (current.time >=
264
+ confirmedAt.time + Number(exit.params.timelock.value)) {
265
+ return exit;
266
+ }
267
+ }
268
+ }
269
+ return undefined;
270
+ }