@arkade-os/sdk 0.0.16

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 (103) hide show
  1. package/README.md +312 -0
  2. package/dist/cjs/arknote/index.js +86 -0
  3. package/dist/cjs/forfeit.js +38 -0
  4. package/dist/cjs/identity/inMemoryKey.js +40 -0
  5. package/dist/cjs/identity/index.js +2 -0
  6. package/dist/cjs/index.js +48 -0
  7. package/dist/cjs/musig2/index.js +10 -0
  8. package/dist/cjs/musig2/keys.js +57 -0
  9. package/dist/cjs/musig2/nonces.js +44 -0
  10. package/dist/cjs/musig2/sign.js +102 -0
  11. package/dist/cjs/networks.js +26 -0
  12. package/dist/cjs/package.json +3 -0
  13. package/dist/cjs/providers/ark.js +530 -0
  14. package/dist/cjs/providers/onchain.js +61 -0
  15. package/dist/cjs/script/address.js +45 -0
  16. package/dist/cjs/script/base.js +51 -0
  17. package/dist/cjs/script/default.js +40 -0
  18. package/dist/cjs/script/tapscript.js +528 -0
  19. package/dist/cjs/script/vhtlc.js +84 -0
  20. package/dist/cjs/tree/signingSession.js +238 -0
  21. package/dist/cjs/tree/validation.js +184 -0
  22. package/dist/cjs/tree/vtxoTree.js +197 -0
  23. package/dist/cjs/utils/bip21.js +114 -0
  24. package/dist/cjs/utils/coinselect.js +73 -0
  25. package/dist/cjs/utils/psbt.js +124 -0
  26. package/dist/cjs/utils/transactionHistory.js +148 -0
  27. package/dist/cjs/utils/txSizeEstimator.js +95 -0
  28. package/dist/cjs/wallet/index.js +8 -0
  29. package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +153 -0
  30. package/dist/cjs/wallet/serviceWorker/db/vtxo/index.js +2 -0
  31. package/dist/cjs/wallet/serviceWorker/request.js +75 -0
  32. package/dist/cjs/wallet/serviceWorker/response.js +187 -0
  33. package/dist/cjs/wallet/serviceWorker/wallet.js +332 -0
  34. package/dist/cjs/wallet/serviceWorker/worker.js +452 -0
  35. package/dist/cjs/wallet/wallet.js +720 -0
  36. package/dist/esm/arknote/index.js +81 -0
  37. package/dist/esm/forfeit.js +35 -0
  38. package/dist/esm/identity/inMemoryKey.js +36 -0
  39. package/dist/esm/identity/index.js +1 -0
  40. package/dist/esm/index.js +39 -0
  41. package/dist/esm/musig2/index.js +3 -0
  42. package/dist/esm/musig2/keys.js +21 -0
  43. package/dist/esm/musig2/nonces.js +8 -0
  44. package/dist/esm/musig2/sign.js +63 -0
  45. package/dist/esm/networks.js +22 -0
  46. package/dist/esm/package.json +3 -0
  47. package/dist/esm/providers/ark.js +526 -0
  48. package/dist/esm/providers/onchain.js +57 -0
  49. package/dist/esm/script/address.js +41 -0
  50. package/dist/esm/script/base.js +46 -0
  51. package/dist/esm/script/default.js +37 -0
  52. package/dist/esm/script/tapscript.js +491 -0
  53. package/dist/esm/script/vhtlc.js +81 -0
  54. package/dist/esm/tree/signingSession.js +200 -0
  55. package/dist/esm/tree/validation.js +179 -0
  56. package/dist/esm/tree/vtxoTree.js +157 -0
  57. package/dist/esm/utils/bip21.js +110 -0
  58. package/dist/esm/utils/coinselect.js +69 -0
  59. package/dist/esm/utils/psbt.js +118 -0
  60. package/dist/esm/utils/transactionHistory.js +145 -0
  61. package/dist/esm/utils/txSizeEstimator.js +91 -0
  62. package/dist/esm/wallet/index.js +5 -0
  63. package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +149 -0
  64. package/dist/esm/wallet/serviceWorker/db/vtxo/index.js +1 -0
  65. package/dist/esm/wallet/serviceWorker/request.js +72 -0
  66. package/dist/esm/wallet/serviceWorker/response.js +184 -0
  67. package/dist/esm/wallet/serviceWorker/wallet.js +328 -0
  68. package/dist/esm/wallet/serviceWorker/worker.js +448 -0
  69. package/dist/esm/wallet/wallet.js +716 -0
  70. package/dist/types/arknote/index.d.ts +17 -0
  71. package/dist/types/forfeit.d.ts +15 -0
  72. package/dist/types/identity/inMemoryKey.d.ts +12 -0
  73. package/dist/types/identity/index.d.ts +7 -0
  74. package/dist/types/index.d.ts +22 -0
  75. package/dist/types/musig2/index.d.ts +4 -0
  76. package/dist/types/musig2/keys.d.ts +9 -0
  77. package/dist/types/musig2/nonces.d.ts +13 -0
  78. package/dist/types/musig2/sign.d.ts +27 -0
  79. package/dist/types/networks.d.ts +16 -0
  80. package/dist/types/providers/ark.d.ts +126 -0
  81. package/dist/types/providers/onchain.d.ts +36 -0
  82. package/dist/types/script/address.d.ts +10 -0
  83. package/dist/types/script/base.d.ts +26 -0
  84. package/dist/types/script/default.d.ts +19 -0
  85. package/dist/types/script/tapscript.d.ts +94 -0
  86. package/dist/types/script/vhtlc.d.ts +31 -0
  87. package/dist/types/tree/signingSession.d.ts +32 -0
  88. package/dist/types/tree/validation.d.ts +22 -0
  89. package/dist/types/tree/vtxoTree.d.ts +32 -0
  90. package/dist/types/utils/bip21.d.ts +21 -0
  91. package/dist/types/utils/coinselect.d.ts +21 -0
  92. package/dist/types/utils/psbt.d.ts +11 -0
  93. package/dist/types/utils/transactionHistory.d.ts +2 -0
  94. package/dist/types/utils/txSizeEstimator.d.ts +27 -0
  95. package/dist/types/wallet/index.d.ts +122 -0
  96. package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +18 -0
  97. package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +12 -0
  98. package/dist/types/wallet/serviceWorker/request.d.ts +68 -0
  99. package/dist/types/wallet/serviceWorker/response.d.ts +107 -0
  100. package/dist/types/wallet/serviceWorker/wallet.d.ts +23 -0
  101. package/dist/types/wallet/serviceWorker/worker.d.ts +26 -0
  102. package/dist/types/wallet/wallet.d.ts +42 -0
  103. package/package.json +88 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Select coins to reach a target amount, prioritizing those closer to expiry
