@arkade-os/sdk 0.4.14 → 0.4.15
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 +208 -142
- package/dist/cjs/contracts/handlers/helpers.js +24 -0
- package/dist/cjs/contracts/handlers/vhtlc.js +2 -4
- package/dist/cjs/identity/index.js +6 -0
- package/dist/cjs/identity/seedIdentity.js +3 -3
- package/dist/cjs/index.js +5 -3
- package/dist/cjs/script/tapscript.js +8 -2
- package/dist/cjs/wallet/vtxo-manager.js +1 -0
- package/dist/cjs/wallet/wallet.js +112 -37
- package/dist/esm/contracts/handlers/helpers.js +23 -0
- package/dist/esm/contracts/handlers/vhtlc.js +3 -5
- package/dist/esm/identity/index.js +5 -0
- package/dist/esm/identity/seedIdentity.js +3 -3
- package/dist/esm/index.js +2 -1
- package/dist/esm/script/tapscript.js +8 -2
- package/dist/esm/wallet/vtxo-manager.js +1 -0
- package/dist/esm/wallet/wallet.js +113 -38
- package/dist/types/contracts/handlers/helpers.d.ts +10 -0
- package/dist/types/identity/index.d.ts +20 -0
- package/dist/types/identity/seedIdentity.d.ts +2 -2
- package/dist/types/index.d.ts +3 -3
- package/package.json +18 -10
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Arkade TypeScript SDK
|
|
2
2
|
|
|
3
|
-
The Arkade SDK is a TypeScript library for building Bitcoin wallets
|
|
3
|
+
The Arkade SDK is a TypeScript library for building Bitcoin wallets using the Arkade protocol.
|
|
4
4
|
|
|
5
|
-
[](https://arkade-os.github.io/ts-sdk/)
|
|
6
6
|
[](https://deepwiki.com/arkade-os/ts-sdk)
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
@@ -19,52 +19,43 @@ npm install @arkade-os/sdk
|
|
|
19
19
|
import {
|
|
20
20
|
MnemonicIdentity,
|
|
21
21
|
Wallet,
|
|
22
|
-
IndexedDBWalletRepository,
|
|
23
|
-
IndexedDBContractRepository
|
|
24
22
|
} from '@arkade-os/sdk'
|
|
25
23
|
import { generateMnemonic } from '@scure/bip39'
|
|
26
24
|
import { wordlist } from '@scure/bip39/wordlists/english.js'
|
|
27
25
|
|
|
28
26
|
// Generate a new mnemonic or use an existing one
|
|
29
27
|
const mnemonic = generateMnemonic(wordlist)
|
|
30
|
-
const identity = MnemonicIdentity.fromMnemonic(mnemonic
|
|
28
|
+
const identity = MnemonicIdentity.fromMnemonic(mnemonic)
|
|
31
29
|
|
|
32
|
-
// Create a wallet with
|
|
30
|
+
// Create a wallet with Arkade support
|
|
33
31
|
const wallet = await Wallet.create({
|
|
34
32
|
identity,
|
|
35
|
-
|
|
36
|
-
esploraUrl: 'https://mutinynet.com/api',
|
|
37
|
-
arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
38
|
-
// Optional: provide repositories for persistence (defaults to IndexedDB)
|
|
39
|
-
// storage: {
|
|
40
|
-
// walletRepository: new IndexedDBWalletRepository('my-wallet-db'),
|
|
41
|
-
// contractRepository: new IndexedDBContractRepository('my-wallet-db')
|
|
42
|
-
// }
|
|
33
|
+
arkServerUrl: 'https://arkade.computer',
|
|
43
34
|
})
|
|
44
35
|
```
|
|
45
36
|
|
|
46
|
-
###
|
|
37
|
+
### Read-Only Wallets (Watch-Only)
|
|
47
38
|
|
|
48
|
-
The SDK supports
|
|
39
|
+
The SDK supports read-only wallets that allow you to query wallet state without exposing private keys. This is useful for:
|
|
49
40
|
|
|
50
41
|
- **Watch-only wallets**: Monitor addresses and balances without transaction capabilities
|
|
51
42
|
- **Public interfaces**: Display wallet information safely in public-facing applications
|
|
52
43
|
- **Separate concerns**: Keep signing operations isolated from query operations
|
|
53
44
|
|
|
54
|
-
#### Creating a
|
|
45
|
+
#### Creating a Read-Only Wallet
|
|
55
46
|
|
|
56
47
|
```typescript
|
|
57
48
|
import { ReadonlySingleKey, ReadonlyWallet } from '@arkade-os/sdk'
|
|
58
49
|
|
|
59
|
-
// Create a
|
|
60
|
-
const identity = SingleKey.fromHex('
|
|
50
|
+
// Create a read-only identity from a public key
|
|
51
|
+
const identity = SingleKey.fromHex('e09ca...56609')
|
|
61
52
|
const publicKey = await identity.compressedPublicKey()
|
|
62
53
|
const readonlyIdentity = ReadonlySingleKey.fromPublicKey(publicKey)
|
|
63
54
|
|
|
64
|
-
// Create a
|
|
55
|
+
// Create a read-only wallet
|
|
65
56
|
const readonlyWallet = await ReadonlyWallet.create({
|
|
66
57
|
identity: readonlyIdentity,
|
|
67
|
-
arkServerUrl: 'https://
|
|
58
|
+
arkServerUrl: 'https://arkade.computer'
|
|
68
59
|
})
|
|
69
60
|
|
|
70
61
|
// Query operations work normally
|
|
@@ -74,43 +65,43 @@ const vtxos = await readonlyWallet.getVtxos()
|
|
|
74
65
|
const history = await readonlyWallet.getTransactionHistory()
|
|
75
66
|
|
|
76
67
|
// Transaction methods are not available (TypeScript will prevent this)
|
|
77
|
-
// await readonlyWallet.
|
|
68
|
+
// await readonlyWallet.send(...) // ❌ Type error!
|
|
78
69
|
```
|
|
79
70
|
|
|
80
|
-
#### Converting Wallets to
|
|
71
|
+
#### Converting Wallets to Read-Only
|
|
81
72
|
|
|
82
73
|
```typescript
|
|
83
|
-
import { Wallet,
|
|
74
|
+
import { Wallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
84
75
|
|
|
85
76
|
// Create a full wallet
|
|
86
|
-
const identity =
|
|
77
|
+
const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
87
78
|
const wallet = await Wallet.create({
|
|
88
79
|
identity,
|
|
89
|
-
arkServerUrl: 'https://
|
|
80
|
+
arkServerUrl: 'https://arkade.computer'
|
|
90
81
|
})
|
|
91
82
|
|
|
92
|
-
// Convert to
|
|
83
|
+
// Convert to read-only wallet (safe to share)
|
|
93
84
|
const readonlyWallet = await wallet.toReadonly()
|
|
94
85
|
|
|
95
|
-
// The
|
|
86
|
+
// The read-only wallet can query but not transact
|
|
96
87
|
const balance = await readonlyWallet.getBalance()
|
|
97
88
|
```
|
|
98
89
|
|
|
99
|
-
#### Converting Identity to
|
|
90
|
+
#### Converting Identity to Read-Only
|
|
100
91
|
|
|
101
92
|
```typescript
|
|
102
|
-
import {
|
|
93
|
+
import { MnemonicIdentity } from '@arkade-os/sdk'
|
|
103
94
|
|
|
104
95
|
// Full identity
|
|
105
|
-
const identity =
|
|
96
|
+
const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
106
97
|
|
|
107
|
-
// Convert to
|
|
98
|
+
// Convert to read-only (no signing capability)
|
|
108
99
|
const readonlyIdentity = await identity.toReadonly()
|
|
109
100
|
|
|
110
|
-
// Use in
|
|
101
|
+
// Use in read-only wallet
|
|
111
102
|
const readonlyWallet = await ReadonlyWallet.create({
|
|
112
103
|
identity: readonlyIdentity,
|
|
113
|
-
arkServerUrl: 'https://
|
|
104
|
+
arkServerUrl: 'https://arkade.computer'
|
|
114
105
|
})
|
|
115
106
|
```
|
|
116
107
|
|
|
@@ -131,18 +122,17 @@ import { wordlist } from '@scure/bip39/wordlists/english.js'
|
|
|
131
122
|
const mnemonic = generateMnemonic(wordlist)
|
|
132
123
|
|
|
133
124
|
// Create identity from a 12 or 24 word mnemonic
|
|
134
|
-
const identity = MnemonicIdentity.fromMnemonic(mnemonic
|
|
125
|
+
const identity = MnemonicIdentity.fromMnemonic(mnemonic)
|
|
135
126
|
|
|
136
127
|
// With optional passphrase for additional security
|
|
137
128
|
const identityWithPassphrase = MnemonicIdentity.fromMnemonic(mnemonic, {
|
|
138
|
-
isMainnet: true,
|
|
139
129
|
passphrase: 'my secret passphrase'
|
|
140
130
|
})
|
|
141
131
|
|
|
142
132
|
// Create wallet as usual
|
|
143
133
|
const wallet = await Wallet.create({
|
|
144
|
-
identity,
|
|
145
|
-
arkServerUrl: 'https://
|
|
134
|
+
identity: identityWithPassphrase,
|
|
135
|
+
arkServerUrl: 'https://arkade.computer'
|
|
146
136
|
})
|
|
147
137
|
```
|
|
148
138
|
|
|
@@ -154,13 +144,13 @@ import { mnemonicToSeedSync } from '@scure/bip39'
|
|
|
154
144
|
|
|
155
145
|
// If you already have a 64-byte seed
|
|
156
146
|
const seed = mnemonicToSeedSync(mnemonic)
|
|
157
|
-
const identity = SeedIdentity.fromSeed(seed
|
|
147
|
+
const identity = SeedIdentity.fromSeed(seed)
|
|
158
148
|
|
|
159
149
|
// Or with a custom output descriptor
|
|
160
|
-
const
|
|
150
|
+
const identityWithDescriptor = SeedIdentity.fromSeed(seed, { descriptor })
|
|
161
151
|
|
|
162
152
|
// Or with a custom descriptor and passphrase (MnemonicIdentity)
|
|
163
|
-
const
|
|
153
|
+
const identityWithDescriptorAndPassphrase = MnemonicIdentity.fromMnemonic(mnemonic, {
|
|
164
154
|
descriptor,
|
|
165
155
|
passphrase: 'my secret passphrase'
|
|
166
156
|
})
|
|
@@ -171,9 +161,13 @@ const identity3 = MnemonicIdentity.fromMnemonic(mnemonic, {
|
|
|
171
161
|
Create watch-only wallets from an output descriptor:
|
|
172
162
|
|
|
173
163
|
```typescript
|
|
174
|
-
import { ReadonlyDescriptorIdentity, ReadonlyWallet } from '@arkade-os/sdk'
|
|
164
|
+
import { MnemonicIdentity, ReadonlyDescriptorIdentity, ReadonlyWallet } from '@arkade-os/sdk'
|
|
165
|
+
import { generateMnemonic } from '@scure/bip39'
|
|
166
|
+
import { wordlist } from '@scure/bip39/wordlists/english.js'
|
|
175
167
|
|
|
176
168
|
// From a full identity
|
|
169
|
+
const mnemonic = generateMnemonic(wordlist)
|
|
170
|
+
const identity = MnemonicIdentity.fromMnemonic(mnemonic)
|
|
177
171
|
const readonly = await identity.toReadonly()
|
|
178
172
|
|
|
179
173
|
// Or directly from a descriptor (e.g., from another wallet)
|
|
@@ -183,7 +177,7 @@ const readonlyFromDescriptor = ReadonlyDescriptorIdentity.fromDescriptor(descrip
|
|
|
183
177
|
// Use in a watch-only wallet
|
|
184
178
|
const readonlyWallet = await ReadonlyWallet.create({
|
|
185
179
|
identity: readonly,
|
|
186
|
-
arkServerUrl: 'https://
|
|
180
|
+
arkServerUrl: 'https://arkade.computer'
|
|
187
181
|
})
|
|
188
182
|
|
|
189
183
|
// Can query but not sign
|
|
@@ -197,23 +191,59 @@ const balance = await readonlyWallet.getBalance()
|
|
|
197
191
|
|
|
198
192
|
The descriptor format (`tr([fingerprint/path']xpub.../0/0)`) is HD-ready — future versions will support deriving multiple addresses and change outputs from the same seed.
|
|
199
193
|
|
|
194
|
+
### Batch Signing for Browser Wallets
|
|
195
|
+
|
|
196
|
+
Arkade send transactions require N+1 PSBT signatures (N checkpoints + 1 main tx). With local identities like `SingleKey` or `SeedIdentity` this is invisible, but browser wallet extensions (Xverse, UniSat, OKX, etc.) show a confirmation popup per signature. The `BatchSignableIdentity` interface lets wallet providers reduce N+1 popups to a single batch confirmation.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import {
|
|
200
|
+
BatchSignableIdentity,
|
|
201
|
+
SignRequest,
|
|
202
|
+
isBatchSignable,
|
|
203
|
+
Wallet
|
|
204
|
+
} from '@arkade-os/sdk'
|
|
205
|
+
|
|
206
|
+
// Implement the interface in your wallet provider
|
|
207
|
+
class MyBrowserWallet implements BatchSignableIdentity {
|
|
208
|
+
// ... implement Identity methods (sign, signMessage, xOnlyPublicKey, etc.)
|
|
209
|
+
|
|
210
|
+
async signMultiple(requests: SignRequest[]): Promise<Transaction[]> {
|
|
211
|
+
// Convert all PSBTs to your wallet's batch signing API format
|
|
212
|
+
const psbts = requests.map(r => r.tx.toPSBT())
|
|
213
|
+
// Single wallet popup for all signatures
|
|
214
|
+
const signedPsbts = await myWalletExtension.signAllPSBTs(psbts)
|
|
215
|
+
return signedPsbts.map(psbt => Transaction.fromPSBT(psbt))
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// The SDK automatically detects batch-capable identities
|
|
220
|
+
const identity = new MyBrowserWallet()
|
|
221
|
+
console.log(isBatchSignable(identity)) // true
|
|
222
|
+
|
|
223
|
+
// Wallet.send() uses one popup instead of N+1
|
|
224
|
+
const wallet = await Wallet.create({ identity, arkServerUrl: '...' })
|
|
225
|
+
await wallet.sendBitcoin({ address: arkAddress, amount: 1000 })
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Identities without `signMultiple` continue to work unchanged — each checkpoint is signed individually via `sign()`.
|
|
229
|
+
|
|
200
230
|
### Receiving Bitcoin
|
|
201
231
|
|
|
202
232
|
```typescript
|
|
203
233
|
import { waitForIncomingFunds } from '@arkade-os/sdk'
|
|
204
234
|
|
|
205
235
|
// Get wallet addresses
|
|
206
|
-
const
|
|
236
|
+
const arkadeAddress = await wallet.getAddress()
|
|
207
237
|
const boardingAddress = await wallet.getBoardingAddress()
|
|
208
|
-
console.log('
|
|
209
|
-
console.log('Boarding Address:', boardingAddress)
|
|
238
|
+
console.log('Arkade Address:', arkadeAddress)
|
|
239
|
+
console.log('Boarding (Mainnet) Address:', boardingAddress)
|
|
210
240
|
|
|
211
241
|
const incomingFunds = await waitForIncomingFunds(wallet)
|
|
212
242
|
if (incomingFunds.type === "vtxo") {
|
|
213
|
-
// Virtual
|
|
243
|
+
// Virtual UTXOs received
|
|
214
244
|
console.log("VTXOs: ", incomingFunds.vtxos)
|
|
215
245
|
} else if (incomingFunds.type === "utxo") {
|
|
216
|
-
// Boarding
|
|
246
|
+
// Boarding UTXOs received
|
|
217
247
|
console.log("UTXOs: ", incomingFunds.coins)
|
|
218
248
|
}
|
|
219
249
|
```
|
|
@@ -225,7 +255,7 @@ Onboarding allows you to swap on-chain funds into VTXOs:
|
|
|
225
255
|
```typescript
|
|
226
256
|
import { Ramps } from '@arkade-os/sdk'
|
|
227
257
|
|
|
228
|
-
const
|
|
258
|
+
const boardingTxId = await new Ramps(wallet).onboard();
|
|
229
259
|
```
|
|
230
260
|
|
|
231
261
|
### Checking Balance
|
|
@@ -241,7 +271,7 @@ console.log('Offchain Preconfirmed:', balance.preconfirmed)
|
|
|
241
271
|
console.log('Recoverable:', balance.recoverable)
|
|
242
272
|
|
|
243
273
|
// Get virtual UTXOs (off-chain)
|
|
244
|
-
const
|
|
274
|
+
const virtualUtxos = await wallet.getVtxos()
|
|
245
275
|
|
|
246
276
|
// Get boarding UTXOs
|
|
247
277
|
const boardingUtxos = await wallet.getBoardingUtxos()
|
|
@@ -250,60 +280,73 @@ const boardingUtxos = await wallet.getBoardingUtxos()
|
|
|
250
280
|
### Sending Bitcoin
|
|
251
281
|
|
|
252
282
|
```typescript
|
|
253
|
-
// Send bitcoin
|
|
254
|
-
const txid = await wallet.
|
|
255
|
-
address: '
|
|
256
|
-
amount:
|
|
283
|
+
// Send bitcoin instantly offchain
|
|
284
|
+
const txid = await wallet.send({
|
|
285
|
+
address: 'ark1q...', // arkade address
|
|
286
|
+
amount: 50_000, // in satoshis
|
|
257
287
|
})
|
|
258
288
|
```
|
|
259
289
|
|
|
260
290
|
### Assets (Issue, Reissue, Burn, Send)
|
|
261
291
|
|
|
262
|
-
The wallet's `assetManager` lets you create and manage assets on
|
|
292
|
+
The wallet's `assetManager` lets you create and manage assets on Arkade. The `send` method supports sending assets.
|
|
263
293
|
|
|
264
294
|
```typescript
|
|
265
295
|
// Issue a new asset (non-reissuable by default)
|
|
266
|
-
const
|
|
267
|
-
amount:
|
|
268
|
-
metadata: {
|
|
296
|
+
const { assetId: controlAssetId } = await wallet.assetManager.issue({
|
|
297
|
+
amount: 1,
|
|
298
|
+
metadata: {
|
|
299
|
+
ticker: 'ctrl-MTK'
|
|
300
|
+
}
|
|
269
301
|
})
|
|
270
302
|
|
|
271
|
-
// Issue a new asset
|
|
272
|
-
const
|
|
303
|
+
// Issue a new asset referencing the control asset
|
|
304
|
+
const { assetId } = await wallet.assetManager.issue({
|
|
273
305
|
amount: 500,
|
|
274
|
-
controlAssetId
|
|
306
|
+
controlAssetId,
|
|
275
307
|
})
|
|
276
308
|
|
|
277
|
-
// Reissue more supply of the asset
|
|
278
|
-
const
|
|
279
|
-
assetId
|
|
309
|
+
// Reissue more supply of the asset (requires ownership of the control asset)
|
|
310
|
+
const reissuanceTxId = await wallet.assetManager.reissue({
|
|
311
|
+
assetId,
|
|
280
312
|
amount: 500,
|
|
281
313
|
})
|
|
282
314
|
|
|
283
315
|
// Burn some of the asset
|
|
284
|
-
const
|
|
285
|
-
assetId
|
|
316
|
+
const burnTxId = await wallet.assetManager.burn({
|
|
317
|
+
assetId,
|
|
286
318
|
amount: 200,
|
|
287
319
|
})
|
|
288
320
|
|
|
289
|
-
// Send asset to another
|
|
290
|
-
const
|
|
291
|
-
address: '
|
|
292
|
-
assets: [{ assetId
|
|
321
|
+
// Send asset to another Arkade address
|
|
322
|
+
const sendTxId = await wallet.send({
|
|
323
|
+
address: 'ark1q...',
|
|
324
|
+
assets: [{ assetId, amount: 100 }],
|
|
293
325
|
})
|
|
326
|
+
|
|
327
|
+
// Check remaining balance
|
|
328
|
+
const { assets } = await wallet.getBalance()
|
|
329
|
+
const assetBalance = assets.find(asset => asset.assetId === assetId)?.amount
|
|
294
330
|
```
|
|
295
331
|
|
|
296
|
-
### Batch
|
|
332
|
+
### Batch Settlement
|
|
297
333
|
|
|
298
|
-
|
|
334
|
+
The `settle` method can be used to move preconfirmed balances into finalized balances and to manually convert UTXOs to VTXOs.
|
|
299
335
|
|
|
300
336
|
```typescript
|
|
337
|
+
// Fetch offchain preconfirmed VTXOs and onchain boarding UTXOs
|
|
338
|
+
const [virtualUtxos, boardingUtxos] = await Promise.all([
|
|
339
|
+
wallet.getVtxos(),
|
|
340
|
+
wallet.getBoardingUtxos()
|
|
341
|
+
])
|
|
342
|
+
|
|
301
343
|
// For settling transactions
|
|
302
|
-
const
|
|
303
|
-
inputs,
|
|
344
|
+
const settlementTxId = await wallet.settle({
|
|
345
|
+
inputs: [...virtualUtxos, ...boardingUtxos],
|
|
346
|
+
// Optional: specify a mainnet output
|
|
304
347
|
outputs: [{
|
|
305
|
-
address:
|
|
306
|
-
amount:
|
|
348
|
+
address: "bc1p...",
|
|
349
|
+
amount: 100_000n
|
|
307
350
|
}]
|
|
308
351
|
})
|
|
309
352
|
```
|
|
@@ -325,9 +368,16 @@ VTXO renewal at 3 days and boarding UTXO sweep enabled.
|
|
|
325
368
|
```typescript
|
|
326
369
|
const wallet = await Wallet.create({
|
|
327
370
|
identity,
|
|
328
|
-
arkServerUrl: 'https://
|
|
329
|
-
// Enable settlement with defaults explicitly
|
|
330
|
-
settlementConfig: {
|
|
371
|
+
arkServerUrl: 'https://arkade.computer',
|
|
372
|
+
// Enable settlement with defaults explicitly:
|
|
373
|
+
settlementConfig: {
|
|
374
|
+
// Seconds before VTXO expiry to trigger renewal
|
|
375
|
+
vtxoThreshold: 259200, // 3 days
|
|
376
|
+
// Whether to sweep expired boarding UTXOs back to a fresh boarding address
|
|
377
|
+
boardingUtxoSweep: true,
|
|
378
|
+
// Polling interval in milliseconds for checking boarding UTXOs
|
|
379
|
+
pollIntervalMs: 60000 // 1 minute
|
|
380
|
+
},
|
|
331
381
|
})
|
|
332
382
|
```
|
|
333
383
|
|
|
@@ -335,7 +385,7 @@ const wallet = await Wallet.create({
|
|
|
335
385
|
// Enable both VTXO renewal and boarding UTXO sweep
|
|
336
386
|
const wallet = await Wallet.create({
|
|
337
387
|
identity,
|
|
338
|
-
arkServerUrl: 'https://
|
|
388
|
+
arkServerUrl: 'https://arkade.computer',
|
|
339
389
|
settlementConfig: {
|
|
340
390
|
vtxoThreshold: 86400, // renew when 24 hours remain (in seconds)
|
|
341
391
|
boardingUtxoSweep: true, // sweep expired boarding UTXOs
|
|
@@ -347,7 +397,7 @@ const wallet = await Wallet.create({
|
|
|
347
397
|
// Explicitly disable all settlement
|
|
348
398
|
const wallet = await Wallet.create({
|
|
349
399
|
identity,
|
|
350
|
-
arkServerUrl: 'https://
|
|
400
|
+
arkServerUrl: 'https://arkade.computer',
|
|
351
401
|
settlementConfig: false,
|
|
352
402
|
})
|
|
353
403
|
```
|
|
@@ -359,7 +409,7 @@ import { VtxoManager } from '@arkade-os/sdk'
|
|
|
359
409
|
|
|
360
410
|
const manager = new VtxoManager(
|
|
361
411
|
wallet,
|
|
362
|
-
undefined, // deprecated
|
|
412
|
+
undefined, // renewalConfig (deprecated)
|
|
363
413
|
wallet.settlementConfig // new settlementConfig
|
|
364
414
|
)
|
|
365
415
|
```
|
|
@@ -374,17 +424,15 @@ This settles expiring and recoverable VTXOs back to your wallet, refreshing thei
|
|
|
374
424
|
```typescript
|
|
375
425
|
// Renew all VTXOs to prevent expiration
|
|
376
426
|
const txid = await manager.renewVtxos()
|
|
377
|
-
console.log('Renewed:', txid)
|
|
378
|
-
|
|
379
427
|
// Check which VTXOs are expiring soon
|
|
380
428
|
const expiringVtxos = await manager.getExpiringVtxos()
|
|
381
|
-
// Override thresholdMs (e.g.,
|
|
382
|
-
const urgentlyExpiring = await manager.getExpiringVtxos(
|
|
429
|
+
// Override thresholdMs (e.g., get VTXOs expiring in the next 60 seconds)
|
|
430
|
+
const urgentlyExpiring = await manager.getExpiringVtxos(60_000)
|
|
383
431
|
```
|
|
384
432
|
|
|
385
433
|
#### Boarding UTXO Sweep
|
|
386
434
|
|
|
387
|
-
When a boarding UTXO's CSV timelock expires, it can no longer be onboarded into
|
|
435
|
+
When a boarding UTXO's CSV timelock expires, it can no longer be onboarded into Arkade cooperatively. The sweep feature detects these expired UTXOs and builds a raw on-chain transaction that spends them via the unilateral exit path back to a fresh boarding address, restarting the timelock.
|
|
388
436
|
|
|
389
437
|
- Multiple expired UTXOs are batched into a single transaction (many inputs, one output)
|
|
390
438
|
- A dust check ensures the sweep is skipped if fees would consume the entire value
|
|
@@ -422,19 +470,19 @@ const balance = await manager.getRecoverableBalance()
|
|
|
422
470
|
|
|
423
471
|
Delegation allows you to outsource VTXO renewal to a third-party delegator service. Instead of renewing VTXOs yourself, the delegator will automatically settle them before they expire, sending the funds back to your wallet address (minus a service fee). This is useful for wallets that cannot be online 24/7.
|
|
424
472
|
|
|
425
|
-
When a `delegatorProvider` is configured, the wallet address includes an extra tapscript path that authorizes the delegator to co-sign renewals alongside the
|
|
473
|
+
When a `delegatorProvider` is configured, the wallet address includes an extra tapscript path that authorizes the delegator to co-sign renewals alongside the Arkade server.
|
|
474
|
+
|
|
475
|
+
To run a delegator, you'll need to set up a [Fulmine server](https://github.com/ArkLabsHQ/fulmine) with the [Delegation API](https://github.com/ArkLabsHQ/fulmine?tab=readme-ov-file#-delegate-api) enabled.
|
|
426
476
|
|
|
427
477
|
#### Setting Up a Wallet with Delegation
|
|
428
478
|
|
|
429
479
|
```typescript
|
|
430
|
-
import { Wallet,
|
|
431
|
-
|
|
432
|
-
const identity = SingleKey.fromHex('your_private_key_hex')
|
|
480
|
+
import { Wallet, MnemonicIdentity, RestDelegatorProvider } from '@arkade-os/sdk'
|
|
433
481
|
|
|
434
482
|
const wallet = await Wallet.create({
|
|
435
|
-
identity,
|
|
436
|
-
arkServerUrl: 'https://
|
|
437
|
-
delegatorProvider: new RestDelegatorProvider('
|
|
483
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
484
|
+
arkServerUrl: 'https://arkade.computer',
|
|
485
|
+
delegatorProvider: new RestDelegatorProvider('http://localhost:7001'),
|
|
438
486
|
})
|
|
439
487
|
```
|
|
440
488
|
|
|
@@ -445,13 +493,14 @@ const wallet = await Wallet.create({
|
|
|
445
493
|
Once the wallet is configured with a delegator, use `wallet.delegatorManager` to delegate your VTXOs:
|
|
446
494
|
|
|
447
495
|
```typescript
|
|
448
|
-
// Get spendable VTXOs
|
|
496
|
+
// Get spendable VTXOs (including recoverable)
|
|
449
497
|
const vtxos = (await wallet.getVtxos({ withRecoverable: true }))
|
|
450
498
|
.filter(v => v.virtualStatus.type === 'confirmed')
|
|
451
499
|
|
|
452
500
|
// Delegate all VTXOs — the delegator will renew them before expiry
|
|
453
|
-
const
|
|
454
|
-
const
|
|
501
|
+
const arkadeAddress = await wallet.getAddress()
|
|
502
|
+
const delegatorManager = await wallet.getDelegatorManager();
|
|
503
|
+
const delegationResult = await delegatorManager.delegate(vtxos, arkadeAddress)
|
|
455
504
|
|
|
456
505
|
console.log('Delegated:', result.delegated.length)
|
|
457
506
|
console.log('Failed:', result.failed.length)
|
|
@@ -462,7 +511,7 @@ The `delegate` method groups VTXOs by expiry date and submits them to the delega
|
|
|
462
511
|
```typescript
|
|
463
512
|
// Delegate with a specific renewal time
|
|
464
513
|
const delegateAt = new Date(Date.now() + 12 * 60 * 60 * 1000) // 12 hours from now
|
|
465
|
-
await
|
|
514
|
+
await delegatorManager.delegate(vtxos, arkadeAddress, delegateAt)
|
|
466
515
|
```
|
|
467
516
|
|
|
468
517
|
#### Service Worker Integration
|
|
@@ -470,13 +519,13 @@ await wallet.delegatorManager.delegate(vtxos, myAddress, delegateAt)
|
|
|
470
519
|
When using a service worker wallet, pass the `delegatorUrl` option. The service worker will automatically delegate VTXOs after each VTXO update:
|
|
471
520
|
|
|
472
521
|
```typescript
|
|
473
|
-
import { ServiceWorkerWallet,
|
|
522
|
+
import { ServiceWorkerWallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
474
523
|
|
|
475
524
|
const wallet = await ServiceWorkerWallet.setup({
|
|
476
525
|
serviceWorkerPath: '/service-worker.js',
|
|
477
|
-
arkServerUrl: 'https://
|
|
478
|
-
identity:
|
|
479
|
-
delegatorUrl: '
|
|
526
|
+
arkServerUrl: 'https://arkade.computer',
|
|
527
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
528
|
+
delegatorUrl: 'http://localhost:7001',
|
|
480
529
|
})
|
|
481
530
|
```
|
|
482
531
|
|
|
@@ -500,9 +549,9 @@ console.log('Fee address:', info.delegatorAddress)
|
|
|
500
549
|
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.
|
|
501
550
|
|
|
502
551
|
```typescript
|
|
503
|
-
import { BIP322,
|
|
552
|
+
import { BIP322, MnemonicIdentity } from '@arkade-os/sdk'
|
|
504
553
|
|
|
505
|
-
const identity =
|
|
554
|
+
const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
506
555
|
|
|
507
556
|
// Sign a message (P2TR key-spend)
|
|
508
557
|
const signature = await BIP322.sign('Hello Bitcoin!', identity)
|
|
@@ -520,6 +569,23 @@ BIP322.verify('Hello Bitcoin!', sig, '1A1zP1...') // legacy P2PKH
|
|
|
520
569
|
```typescript
|
|
521
570
|
// Get transaction history
|
|
522
571
|
const history = await wallet.getTransactionHistory()
|
|
572
|
+
/*
|
|
573
|
+
{
|
|
574
|
+
key: {
|
|
575
|
+
boardingTxid: string;
|
|
576
|
+
commitmentTxid: string;
|
|
577
|
+
arkTxid: string;
|
|
578
|
+
};
|
|
579
|
+
type: "SENT" | "RECEIVED";
|
|
580
|
+
amount: number;
|
|
581
|
+
settled: boolean;
|
|
582
|
+
createdAt: number;
|
|
583
|
+
assets?: Array<{
|
|
584
|
+
assetId: string,
|
|
585
|
+
amount: number
|
|
586
|
+
}>
|
|
587
|
+
}
|
|
588
|
+
*/
|
|
523
589
|
```
|
|
524
590
|
|
|
525
591
|
### Offboarding
|
|
@@ -527,20 +593,25 @@ const history = await wallet.getTransactionHistory()
|
|
|
527
593
|
Collaborative exit or "offboarding" allows you to withdraw your virtual funds to an on-chain address:
|
|
528
594
|
|
|
529
595
|
```typescript
|
|
530
|
-
import { Ramps } from '@arkade-os/sdk'
|
|
596
|
+
import { Wallet, MnemonicIdentity, Ramps } from '@arkade-os/sdk'
|
|
597
|
+
|
|
598
|
+
const wallet = await Wallet.create({
|
|
599
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
600
|
+
arkServerUrl: 'https://arkade.computer'
|
|
601
|
+
})
|
|
531
602
|
|
|
532
603
|
// Get fee information from the server
|
|
533
|
-
const
|
|
604
|
+
const { fees } = await wallet.arkProvider.getInfo();
|
|
534
605
|
|
|
535
606
|
const exitTxid = await new Ramps(wallet).offboard(
|
|
536
|
-
|
|
537
|
-
|
|
607
|
+
'bc1p...',
|
|
608
|
+
fees
|
|
538
609
|
);
|
|
539
610
|
```
|
|
540
611
|
|
|
541
612
|
### Unilateral Exit
|
|
542
613
|
|
|
543
|
-
Unilateral exit allows you to withdraw your funds from the
|
|
614
|
+
Unilateral exit allows you to withdraw your funds from the Arkade protocol back to the Bitcoin blockchain without requiring cooperation from the Arkade server. This process involves two main steps:
|
|
544
615
|
|
|
545
616
|
1. **Unrolling**: Broadcasting the transaction chain from off-chain back to on-chain
|
|
546
617
|
2. **Completing the exit**: Spending the unrolled VTXOs after the timelock expires
|
|
@@ -548,10 +619,10 @@ Unilateral exit allows you to withdraw your funds from the Ark protocol back to
|
|
|
548
619
|
#### Step 1: Unrolling VTXOs
|
|
549
620
|
|
|
550
621
|
```typescript
|
|
551
|
-
import {
|
|
622
|
+
import { MnemonicIdentity, OnchainWallet, Unroll } from '@arkade-os/sdk'
|
|
552
623
|
|
|
553
624
|
// Create an identity for the onchain wallet
|
|
554
|
-
const onchainIdentity =
|
|
625
|
+
const onchainIdentity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
555
626
|
|
|
556
627
|
// Create an onchain wallet to pay for P2A outputs in VTXO branches
|
|
557
628
|
// OnchainWallet implements the AnchorBumper interface
|
|
@@ -643,15 +714,13 @@ bus.start()
|
|
|
643
714
|
|
|
644
715
|
```typescript
|
|
645
716
|
// app.ts
|
|
646
|
-
import { ServiceWorkerWallet,
|
|
647
|
-
|
|
648
|
-
const identity = SingleKey.fromHex('your_private_key_hex')
|
|
717
|
+
import { ServiceWorkerWallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
649
718
|
|
|
650
719
|
// One-liner: registers the SW, initializes the MessageBus, and creates the wallet
|
|
651
720
|
const wallet = await ServiceWorkerWallet.setup({
|
|
652
721
|
serviceWorkerPath: '/service-worker.js',
|
|
653
|
-
arkServerUrl: 'https://
|
|
654
|
-
identity,
|
|
722
|
+
arkServerUrl: 'https://arkade.computer',
|
|
723
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
655
724
|
})
|
|
656
725
|
|
|
657
726
|
// Use like any other wallet — calls are proxied to the service worker
|
|
@@ -677,12 +746,9 @@ differ in orchestration and communication.
|
|
|
677
746
|
See the platform READMEs for architecture details, runtime flow, and usage
|
|
678
747
|
examples.
|
|
679
748
|
|
|
680
|
-
|
|
681
|
-
|
|
682
749
|
### Repositories (Storage)
|
|
683
750
|
|
|
684
|
-
The `StorageAdapter` API is deprecated. Use repositories instead. If you omit
|
|
685
|
-
`storage`, the SDK uses IndexedDB repositories with the default database name.
|
|
751
|
+
The `StorageAdapter` API is deprecated. Use repositories instead. If you omit `storage`, the SDK uses IndexedDB repositories with the default database name.
|
|
686
752
|
|
|
687
753
|
#### Migration from v1 StorageAdapter
|
|
688
754
|
|
|
@@ -778,7 +844,7 @@ See [examples/node/multiple-wallets.ts](examples/node/multiple-wallets.ts) for
|
|
|
778
844
|
a full working example using `better-sqlite3`.
|
|
779
845
|
|
|
780
846
|
```typescript
|
|
781
|
-
import {
|
|
847
|
+
import { MnemonicIdentity, Wallet } from '@arkade-os/sdk'
|
|
782
848
|
import { SQLiteWalletRepository, SQLiteContractRepository, SQLExecutor } from '@arkade-os/sdk/repositories/sqlite'
|
|
783
849
|
import Database from 'better-sqlite3'
|
|
784
850
|
|
|
@@ -792,8 +858,8 @@ const executor: SQLExecutor = {
|
|
|
792
858
|
}
|
|
793
859
|
|
|
794
860
|
const wallet = await Wallet.create({
|
|
795
|
-
identity:
|
|
796
|
-
arkServerUrl: 'https://
|
|
861
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
862
|
+
arkServerUrl: 'https://arkade.computer',
|
|
797
863
|
storage: {
|
|
798
864
|
walletRepository: new SQLiteWalletRepository(executor),
|
|
799
865
|
contractRepository: new SQLiteContractRepository(executor),
|
|
@@ -811,7 +877,7 @@ import { RealmWalletRepository, RealmContractRepository, ArkRealmSchemas } from
|
|
|
811
877
|
const realm = await Realm.open({ schema: [...ArkRealmSchemas, ...yourSchemas] })
|
|
812
878
|
const wallet = await Wallet.create({
|
|
813
879
|
identity,
|
|
814
|
-
arkServerUrl: 'https://
|
|
880
|
+
arkServerUrl: 'https://arkade.computer',
|
|
815
881
|
storage: {
|
|
816
882
|
walletRepository: new RealmWalletRepository(realm),
|
|
817
883
|
contractRepository: new RealmContractRepository(realm),
|
|
@@ -825,11 +891,11 @@ In the browser, the SDK defaults to IndexedDB repositories when no `storage`
|
|
|
825
891
|
is provided:
|
|
826
892
|
|
|
827
893
|
```typescript
|
|
828
|
-
import {
|
|
894
|
+
import { MnemonicIdentity, Wallet } from '@arkade-os/sdk'
|
|
829
895
|
|
|
830
896
|
const wallet = await Wallet.create({
|
|
831
|
-
identity:
|
|
832
|
-
arkServerUrl: 'https://
|
|
897
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
898
|
+
arkServerUrl: 'https://arkade.computer',
|
|
833
899
|
// Uses IndexedDB by default in the browser
|
|
834
900
|
})
|
|
835
901
|
```
|
|
@@ -841,14 +907,15 @@ For ephemeral storage (no persistence), pass the in-memory repositories:
|
|
|
841
907
|
|
|
842
908
|
```typescript
|
|
843
909
|
import {
|
|
910
|
+
MnemonicIdentity,
|
|
911
|
+
Wallet,
|
|
844
912
|
InMemoryWalletRepository,
|
|
845
|
-
InMemoryContractRepository
|
|
846
|
-
Wallet
|
|
913
|
+
InMemoryContractRepository
|
|
847
914
|
} from '@arkade-os/sdk'
|
|
848
915
|
|
|
849
916
|
const wallet = await Wallet.create({
|
|
850
|
-
identity,
|
|
851
|
-
arkServerUrl: 'https://
|
|
917
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
918
|
+
arkServerUrl: 'https://arkade.computer',
|
|
852
919
|
storage: {
|
|
853
920
|
walletRepository: new InMemoryWalletRepository(),
|
|
854
921
|
contractRepository: new InMemoryContractRepository()
|
|
@@ -869,7 +936,7 @@ import { EventSource } from "eventsource";
|
|
|
869
936
|
(globalThis as any).EventSource = EventSource;
|
|
870
937
|
|
|
871
938
|
// Use dynamic import so the polyfill is set before the SDK evaluates
|
|
872
|
-
const { Wallet
|
|
939
|
+
const { Wallet } = await import("@arkade-os/sdk");
|
|
873
940
|
```
|
|
874
941
|
|
|
875
942
|
If you also need IndexedDB persistence (e.g. for `WalletRepository`), set up the shim before any SDK import:
|
|
@@ -895,16 +962,15 @@ See [`examples/node/multiple-wallets.ts`](examples/node/multiple-wallets.ts) for
|
|
|
895
962
|
For React Native and Expo applications where standard EventSource and fetch streaming may not work properly, use the Expo-compatible providers:
|
|
896
963
|
|
|
897
964
|
```typescript
|
|
898
|
-
import { Wallet,
|
|
965
|
+
import { Wallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
899
966
|
import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo'
|
|
900
967
|
|
|
901
|
-
const identity =
|
|
968
|
+
const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
902
969
|
|
|
903
970
|
const wallet = await Wallet.create({
|
|
904
971
|
identity: identity,
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
indexerProvider: new ExpoIndexerProvider('https://mutinynet.arkade.sh'), // For address subscriptions and VTXO updates
|
|
972
|
+
arkProvider: new ExpoArkProvider('https://arkade.computer'), // For settlement events and transactions streaming
|
|
973
|
+
indexerProvider: new ExpoIndexerProvider('https://arkade.computer'), // For address subscriptions and VTXO updates
|
|
908
974
|
})
|
|
909
975
|
|
|
910
976
|
// use expo/fetch for streaming support (SSE)
|
|
@@ -933,9 +999,9 @@ const executor = {
|
|
|
933
999
|
|
|
934
1000
|
const wallet = await Wallet.create({
|
|
935
1001
|
identity,
|
|
936
|
-
arkServerUrl: 'https://
|
|
937
|
-
arkProvider: new ExpoArkProvider('https://
|
|
938
|
-
indexerProvider: new ExpoIndexerProvider('https://
|
|
1002
|
+
arkServerUrl: 'https://arkade.computer',
|
|
1003
|
+
arkProvider: new ExpoArkProvider('https://arkade.computer'),
|
|
1004
|
+
indexerProvider: new ExpoIndexerProvider('https://arkade.computer'),
|
|
939
1005
|
storage: {
|
|
940
1006
|
walletRepository: new SQLiteWalletRepository(executor),
|
|
941
1007
|
contractRepository: new SQLiteContractRepository(executor),
|
|
@@ -958,7 +1024,7 @@ if (!global.crypto) global.crypto = {} as any;
|
|
|
958
1024
|
global.crypto.getRandomValues = Crypto.getRandomValues;
|
|
959
1025
|
|
|
960
1026
|
// Now import the SDK
|
|
961
|
-
import { Wallet,
|
|
1027
|
+
import { Wallet, MnemonicIdentity } from '@arkade-os/sdk';
|
|
962
1028
|
import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo';
|
|
963
1029
|
```
|
|
964
1030
|
|
|
@@ -1069,7 +1135,7 @@ await wallet.contractRepository.saveToContractCollection(
|
|
|
1069
1135
|
const swaps = await wallet.contractRepository.getContractCollection('swaps')
|
|
1070
1136
|
```
|
|
1071
1137
|
|
|
1072
|
-
_For complete API documentation, visit our [
|
|
1138
|
+
_For complete API documentation, visit our [TypeDoc documentation](https://arkade-os.github.io/ts-sdk/)._
|
|
1073
1139
|
|
|
1074
1140
|
## Development
|
|
1075
1141
|
|