@arkade-os/sdk 0.2.3 → 0.3.0-alpha.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.
- package/README.md +114 -43
- package/dist/cjs/adapters/asyncStorage.js +5 -0
- package/dist/cjs/adapters/fileSystem.js +5 -0
- package/dist/cjs/adapters/indexedDB.js +5 -0
- package/dist/cjs/adapters/localStorage.js +5 -0
- package/dist/cjs/bip322/index.js +2 -2
- package/dist/cjs/identity/index.js +15 -0
- package/dist/cjs/identity/singleKey.js +20 -1
- package/dist/cjs/index.js +5 -3
- package/dist/cjs/musig2/keys.js +6 -6
- package/dist/cjs/musig2/sign.js +5 -5
- package/dist/cjs/repositories/contractRepository.js +130 -0
- package/dist/cjs/repositories/index.js +18 -0
- package/dist/cjs/repositories/walletRepository.js +136 -0
- package/dist/cjs/storage/asyncStorage.js +47 -0
- package/dist/cjs/storage/fileSystem.js +138 -0
- package/dist/cjs/storage/inMemory.js +21 -0
- package/dist/cjs/storage/indexedDB.js +97 -0
- package/dist/cjs/storage/localStorage.js +48 -0
- package/dist/cjs/tree/signingSession.js +4 -4
- package/dist/cjs/wallet/onchain.js +12 -6
- package/dist/cjs/wallet/serviceWorker/request.js +4 -14
- package/dist/cjs/wallet/serviceWorker/response.js +0 -13
- package/dist/cjs/wallet/serviceWorker/wallet.js +124 -130
- package/dist/cjs/wallet/serviceWorker/worker.js +84 -53
- package/dist/cjs/wallet/wallet.js +21 -4
- package/dist/esm/adapters/asyncStorage.js +1 -0
- package/dist/esm/adapters/fileSystem.js +1 -0
- package/dist/esm/adapters/indexedDB.js +1 -0
- package/dist/esm/adapters/localStorage.js +1 -0
- package/dist/esm/bip322/index.js +1 -1
- package/dist/esm/identity/index.js +1 -1
- package/dist/esm/identity/singleKey.js +21 -2
- package/dist/esm/index.js +4 -3
- package/dist/esm/musig2/keys.js +6 -6
- package/dist/esm/musig2/sign.js +4 -4
- package/dist/esm/repositories/contractRepository.js +126 -0
- package/dist/esm/repositories/index.js +2 -0
- package/dist/esm/repositories/walletRepository.js +132 -0
- package/dist/esm/storage/asyncStorage.js +43 -0
- package/dist/esm/storage/fileSystem.js +101 -0
- package/dist/esm/storage/inMemory.js +17 -0
- package/dist/esm/storage/indexedDB.js +93 -0
- package/dist/esm/storage/localStorage.js +44 -0
- package/dist/esm/tree/signingSession.js +1 -1
- package/dist/esm/wallet/onchain.js +12 -6
- package/dist/esm/wallet/serviceWorker/request.js +4 -14
- package/dist/esm/wallet/serviceWorker/response.js +0 -13
- package/dist/esm/wallet/serviceWorker/wallet.js +125 -131
- package/dist/esm/wallet/serviceWorker/worker.js +85 -54
- package/dist/esm/wallet/wallet.js +21 -4
- package/dist/types/adapters/asyncStorage.d.ts +2 -0
- package/dist/types/adapters/fileSystem.d.ts +2 -0
- package/dist/types/adapters/indexedDB.d.ts +2 -0
- package/dist/types/adapters/localStorage.d.ts +2 -0
- package/dist/types/identity/index.d.ts +3 -1
- package/dist/types/identity/singleKey.d.ts +12 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/repositories/contractRepository.d.ts +20 -0
- package/dist/types/repositories/index.d.ts +2 -0
- package/dist/types/repositories/walletRepository.d.ts +38 -0
- package/dist/types/storage/asyncStorage.d.ts +9 -0
- package/dist/types/storage/fileSystem.d.ts +11 -0
- package/dist/types/storage/inMemory.d.ts +8 -0
- package/dist/types/storage/index.d.ts +6 -0
- package/dist/types/storage/indexedDB.d.ts +12 -0
- package/dist/types/storage/localStorage.d.ts +8 -0
- package/dist/types/wallet/index.d.ts +3 -0
- package/dist/types/wallet/onchain.d.ts +3 -2
- package/dist/types/wallet/serviceWorker/request.d.ts +1 -7
- package/dist/types/wallet/serviceWorker/response.d.ts +1 -8
- package/dist/types/wallet/serviceWorker/wallet.d.ts +67 -21
- package/dist/types/wallet/serviceWorker/worker.d.ts +16 -4
- package/dist/types/wallet/wallet.d.ts +4 -0
- package/package.json +38 -14
- package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +0 -185
- package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +0 -181
- package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +0 -20
- package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +0 -14
- /package/dist/cjs/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
- /package/dist/esm/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Arkade TypeScript SDK
|
|
2
|
+
|
|
2
3
|
The Arkade SDK is a TypeScript library for building Bitcoin wallets with support for both on-chain and off-chain transactions via the Ark protocol.
|
|
3
4
|
|
|
4
5
|
[](https://arkade-os.github.io/ts-sdk/)
|
|
5
|
-
[](https://deepwiki.com/
|
|
6
|
+
[](https://deepwiki.com/ark-ts-sdk)
|
|
6
7
|
|
|
7
8
|
## Installation
|
|
8
9
|
|
|
@@ -22,16 +23,20 @@ const identity = SingleKey.fromHex('your_private_key_hex')
|
|
|
22
23
|
|
|
23
24
|
// Create a wallet with Ark support
|
|
24
25
|
const wallet = await Wallet.create({
|
|
25
|
-
identity
|
|
26
|
-
// Esplora API, can be left empty mempool.space API will be used
|
|
26
|
+
identity,
|
|
27
|
+
// Esplora API, can be left empty - mempool.space API will be used
|
|
27
28
|
esploraUrl: 'https://mutinynet.com/api',
|
|
28
29
|
arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
30
|
+
// Optional: specify storage adapter (defaults to InMemoryStorageAdapter)
|
|
31
|
+
// storage: new LocalStorageAdapter() // for browser persistence
|
|
29
32
|
})
|
|
30
33
|
```
|
|
31
34
|
|
|
32
35
|
### Receiving Bitcoin
|
|
33
36
|
|
|
34
37
|
```typescript
|
|
38
|
+
import { waitForIncomingFunds } from '@arkade-os/sdk'
|
|
39
|
+
|
|
35
40
|
// Get wallet addresses
|
|
36
41
|
const arkAddress = await wallet.getAddress()
|
|
37
42
|
const boardingAddress = await wallet.getBoardingAddress()
|
|
@@ -40,17 +45,17 @@ console.log('Boarding Address:', boardingAddress)
|
|
|
40
45
|
|
|
41
46
|
const incomingFunds = await waitForIncomingFunds(wallet)
|
|
42
47
|
if (incomingFunds.type === "vtxo") {
|
|
43
|
-
//
|
|
48
|
+
// Virtual coins received
|
|
44
49
|
console.log("VTXOs: ", incomingFunds.vtxos)
|
|
45
50
|
} else if (incomingFunds.type === "utxo") {
|
|
46
|
-
//
|
|
51
|
+
// Boarding coins received
|
|
47
52
|
console.log("UTXOs: ", incomingFunds.coins)
|
|
48
53
|
}
|
|
49
54
|
```
|
|
50
55
|
|
|
51
56
|
### Onboarding
|
|
52
57
|
|
|
53
|
-
Onboarding allows you to swap
|
|
58
|
+
Onboarding allows you to swap on-chain funds into VTXOs:
|
|
54
59
|
|
|
55
60
|
```typescript
|
|
56
61
|
import { Ramps } from '@arkade-os/sdk'
|
|
@@ -88,9 +93,9 @@ const txid = await wallet.sendBitcoin({
|
|
|
88
93
|
})
|
|
89
94
|
```
|
|
90
95
|
|
|
91
|
-
### Batch Settlements
|
|
96
|
+
### Batch Settlements
|
|
92
97
|
|
|
93
|
-
This can be used to move preconfirmed balances into finalized balances
|
|
98
|
+
This can be used to move preconfirmed balances into finalized balances and to manually convert UTXOs and VTXOs.
|
|
94
99
|
|
|
95
100
|
```typescript
|
|
96
101
|
// For settling transactions
|
|
@@ -127,7 +132,7 @@ console.log('History:', history)
|
|
|
127
132
|
|
|
128
133
|
### Offboarding
|
|
129
134
|
|
|
130
|
-
Collaborative exit or "offboarding" allows you to withdraw your virtual funds to an
|
|
135
|
+
Collaborative exit or "offboarding" allows you to withdraw your virtual funds to an on-chain address:
|
|
131
136
|
|
|
132
137
|
```typescript
|
|
133
138
|
import { Ramps } from '@arkade-os/sdk'
|
|
@@ -145,11 +150,14 @@ Unilateral exit allows you to withdraw your funds from the Ark protocol back to
|
|
|
145
150
|
#### Step 1: Unrolling VTXOs
|
|
146
151
|
|
|
147
152
|
```typescript
|
|
148
|
-
import { Unroll, OnchainWallet } from '@arkade-os/sdk'
|
|
153
|
+
import { Unroll, OnchainWallet, SingleKey } from '@arkade-os/sdk'
|
|
154
|
+
|
|
155
|
+
// Create an identity for the onchain wallet
|
|
156
|
+
const onchainIdentity = SingleKey.fromHex('your_onchain_private_key_hex');
|
|
149
157
|
|
|
150
158
|
// Create an onchain wallet to pay for P2A outputs in VTXO branches
|
|
151
159
|
// OnchainWallet implements the AnchorBumper interface
|
|
152
|
-
const onchainWallet =
|
|
160
|
+
const onchainWallet = await OnchainWallet.create(onchainIdentity, 'regtest');
|
|
153
161
|
|
|
154
162
|
// Unroll a specific VTXO
|
|
155
163
|
const vtxo = { txid: 'your_vtxo_txid', vout: 0 };
|
|
@@ -177,6 +185,7 @@ for await (const step of session) {
|
|
|
177
185
|
```
|
|
178
186
|
|
|
179
187
|
The unrolling process works by:
|
|
188
|
+
|
|
180
189
|
- Traversing the transaction chain from the root (most recent) to the leaf (oldest)
|
|
181
190
|
- Broadcasting each transaction that isn't already on-chain
|
|
182
191
|
- Waiting for confirmations between steps
|
|
@@ -196,6 +205,7 @@ await Unroll.completeUnroll(
|
|
|
196
205
|
```
|
|
197
206
|
|
|
198
207
|
**Important Notes:**
|
|
208
|
+
|
|
199
209
|
- Each VTXO may require multiple unroll steps depending on the transaction chain length
|
|
200
210
|
- Each unroll step must be confirmed before proceeding to the next
|
|
201
211
|
- The `completeUnroll` method can only be called after VTXOs are fully unrolled and the timelock has expired
|
|
@@ -203,42 +213,103 @@ await Unroll.completeUnroll(
|
|
|
203
213
|
|
|
204
214
|
### Running the wallet in a service worker
|
|
205
215
|
|
|
206
|
-
|
|
216
|
+
**Ultra-simplified setup!** We handle all the complex service worker registration and identity management for you:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// SIMPLE SETUP with identity! 🎉
|
|
220
|
+
import { ServiceWorkerWallet, SingleKey } from '@arkade-os/sdk';
|
|
221
|
+
|
|
222
|
+
// Create your identity
|
|
223
|
+
const identity = SingleKey.fromHex('your_private_key_hex');
|
|
224
|
+
// Or generate a new one:
|
|
225
|
+
// const identity = SingleKey.fromRandomBytes();
|
|
226
|
+
|
|
227
|
+
const wallet = await ServiceWorkerWallet.setup({
|
|
228
|
+
serviceWorkerPath: '/service-worker.js',
|
|
229
|
+
arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
230
|
+
identity
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// That's it! Ready to use immediately:
|
|
234
|
+
const address = await wallet.getAddress();
|
|
235
|
+
const balance = await wallet.getBalance();
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
You'll also need to create a service worker file:
|
|
207
239
|
|
|
208
240
|
```typescript
|
|
209
|
-
// service-worker.
|
|
241
|
+
// service-worker.js
|
|
210
242
|
import { Worker } from '@arkade-os/sdk'
|
|
211
243
|
|
|
212
244
|
// Worker handles communication between the main thread and service worker
|
|
213
245
|
new Worker().start()
|
|
214
246
|
```
|
|
215
247
|
|
|
216
|
-
|
|
248
|
+
### Storage Adapters
|
|
249
|
+
|
|
250
|
+
Choose the appropriate storage adapter for your environment:
|
|
217
251
|
|
|
218
252
|
```typescript
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
253
|
+
import {
|
|
254
|
+
SingleKey,
|
|
255
|
+
Wallet,
|
|
256
|
+
InMemoryStorageAdapter, // Works everywhere, data lost on restart
|
|
257
|
+
} from '@arkade-os/sdk'
|
|
258
|
+
|
|
259
|
+
// Import additional storage adapters as needed:
|
|
260
|
+
import { LocalStorageAdapter } from '@arkade-os/sdk/adapters/localStorage' // Browser/PWA persistent storage
|
|
261
|
+
import { IndexedDBStorageAdapter } from '@arkade-os/sdk/adapters/indexedDB' // Browser/PWA/Service Worker advanced storage
|
|
262
|
+
import { AsyncStorageAdapter } from '@arkade-os/sdk/adapters/asyncStorage' // React Native persistent storage
|
|
263
|
+
import { FileSystemStorageAdapter } from '@arkade-os/sdk/adapters/fileSystem' // Node.js file-based storage
|
|
264
|
+
|
|
265
|
+
// Node.js
|
|
266
|
+
const storage = new FileSystemStorageAdapter('./wallet-data')
|
|
267
|
+
|
|
268
|
+
// Browser/PWA
|
|
269
|
+
const storage = new LocalStorageAdapter()
|
|
270
|
+
// or for advanced features:
|
|
271
|
+
const storage = new IndexedDBStorageAdapter('my-app', 1)
|
|
272
|
+
|
|
273
|
+
// React Native
|
|
274
|
+
const storage = new AsyncStorageAdapter()
|
|
275
|
+
|
|
276
|
+
// Service Worker
|
|
277
|
+
const storage = new IndexedDBStorageAdapter('service-worker-wallet', 1)
|
|
278
|
+
|
|
279
|
+
// Load identity from storage (simple pattern everywhere)
|
|
280
|
+
const privateKeyHex = await storage.getItem('private-key')
|
|
281
|
+
const identity = SingleKey.fromHex(privateKeyHex)
|
|
282
|
+
|
|
283
|
+
// Create wallet (same API everywhere)
|
|
284
|
+
const wallet = await Wallet.create({
|
|
285
|
+
identity,
|
|
230
286
|
arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
287
|
+
storage // optional
|
|
231
288
|
})
|
|
289
|
+
```
|
|
232
290
|
|
|
233
|
-
|
|
234
|
-
const status = await wallet.getStatus()
|
|
235
|
-
console.log('Service worker status:', status.walletInitialized)
|
|
291
|
+
### Repository Pattern
|
|
236
292
|
|
|
237
|
-
|
|
238
|
-
await wallet.clear()
|
|
239
|
-
```
|
|
293
|
+
Access low-level data management through repositories:
|
|
240
294
|
|
|
241
|
-
|
|
295
|
+
```typescript
|
|
296
|
+
// VTXO management (automatically cached for performance)
|
|
297
|
+
const addr = await wallet.getAddress()
|
|
298
|
+
const vtxos = await wallet.walletRepository.getVtxos(addr)
|
|
299
|
+
await wallet.walletRepository.saveVtxos(addr, vtxos)
|
|
300
|
+
|
|
301
|
+
// Contract data for SDK integrations
|
|
302
|
+
await wallet.contractRepository.setContractData('my-contract', 'status', 'active')
|
|
303
|
+
const status = await wallet.contractRepository.getContractData('my-contract', 'status')
|
|
304
|
+
|
|
305
|
+
// Collection management for related data
|
|
306
|
+
await wallet.contractRepository.saveToContractCollection(
|
|
307
|
+
'swaps',
|
|
308
|
+
{ id: 'swap-1', amount: 50000, type: 'reverse' },
|
|
309
|
+
'id' // key field
|
|
310
|
+
)
|
|
311
|
+
const swaps = await wallet.contractRepository.getContractCollection('swaps')
|
|
312
|
+
```
|
|
242
313
|
|
|
243
314
|
## Development
|
|
244
315
|
|
|
@@ -251,17 +322,17 @@ _For complete API documentation, visit our [TypeScript documentation](https://ar
|
|
|
251
322
|
|
|
252
323
|
1. Install dependencies:
|
|
253
324
|
|
|
254
|
-
```bash
|
|
255
|
-
pnpm install
|
|
256
|
-
pnpm format
|
|
257
|
-
pnpm lint
|
|
258
|
-
```
|
|
325
|
+
```bash
|
|
326
|
+
pnpm install
|
|
327
|
+
pnpm format
|
|
328
|
+
pnpm lint
|
|
329
|
+
```
|
|
259
330
|
|
|
260
|
-
|
|
331
|
+
1. Install nigiri for integration tests:
|
|
261
332
|
|
|
262
|
-
```bash
|
|
263
|
-
curl https://getnigiri.vulpem.com | bash
|
|
264
|
-
```
|
|
333
|
+
```bash
|
|
334
|
+
curl https://getnigiri.vulpem.com | bash
|
|
335
|
+
```
|
|
265
336
|
|
|
266
337
|
### Running Tests
|
|
267
338
|
|
|
@@ -296,9 +367,9 @@ pnpm test:coverage
|
|
|
296
367
|
### Building the documentation
|
|
297
368
|
|
|
298
369
|
```bash
|
|
299
|
-
# Build the
|
|
370
|
+
# Build the TypeScript documentation
|
|
300
371
|
pnpm docs:build
|
|
301
|
-
#
|
|
372
|
+
# Open the docs in the browser
|
|
302
373
|
pnpm docs:open
|
|
303
374
|
```
|
|
304
375
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AsyncStorageAdapter = void 0;
|
|
4
|
+
var asyncStorage_1 = require("../storage/asyncStorage");
|
|
5
|
+
Object.defineProperty(exports, "AsyncStorageAdapter", { enumerable: true, get: function () { return asyncStorage_1.AsyncStorageAdapter; } });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileSystemStorageAdapter = void 0;
|
|
4
|
+
var fileSystem_1 = require("../storage/fileSystem");
|
|
5
|
+
Object.defineProperty(exports, "FileSystemStorageAdapter", { enumerable: true, get: function () { return fileSystem_1.FileSystemStorageAdapter; } });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IndexedDBStorageAdapter = void 0;
|
|
4
|
+
var indexedDB_1 = require("../storage/indexedDB");
|
|
5
|
+
Object.defineProperty(exports, "IndexedDBStorageAdapter", { enumerable: true, get: function () { return indexedDB_1.IndexedDBStorageAdapter; } });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalStorageAdapter = void 0;
|
|
4
|
+
var localStorage_1 = require("../storage/localStorage");
|
|
5
|
+
Object.defineProperty(exports, "LocalStorageAdapter", { enumerable: true, get: function () { return localStorage_1.LocalStorageAdapter; } });
|
package/dist/cjs/bip322/index.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.BIP322 = void 0;
|
|
|
4
4
|
exports.craftToSpendTx = craftToSpendTx;
|
|
5
5
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
6
|
const errors_1 = require("./errors");
|
|
7
|
-
const
|
|
7
|
+
const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
|
|
8
8
|
const base_1 = require("@scure/base");
|
|
9
9
|
/**
|
|
10
10
|
* BIP-322 signature implementation for Bitcoin message signing.
|
|
@@ -174,5 +174,5 @@ function craftToSignTx(toSpend, inputs, outputs) {
|
|
|
174
174
|
return tx;
|
|
175
175
|
}
|
|
176
176
|
function hashMessage(message) {
|
|
177
|
-
return
|
|
177
|
+
return secp256k1_js_1.schnorr.utils.taggedHash(TAG_BIP322, new TextEncoder().encode(message));
|
|
178
178
|
}
|
|
@@ -1,2 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./singleKey"), exports);
|
|
@@ -5,6 +5,7 @@ const utils_1 = require("@scure/btc-signer/utils");
|
|
|
5
5
|
const base_1 = require("@scure/base");
|
|
6
6
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
7
7
|
const signingSession_1 = require("../tree/signingSession");
|
|
8
|
+
const secp256k1_1 = require("@noble/secp256k1");
|
|
8
9
|
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
9
10
|
const ALL_SIGHASH = Object.values(btc_signer_1.SigHash).filter((x) => typeof x === "number");
|
|
10
11
|
/**
|
|
@@ -18,6 +19,9 @@ const ALL_SIGHASH = Object.values(btc_signer_1.SigHash).filter((x) => typeof x =
|
|
|
18
19
|
* // Create from raw bytes
|
|
19
20
|
* const key = SingleKey.fromPrivateKey(privateKeyBytes);
|
|
20
21
|
*
|
|
22
|
+
* // Create random key
|
|
23
|
+
* const randomKey = SingleKey.fromRandomBytes();
|
|
24
|
+
*
|
|
21
25
|
* // Sign a transaction
|
|
22
26
|
* const signedTx = await key.sign(transaction);
|
|
23
27
|
* ```
|
|
@@ -32,6 +36,17 @@ class SingleKey {
|
|
|
32
36
|
static fromHex(privateKeyHex) {
|
|
33
37
|
return new SingleKey(base_1.hex.decode(privateKeyHex));
|
|
34
38
|
}
|
|
39
|
+
static fromRandomBytes() {
|
|
40
|
+
return new SingleKey((0, utils_1.randomPrivateKeyBytes)());
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Export the private key as a hex string.
|
|
44
|
+
*
|
|
45
|
+
* @returns The private key as a hex string
|
|
46
|
+
*/
|
|
47
|
+
toHex() {
|
|
48
|
+
return base_1.hex.encode(this.key);
|
|
49
|
+
}
|
|
35
50
|
async sign(tx, inputIndexes) {
|
|
36
51
|
const txCpy = tx.clone();
|
|
37
52
|
if (!inputIndexes) {
|
|
@@ -59,10 +74,14 @@ class SingleKey {
|
|
|
59
74
|
return txCpy;
|
|
60
75
|
}
|
|
61
76
|
xOnlyPublicKey() {
|
|
62
|
-
return (0, utils_1.pubSchnorr)(this.key);
|
|
77
|
+
return Promise.resolve((0, utils_1.pubSchnorr)(this.key));
|
|
63
78
|
}
|
|
64
79
|
signerSession() {
|
|
65
80
|
return signingSession_1.TreeSignerSession.random();
|
|
66
81
|
}
|
|
82
|
+
async signMessage(message) {
|
|
83
|
+
const msgBytes = new TextEncoder().encode(message);
|
|
84
|
+
return secp256k1_1.schnorr.sign((0, utils_1.sha256)(msgBytes), this.key);
|
|
85
|
+
}
|
|
67
86
|
}
|
|
68
87
|
exports.SingleKey = SingleKey;
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.
|
|
3
|
+
exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.waitForIncomingFunds = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
4
4
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
5
5
|
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return btc_signer_1.Transaction; } });
|
|
6
6
|
const singleKey_1 = require("./identity/singleKey");
|
|
@@ -62,8 +62,6 @@ const bip322_1 = require("./bip322");
|
|
|
62
62
|
Object.defineProperty(exports, "BIP322", { enumerable: true, get: function () { return bip322_1.BIP322; } });
|
|
63
63
|
const arknote_1 = require("./arknote");
|
|
64
64
|
Object.defineProperty(exports, "ArkNote", { enumerable: true, get: function () { return arknote_1.ArkNote; } });
|
|
65
|
-
const idb_1 = require("./wallet/serviceWorker/db/vtxo/idb");
|
|
66
|
-
Object.defineProperty(exports, "IndexedDBVtxoRepository", { enumerable: true, get: function () { return idb_1.IndexedDBVtxoRepository; } });
|
|
67
65
|
const networks_1 = require("./networks");
|
|
68
66
|
Object.defineProperty(exports, "networks", { enumerable: true, get: function () { return networks_1.networks; } });
|
|
69
67
|
const indexer_1 = require("./providers/indexer");
|
|
@@ -74,3 +72,7 @@ const anchor_1 = require("./utils/anchor");
|
|
|
74
72
|
Object.defineProperty(exports, "P2A", { enumerable: true, get: function () { return anchor_1.P2A; } });
|
|
75
73
|
const unroll_1 = require("./wallet/unroll");
|
|
76
74
|
Object.defineProperty(exports, "Unroll", { enumerable: true, get: function () { return unroll_1.Unroll; } });
|
|
75
|
+
const walletRepository_1 = require("./repositories/walletRepository");
|
|
76
|
+
Object.defineProperty(exports, "WalletRepositoryImpl", { enumerable: true, get: function () { return walletRepository_1.WalletRepositoryImpl; } });
|
|
77
|
+
const contractRepository_1 = require("./repositories/contractRepository");
|
|
78
|
+
Object.defineProperty(exports, "ContractRepositoryImpl", { enumerable: true, get: function () { return contractRepository_1.ContractRepositoryImpl; } });
|
package/dist/cjs/musig2/keys.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.aggregateKeys = aggregateKeys;
|
|
37
37
|
const musig = __importStar(require("@scure/btc-signer/musig2"));
|
|
38
|
-
const
|
|
38
|
+
const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
|
|
39
39
|
// Aggregates multiple public keys according to the MuSig2 algorithm
|
|
40
40
|
function aggregateKeys(publicKeys, sort, options = {}) {
|
|
41
41
|
if (sort) {
|
|
@@ -44,14 +44,14 @@ function aggregateKeys(publicKeys, sort, options = {}) {
|
|
|
44
44
|
const { aggPublicKey: preTweakedKey } = musig.keyAggregate(publicKeys);
|
|
45
45
|
if (!options.taprootTweak) {
|
|
46
46
|
return {
|
|
47
|
-
preTweakedKey: preTweakedKey.
|
|
48
|
-
finalKey: preTweakedKey.
|
|
47
|
+
preTweakedKey: preTweakedKey.toBytes(true),
|
|
48
|
+
finalKey: preTweakedKey.toBytes(true),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
const tweakBytes =
|
|
51
|
+
const tweakBytes = secp256k1_js_1.schnorr.utils.taggedHash("TapTweak", preTweakedKey.toBytes(true).subarray(1), options.taprootTweak ?? new Uint8Array(0));
|
|
52
52
|
const { aggPublicKey: finalKey } = musig.keyAggregate(publicKeys, [tweakBytes], [true]);
|
|
53
53
|
return {
|
|
54
|
-
preTweakedKey: preTweakedKey.
|
|
55
|
-
finalKey: finalKey.
|
|
54
|
+
preTweakedKey: preTweakedKey.toBytes(true),
|
|
55
|
+
finalKey: finalKey.toBytes(true),
|
|
56
56
|
};
|
|
57
57
|
}
|
package/dist/cjs/musig2/sign.js
CHANGED
|
@@ -36,10 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.PartialSig = exports.PartialSignatureError = void 0;
|
|
37
37
|
exports.sign = sign;
|
|
38
38
|
const musig = __importStar(require("@scure/btc-signer/musig2"));
|
|
39
|
-
const
|
|
39
|
+
const utils_js_1 = require("@noble/curves/utils.js");
|
|
40
40
|
const secp256k1_1 = require("@noble/secp256k1");
|
|
41
41
|
const keys_1 = require("./keys");
|
|
42
|
-
const
|
|
42
|
+
const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
|
|
43
43
|
// Add this error type for decode failures
|
|
44
44
|
class PartialSignatureError extends Error {
|
|
45
45
|
constructor(message) {
|
|
@@ -77,8 +77,8 @@ class PartialSig {
|
|
|
77
77
|
throw new PartialSignatureError("Invalid partial signature length");
|
|
78
78
|
}
|
|
79
79
|
// Verify s is less than curve order
|
|
80
|
-
const s = (0,
|
|
81
|
-
if (s >= secp256k1_1.CURVE.n) {
|
|
80
|
+
const s = (0, utils_js_1.bytesToNumberBE)(bytes);
|
|
81
|
+
if (s >= secp256k1_1.Point.CURVE().n) {
|
|
82
82
|
throw new PartialSignatureError("s value overflows curve order");
|
|
83
83
|
}
|
|
84
84
|
// For decode we don't have R, so we'll need to compute it later
|
|
@@ -94,7 +94,7 @@ function sign(secNonce, privateKey, combinedNonce, publicKeys, message, options)
|
|
|
94
94
|
let tweakBytes;
|
|
95
95
|
if (options?.taprootTweak !== undefined) {
|
|
96
96
|
const { preTweakedKey } = (0, keys_1.aggregateKeys)(options?.sortKeys ? musig.sortKeys(publicKeys) : publicKeys, true);
|
|
97
|
-
tweakBytes =
|
|
97
|
+
tweakBytes = secp256k1_js_1.schnorr.utils.taggedHash("TapTweak", preTweakedKey.subarray(1), options.taprootTweak);
|
|
98
98
|
}
|
|
99
99
|
const session = new musig.Session(combinedNonce, options?.sortKeys ? musig.sortKeys(publicKeys) : publicKeys, message, tweakBytes ? [tweakBytes] : undefined, tweakBytes ? [true] : undefined);
|
|
100
100
|
const partialSig = session.sign(secNonce, privateKey);
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContractRepositoryImpl = void 0;
|
|
4
|
+
class ContractRepositoryImpl {
|
|
5
|
+
constructor(storage) {
|
|
6
|
+
this.cache = new Map();
|
|
7
|
+
this.storage = storage;
|
|
8
|
+
}
|
|
9
|
+
async getContractData(contractId, key) {
|
|
10
|
+
const storageKey = `contract:${contractId}:${key}`;
|
|
11
|
+
const cached = this.cache.get(storageKey);
|
|
12
|
+
if (cached !== undefined)
|
|
13
|
+
return cached;
|
|
14
|
+
const stored = await this.storage.getItem(storageKey);
|
|
15
|
+
if (!stored)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const data = JSON.parse(stored);
|
|
19
|
+
this.cache.set(storageKey, data);
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error(`Failed to parse contract data for ${contractId}:${key}:`, error);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async setContractData(contractId, key, data) {
|
|
28
|
+
const storageKey = `contract:${contractId}:${key}`;
|
|
29
|
+
try {
|
|
30
|
+
// First persist to storage, only update cache if successful
|
|
31
|
+
await this.storage.setItem(storageKey, JSON.stringify(data));
|
|
32
|
+
this.cache.set(storageKey, data);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// Storage operation failed, cache remains unchanged
|
|
36
|
+
console.error(`Failed to persist contract data for ${contractId}:${key}:`, error);
|
|
37
|
+
throw error; // Rethrow to notify caller of failure
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async deleteContractData(contractId, key) {
|
|
41
|
+
const storageKey = `contract:${contractId}:${key}`;
|
|
42
|
+
try {
|
|
43
|
+
// First remove from persistent storage, only delete from cache if successful
|
|
44
|
+
await this.storage.removeItem(storageKey);
|
|
45
|
+
this.cache.delete(storageKey);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// Storage operation failed, cache remains unchanged
|
|
49
|
+
console.error(`Failed to remove contract data for ${contractId}:${key}:`, error);
|
|
50
|
+
throw error; // Rethrow to notify caller of failure
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async getContractCollection(contractType) {
|
|
54
|
+
const storageKey = `collection:${contractType}`;
|
|
55
|
+
const cached = this.cache.get(storageKey);
|
|
56
|
+
if (cached !== undefined)
|
|
57
|
+
return cached;
|
|
58
|
+
const stored = await this.storage.getItem(storageKey);
|
|
59
|
+
if (!stored) {
|
|
60
|
+
this.cache.set(storageKey, []);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const collection = JSON.parse(stored);
|
|
65
|
+
this.cache.set(storageKey, collection);
|
|
66
|
+
return collection;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error(`Failed to parse contract collection ${contractType}:`, error);
|
|
70
|
+
this.cache.set(storageKey, []);
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async saveToContractCollection(contractType, item, idField) {
|
|
75
|
+
const collection = await this.getContractCollection(contractType);
|
|
76
|
+
// Validate that the item has the required id field
|
|
77
|
+
const itemId = item[idField];
|
|
78
|
+
if (itemId === undefined || itemId === null) {
|
|
79
|
+
throw new Error(`Item is missing required field '${String(idField)}'`);
|
|
80
|
+
}
|
|
81
|
+
// Find existing item index without mutating the original collection
|
|
82
|
+
const existingIndex = collection.findIndex((i) => i[idField] === itemId);
|
|
83
|
+
// Build new collection without mutating the cached one
|
|
84
|
+
let newCollection;
|
|
85
|
+
if (existingIndex !== -1) {
|
|
86
|
+
// Replace existing item
|
|
87
|
+
newCollection = [
|
|
88
|
+
...collection.slice(0, existingIndex),
|
|
89
|
+
item,
|
|
90
|
+
...collection.slice(existingIndex + 1),
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Add new item
|
|
95
|
+
newCollection = [...collection, item];
|
|
96
|
+
}
|
|
97
|
+
const storageKey = `collection:${contractType}`;
|
|
98
|
+
try {
|
|
99
|
+
// First persist to storage, only update cache if successful
|
|
100
|
+
await this.storage.setItem(storageKey, JSON.stringify(newCollection));
|
|
101
|
+
this.cache.set(storageKey, newCollection);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
// Storage operation failed, cache remains unchanged
|
|
105
|
+
console.error(`Failed to persist contract collection ${contractType}:`, error);
|
|
106
|
+
throw error; // Rethrow to notify caller of failure
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async removeFromContractCollection(contractType, id, idField) {
|
|
110
|
+
// Validate input parameters
|
|
111
|
+
if (id === undefined || id === null) {
|
|
112
|
+
throw new Error(`Invalid id provided for removal: ${String(id)}`);
|
|
113
|
+
}
|
|
114
|
+
const collection = await this.getContractCollection(contractType);
|
|
115
|
+
// Build new collection without the specified item
|
|
116
|
+
const filtered = collection.filter((item) => item[idField] !== id);
|
|
117
|
+
const storageKey = `collection:${contractType}`;
|
|
118
|
+
try {
|
|
119
|
+
// First persist to storage, only update cache if successful
|
|
120
|
+
await this.storage.setItem(storageKey, JSON.stringify(filtered));
|
|
121
|
+
this.cache.set(storageKey, filtered);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// Storage operation failed, cache remains unchanged
|
|
125
|
+
console.error(`Failed to persist contract collection removal for ${contractType}:`, error);
|
|
126
|
+
throw error; // Rethrow to notify caller of failure
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.ContractRepositoryImpl = ContractRepositoryImpl;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./walletRepository"), exports);
|
|
18
|
+
__exportStar(require("./contractRepository"), exports);
|