@arkade-os/sdk 0.4.0-next.0 → 0.4.0-next.2

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 (60) hide show
  1. package/README.md +127 -24
  2. package/dist/cjs/bip322/index.js +270 -0
  3. package/dist/cjs/index.js +4 -2
  4. package/dist/cjs/intent/index.js +19 -9
  5. package/dist/cjs/repositories/indexedDB/contractRepository.js +1 -1
  6. package/dist/cjs/repositories/indexedDB/db.js +8 -46
  7. package/dist/cjs/repositories/indexedDB/walletRepository.js +1 -1
  8. package/dist/cjs/repositories/realm/contractRepository.js +120 -0
  9. package/dist/cjs/repositories/realm/index.js +9 -0
  10. package/dist/cjs/repositories/realm/schemas.js +108 -0
  11. package/dist/cjs/repositories/realm/types.js +7 -0
  12. package/dist/cjs/repositories/realm/walletRepository.js +273 -0
  13. package/dist/cjs/repositories/serialization.js +49 -0
  14. package/dist/cjs/repositories/sqlite/contractRepository.js +139 -0
  15. package/dist/cjs/repositories/sqlite/index.js +7 -0
  16. package/dist/cjs/repositories/sqlite/types.js +2 -0
  17. package/dist/cjs/repositories/sqlite/walletRepository.js +328 -0
  18. package/dist/cjs/wallet/serviceWorker/wallet.js +9 -1
  19. package/dist/cjs/wallet/vtxo-manager.js +7 -0
  20. package/dist/cjs/worker/messageBus.js +22 -2
  21. package/dist/esm/bip322/index.js +267 -0
  22. package/dist/esm/index.js +4 -1
  23. package/dist/esm/intent/index.js +16 -7
  24. package/dist/esm/repositories/indexedDB/contractRepository.js +1 -1
  25. package/dist/esm/repositories/indexedDB/db.js +2 -40
  26. package/dist/esm/repositories/indexedDB/walletRepository.js +1 -1
  27. package/dist/esm/repositories/realm/contractRepository.js +116 -0
  28. package/dist/esm/repositories/realm/index.js +3 -0
  29. package/dist/esm/repositories/realm/schemas.js +105 -0
  30. package/dist/esm/repositories/realm/types.js +6 -0
  31. package/dist/esm/repositories/realm/walletRepository.js +269 -0
  32. package/dist/esm/repositories/serialization.js +40 -0
  33. package/dist/esm/repositories/sqlite/contractRepository.js +135 -0
  34. package/dist/esm/repositories/sqlite/index.js +2 -0
  35. package/dist/esm/repositories/sqlite/types.js +1 -0
  36. package/dist/esm/repositories/sqlite/walletRepository.js +324 -0
  37. package/dist/esm/wallet/serviceWorker/wallet.js +9 -1
  38. package/dist/esm/wallet/vtxo-manager.js +7 -0
  39. package/dist/esm/worker/messageBus.js +22 -2
  40. package/dist/types/bip322/index.d.ts +55 -0
  41. package/dist/types/index.d.ts +3 -2
  42. package/dist/types/intent/index.d.ts +13 -0
  43. package/dist/types/repositories/indexedDB/db.d.ts +2 -54
  44. package/dist/types/repositories/realm/contractRepository.d.ts +24 -0
  45. package/dist/types/repositories/realm/index.d.ts +4 -0
  46. package/dist/types/repositories/realm/schemas.d.ts +208 -0
  47. package/dist/types/repositories/realm/types.d.ts +16 -0
  48. package/dist/types/repositories/realm/walletRepository.d.ts +31 -0
  49. package/dist/types/repositories/serialization.d.ts +40 -0
  50. package/dist/types/repositories/sqlite/contractRepository.d.ts +33 -0
  51. package/dist/types/repositories/sqlite/index.d.ts +3 -0
  52. package/dist/types/repositories/sqlite/types.d.ts +18 -0
  53. package/dist/types/repositories/sqlite/walletRepository.d.ts +40 -0
  54. package/package.json +18 -14
  55. package/dist/cjs/adapters/expo-db.js +0 -35
  56. package/dist/esm/adapters/expo-db.js +0 -27
  57. package/dist/types/adapters/expo-db.d.ts +0 -7
  58. /package/dist/cjs/{db → repositories/indexedDB}/manager.js +0 -0
  59. /package/dist/esm/{db → repositories/indexedDB}/manager.js +0 -0
  60. /package/dist/types/{db → repositories/indexedDB}/manager.d.ts +0 -0
package/README.md CHANGED
@@ -434,6 +434,26 @@ console.log('Service fee (sats):', info.fee)
434
434
  console.log('Fee address:', info.delegatorAddress)