3
+ * @param coins List of coins to select from
4
+ * @param targetAmount Target amount to reach in satoshis
5
+ * @returns Selected coins and change amount, or null if insufficient funds
6
+ */
7
+ export function selectCoins(coins, targetAmount) {
8
+ // Sort coins by amount (descending)
9
+ const sortedCoins = [...coins].sort((a, b) => b.value - a.value);
10
+ const selectedCoins = [];
11
+ let selectedAmount = 0;
12
+ // Select coins until we have enough
13
+ for (const coin of sortedCoins) {
14
+ selectedCoins.push(coin);
15
+ selectedAmount += coin.value;
16
+ if (selectedAmount >= targetAmount) {
17
+ break;
18
+ }
19
+ }
20
+ // Check if we have enough
21
+ if (selectedAmount < targetAmount) {
22
+ return { inputs: null, changeAmount: 0 };
23
+ }
24
+ // Calculate change
25
+ const changeAmount = selectedAmount - targetAmount;
26
+ return {
27
+ inputs: selectedCoins,
28
+ changeAmount,
29
+ };
30
+ }
31
+ /**
32
+ * Select virtual coins to reach a target amount, prioritizing those closer to expiry
33
+ * @param coins List of virtual coins to select from
34
+ * @param targetAmount Target amount to reach in satoshis
35
+ * @returns Selected coins and change amount, or null if insufficient funds
36
+ */
37
+ export function selectVirtualCoins(coins, targetAmount) {
38
+ // Sort VTXOs by expiry (ascending) and amount (descending)
39
+ const sortedCoins = [...coins].sort((a, b) => {
40
+ // First sort by expiry if available
41
+ const expiryA = a.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
42
+ const expiryB = b.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
43
+ if (expiryA !== expiryB) {
44
+ return expiryA - expiryB; // Earlier expiry first
45
+ }
46
+ // Then sort by amount
47
+ return b.value - a.value; // Larger amount first
48
+ });
49
+ const selectedCoins = [];
50
+ let selectedAmount = 0;
51
+ // Select coins until we have enough
52
+ for (const coin of sortedCoins) {
53
+ selectedCoins.push(coin);
54
+ selectedAmount += coin.value;
55
+ if (selectedAmount >= targetAmount) {
56
+ break;
57
+ }
58
+ }
59
+ // Check if we have enough
60
+ if (selectedAmount < targetAmount) {
61
+ return { inputs: null, changeAmount: 0 };
62
+ }
63
+ // Calculate change
64
+ const changeAmount = selectedAmount - targetAmount;
65
+ return {
66
+ inputs: selectedCoins,
67
+ changeAmount,
68
+ };
69
+ }
@@ -0,0 +1,118 @@
1
+ import { DEFAULT_SEQUENCE, RawWitness, Transaction } from "@scure/btc-signer";
2
+ import { CLTVMultisigTapscript, decodeTapscript } from '../script/tapscript.js';
3
+ import { scriptFromTapLeafScript, VtxoScript, } from '../script/base.js';
4
+ import { ArkAddress } from '../script/address.js';
5
+ import { hex } from "@scure/base";
6
+ const ARK_UNKNOWN_KEY_TYPE = 255;
7
+ // Constant for condition witness key prefix
8
+ export const CONDITION_WITNESS_KEY_PREFIX = new TextEncoder().encode("condition");
9
+ export const VTXO_TAPROOT_TREE_KEY_PREFIX = new TextEncoder().encode("taptree");
10
+ export function addVtxoTaprootTree(inIndex, tx, scripts) {
11
+ tx.updateInput(inIndex, {
12
+ unknown: [
13
+ ...(tx.getInput(inIndex)?.unknown ?? []),
14
+ [
15
+ {
16
+ type: ARK_UNKNOWN_KEY_TYPE,
17
+ key: VTXO_TAPROOT_TREE_KEY_PREFIX,
18
+ },
19
+ encodeTaprootTree(scripts),
20
+ ],
21
+ ],
22
+ });
23
+ }
24
+ export function addConditionWitness(inIndex, tx, witness) {
25
+ const witnessBytes = RawWitness.encode(witness);
26
+ tx.updateInput(inIndex, {
27
+ unknown: [
28
+ ...(tx.getInput(inIndex)?.unknown ?? []),
29
+ [
30
+ {
31
+ type: ARK_UNKNOWN_KEY_TYPE,
32
+ key: CONDITION_WITNESS_KEY_PREFIX,
33
+ },
34
+ witnessBytes,
35
+ ],
36
+ ],
37
+ });
38
+ }
39
+ export function createVirtualTx(inputs, outputs) {
40
+ let lockTime;
41
+ for (const input of inputs) {
42
+ const tapscript = decodeTapscript(scriptFromTapLeafScript(input.tapLeafScript));
43
+ if (CLTVMultisigTapscript.is(tapscript)) {
44
+ lockTime = Number(tapscript.params.absoluteTimelock);
45
+ }
46
+ }
47
+ const tx = new Transaction({
48
+ allowUnknown: true,
49
+ lockTime,
50
+ });
51
+ for (const [i, input] of inputs.entries()) {
52
+ tx.addInput({
53
+ txid: input.txid,
54
+ index: input.vout,
55
+ sequence: lockTime ? DEFAULT_SEQUENCE - 1 : undefined,
56
+ witnessUtxo: {
57
+ script: VtxoScript.decode(input.scripts).pkScript,
58
+ amount: BigInt(input.value),
59
+ },
60
+ tapLeafScript: [input.tapLeafScript],
61
+ });
62
+ // add BIP371 encoded taproot tree to the unknown key field
63
+ addVtxoTaprootTree(i, tx, input.scripts.map(hex.decode));
64
+ }
65
+ for (const output of outputs) {
66
+ tx.addOutput({
67
+ amount: output.amount,
68
+ script: ArkAddress.decode(output.address).pkScript,
69
+ });
70
+ }
71
+ return tx;
72
+ }
73
+ function encodeTaprootTree(leaves) {
74
+ const chunks = [];
75
+ // Write number of leaves as compact size uint
76
+ chunks.push(encodeCompactSizeUint(leaves.length));
77
+ for (const tapscript of leaves) {
78
+ // Write depth (always 1 for now)
79
+ chunks.push(new Uint8Array([1]));
80
+ // Write leaf version (0xc0 for tapscript)
81
+ chunks.push(new Uint8Array([0xc0]));
82
+ // Write script length and script
83
+ chunks.push(encodeCompactSizeUint(tapscript.length));
84
+ chunks.push(tapscript);
85
+ }
86
+ // Concatenate all chunks
87
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
88
+ const result = new Uint8Array(totalLength);
89
+ let offset = 0;
90
+ for (const chunk of chunks) {
91
+ result.set(chunk, offset);
92
+ offset += chunk.length;
93
+ }
94
+ return result;
95
+ }
96
+ function encodeCompactSizeUint(value) {
97
+ if (value < 0xfd) {
98
+ return new Uint8Array([value]);
99
+ }
100
+ else if (value <= 0xffff) {
101
+ const buffer = new Uint8Array(3);
102
+ buffer[0] = 0xfd;
103
+ new DataView(buffer.buffer).setUint16(1, value, true);
104
+ return buffer;
105
+ }
106
+ else if (value <= 0xffffffff) {
107
+ const buffer = new Uint8Array(5);
108
+ buffer[0] = 0xfe;
109
+ new DataView(buffer.buffer).setUint32(1, value, true);
110
+ return buffer;
111
+ }
112
+ else {
113
+ const buffer = new Uint8Array(9);
114
+ buffer[0] = 0xff;
115
+ new DataView(buffer.buffer).setBigUint64(1, BigInt(value), true);
116
+ return buffer;
117
+ }
118
+ }
@@ -0,0 +1,145 @@
1
+ import { TxType } from '../wallet/index.js';
2
+ /**
3
+ * Helper function to find vtxos that were spent in a settlement
4
+ */
5
+ function findVtxosSpentInSettlement(vtxos, vtxo) {
6
+ if (vtxo.virtualStatus.state === "pending") {
7
+ return [];
8
+ }
9
+ return vtxos.filter((v) => {
10
+ if (!v.spentBy)
11
+ return false;
12
+ return v.spentBy === vtxo.virtualStatus.batchTxID;
13
+ });
14
+ }
15
+ /**
16
+ * Helper function to find vtxos that were spent in a payment
17
+ */
18
+ function findVtxosSpentInPayment(vtxos, vtxo) {
19
+ return vtxos.filter((v) => {
20
+ if (!v.spentBy)
21
+ return false;
22
+ return v.spentBy === vtxo.txid;
23
+ });
24
+ }
25
+ /**
26
+ * Helper function to find vtxos that resulted from a spentBy transaction
27
+ */
28
+ function findVtxosResultedFromSpentBy(vtxos, spentBy) {
29
+ return vtxos.filter((v) => {
30
+ if (v.virtualStatus.state !== "pending" &&
31
+ v.virtualStatus.batchTxID === spentBy) {
32
+ return true;
33
+ }
34
+ return v.txid === spentBy;
35
+ });
36
+ }
37
+ /**
38
+ * Helper function to reduce vtxos to their total amount
39
+ */
40
+ function reduceVtxosAmount(vtxos) {
41
+ return vtxos.reduce((sum, v) => sum + v.value, 0);
42
+ }
43
+ /**
44
+ * Helper function to get a vtxo from a list of vtxos
45
+ */
46
+ function getVtxo(resultedVtxos, spentVtxos) {
47
+ if (resultedVtxos.length === 0) {
48
+ return spentVtxos[0];
49
+ }
50
+ return resultedVtxos[0];
51
+ }
52
+ export function vtxosToTxs(spendable, spent, boardingRounds) {
53
+ const txs = [];
54
+ // Receive case
55
+ // All vtxos are received unless:
56
+ // - they resulted from a settlement (either boarding or refresh)
57
+ // - they are the change of a spend tx
58
+ let vtxosLeftToCheck = [...spent];
59
+ for (const vtxo of [...spendable, ...spent]) {
60
+ if (vtxo.virtualStatus.state !== "pending" &&
61
+ boardingRounds.has(vtxo.virtualStatus.batchTxID || "")) {
62
+ continue;
63
+ }
64
+ const settleVtxos = findVtxosSpentInSettlement(vtxosLeftToCheck, vtxo);
65
+ vtxosLeftToCheck = removeVtxosFromList(vtxosLeftToCheck, settleVtxos);
66
+ const settleAmount = reduceVtxosAmount(settleVtxos);
67
+ if (vtxo.value <= settleAmount) {
68
+ continue; // settlement or change, ignore
69
+ }
70
+ const spentVtxos = findVtxosSpentInPayment(vtxosLeftToCheck, vtxo);
71
+ vtxosLeftToCheck = removeVtxosFromList(vtxosLeftToCheck, spentVtxos);
72
+ const spentAmount = reduceVtxosAmount(spentVtxos);
73
+ if (vtxo.value <= spentAmount) {
74
+ continue; // settlement or change, ignore
75
+ }
76
+ const txKey = {
77
+ roundTxid: vtxo.virtualStatus.batchTxID || "",
78
+ boardingTxid: "",
79
+ redeemTxid: "",
80
+ };
81
+ let settled = vtxo.virtualStatus.state !== "pending";
82
+ if (vtxo.virtualStatus.state === "pending") {
83
+ txKey.redeemTxid = vtxo.txid;
84
+ if (vtxo.spentBy) {
85
+ settled = true;
86
+ }
87
+ }
88
+ txs.push({
89
+ key: txKey,
90
+ amount: vtxo.value - settleAmount - spentAmount,
91
+ type: TxType.TxReceived,
92
+ createdAt: vtxo.createdAt.getTime(),
93
+ settled,
94
+ });
95
+ }
96
+ // send case
97
+ // All "spentBy" vtxos are payments unless:
98
+ // - they are settlements
99
+ // aggregate spent by spentId
100
+ const vtxosBySpentBy = new Map();
101
+ for (const v of spent) {
102
+ if (!v.spentBy)
103
+ continue;
104
+ if (!vtxosBySpentBy.has(v.spentBy)) {
105
+ vtxosBySpentBy.set(v.spentBy, []);
106
+ }
107
+ const currentVtxos = vtxosBySpentBy.get(v.spentBy);
108
+ vtxosBySpentBy.set(v.spentBy, [...currentVtxos, v]);
109
+ }
110
+ for (const [sb, vtxos] of vtxosBySpentBy) {
111
+ const resultedVtxos = findVtxosResultedFromSpentBy([...spendable, ...spent], sb);
112
+ const resultedAmount = reduceVtxosAmount(resultedVtxos);
113
+ const spentAmount = reduceVtxosAmount(vtxos);
114
+ if (spentAmount <= resultedAmount) {
115
+ continue; // settlement or change, ignore
116
+ }
117
+ const vtxo = getVtxo(resultedVtxos, vtxos);
118
+ const txKey = {
119
+ roundTxid: vtxo.virtualStatus.batchTxID || "",
120
+ boardingTxid: "",
121
+ redeemTxid: "",
122
+ };
123
+ if (vtxo.virtualStatus.state === "pending") {
124
+ txKey.redeemTxid = vtxo.txid;
125
+ }
126
+ txs.push({
127
+ key: txKey,
128
+ amount: spentAmount - resultedAmount,
129
+ type: TxType.TxSent,
130
+ createdAt: vtxo.createdAt.getTime(),
131
+ settled: true,
132
+ });
133
+ }
134
+ return txs;
135
+ }
136
+ function removeVtxosFromList(vtxos, vtxosToRemove) {
137
+ return vtxos.filter((v) => {
138
+ for (const vtxoToRemove of vtxosToRemove) {
139
+ if (v.txid === vtxoToRemove.txid && v.vout === vtxoToRemove.vout) {
140
+ return false;
141
+ }
142
+ }
143
+ return true;
144
+ });
145
+ }
@@ -0,0 +1,91 @@
1
+ export class TxWeightEstimator {
2
+ constructor(hasWitness, inputCount, outputCount, inputSize, inputWitnessSize, outputSize) {
3
+ this.hasWitness = hasWitness;
4
+ this.inputCount = inputCount;
5
+ this.outputCount = outputCount;
6
+ this.inputSize = inputSize;
7
+ this.inputWitnessSize = inputWitnessSize;
8
+ this.outputSize = outputSize;
9
+ }
10
+ static create() {
11
+ return new TxWeightEstimator(false, 0, 0, 0, 0, 0);
12
+ }
13
+ addKeySpendInput(isDefault = true) {
14
+ this.inputCount++;
15
+ this.inputWitnessSize += 64 + 1 + (isDefault ? 0 : 1);
16
+ this.inputSize += TxWeightEstimator.INPUT_SIZE;
17
+ this.hasWitness = true;
18
+ return this;
19
+ }
20
+ addP2PKHInput() {
21
+ this.inputCount++;
22
+ this.inputWitnessSize++;
23
+ this.inputSize +=
24
+ TxWeightEstimator.INPUT_SIZE +
25
+ TxWeightEstimator.P2PKH_SCRIPT_SIG_SIZE;
26
+ return this;
27
+ }
28
+ addTapscriptInput(leafWitnessSize, leafScriptSize, leafControlBlockSize) {
29
+ const controlBlockWitnessSize = 1 +
30
+ TxWeightEstimator.BASE_CONTROL_BLOCK_SIZE +
31
+ 1 +
32
+ leafScriptSize +
33
+ 1 +
34
+ leafControlBlockSize;
35
+ this.inputCount++;
36
+ this.inputWitnessSize += leafWitnessSize + controlBlockWitnessSize;
37
+ this.inputSize += TxWeightEstimator.INPUT_SIZE;
38
+ this.hasWitness = true;
39
+ this.inputCount++;
40
+ return this;
41
+ }
42
+ addP2WKHOutput() {
43
+ this.outputCount++;
44
+ this.outputSize +=
45
+ TxWeightEstimator.OUTPUT_SIZE + TxWeightEstimator.P2WKH_OUTPUT_SIZE;
46
+ return this;
47
+ }
48
+ vsize() {
49
+ const getVarIntSize = (n) => {
50
+ if (n < 0xfd)
51
+ return 1;
52
+ if (n < 0xffff)
53
+ return 3;
54
+ if (n < 0xffffffff)
55
+ return 5;
56
+ return 9;
57
+ };
58
+ const inputCount = getVarIntSize(this.inputCount);
59
+ const outputCount = getVarIntSize(this.outputCount);
60
+ // Calculate the size of the transaction without witness data
61
+ const txSizeStripped = TxWeightEstimator.BASE_TX_SIZE +
62
+ inputCount +
63
+ this.inputSize +
64
+ outputCount +
65
+ this.outputSize;
66
+ // Calculate the total weight
67
+ let weight = txSizeStripped * TxWeightEstimator.WITNESS_SCALE_FACTOR;
68
+ // Add witness data if present
69
+ if (this.hasWitness) {
70
+ weight +=
71
+ TxWeightEstimator.WITNESS_HEADER_SIZE + this.inputWitnessSize;
72
+ }
73
+ // Convert weight to vsize (weight / 4, rounded up)
74
+ return vsize(weight);
75
+ }
76
+ }
77
+ TxWeightEstimator.P2PKH_SCRIPT_SIG_SIZE = 1 + 73 + 1 + 33;
78
+ TxWeightEstimator.INPUT_SIZE = 32 + 4 + 1 + 4;
79
+ TxWeightEstimator.BASE_CONTROL_BLOCK_SIZE = 1 + 32;
80
+ TxWeightEstimator.OUTPUT_SIZE = 8 + 1;
81
+ TxWeightEstimator.P2WKH_OUTPUT_SIZE = 1 + 1 + 20;
82
+ TxWeightEstimator.BASE_TX_SIZE = 8 + 2; // Version + LockTime
83
+ TxWeightEstimator.WITNESS_HEADER_SIZE = 2; // Flag + Marker
84
+ TxWeightEstimator.WITNESS_SCALE_FACTOR = 4;
85
+ const vsize = (weight) => {
86
+ const value = BigInt(Math.ceil(weight / TxWeightEstimator.WITNESS_SCALE_FACTOR));
87
+ return {
88
+ value,
89
+ fee: (feeRate) => feeRate * value,
90
+ };
91
+ };
@@ -0,0 +1,5 @@
1
+ export var TxType;
2
+ (function (TxType) {
3
+ TxType["TxSent"] = "SENT";
4
+ TxType["TxReceived"] = "RECEIVED";
5
+ })(TxType || (TxType = {}));
@@ -0,0 +1,149 @@
1
+ export class IndexedDBVtxoRepository {
2
+ constructor() {
3
+ this.db = null;
4
+ }
5
+ static delete() {
6
+ return new Promise((resolve, reject) => {
7
+ try {
8
+ const request = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME);
9
+ request.onblocked = () => {
10
+ // If blocked, wait a bit and try again
11
+ setTimeout(() => {
12
+ const retryRequest = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME);
13
+ retryRequest.onsuccess = () => resolve();
14
+ retryRequest.onerror = () => reject(retryRequest.error ||
15
+ new Error("Failed to delete database"));
16
+ }, 100);
17
+ };
18
+ request.onsuccess = () => {
19
+ resolve();
20
+ };
21
+ request.onerror = () => {
22
+ reject(request.error || new Error("Failed to delete database"));
23
+ };
24
+ }
25
+ catch (error) {
26
+ reject(error instanceof Error
27
+ ? error
28
+ : new Error("Failed to delete database"));
29
+ }
30
+ });
31
+ }
32
+ async close() {
33
+ if (this.db) {
34
+ this.db.close();
35
+ this.db = null;
36
+ }
37
+ }
38
+ async open() {
39
+ return new Promise((resolve, reject) => {
40
+ const request = indexedDB.open(IndexedDBVtxoRepository.DB_NAME, IndexedDBVtxoRepository.DB_VERSION);
41
+ request.onerror = () => {
42
+ reject(request.error);
43
+ };
44
+ request.onsuccess = () => {
45
+ this.db = request.result;
46
+ resolve();
47
+ };
48
+ request.onupgradeneeded = (event) => {
49
+ const db = event.target.result;
50
+ if (!db.objectStoreNames.contains(IndexedDBVtxoRepository.STORE_NAME)) {
51
+ const store = db.createObjectStore(IndexedDBVtxoRepository.STORE_NAME, {
52
+ keyPath: ["txid", "vout"],
53
+ });
54
+ store.createIndex("state", "virtualStatus.state", {
55
+ unique: false,
56
+ });
57
+ store.createIndex("spentBy", "spentBy", {
58
+ unique: false,
59
+ });
60
+ }
61
+ };
62
+ });
63
+ }
64
+ async addOrUpdate(vtxos) {
65
+ if (!this.db) {
66
+ throw new Error("Database not opened");
67
+ }
68
+ return new Promise((resolve, reject) => {
69
+ const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite");
70
+ const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
71
+ const requests = vtxos.map((vtxo) => {
72
+ return new Promise((resolveRequest, rejectRequest) => {
73
+ const request = store.put(vtxo);
74
+ request.onsuccess = () => resolveRequest();
75
+ request.onerror = () => rejectRequest(request.error);
76
+ });
77
+ });
78
+ Promise.all(requests)
79
+ .then(() => resolve())
80
+ .catch(reject);
81
+ });
82
+ }
83
+ async deleteAll() {
84
+ if (!this.db) {
85
+ throw new Error("Database not opened");
86
+ }
87
+ return new Promise((resolve, reject) => {
88
+ const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite");
89
+ const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
90
+ const request = store.clear();
91
+ request.onsuccess = () => resolve();
92
+ request.onerror = () => reject(request.error);
93
+ });
94
+ }
95
+ async getSpendableVtxos() {
96
+ if (!this.db) {
97
+ throw new Error("Database not opened");
98
+ }
99
+ return new Promise((resolve, reject) => {
100
+ const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
101
+ const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
102
+ const spentByIndex = store.index("spentBy");
103
+ // Get vtxos where spentBy is empty string
104
+ const request = spentByIndex.getAll(IDBKeyRange.only(""));
105
+ request.onsuccess = () => {
106
+ resolve(request.result);
107
+ };
108
+ request.onerror = () => reject(request.error);
109
+ });
110
+ }
111
+ async getAllVtxos() {
112
+ if (!this.db) {
113
+ throw new Error("Database not opened");
114
+ }
115
+ return new Promise((resolve, reject) => {
116
+ const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
117
+ const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
118
+ const spentByIndex = store.index("spentBy");
119
+ // Get vtxos where spentBy is empty string
120
+ const spendableRequest = spentByIndex.getAll(IDBKeyRange.only(""));
121
+ // Get all vtxos where spentBy is populated
122
+ const spentRequest = spentByIndex.getAll(IDBKeyRange.lowerBound("", true));
123
+ Promise.all([
124
+ new Promise((resolveSpendable, rejectSpendable) => {
125
+ spendableRequest.onsuccess = () => {
126
+ resolveSpendable(spendableRequest.result);
127
+ };
128
+ spendableRequest.onerror = () => rejectSpendable(spendableRequest.error);
129
+ }),
130
+ new Promise((resolveSpent, rejectSpent) => {
131
+ spentRequest.onsuccess = () => {
132
+ resolveSpent(spentRequest.result);
133
+ };
134
+ spentRequest.onerror = () => rejectSpent(spentRequest.error);
135
+ }),
136
+ ])
137
+ .then(([spendableVtxos, spentVtxos]) => {
138
+ resolve({
139
+ spendable: spendableVtxos,
140
+ spent: spentVtxos,
141
+ });
142
+ })
143
+ .catch(reject);
144
+ });
145
+ }
146
+ }
147
+ IndexedDBVtxoRepository.DB_NAME = "wallet-db";
148
+ IndexedDBVtxoRepository.STORE_NAME = "vtxos";
149
+ IndexedDBVtxoRepository.DB_VERSION = 1;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,72 @@
1
+ export var Request;
2
+ (function (Request) {
3
+ function isBase(message) {
4
+ return (typeof message === "object" && message !== null && "type" in message);
5
+ }
6
+ Request.isBase = isBase;
7
+ function isInitWallet(message) {
8
+ return (message.type === "INIT_WALLET" &&
9
+ "privateKey" in message &&
10
+ typeof message.privateKey === "string" &&
11
+ "arkServerUrl" in message &&
12
+ typeof message.arkServerUrl === "string" &&
13
+ "network" in message &&
14
+ typeof message.network === "string" &&
15
+ ("arkServerPublicKey" in message
16
+ ? typeof message.arkServerPublicKey === "string" ||
17
+ message.arkServerPublicKey === undefined
18
+ : true));
19
+ }
20
+ Request.isInitWallet = isInitWallet;
21
+ function isSettle(message) {
22
+ return message.type === "SETTLE";
23
+ }
24
+ Request.isSettle = isSettle;
25
+ function isGetAddress(message) {
26
+ return message.type === "GET_ADDRESS";
27
+ }
28
+ Request.isGetAddress = isGetAddress;
29
+ function isGetAddressInfo(message) {
30
+ return message.type === "GET_ADDRESS_INFO";
31
+ }
32
+ Request.isGetAddressInfo = isGetAddressInfo;
33
+ function isGetBalance(message) {
34
+ return message.type === "GET_BALANCE";
35
+ }
36
+ Request.isGetBalance = isGetBalance;
37
+ function isGetCoins(message) {
38
+ return message.type === "GET_COINS";
39
+ }
40
+ Request.isGetCoins = isGetCoins;
41
+ function isGetVtxos(message) {
42
+ return message.type === "GET_VTXOS";
43
+ }
44
+ Request.isGetVtxos = isGetVtxos;
45
+ function isGetVirtualCoins(message) {
46
+ return message.type === "GET_VIRTUAL_COINS";
47
+ }
48
+ Request.isGetVirtualCoins = isGetVirtualCoins;
49
+ function isGetBoardingUtxos(message) {
50
+ return message.type === "GET_BOARDING_UTXOS";
51
+ }
52
+ Request.isGetBoardingUtxos = isGetBoardingUtxos;
53
+ function isSendBitcoin(message) {
54
+ return (message.type === "SEND_BITCOIN" &&
55
+ "params" in message &&
56
+ message.params !== null &&
57
+ typeof message.params === "object" &&
58
+ "address" in message.params &&
59
+ typeof message.params.address === "string" &&
60
+ "amount" in message.params &&
61
+ typeof message.params.amount === "number");
62
+ }
63
+ Request.isSendBitcoin = isSendBitcoin;
64
+ function isGetTransactionHistory(message) {
65
+ return message.type === "GET_TRANSACTION_HISTORY";
66
+ }
67
+ Request.isGetTransactionHistory = isGetTransactionHistory;
68
+ function isGetStatus(message) {
69
+ return message.type === "GET_STATUS";
70
+ }
71
+ Request.isGetStatus = isGetStatus;
72
+ })(Request || (Request = {}));