435
435
  ```
436
436
 
437
+ ### BIP-322 Message Signing
438
+
439
+ Sign and verify messages using [BIP-322](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki). Supports P2TR (Taproot) signing, and verification for P2TR, P2WPKH, and legacy P2PKH addresses.
440
+
441
+ ```typescript
442
+ import { BIP322, SingleKey } from '@arkade-os/sdk'
443
+
444
+ const identity = SingleKey.fromHex('your_private_key_hex')
445
+
446
+ // Sign a message (P2TR key-spend)
447
+ const signature = await BIP322.sign('Hello Bitcoin!', identity)
448
+
449
+ // Verify against a P2TR address
450
+ const valid = BIP322.verify('Hello Bitcoin!', signature, 'bc1p...')
451
+
452
+ // Also works with P2WPKH and legacy P2PKH addresses
453
+ BIP322.verify('Hello Bitcoin!', sig, 'bc1q...') // P2WPKH
454
+ BIP322.verify('Hello Bitcoin!', sig, '1A1zP1...') // legacy P2PKH
455
+ ```
456
+
437
457
  ### Transaction History
438
458
 
439
459
  ```typescript
@@ -687,25 +707,69 @@ class MyWalletRepository implements WalletRepository {
687
707
  }
688
708
  ```
689
709
 
690
- Note: `IndexedDB*Repository` requires [indexeddbshim](https://github.com/indexeddbshim/indexeddbshim) in Node or other
691
- **non-browser environments**. It is a dev dependency of the SDK, so you must
692
- install and initialize it in your app before using the repositories. This
693
- also applies when you rely on the default storage behavior (no `storage`).
710
+ #### SQLite Repository (Node.js / React Native)
694
711
 
695
- Please see the working example in [examples/node/multiple-wallets.ts](examples/node/multiple-wallets.ts).
712
+ For Node.js or React Native environments, use the SQLite repository with any
713
+ SQLite driver. The SDK accepts a `SQLExecutor` interface — you provide the
714
+ driver, the SDK handles the schema.
715
+
716
+ See [examples/node/multiple-wallets.ts](examples/node/multiple-wallets.ts) for
717
+ a full working example using `better-sqlite3`.
696
718
 
697
719
  ```typescript
698
720
  import { SingleKey, Wallet } from '@arkade-os/sdk'
699
- import setGlobalVars from 'indexeddbshim'
721
+ import { SQLiteWalletRepository, SQLiteContractRepository, SQLExecutor } from '@arkade-os/sdk/repositories/sqlite'
722
+ import Database from 'better-sqlite3'
700
723
 
701
- setGlobalVars()
724
+ const db = new Database('my-wallet.sqlite')
725
+ db.pragma('journal_mode = WAL')
702
726
 
703
- const identity = SingleKey.fromHex('your_private_key_hex')
727
+ const executor: SQLExecutor = {
728
+ run: async (sql, params) => { db.prepare(sql).run(...(params ?? [])) },
729
+ get: async (sql, params) => db.prepare(sql).get(...(params ?? [])) as any,
730
+ all: async (sql, params) => db.prepare(sql).all(...(params ?? [])) as any,
731
+ }
732
+
733
+ const wallet = await Wallet.create({
734
+ identity: SingleKey.fromHex('your_private_key_hex'),
735
+ arkServerUrl: 'https://mutinynet.arkade.sh',
736
+ storage: {
737
+ walletRepository: new SQLiteWalletRepository(executor),
738
+ contractRepository: new SQLiteContractRepository(executor),
739
+ },
740
+ })
741
+ ```
742
+
743
+ #### Realm Repository (React Native)
744
+
745
+ For React Native apps using Realm, pass your Realm instance directly:
746
+
747
+ ```typescript
748
+ import { RealmWalletRepository, RealmContractRepository, ArkRealmSchemas } from '@arkade-os/sdk/repositories/realm'
704
749
 
705
- // Create wallet with default IndexedDB storage
750
+ const realm = await Realm.open({ schema: [...ArkRealmSchemas, ...yourSchemas] })
706
751
  const wallet = await Wallet.create({
707
752
  identity,
708
753
  arkServerUrl: 'https://mutinynet.arkade.sh',
754
+ storage: {
755
+ walletRepository: new RealmWalletRepository(realm),
756
+ contractRepository: new RealmContractRepository(realm),
757
+ },
758
+ })
759
+ ```
760
+
761
+ #### IndexedDB Repository (Browser)
762
+
763
+ In the browser, the SDK defaults to IndexedDB repositories when no `storage`
764
+ is provided:
765
+
766
+ ```typescript
767
+ import { SingleKey, Wallet } from '@arkade-os/sdk'
768
+
769
+ const wallet = await Wallet.create({
770
+ identity: SingleKey.fromHex('your_private_key_hex'),
771
+ arkServerUrl: 'https://mutinynet.arkade.sh',
772
+ // Uses IndexedDB by default in the browser
709
773
  })
710
774
  ```
711
775
 
@@ -731,6 +795,40 @@ const wallet = await Wallet.create({
731
795
  })
732
796
  ```
733
797
 
798
+ ### Using with Node.js
799
+
800
+ Node.js does not provide a global `EventSource` implementation. The SDK relies on `EventSource` for Server-Sent Events during settlement (onboarding/offboarding) and contract watching. You must polyfill it before using the SDK:
801
+
802
+ ```bash
803
+ npm install eventsource
804
+ ```
805
+
806
+ ```typescript
807
+ import { EventSource } from "eventsource";
808
+ (globalThis as any).EventSource = EventSource;
809
+
810
+ // Use dynamic import so the polyfill is set before the SDK evaluates
811
+ const { Wallet, SingleKey, Ramps } = await import("@arkade-os/sdk");
812
+ ```
813
+
814
+ If you also need IndexedDB persistence (e.g. for `WalletRepository`), set up the shim before any SDK import:
815
+
816
+ ```typescript
817
+ // Must define `self` BEFORE calling setGlobalVars
818
+ if (typeof self === "undefined") {
819
+ (globalThis as any).self = globalThis;
820
+ }
821
+ import setGlobalVars from "indexeddbshim/src/node-UnicodeIdentifiers";
822
+ (globalThis as any).window = globalThis;
823
+ setGlobalVars(null, { checkOrigin: false });
824
+ ```
825
+
826
+ > **Note:** `eventsource` and `indexeddbshim` are optional peer dependencies.
827
+ > Without the `EventSource` polyfill, settlement operations will fail with
828
+ > `ReferenceError: EventSource is not defined`.
829
+
830
+ See [`examples/node/multiple-wallets.ts`](examples/node/multiple-wallets.ts) for a complete working example.
831
+
734
832
  ### Using with Expo/React Native
735
833
 
736
834
  For React Native and Expo applications where standard EventSource and fetch streaming may not work properly, use the Expo-compatible providers:
@@ -759,26 +857,31 @@ Both ExpoArkProvider and ExpoIndexerProvider are available as adapters following
759
857
  - **ExpoArkProvider**: Handles settlement events and transaction streaming using expo/fetch for Server-Sent Events
760
858
  - **ExpoIndexerProvider**: Handles address subscriptions and VTXO updates using expo/fetch for JSON streaming
761
859
 
762
- To use IndexedDB repositories in Expo/React Native, call `setupExpoDb()` before any SDK import.
763
- This sets up `indexeddbshim` backed by expo-sqlite under the hood:
860
+ For persistence in Expo/React Native, use the SQLite repository with `expo-sqlite`:
764
861
 
765
862
  ```typescript
766
- import { setupExpoDb } from '@arkade-os/sdk/adapters/expo-db';
863
+ import { SQLiteWalletRepository, SQLiteContractRepository } from '@arkade-os/sdk/repositories/sqlite'
864
+ import * as SQLite from 'expo-sqlite'
865
+
866
+ const db = SQLite.openDatabaseSync('my-wallet.db')
867
+ const executor = {
868
+ run: (sql, params) => db.runAsync(sql, params ?? []),
869
+ get: (sql, params) => db.getFirstAsync(sql, params ?? []),
870
+ all: (sql, params) => db.getAllAsync(sql, params ?? []),
871
+ }
767
872
 
768
- setupExpoDb();
873
+ const wallet = await Wallet.create({
874
+ identity,
875
+ arkServerUrl: 'https://mutinynet.arkade.sh',
876
+ arkProvider: new ExpoArkProvider('https://mutinynet.arkade.sh'),
877
+ indexerProvider: new ExpoIndexerProvider('https://mutinynet.arkade.sh'),
878
+ storage: {
879
+ walletRepository: new SQLiteWalletRepository(executor),
880
+ contractRepository: new SQLiteContractRepository(executor),
881
+ },
882
+ })
769
883
  ```
770
884
 
771
- > **Note:** `setupExpoDb` accepts an optional `SetupExpoDbOptions` object to
772
- > customise `origin`, `checkOrigin`, and `cacheDatabaseInstances`.
773
-
774
- > **Note:** `expo-sqlite` and `indexeddbshim` are optional peer dependencies,
775
- > only required when importing from `@arkade-os/sdk/adapters/expo-db`. The
776
- > streaming providers (`@arkade-os/sdk/adapters/expo`) have no expo-sqlite
777
- > dependency. Install them with:
778
- > ```bash
779
- > npx expo install expo-sqlite && npm install indexeddbshim
780
- > ```
781
-
782
885
  #### Crypto Polyfill Requirement
783
886
 
784
887
  Install `expo-crypto` and polyfill `crypto.getRandomValues()` at the top of your app entry point:
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BIP322 = void 0;
4
+ const btc_signer_1 = require("@scure/btc-signer");
5
+ const utils_js_1 = require("@scure/btc-signer/utils.js");
6
+ const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
7
+ const utils_js_2 = require("@noble/curves/utils.js");
8
+ const base_1 = require("@scure/base");
9
+ const transaction_1 = require("../utils/transaction");
10
+ const intent_1 = require("../intent");
11
+ const TAG_BIP322 = "BIP0322-signed-message";
12
+ /**
13
+ * BIP-322 simple message signing and verification.
14
+ *
15
+ * Supports P2TR (Taproot) signing and verification, P2WPKH verification,
16
+ * and legacy P2PKH verification (Bitcoin Core signmessage format).
17
+ *
18
+ * Reuses the same toSpend/toSign transaction construction as Intent proofs,
19
+ * but with the standard BIP-322 tagged hash ("BIP0322-signed-message")
20
+ * instead of the Ark-specific tag.
21
+ *
22
+ * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * // Sign a message (P2TR)
27
+ * const signature = await BIP322.sign("Hello Bitcoin!", identity);
28
+ *
29
+ * // Verify a signature (P2TR or P2WPKH)
30
+ * const valid = BIP322.verify("Hello Bitcoin!", signature, "bc1p...");
31
+ * const valid2 = BIP322.verify("Hello Bitcoin!", signature, "bc1q...");
32
+ * ```
33
+ */
34
+ var BIP322;
35
+ (function (BIP322) {
36
+ /**
37
+ * Sign a message using the BIP-322 simple signature scheme.
38
+ *
39
+ * Constructs the standard BIP-322 toSpend and toSign transactions,
40
+ * signs via the Identity interface, and returns the base64-encoded
41
+ * witness stack.
42
+ *
43
+ * @param message - The message to sign
44
+ * @param identity - Identity instance (holds the private key internally)
45
+ * @param network - Optional Bitcoin network for P2TR address derivation
46
+ * @returns Base64-encoded BIP-322 simple signature (witness stack)
47
+ */
48
+ async function sign(message, identity, network) {
49
+ const xOnlyPubKey = await identity.xOnlyPublicKey();
50
+ const payment = (0, btc_signer_1.p2tr)(xOnlyPubKey, undefined, network);
51
+ // Build BIP-322 toSpend using shared construction with BIP-322 tag
52
+ const toSpend = (0, intent_1.craftToSpendTx)(message, payment.script, TAG_BIP322);
53
+ // Build BIP-322 toSign: version 0, single input spending toSpend, OP_RETURN output
54
+ const toSign = craftBIP322ToSignP2TR(toSpend, payment.script, xOnlyPubKey);
55
+ // Sign with identity (handles P2TR key-spend internally)
56
+ const signed = await identity.sign(toSign, [0]);
57
+ // Finalize and extract witness
58
+ signed.finalizeIdx(0);
59
+ const input = signed.getInput(0);
60
+ if (!input.finalScriptWitness) {
61
+ throw new Error("BIP-322: failed to produce witness after signing");
62
+ }
63
+ return base_1.base64.encode(btc_signer_1.RawWitness.encode(input.finalScriptWitness));
64
+ }
65
+ BIP322.sign = sign;
66
+ /**
67
+ * Verify a BIP-322 signature for a P2TR, P2WPKH, or legacy P2PKH address.
68
+ *
69
+ * For segwit addresses (P2TR, P2WPKH), reconstructs the BIP-322
70
+ * toSpend/toSign transactions and verifies the witness signature.
71
+ *
72
+ * For P2PKH addresses, verifies using the Bitcoin Core legacy
73
+ * `signmessage` format (compact recoverable ECDSA signature).
74
+ *
75
+ * @param message - The original message that was signed
76
+ * @param signature - Base64-encoded signature (BIP-322 witness or legacy compact)
77
+ * @param address - P2TR, P2WPKH, or P2PKH address of the signer
78
+ * @param network - Optional Bitcoin network for address decoding
79
+ * @returns true if the signature is valid
80
+ */
81
+ function verify(message, signature, address, network) {
82
+ let decoded;
83
+ try {
84
+ decoded = (0, btc_signer_1.Address)(network).decode(address);
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ // Legacy P2PKH: signature is base64 of 65 raw bytes, not a witness
90
+ if (decoded.type === "pkh") {
91
+ try {
92
+ return verifyLegacy(message, base_1.base64.decode(signature), decoded.hash);
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ }
98
+ // BIP-322 simple: signature is base64 of RawWitness
99
+ let pkScript;
100
+ let witnessItems;
101
+ try {
102
+ pkScript = btc_signer_1.OutScript.encode(decoded);
103
+ witnessItems = btc_signer_1.RawWitness.decode(base_1.base64.decode(signature));
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ if (witnessItems.length === 0) {
109
+ return false;
110
+ }
111
+ if (decoded.type === "tr") {
112
+ return verifyP2TR(message, witnessItems, pkScript, decoded.pubkey);
113
+ }
114
+ if (decoded.type === "wpkh") {
115
+ return verifyP2WPKH(message, witnessItems, pkScript, decoded.hash);
116
+ }
117
+ throw new Error(`BIP-322 verify: unsupported address type '${decoded.type}'`);
118
+ }
119
+ BIP322.verify = verify;
120
+ })(BIP322 || (exports.BIP322 = BIP322 = {}));
121
+ function verifyP2TR(message, witnessItems, pkScript, pubkey) {
122
+ // P2TR key-spend witness is exactly [schnorr_signature].
123
+ // Multiple items indicates a script-path spend, which BIP-322 simple doesn't cover.
124
+ if (witnessItems.length !== 1) {
125
+ return false;
126
+ }
127
+ const sig = witnessItems[0];
128
+ if (sig.length !== 64 && sig.length !== 65) {
129
+ return false;
130
+ }
131
+ // BIP-322 simple only allows SIGHASH_DEFAULT (64-byte sig) or SIGHASH_ALL (0x01).
132
+ const sighashType = sig.length === 65 ? sig[64] : btc_signer_1.SigHash.DEFAULT;
133
+ if (sighashType !== btc_signer_1.SigHash.DEFAULT && sighashType !== btc_signer_1.SigHash.ALL) {
134
+ return false;
135
+ }
136
+ const toSpend = (0, intent_1.craftToSpendTx)(message, pkScript, TAG_BIP322);
137
+ const toSign = craftBIP322ToSignP2TR(toSpend, pkScript, pubkey);
138
+ const sighash = toSign.preimageWitnessV1(0, [pkScript], sighashType, [0n]);
139
+ const rawSig = sig.length === 65 ? sig.subarray(0, 64) : sig;
140
+ return secp256k1_js_1.schnorr.verify(rawSig, sighash, pubkey);
141
+ }
142
+ function verifyP2WPKH(message, witnessItems, pkScript, addressHash) {
143
+ // P2WPKH witness: [der_signature || sighash_byte, compressed_pubkey]
144
+ if (witnessItems.length !== 2) {
145
+ return false;
146
+ }
147
+ const sigWithHash = witnessItems[0];
148
+ const pubkey = witnessItems[1];
149
+ if (pubkey.length !== 33 || sigWithHash.length < 2) {
150
+ return false;
151
+ }
152
+ // Verify the pubkey matches the address hash
153
+ const derived = (0, btc_signer_1.p2wpkh)(pubkey);
154
+ if (!(0, utils_js_2.equalBytes)(derived.hash, addressHash)) {
155
+ return false;
156
+ }
157
+ // Extract sighash type (last byte) and DER signature
158
+ const sighashType = sigWithHash[sigWithHash.length - 1];
159
+ const derSig = sigWithHash.subarray(0, sigWithHash.length - 1);
160
+ // Build toSpend and toSign
161
+ const toSpend = (0, intent_1.craftToSpendTx)(message, pkScript, TAG_BIP322);
162
+ const toSign = craftBIP322ToSignSimple(toSpend, pkScript);
163
+ // BIP-143 scriptCode for P2WPKH: equivalent P2PKH script
164
+ const scriptCode = btc_signer_1.OutScript.encode({ type: "pkh", hash: addressHash });
165
+ const sighash = toSign.preimageWitnessV0(0, scriptCode, sighashType, 0n);
166
+ return secp256k1_js_1.secp256k1.verify(derSig, sighash, pubkey, {
167
+ prehash: false,
168
+ format: "der",
169
+ });
170
+ }
171
+ /**
172
+ * Verify a legacy Bitcoin Core signmessage signature for a P2PKH address.
173
+ *
174
+ * The signature is 65 bytes: [recovery_flag, r(32), s(32)].
175
+ * The recovery flag encodes both the recovery ID and whether the key is
176
+ * compressed: flag = 27 + recovery_id (+ 4 if compressed).
177
+ */
178
+ function verifyLegacy(message, sigBytes, addressHash) {
179
+ if (sigBytes.length !== 65) {
180
+ return false;
181
+ }
182
+ const flag = sigBytes[0];
183
+ if (flag < 27 || flag > 34) {
184
+ return false;
185
+ }
186
+ const compressed = flag >= 31;
187
+ const recoveryId = compressed ? flag - 31 : flag - 27;
188
+ const compactSig = sigBytes.subarray(1, 65);
189
+ const msgHash = bitcoinMessageHash(message);
190
+ try {
191
+ const sig = secp256k1_js_1.secp256k1.Signature.fromBytes(compactSig, "compact").addRecoveryBit(recoveryId);
192
+ const point = sig.recoverPublicKey(msgHash);
193
+ const pubkeyBytes = point.toBytes(compressed);
194
+ return (0, utils_js_2.equalBytes)((0, utils_js_1.hash160)(pubkeyBytes), addressHash);
195
+ }
196
+ catch {
197
+ return false;
198
+ }
199
+ }
200
+ /**
201
+ * Compute the Bitcoin message hash: SHA256d(magic_prefix + CompactSize(len) + message).
202
+ */
203
+ function bitcoinMessageHash(message) {
204
+ const MAGIC = new TextEncoder().encode("\x18Bitcoin Signed Message:\n");
205
+ const msgBytes = new TextEncoder().encode(message);
206
+ return (0, utils_js_1.sha256x2)((0, utils_js_1.concatBytes)(MAGIC, encodeCompactSize(msgBytes.length), msgBytes));
207
+ }
208
+ function encodeCompactSize(n) {
209
+ if (n < 253)
210
+ return new Uint8Array([n]);
211
+ if (n <= 0xffff) {
212
+ const buf = new Uint8Array(3);
213
+ buf[0] = 253;
214
+ buf[1] = n & 0xff;
215
+ buf[2] = (n >> 8) & 0xff;
216
+ return buf;
217
+ }
218
+ const buf = new Uint8Array(5);
219
+ buf[0] = 254;
220
+ buf[1] = n & 0xff;
221
+ buf[2] = (n >> 8) & 0xff;
222
+ buf[3] = (n >> 16) & 0xff;
223
+ buf[4] = (n >> 24) & 0xff;
224
+ return buf;
225
+ }
226
+ /**
227
+ * Build the BIP-322 "toSign" transaction for P2TR key-spend.
228
+ */
229
+ function craftBIP322ToSignP2TR(toSpend, pkScript, tapInternalKey) {
230
+ const tx = new transaction_1.Transaction({ version: 0 });
231
+ tx.addInput({
232
+ txid: toSpend.id,
233
+ index: 0,
234
+ sequence: 0,
235
+ witnessUtxo: {
236
+ script: pkScript,
237
+ amount: 0n,
238
+ },
239
+ tapInternalKey,
240
+ sighashType: btc_signer_1.SigHash.DEFAULT,
241
+ });
242
+ tx.addOutput({
243
+ amount: 0n,
244
+ script: intent_1.OP_RETURN_EMPTY_PKSCRIPT,
245
+ });
246
+ return tx;
247
+ }
248
+ /**
249
+ * Build the BIP-322 "toSign" transaction (generic, no key metadata).
250
+ *
251
+ * Used for P2WPKH verification where the toSign only needs
252
+ * the witnessUtxo, not tapInternalKey.
253
+ */
254
+ function craftBIP322ToSignSimple(toSpend, pkScript) {
255
+ const tx = new transaction_1.Transaction({ version: 0 });
256
+ tx.addInput({
257
+ txid: toSpend.id,
258
+ index: 0,
259
+ sequence: 0,
260
+ witnessUtxo: {
261
+ script: pkScript,
262
+ amount: 0n,
263
+ },
264
+ });
265
+ tx.addOutput({
266
+ amount: 0n,
267
+ script: intent_1.OP_RETURN_EMPTY_PKSCRIPT,
268
+ });
269
+ return tx;
270
+ }
package/dist/cjs/index.js CHANGED
@@ -37,7 +37,7 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.WalletMessageHandler = exports.MessageBus = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DelegateVtxo = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.RestDelegatorProvider = exports.DelegatorManagerImpl = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = exports.asset = void 0;
40
- exports.isArkContract = exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = void 0;
40
+ exports.isArkContract = exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = void 0;
41
41
  const transaction_1 = require("./utils/transaction");
42
42
  Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
43
43
  const singleKey_1 = require("./identity/singleKey");
@@ -119,6 +119,8 @@ Object.defineProperty(exports, "CosignerPublicKey", { enumerable: true, get: fun
119
119
  Object.defineProperty(exports, "VtxoTreeExpiry", { enumerable: true, get: function () { return unknownFields_1.VtxoTreeExpiry; } });
120
120
  const intent_1 = require("./intent");
121
121
  Object.defineProperty(exports, "Intent", { enumerable: true, get: function () { return intent_1.Intent; } });
122
+ const bip322_1 = require("./bip322");
123
+ Object.defineProperty(exports, "BIP322", { enumerable: true, get: function () { return bip322_1.BIP322; } });
122
124
  const arknote_1 = require("./arknote");
123
125
  Object.defineProperty(exports, "ArkNote", { enumerable: true, get: function () { return arknote_1.ArkNote; } });
124
126
  const networks_1 = require("./networks");
@@ -168,7 +170,7 @@ Object.defineProperty(exports, "decodeArkContract", { enumerable: true, get: fun
168
170
  Object.defineProperty(exports, "contractFromArkContract", { enumerable: true, get: function () { return contracts_1.contractFromArkContract; } });
169
171
  Object.defineProperty(exports, "contractFromArkContractWithAddress", { enumerable: true, get: function () { return contracts_1.contractFromArkContractWithAddress; } });
170
172
  Object.defineProperty(exports, "isArkContract", { enumerable: true, get: function () { return contracts_1.isArkContract; } });
171
- const manager_1 = require("./db/manager");
173
+ const manager_1 = require("./repositories/indexedDB/manager");
172
174
  Object.defineProperty(exports, "closeDatabase", { enumerable: true, get: function () { return manager_1.closeDatabase; } });
173
175
  Object.defineProperty(exports, "openDatabase", { enumerable: true, get: function () { return manager_1.openDatabase; } });
174
176
  const wallet_message_handler_1 = require("./wallet/serviceWorker/wallet-message-handler");
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Intent = void 0;
3
+ exports.TAG_INTENT_PROOF = exports.OP_RETURN_EMPTY_PKSCRIPT = exports.Intent = void 0;
4
+ exports.craftToSpendTx = craftToSpendTx;
4
5
  const btc_signer_1 = require("@scure/btc-signer");
5
6
  const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
6
7
  const transaction_1 = require("../utils/transaction");
@@ -106,10 +107,10 @@ var Intent;
106
107
  }
107
108
  Intent.encodeMessage = encodeMessage;
108
109
  })(Intent || (exports.Intent = Intent = {}));
109
- const OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([btc_signer_1.OP.RETURN]);
110
+ exports.OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([btc_signer_1.OP.RETURN]);
110
111
  const ZERO_32 = new Uint8Array(32).fill(0);
111
112
  const MAX_INDEX = 0xffffffff;
112
- const TAG_INTENT_PROOF = "ark-intent-proof-message";
113
+ exports.TAG_INTENT_PROOF = "ark-intent-proof-message";
113
114
  function validateInput(input) {
114
115
  if (input.index === undefined)
115
116
  throw new Error("intent proof input requires index");
@@ -134,9 +135,18 @@ function validateOutputs(outputs) {
134
135
  outputs.forEach(validateOutput);
135
136
  return true;
136
137
  }
137
- // craftToSpendTx creates the initial transaction that will be spent in the proof
138
- function craftToSpendTx(message, pkScript) {
139
- const messageHash = hashMessage(message);
138
+ /**
139
+ * Creates the "to_spend" transaction used by both intent proofs and BIP-322.
140
+ *
141
+ * The message is hashed with the given tagged-hash tag before being placed
142
+ * into the scriptSig as `OP_0 <hash>`.
143
+ *
144
+ * @param message - The message to embed
145
+ * @param pkScript - The scriptPubKey of the signer's address
146
+ * @param tag - Tagged-hash tag (defaults to the Ark intent proof tag)
147
+ */
148
+ function craftToSpendTx(message, pkScript, tag = exports.TAG_INTENT_PROOF) {
149
+ const messageHash = hashMessage(message, tag);
140
150
  const tx = new transaction_1.Transaction({
141
151
  version: 0,
142
152
  });
@@ -194,7 +204,7 @@ function craftToSignTx(toSpend, inputs, outputs) {
194
204
  outputs = [
195
205
  {
196
206
  amount: 0n,
197
- script: OP_RETURN_EMPTY_PKSCRIPT,
207
+ script: exports.OP_RETURN_EMPTY_PKSCRIPT,
198
208
  },
199
209
  ];
200
210
  }
@@ -206,8 +216,8 @@ function craftToSignTx(toSpend, inputs, outputs) {
206
216
  }
207
217
  return tx;
208
218
  }
209
- function hashMessage(message) {
210
- return secp256k1_js_1.schnorr.utils.taggedHash(TAG_INTENT_PROOF, new TextEncoder().encode(message));
219
+ function hashMessage(message, tag = exports.TAG_INTENT_PROOF) {
220
+ return secp256k1_js_1.schnorr.utils.taggedHash(tag, new TextEncoder().encode(message));
211
221
  }
212
222
  function prepareCoinAsIntentProofInput(coin) {
213
223
  if (!("tapTree" in coin)) {
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IndexedDBContractRepository = void 0;
4
4
  const db_1 = require("./db");
5
- const manager_1 = require("../../db/manager");
5
+ const manager_1 = require("./manager");
6
6
  const schema_1 = require("./schema");
7
7
  const utils_1 = require("../../worker/browser/utils");
8
8
  /**
@@ -1,8 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deserializeUtxo = exports.deserializeVtxo = exports.deserializeTapLeaf = exports.serializeUtxo = exports.serializeVtxo = exports.serializeTapLeaf = exports.DB_VERSION = exports.LEGACY_STORE_CONTRACT_COLLECTIONS = exports.STORE_CONTRACTS = exports.STORE_WALLET_STATE = exports.STORE_TRANSACTIONS = exports.STORE_UTXOS = exports.STORE_VTXOS = void 0;
4
- const base_1 = require("@scure/base");
5
- const btc_signer_1 = require("@scure/btc-signer");
6
4
  const schema_1 = require("./schema");
7
5
  Object.defineProperty(exports, "DB_VERSION", { enumerable: true, get: function () { return schema_1.DB_VERSION; } });
8
6
  Object.defineProperty(exports, "STORE_CONTRACTS", { enumerable: true, get: function () { return schema_1.STORE_CONTRACTS; } });
@@ -11,47 +9,11 @@ Object.defineProperty(exports, "STORE_TRANSACTIONS", { enumerable: true, get: fu
11
9
  Object.defineProperty(exports, "STORE_UTXOS", { enumerable: true, get: function () { return schema_1.STORE_UTXOS; } });
12
10
  Object.defineProperty(exports, "STORE_VTXOS", { enumerable: true, get: function () { return schema_1.STORE_VTXOS; } });
13
11
  Object.defineProperty(exports, "STORE_WALLET_STATE", { enumerable: true, get: function () { return schema_1.STORE_WALLET_STATE; } });
14
- const serializeTapLeaf = ([cb, s]) => ({
15
- cb: base_1.hex.encode(btc_signer_1.TaprootControlBlock.encode(cb)),
16
- s: base_1.hex.encode(s),
17
- });
18
- exports.serializeTapLeaf = serializeTapLeaf;
19
- const serializeVtxo = (v) => ({
20
- ...v,
21
- tapTree: base_1.hex.encode(v.tapTree),
22
- forfeitTapLeafScript: (0, exports.serializeTapLeaf)(v.forfeitTapLeafScript),
23
- intentTapLeafScript: (0, exports.serializeTapLeaf)(v.intentTapLeafScript),
24
- extraWitness: v.extraWitness?.map(base_1.hex.encode),
25
- });
26
- exports.serializeVtxo = serializeVtxo;
27
- const serializeUtxo = (u) => ({
28
- ...u,
29
- tapTree: base_1.hex.encode(u.tapTree),
30
- forfeitTapLeafScript: (0, exports.serializeTapLeaf)(u.forfeitTapLeafScript),
31
- intentTapLeafScript: (0, exports.serializeTapLeaf)(u.intentTapLeafScript),
32
- extraWitness: u.extraWitness?.map(base_1.hex.encode),
33
- });
34
- exports.serializeUtxo = serializeUtxo;
35
- const deserializeTapLeaf = (t) => {
36
- const cb = btc_signer_1.TaprootControlBlock.decode(base_1.hex.decode(t.cb));
37
- const s = base_1.hex.decode(t.s);
38
- return [cb, s];
39
- };
40
- exports.deserializeTapLeaf = deserializeTapLeaf;
41
- const deserializeVtxo = (o) => ({
42
- ...o,
43
- createdAt: new Date(o.createdAt),
44
- tapTree: base_1.hex.decode(o.tapTree),
45
- forfeitTapLeafScript: (0, exports.deserializeTapLeaf)(o.forfeitTapLeafScript),
46
- intentTapLeafScript: (0, exports.deserializeTapLeaf)(o.intentTapLeafScript),
47
- extraWitness: o.extraWitness?.map(base_1.hex.decode),
48
- });
49
- exports.deserializeVtxo = deserializeVtxo;
50
- const deserializeUtxo = (o) => ({
51
- ...o,
52
- tapTree: base_1.hex.decode(o.tapTree),
53
- forfeitTapLeafScript: (0, exports.deserializeTapLeaf)(o.forfeitTapLeafScript),
54
- intentTapLeafScript: (0, exports.deserializeTapLeaf)(o.intentTapLeafScript),
55
- extraWitness: o.extraWitness?.map(base_1.hex.decode),
56
- });
57
- exports.deserializeUtxo = deserializeUtxo;
12
+ // Serialization helpers (re-exported from shared module)
13
+ var serialization_1 = require("../serialization");
14
+ Object.defineProperty(exports, "serializeTapLeaf", { enumerable: true, get: function () { return serialization_1.serializeTapLeaf; } });
15
+ Object.defineProperty(exports, "serializeVtxo", { enumerable: true, get: function () { return serialization_1.serializeVtxo; } });
16
+ Object.defineProperty(exports, "serializeUtxo", { enumerable: true, get: function () { return serialization_1.serializeUtxo; } });
17
+ Object.defineProperty(exports, "deserializeTapLeaf", { enumerable: true, get: function () { return serialization_1.deserializeTapLeaf; } });
18
+ Object.defineProperty(exports, "deserializeVtxo", { enumerable: true, get: function () { return serialization_1.deserializeVtxo; } });
19
+ Object.defineProperty(exports, "deserializeUtxo", { enumerable: true, get: function () { return serialization_1.deserializeUtxo; } });
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IndexedDBWalletRepository = void 0;
4
4
  const db_1 = require("./db");
5
- const manager_1 = require("../../db/manager");
5
+ const manager_1 = require("./manager");
6
6
  const schema_1 = require("./schema");
7
7
  const utils_1 = require("../../worker/browser/utils");
8
8
  /**