@arkade-os/sdk 0.4.14 → 0.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -215
- package/dist/cjs/arkfee/estimator.js +1 -1
- package/dist/cjs/arkfee/types.js +2 -1
- package/dist/cjs/arknote/index.js +43 -4
- package/dist/cjs/bip322/index.js +1 -1
- package/dist/cjs/contracts/arkcontract.js +1 -1
- package/dist/cjs/contracts/contractManager.js +40 -24
- package/dist/cjs/contracts/contractWatcher.js +29 -22
- package/dist/cjs/contracts/handlers/default.js +1 -1
- package/dist/cjs/contracts/handlers/delegate.js +1 -1
- package/dist/cjs/contracts/handlers/helpers.js +25 -1
- package/dist/cjs/contracts/handlers/vhtlc.js +2 -4
- package/dist/cjs/extension/asset/assetGroup.js +92 -5
- package/dist/cjs/extension/asset/assetId.js +67 -3
- package/dist/cjs/extension/asset/assetInput.js +18 -0
- package/dist/cjs/extension/asset/assetOutput.js +15 -0
- package/dist/cjs/extension/asset/assetRef.js +66 -0
- package/dist/cjs/extension/asset/metadata.js +15 -0
- package/dist/cjs/extension/asset/packet.js +4 -1
- package/dist/cjs/extension/index.js +1 -1
- package/dist/cjs/forfeit.js +14 -0
- package/dist/cjs/identity/index.js +6 -0
- package/dist/cjs/identity/seedIdentity.js +5 -5
- package/dist/cjs/identity/singleKey.js +4 -0
- package/dist/cjs/index.js +5 -3
- package/dist/cjs/intent/index.js +28 -12
- package/dist/cjs/providers/ark.js +3 -2
- package/dist/cjs/providers/delegator.js +20 -1
- package/dist/cjs/providers/expoArk.js +2 -2
- package/dist/cjs/providers/indexer.js +2 -2
- package/dist/cjs/providers/onchain.js +2 -1
- package/dist/cjs/repositories/realm/schemas.js +2 -2
- package/dist/cjs/repositories/realm/types.js +1 -1
- package/dist/cjs/script/address.js +37 -6
- package/dist/cjs/script/base.js +70 -1
- package/dist/cjs/script/default.js +3 -0
- package/dist/cjs/script/delegate.js +4 -0
- package/dist/cjs/script/tapscript.js +25 -4
- package/dist/cjs/script/vhtlc.js +35 -27
- package/dist/cjs/storage/fileSystem.js +1 -1
- package/dist/cjs/storage/inMemory.js +1 -1
- package/dist/cjs/storage/indexedDB.js +1 -1
- package/dist/cjs/storage/localStorage.js +1 -1
- package/dist/cjs/tree/validation.js +1 -1
- package/dist/cjs/utils/arkTransaction.js +5 -5
- package/dist/cjs/utils/bip21.js +16 -3
- package/dist/cjs/utils/syncCursors.js +4 -4
- package/dist/cjs/utils/transaction.js +1 -1
- package/dist/cjs/utils/transactionHistory.js +11 -11
- package/dist/cjs/utils/unknownFields.js +3 -3
- package/dist/cjs/wallet/asset-manager.js +4 -4
- package/dist/cjs/wallet/batch.js +5 -5
- package/dist/cjs/wallet/delegator.js +9 -8
- package/dist/cjs/wallet/expo/background.js +3 -3
- package/dist/cjs/wallet/expo/wallet.js +7 -7
- package/dist/cjs/wallet/index.js +43 -0
- package/dist/cjs/wallet/onchain.js +43 -5
- package/dist/cjs/wallet/ramps.js +44 -14
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +22 -22
- package/dist/cjs/wallet/serviceWorker/wallet.js +28 -24
- package/dist/cjs/wallet/unroll.js +12 -8
- package/dist/cjs/wallet/utils.js +1 -1
- package/dist/cjs/wallet/vtxo-manager.js +123 -82
- package/dist/cjs/wallet/wallet.js +231 -98
- package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +1 -1
- package/dist/cjs/worker/expo/processors/contractPollProcessor.js +2 -2
- package/dist/cjs/worker/expo/taskRunner.js +3 -3
- package/dist/cjs/worker/messageBus.js +3 -0
- package/dist/esm/arkfee/estimator.js +1 -1
- package/dist/esm/arkfee/types.js +2 -1
- package/dist/esm/arknote/index.js +43 -4
- package/dist/esm/bip322/index.js +1 -1
- package/dist/esm/contracts/arkcontract.js +1 -1
- package/dist/esm/contracts/contractManager.js +40 -24
- package/dist/esm/contracts/contractWatcher.js +29 -22
- package/dist/esm/contracts/handlers/default.js +1 -1
- package/dist/esm/contracts/handlers/delegate.js +1 -1
- package/dist/esm/contracts/handlers/helpers.js +24 -1
- package/dist/esm/contracts/handlers/vhtlc.js +3 -5
- package/dist/esm/extension/asset/assetGroup.js +92 -5
- package/dist/esm/extension/asset/assetId.js +67 -3
- package/dist/esm/extension/asset/assetInput.js +18 -0
- package/dist/esm/extension/asset/assetOutput.js +15 -0
- package/dist/esm/extension/asset/assetRef.js +66 -0
- package/dist/esm/extension/asset/metadata.js +15 -0
- package/dist/esm/extension/asset/packet.js +4 -1
- package/dist/esm/extension/index.js +1 -1
- package/dist/esm/forfeit.js +14 -0
- package/dist/esm/identity/index.js +5 -0
- package/dist/esm/identity/seedIdentity.js +5 -5
- package/dist/esm/identity/singleKey.js +4 -0
- package/dist/esm/index.js +3 -2
- package/dist/esm/intent/index.js +28 -12
- package/dist/esm/providers/ark.js +3 -2
- package/dist/esm/providers/delegator.js +20 -1
- package/dist/esm/providers/expoArk.js +2 -2
- package/dist/esm/providers/indexer.js +2 -2
- package/dist/esm/providers/onchain.js +2 -1
- package/dist/esm/repositories/realm/schemas.js +2 -2
- package/dist/esm/repositories/realm/types.js +1 -1
- package/dist/esm/script/address.js +37 -6
- package/dist/esm/script/base.js +70 -1
- package/dist/esm/script/default.js +3 -0
- package/dist/esm/script/delegate.js +4 -0
- package/dist/esm/script/tapscript.js +25 -4
- package/dist/esm/script/vhtlc.js +35 -27
- package/dist/esm/storage/fileSystem.js +1 -1
- package/dist/esm/storage/inMemory.js +1 -1
- package/dist/esm/storage/indexedDB.js +1 -1
- package/dist/esm/storage/localStorage.js +1 -1
- package/dist/esm/tree/validation.js +1 -1
- package/dist/esm/utils/arkTransaction.js +5 -5
- package/dist/esm/utils/bip21.js +16 -3
- package/dist/esm/utils/syncCursors.js +4 -4
- package/dist/esm/utils/transaction.js +1 -1
- package/dist/esm/utils/transactionHistory.js +11 -11
- package/dist/esm/utils/unknownFields.js +3 -3
- package/dist/esm/wallet/asset-manager.js +4 -4
- package/dist/esm/wallet/batch.js +5 -5
- package/dist/esm/wallet/delegator.js +9 -8
- package/dist/esm/wallet/expo/background.js +3 -3
- package/dist/esm/wallet/expo/wallet.js +7 -7
- package/dist/esm/wallet/index.js +43 -0
- package/dist/esm/wallet/onchain.js +43 -5
- package/dist/esm/wallet/ramps.js +44 -14
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +22 -22
- package/dist/esm/wallet/serviceWorker/wallet.js +28 -24
- package/dist/esm/wallet/unroll.js +12 -8
- package/dist/esm/wallet/utils.js +1 -1
- package/dist/esm/wallet/vtxo-manager.js +122 -81
- package/dist/esm/wallet/wallet.js +232 -99
- package/dist/esm/worker/expo/asyncStorageTaskQueue.js +1 -1
- package/dist/esm/worker/expo/processors/contractPollProcessor.js +2 -2
- package/dist/esm/worker/expo/taskRunner.js +3 -3
- package/dist/esm/worker/messageBus.js +3 -0
- package/dist/types/arkfee/estimator.d.ts +1 -1
- package/dist/types/arkfee/types.d.ts +2 -1
- package/dist/types/arknote/index.d.ts +44 -4
- package/dist/types/bip322/index.d.ts +1 -1
- package/dist/types/contracts/arkcontract.d.ts +1 -1
- package/dist/types/contracts/contractManager.d.ts +40 -63
- package/dist/types/contracts/contractWatcher.d.ts +39 -18
- package/dist/types/contracts/handlers/default.d.ts +1 -1
- package/dist/types/contracts/handlers/delegate.d.ts +1 -1
- package/dist/types/contracts/handlers/helpers.d.ts +11 -1
- package/dist/types/contracts/types.d.ts +36 -26
- package/dist/types/extension/asset/assetGroup.d.ts +92 -1
- package/dist/types/extension/asset/assetId.d.ts +67 -3
- package/dist/types/extension/asset/assetInput.d.ts +18 -0
- package/dist/types/extension/asset/assetOutput.d.ts +15 -0
- package/dist/types/extension/asset/assetRef.d.ts +66 -0
- package/dist/types/extension/asset/metadata.d.ts +15 -0
- package/dist/types/extension/asset/packet.d.ts +4 -1
- package/dist/types/extension/index.d.ts +1 -1
- package/dist/types/forfeit.d.ts +14 -0
- package/dist/types/identity/index.d.ts +36 -0
- package/dist/types/identity/seedIdentity.d.ts +10 -8
- package/dist/types/identity/singleKey.d.ts +4 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/intent/index.d.ts +19 -6
- package/dist/types/providers/ark.d.ts +40 -2
- package/dist/types/providers/delegator.d.ts +54 -1
- package/dist/types/providers/expoArk.d.ts +2 -2
- package/dist/types/providers/indexer.d.ts +105 -2
- package/dist/types/providers/onchain.d.ts +62 -1
- package/dist/types/repositories/realm/schemas.d.ts +2 -2
- package/dist/types/repositories/realm/types.d.ts +2 -2
- package/dist/types/repositories/walletRepository.d.ts +16 -0
- package/dist/types/script/address.d.ts +35 -2
- package/dist/types/script/base.d.ts +66 -1
- package/dist/types/script/default.d.ts +3 -0
- package/dist/types/script/delegate.d.ts +4 -0
- package/dist/types/script/tapscript.d.ts +17 -2
- package/dist/types/script/vhtlc.d.ts +35 -27
- package/dist/types/storage/fileSystem.d.ts +1 -1
- package/dist/types/storage/inMemory.d.ts +1 -1
- package/dist/types/storage/index.d.ts +1 -1
- package/dist/types/storage/indexedDB.d.ts +1 -1
- package/dist/types/storage/localStorage.d.ts +1 -1
- package/dist/types/utils/arkTransaction.d.ts +3 -3
- package/dist/types/utils/bip21.d.ts +17 -0
- package/dist/types/utils/syncCursors.d.ts +4 -4
- package/dist/types/utils/transaction.d.ts +1 -1
- package/dist/types/utils/transactionHistory.d.ts +3 -3
- package/dist/types/utils/unknownFields.d.ts +5 -5
- package/dist/types/wallet/asset-manager.d.ts +3 -3
- package/dist/types/wallet/batch.d.ts +27 -7
- package/dist/types/wallet/delegator.d.ts +10 -0
- package/dist/types/wallet/expo/background.d.ts +4 -4
- package/dist/types/wallet/expo/wallet.d.ts +10 -10
- package/dist/types/wallet/index.d.ts +457 -25
- package/dist/types/wallet/onchain.d.ts +42 -4
- package/dist/types/wallet/ramps.d.ts +40 -10
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +4 -4
- package/dist/types/wallet/serviceWorker/wallet.d.ts +71 -33
- package/dist/types/wallet/unroll.d.ts +8 -6
- package/dist/types/wallet/vtxo-manager.d.ts +146 -93
- package/dist/types/wallet/wallet.d.ts +91 -33
- package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +1 -1
- package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +1 -1
- package/dist/types/worker/expo/taskRunner.d.ts +6 -6
- package/dist/types/worker/messageBus.d.ts +5 -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
|
-
import { ReadonlySingleKey, ReadonlyWallet } from '@arkade-os/sdk'
|
|
48
|
+
import { SingleKey, 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,35 +191,71 @@ 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: 'https://arkade.computer' })
|
|
225
|
+
await wallet.send({ address: 'ark1q...', 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
|
|
214
|
-
console.log("VTXOs: ", incomingFunds.
|
|
243
|
+
// Virtual outputs received
|
|
244
|
+
console.log("VTXOs: ", incomingFunds.newVtxos)
|
|
215
245
|
} else if (incomingFunds.type === "utxo") {
|
|
216
|
-
// Boarding
|
|
246
|
+
// Boarding inputs received
|
|
217
247
|
console.log("UTXOs: ", incomingFunds.coins)
|
|
218
248
|
}
|
|
219
249
|
```
|
|
220
250
|
|
|
221
251
|
### Onboarding
|
|
222
252
|
|
|
223
|
-
Onboarding allows you to swap
|
|
253
|
+
Onboarding allows you to swap onchain funds into virtual outputs:
|
|
224
254
|
|
|
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
|
|
@@ -240,105 +270,130 @@ console.log('Offchain Settled:', balance.settled)
|
|
|
240
270
|
console.log('Offchain Preconfirmed:', balance.preconfirmed)
|
|
241
271
|
console.log('Recoverable:', balance.recoverable)
|
|
242
272
|
|
|
243
|
-
// Get virtual
|
|
244
|
-
const
|
|
273
|
+
// Get virtual outputs (available for offchain spending)
|
|
274
|
+
const vtxos = await wallet.getVtxos()
|
|
245
275
|
|
|
246
|
-
// Get boarding
|
|
247
|
-
const
|
|
276
|
+
// Get boarding inputs
|
|
277
|
+
const boardingInputs = await wallet.getBoardingUtxos()
|
|
248
278
|
```
|
|
249
279
|
|
|
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,
|
|
307
|
+
metadata: {
|
|
308
|
+
ticker: 'MTK'
|
|
309
|
+
}
|
|
275
310
|
})
|
|
276
311
|
|
|
277
|
-
// Reissue more supply of the asset
|
|
278
|
-
const
|
|
279
|
-
assetId
|
|
312
|
+
// Reissue more supply of the asset (requires ownership of the control asset)
|
|
313
|
+
const reissuanceTxId = await wallet.assetManager.reissue({
|
|
314
|
+
assetId,
|
|
280
315
|
amount: 500,
|
|
281
316
|
})
|
|
282
317
|
|
|
283
318
|
// Burn some of the asset
|
|
284
|
-
const
|
|
285
|
-
assetId
|
|
319
|
+
const burnTxId = await wallet.assetManager.burn({
|
|
320
|
+
assetId,
|
|
286
321
|
amount: 200,
|
|
287
322
|
})
|
|
288
323
|
|
|
289
|
-
// Send asset to another
|
|
290
|
-
const
|
|
291
|
-
address: '
|
|
292
|
-
assets: [{ assetId
|
|
324
|
+
// Send asset to another Arkade address
|
|
325
|
+
const sendTxId = await wallet.send({
|
|
326
|
+
address: 'ark1q...',
|
|
327
|
+
assets: [{ assetId, amount: 100 }],
|
|
293
328
|
})
|
|
329
|
+
|
|
330
|
+
// Check remaining balance
|
|
331
|
+
const { assets } = await wallet.getBalance()
|
|
332
|
+
const assetBalance = assets.find(asset => asset.assetId === assetId)?.amount
|
|
294
333
|
```
|
|
295
334
|
|
|
296
|
-
### Batch
|
|
335
|
+
### Batch Settlement
|
|
297
336
|
|
|
298
|
-
|
|
337
|
+
The `settle` method can be used to move preconfirmed balances into finalized balances and to manually convert onchain funds to virtual outputs.
|
|
299
338
|
|
|
300
339
|
```typescript
|
|
340
|
+
// Fetch offchain preconfirmed outputs and onchain boarding inputs
|
|
341
|
+
const [vtxos, boardingInputs] = await Promise.all([
|
|
342
|
+
wallet.getVtxos(),
|
|
343
|
+
wallet.getBoardingUtxos()
|
|
344
|
+
])
|
|
345
|
+
|
|
301
346
|
// For settling transactions
|
|
302
|
-
const
|
|
303
|
-
inputs,
|
|
347
|
+
const settlementTxId = await wallet.settle({
|
|
348
|
+
inputs: [...vtxos, ...boardingInputs],
|
|
349
|
+
// Optional: specify a mainnet output
|
|
304
350
|
outputs: [{
|
|
305
|
-
address:
|
|
306
|
-
amount:
|
|
351
|
+
address: "bc1p...",
|
|
352
|
+
amount: 100_000n
|
|
307
353
|
}]
|
|
308
354
|
})
|
|
309
355
|
```
|
|
310
356
|
|
|
311
|
-
###
|
|
357
|
+
### Virtual Output Management (Renewal & Recovery)
|
|
312
358
|
|
|
313
|
-
|
|
359
|
+
Virtual outputs have an expiration time (batch expiry).
|
|
314
360
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
- **
|
|
361
|
+
The SDK provides the `VtxoManager` class to handle:
|
|
362
|
+
|
|
363
|
+
- **Renewal**: Renew virtual outputs before they expire to maintain unilateral control of the funds.
|
|
364
|
+
- **Recovery**: Reclaim swept or expired virtual outputs back to the wallet in case renewal window was missed.
|
|
365
|
+
- **Boarding Input Sweep**: Sweep expired boarding inputs back to a fresh boarding address to restart the timelock.
|
|
318
366
|
|
|
319
367
|
#### Settlement Configuration
|
|
320
368
|
|
|
321
369
|
The recommended way to configure `VtxoManager` is via `settlementConfig` on the wallet.
|
|
322
370
|
If you omit `settlementConfig`, settlement is enabled with the default behavior:
|
|
323
|
-
|
|
371
|
+
Virtual output renewal at 3 days and boarding input sweep enabled.
|
|
324
372
|
|
|
325
373
|
```typescript
|
|
326
374
|
const wallet = await Wallet.create({
|
|
327
375
|
identity,
|
|
328
|
-
arkServerUrl: 'https://
|
|
329
|
-
// Enable settlement with defaults explicitly
|
|
330
|
-
settlementConfig: {
|
|
376
|
+
arkServerUrl: 'https://arkade.computer',
|
|
377
|
+
// Enable settlement with defaults explicitly:
|
|
378
|
+
settlementConfig: {
|
|
379
|
+
// Seconds before virtual output expiry to trigger renewal
|
|
380
|
+
vtxoThreshold: 60 * 60 * 24 * 3, // 3 days
|
|
381
|
+
// Whether to sweep expired boarding inputs back to a fresh boarding address
|
|
382
|
+
boardingUtxoSweep: true,
|
|
383
|
+
// Polling interval in milliseconds for checking boarding inputs
|
|
384
|
+
pollIntervalMs: 60_000 // 1 minute
|
|
385
|
+
},
|
|
331
386
|
})
|
|
332
387
|
```
|
|
333
388
|
|
|
334
389
|
```typescript
|
|
335
|
-
// Enable both
|
|
390
|
+
// Enable both virtual output renewal and boarding input sweep
|
|
336
391
|
const wallet = await Wallet.create({
|
|
337
392
|
identity,
|
|
338
|
-
arkServerUrl: 'https://
|
|
393
|
+
arkServerUrl: 'https://arkade.computer',
|
|
339
394
|
settlementConfig: {
|
|
340
|
-
vtxoThreshold:
|
|
341
|
-
boardingUtxoSweep: true,
|
|
395
|
+
vtxoThreshold: 60 * 60 * 24, // renew when 24 hours remain (in seconds)
|
|
396
|
+
boardingUtxoSweep: true, // sweep expired boarding inputs
|
|
342
397
|
},
|
|
343
398
|
})
|
|
344
399
|
```
|
|
@@ -347,68 +402,60 @@ const wallet = await Wallet.create({
|
|
|
347
402
|
// Explicitly disable all settlement
|
|
348
403
|
const wallet = await Wallet.create({
|
|
349
404
|
identity,
|
|
350
|
-
arkServerUrl: 'https://
|
|
405
|
+
arkServerUrl: 'https://arkade.computer',
|
|
351
406
|
settlementConfig: false,
|
|
352
407
|
})
|
|
353
408
|
```
|
|
354
409
|
|
|
355
|
-
|
|
410
|
+
Access the `VtxoManager` from the wallet after configuring `settlementConfig`:
|
|
356
411
|
|
|
357
412
|
```typescript
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const manager = new VtxoManager(
|
|
361
|
-
wallet,
|
|
362
|
-
undefined, // deprecated renewalConfig
|
|
363
|
-
wallet.settlementConfig // new settlementConfig
|
|
364
|
-
)
|
|
413
|
+
const manager = await wallet.getVtxoManager()
|
|
365
414
|
```
|
|
366
415
|
|
|
367
|
-
> **Migration from `renewalConfig`:**
|
|
416
|
+
> **Migration from `renewalConfig`:** Directly initializing a `VtxoManager` with `renewalConfig` is still supported but deprecated. Prefer `settlementConfig` where `vtxoThreshold` is expressed in **seconds** instead of milliseconds.
|
|
368
417
|
|
|
369
418
|
#### Renewal: Prevent Expiration
|
|
370
419
|
|
|
371
|
-
Renew
|
|
372
|
-
This settles expiring and recoverable
|
|
420
|
+
Renew virtual outputs before they expire to retain unilateral control of funds.
|
|
421
|
+
This settles expiring and recoverable virtual outputs back to your wallet, refreshing their expiration time.
|
|
373
422
|
|
|
374
423
|
```typescript
|
|
375
|
-
// Renew all
|
|
424
|
+
// Renew all virtual outputs to prevent expiration
|
|
376
425
|
const txid = await manager.renewVtxos()
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
// Check which VTXOs are expiring soon
|
|
426
|
+
// Check which virtual outputs are expiring soon
|
|
380
427
|
const expiringVtxos = await manager.getExpiringVtxos()
|
|
381
|
-
// Override thresholdMs (e.g.,
|
|
382
|
-
const urgentlyExpiring = await manager.getExpiringVtxos(
|
|
428
|
+
// Override thresholdMs (e.g., get virtual outputs expiring in the next 60 seconds)
|
|
429
|
+
const urgentlyExpiring = await manager.getExpiringVtxos(60_000)
|
|
383
430
|
```
|
|
384
431
|
|
|
385
|
-
#### Boarding
|
|
432
|
+
#### Boarding Input Sweep
|
|
386
433
|
|
|
387
|
-
When a boarding
|
|
434
|
+
When a boarding input's CSV timelock expires, it can no longer be onboarded into Arkade cooperatively. The sweep feature detects these expired UTXOs and builds a raw onchain transaction that spends them via the unilateral exit path back to a fresh boarding address, restarting the timelock.
|
|
388
435
|
|
|
389
436
|
- Multiple expired UTXOs are batched into a single transaction (many inputs, one output)
|
|
390
437
|
- A dust check ensures the sweep is skipped if fees would consume the entire value
|
|
391
438
|
|
|
392
439
|
```typescript
|
|
393
|
-
// Check for expired boarding
|
|
440
|
+
// Check for expired boarding inputs
|
|
394
441
|
const expired = await manager.getExpiredBoardingUtxos()
|
|
395
|
-
console.log(`${expired.length} expired boarding
|
|
442
|
+
console.log(`${expired.length} expired boarding inputs`)
|
|
396
443
|
|
|
397
444
|
// Sweep them back to a fresh boarding address (requires boardingUtxoSweep: true)
|
|
398
445
|
try {
|
|
399
446
|
const txid = await manager.sweepExpiredBoardingUtxos()
|
|
400
|
-
console.log('Swept expired boarding
|
|
447
|
+
console.log('Swept expired boarding inputs:', txid)
|
|
401
448
|
} catch (e) {
|
|
402
|
-
// "No expired boarding
|
|
449
|
+
// "No expired boarding inputs to sweep" or "Sweep not economical"
|
|
403
450
|
}
|
|
404
451
|
```
|
|
405
452
|
|
|
406
453
|
#### Recovery: Reclaim Swept VTXOs
|
|
407
454
|
|
|
408
|
-
Recover
|
|
455
|
+
Recover virtual outputs that have been swept by the server or consolidate small amounts (subdust).
|
|
409
456
|
|
|
410
457
|
```typescript
|
|
411
|
-
// Recover swept
|
|
458
|
+
// Recover swept virtual outputs and preconfirmed subdust
|
|
412
459
|
const txid = await manager.recoverVtxos((event) => {
|
|
413
460
|
console.log('Settlement event:', event.type)
|
|
414
461
|
})
|
|
@@ -418,71 +465,79 @@ const balance = await manager.getRecoverableBalance()
|
|
|
418
465
|
```
|
|
419
466
|
|
|
420
467
|
|
|
421
|
-
###
|
|
468
|
+
### Delegation
|
|
469
|
+
|
|
470
|
+
Delegation allows users to outsource virtual output renewal to a third-party delegation service.
|
|
471
|
+
|
|
472
|
+
Instead of the delegating user renewing virtual outputs by themself, their delegate will automatically settle them before they expire, sending the funds back to the delegator's wallet address (minus a service fee).
|
|
473
|
+
|
|
474
|
+
This is useful for wallets that cannot be online 24/7.
|
|
422
475
|
|
|
423
|
-
|
|
476
|
+
When a `delegatorProvider` is configured, the wallet address includes an extra tapscript path that authorizes the delegate to co-sign renewals alongside the Arkade server.
|
|
424
477
|
|
|
425
|
-
|
|
478
|
+
To run a delegation service, 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
479
|
|
|
427
480
|
#### Setting Up a Wallet with Delegation
|
|
428
481
|
|
|
429
482
|
```typescript
|
|
430
|
-
import { Wallet,
|
|
431
|
-
|
|
432
|
-
const identity = SingleKey.fromHex('your_private_key_hex')
|
|
483
|
+
import { Wallet, MnemonicIdentity, RestDelegatorProvider } from '@arkade-os/sdk'
|
|
433
484
|
|
|
434
485
|
const wallet = await Wallet.create({
|
|
435
|
-
identity,
|
|
436
|
-
arkServerUrl: 'https://
|
|
437
|
-
delegatorProvider: new RestDelegatorProvider('
|
|
486
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
487
|
+
arkServerUrl: 'https://arkade.computer',
|
|
488
|
+
delegatorProvider: new RestDelegatorProvider('http://localhost:7001'),
|
|
438
489
|
})
|
|
439
490
|
```
|
|
440
491
|
|
|
441
492
|
> **Note:** Adding a `delegatorProvider` changes your wallet address because the offchain tapscript includes an additional delegation path. Funds sent to an address without delegation cannot be delegated, and vice versa.
|
|
442
493
|
|
|
443
|
-
#### Delegating
|
|
494
|
+
#### Delegating Virtual Outputs
|
|
444
495
|
|
|
445
|
-
Once the wallet is configured with a
|
|
496
|
+
Once the wallet is configured with a delegate, use `wallet.delegatorManager` to delegate your virtual outputs:
|
|
446
497
|
|
|
447
498
|
```typescript
|
|
448
|
-
// Get spendable
|
|
449
|
-
const vtxos =
|
|
450
|
-
.filter(v => v.virtualStatus.type === 'confirmed')
|
|
499
|
+
// Get spendable virtual outputs (including recoverable)
|
|
500
|
+
const vtxos = await wallet.getVtxos({ withRecoverable: true })
|
|
451
501
|
|
|
452
|
-
// Delegate all
|
|
453
|
-
const
|
|
454
|
-
const
|
|
502
|
+
// Delegate all virtual outputs — the delegate will renew them before expiry
|
|
503
|
+
const arkadeAddress = await wallet.getAddress()
|
|
504
|
+
const delegatorManager = await wallet.getDelegatorManager();
|
|
505
|
+
const delegationResult = await delegatorManager.delegate(vtxos, arkadeAddress)
|
|
455
506
|
|
|
456
|
-
console.log('Delegated:',
|
|
457
|
-
console.log('Failed:',
|
|
507
|
+
console.log('Delegated:', delegationResult.delegated.length)
|
|
508
|
+
console.log('Failed:', delegationResult.failed.length)
|
|
458
509
|
```
|
|
459
510
|
|
|
460
|
-
The `delegate` method groups
|
|
511
|
+
The `delegate` method groups virtual outputs by expiry date and submits them to the delegation service.
|
|
512
|
+
|
|
513
|
+
By default, delegation is scheduled at 90% of each virtual output's remaining lifetime.
|
|
514
|
+
|
|
515
|
+
You can override this with an explicit date:
|
|
461
516
|
|
|
462
517
|
```typescript
|
|
463
518
|
// Delegate with a specific renewal time
|
|
464
519
|
const delegateAt = new Date(Date.now() + 12 * 60 * 60 * 1000) // 12 hours from now
|
|
465
|
-
await
|
|
520
|
+
await delegatorManager.delegate(vtxos, arkadeAddress, delegateAt)
|
|
466
521
|
```
|
|
467
522
|
|
|
468
523
|
#### Service Worker Integration
|
|
469
524
|
|
|
470
|
-
When using a service worker wallet, pass the `delegatorUrl` option. The service worker will automatically delegate
|
|
525
|
+
When using a service worker wallet, pass the `delegatorUrl` option. The service worker will automatically delegate virtual outputs after each update:
|
|
471
526
|
|
|
472
527
|
```typescript
|
|
473
|
-
import { ServiceWorkerWallet,
|
|
528
|
+
import { ServiceWorkerWallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
474
529
|
|
|
475
530
|
const wallet = await ServiceWorkerWallet.setup({
|
|
476
531
|
serviceWorkerPath: '/service-worker.js',
|
|
477
|
-
arkServerUrl: 'https://
|
|
478
|
-
identity:
|
|
479
|
-
delegatorUrl: '
|
|
532
|
+
arkServerUrl: 'https://arkade.computer',
|
|
533
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
534
|
+
delegatorUrl: 'http://localhost:7001',
|
|
480
535
|
})
|
|
481
536
|
```
|
|
482
537
|
|
|
483
|
-
#### Querying
|
|
538
|
+
#### Querying Delegate Info
|
|
484
539
|
|
|
485
|
-
You can query the
|
|
540
|
+
You can query the delegation service directly to inspect its public key, fee, and payment address:
|
|
486
541
|
|
|
487
542
|
```typescript
|
|
488
543
|
import { RestDelegatorProvider } from '@arkade-os/sdk'
|
|
@@ -490,7 +545,7 @@ import { RestDelegatorProvider } from '@arkade-os/sdk'
|
|
|
490
545
|
const provider = new RestDelegatorProvider('https://delegator.example.com')
|
|
491
546
|
const info = await provider.getDelegateInfo()
|
|
492
547
|
|
|
493
|
-
console.log('
|
|
548
|
+
console.log('Delegate public key:', info.pubkey)
|
|
494
549
|
console.log('Service fee (sats):', info.fee)
|
|
495
550
|
console.log('Fee address:', info.delegatorAddress)
|
|
496
551
|
```
|
|
@@ -500,9 +555,9 @@ console.log('Fee address:', info.delegatorAddress)
|
|
|
500
555
|
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
556
|
|
|
502
557
|
```typescript
|
|
503
|
-
import { BIP322,
|
|
558
|
+
import { BIP322, MnemonicIdentity } from '@arkade-os/sdk'
|
|
504
559
|
|
|
505
|
-
const identity =
|
|
560
|
+
const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
506
561
|
|
|
507
562
|
// Sign a message (P2TR key-spend)
|
|
508
563
|
const signature = await BIP322.sign('Hello Bitcoin!', identity)
|
|
@@ -520,44 +575,66 @@ BIP322.verify('Hello Bitcoin!', sig, '1A1zP1...') // legacy P2PKH
|
|
|
520
575
|
```typescript
|
|
521
576
|
// Get transaction history
|
|
522
577
|
const history = await wallet.getTransactionHistory()
|
|
578
|
+
/*
|
|
579
|
+
{
|
|
580
|
+
key: {
|
|
581
|
+
boardingTxid: string;
|
|
582
|
+
commitmentTxid: string;
|
|
583
|
+
arkTxid: string;
|
|
584
|
+
};
|
|
585
|
+
type: "SENT" | "RECEIVED";
|
|
586
|
+
amount: number;
|
|
587
|
+
settled: boolean;
|
|
588
|
+
createdAt: number;
|
|
589
|
+
assets?: Array<{
|
|
590
|
+
assetId: string,
|
|
591
|
+
amount: number
|
|
592
|
+
}>
|
|
593
|
+
}
|
|
594
|
+
*/
|
|
523
595
|
```
|
|
524
596
|
|
|
525
597
|
### Offboarding
|
|
526
598
|
|
|
527
|
-
Collaborative exit or "offboarding" allows you to withdraw your virtual funds to an
|
|
599
|
+
Collaborative exit or "offboarding" allows you to withdraw your virtual funds to an onchain address:
|
|
528
600
|
|
|
529
601
|
```typescript
|
|
530
|
-
import { Ramps } from '@arkade-os/sdk'
|
|
602
|
+
import { Wallet, MnemonicIdentity, Ramps } from '@arkade-os/sdk'
|
|
603
|
+
|
|
604
|
+
const wallet = await Wallet.create({
|
|
605
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
606
|
+
arkServerUrl: 'https://arkade.computer'
|
|
607
|
+
})
|
|
531
608
|
|
|
532
609
|
// Get fee information from the server
|
|
533
|
-
const
|
|
610
|
+
const { fees: feeInfo } = await wallet.arkProvider.getInfo();
|
|
534
611
|
|
|
535
612
|
const exitTxid = await new Ramps(wallet).offboard(
|
|
536
|
-
|
|
537
|
-
|
|
613
|
+
'bc1p...',
|
|
614
|
+
feeInfo
|
|
538
615
|
);
|
|
539
616
|
```
|
|
540
617
|
|
|
541
618
|
### Unilateral Exit
|
|
542
619
|
|
|
543
|
-
Unilateral exit allows you to withdraw your funds from the
|
|
620
|
+
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
621
|
|
|
545
|
-
1. **Unrolling**: Broadcasting the transaction chain from
|
|
546
|
-
2. **Completing the exit**: Spending the unrolled
|
|
622
|
+
1. **Unrolling**: Broadcasting the transaction chain from offchain back to onchain
|
|
623
|
+
2. **Completing the exit**: Spending the unrolled virtual outputs after the timelock expires
|
|
547
624
|
|
|
548
|
-
#### Step 1: Unrolling
|
|
625
|
+
#### Step 1: Unrolling Virtual Outputs
|
|
549
626
|
|
|
550
627
|
```typescript
|
|
551
|
-
import {
|
|
628
|
+
import { MnemonicIdentity, OnchainWallet, Unroll } from '@arkade-os/sdk'
|
|
552
629
|
|
|
553
630
|
// Create an identity for the onchain wallet
|
|
554
|
-
const onchainIdentity =
|
|
631
|
+
const onchainIdentity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
555
632
|
|
|
556
|
-
// Create an onchain wallet to pay for P2A outputs in
|
|
633
|
+
// Create an onchain wallet to pay for P2A outputs in virtual output branches
|
|
557
634
|
// OnchainWallet implements the AnchorBumper interface
|
|
558
635
|
const onchainWallet = await OnchainWallet.create(onchainIdentity, 'regtest');
|
|
559
636
|
|
|
560
|
-
// Unroll a specific
|
|
637
|
+
// Unroll a specific virtual output
|
|
561
638
|
const vtxo = { txid: 'your_vtxo_txid', vout: 0 };
|
|
562
639
|
const session = await Unroll.Session.create(
|
|
563
640
|
vtxo,
|
|
@@ -576,7 +653,7 @@ for await (const step of session) {
|
|
|
576
653
|
console.log(`Broadcasting transaction ${step.tx.id}`);
|
|
577
654
|
break;
|
|
578
655
|
case Unroll.StepType.DONE:
|
|
579
|
-
console.log(`Unrolling complete for
|
|
656
|
+
console.log(`Unrolling complete for virtual output ${step.vtxoTxid}`);
|
|
580
657
|
break;
|
|
581
658
|
}
|
|
582
659
|
}
|
|
@@ -585,29 +662,29 @@ for await (const step of session) {
|
|
|
585
662
|
The unrolling process works by:
|
|
586
663
|
|
|
587
664
|
- Traversing the transaction chain from the root (most recent) to the leaf (oldest)
|
|
588
|
-
- Broadcasting each transaction that isn't already
|
|
665
|
+
- Broadcasting each transaction that isn't already onchain
|
|
589
666
|
- Waiting for confirmations between steps
|
|
590
667
|
- Using P2A (Pay-to-Anchor) transactions to pay for fees
|
|
591
668
|
|
|
592
669
|
#### Step 2: Completing the Exit
|
|
593
670
|
|
|
594
|
-
Once
|
|
671
|
+
Once virtual outputs are fully unrolled and the unilateral exit timelock has expired, you can complete the exit:
|
|
595
672
|
|
|
596
673
|
```typescript
|
|
597
|
-
// Complete the exit for specific
|
|
674
|
+
// Complete the exit for specific virtual outputs
|
|
598
675
|
await Unroll.completeUnroll(
|
|
599
676
|
wallet,
|
|
600
|
-
[vtxo.txid], // Array of
|
|
677
|
+
[vtxo.txid], // Array of virtual output transaction IDs to complete
|
|
601
678
|
onchainWallet.address // Address to receive the exit amount
|
|
602
679
|
);
|
|
603
680
|
```
|
|
604
681
|
|
|
605
682
|
**Important Notes:**
|
|
606
683
|
|
|
607
|
-
- Each
|
|
684
|
+
- Each virtual output may require multiple unroll steps depending on the transaction chain length
|
|
608
685
|
- Each unroll step must be confirmed before proceeding to the next
|
|
609
|
-
- The `completeUnroll` method can only be called after
|
|
610
|
-
- You need sufficient
|
|
686
|
+
- The `completeUnroll` method can only be called after all virtual outputs are fully unrolled and the timelock has expired
|
|
687
|
+
- You need sufficient onchain funds in the `OnchainWallet` to pay for P2A transaction fees
|
|
611
688
|
|
|
612
689
|
### Running the wallet in a service worker
|
|
613
690
|
|
|
@@ -643,15 +720,13 @@ bus.start()
|
|
|
643
720
|
|
|
644
721
|
```typescript
|
|
645
722
|
// app.ts
|
|
646
|
-
import { ServiceWorkerWallet,
|
|
647
|
-
|
|
648
|
-
const identity = SingleKey.fromHex('your_private_key_hex')
|
|
723
|
+
import { ServiceWorkerWallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
649
724
|
|
|
650
725
|
// One-liner: registers the SW, initializes the MessageBus, and creates the wallet
|
|
651
726
|
const wallet = await ServiceWorkerWallet.setup({
|
|
652
727
|
serviceWorkerPath: '/service-worker.js',
|
|
653
|
-
arkServerUrl: 'https://
|
|
654
|
-
identity,
|
|
728
|
+
arkServerUrl: 'https://arkade.computer',
|
|
729
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
655
730
|
})
|
|
656
731
|
|
|
657
732
|
// Use like any other wallet — calls are proxied to the service worker
|
|
@@ -677,12 +752,9 @@ differ in orchestration and communication.
|
|
|
677
752
|
See the platform READMEs for architecture details, runtime flow, and usage
|
|
678
753
|
examples.
|
|
679
754
|
|
|
680
|
-
|
|
681
|
-
|
|
682
755
|
### Repositories (Storage)
|
|
683
756
|
|
|
684
|
-
The `StorageAdapter` API is deprecated. Use repositories instead. If you omit
|
|
685
|
-
`storage`, the SDK uses IndexedDB repositories with the default database name.
|
|
757
|
+
The `StorageAdapter` API is deprecated. Use repositories instead. If you omit `storage`, the SDK uses IndexedDB repositories with the default database name.
|
|
686
758
|
|
|
687
759
|
#### Migration from v1 StorageAdapter
|
|
688
760
|
|
|
@@ -778,7 +850,7 @@ See [examples/node/multiple-wallets.ts](examples/node/multiple-wallets.ts) for
|
|
|
778
850
|
a full working example using `better-sqlite3`.
|
|
779
851
|
|
|
780
852
|
```typescript
|
|
781
|
-
import {
|
|
853
|
+
import { MnemonicIdentity, Wallet } from '@arkade-os/sdk'
|
|
782
854
|
import { SQLiteWalletRepository, SQLiteContractRepository, SQLExecutor } from '@arkade-os/sdk/repositories/sqlite'
|
|
783
855
|
import Database from 'better-sqlite3'
|
|
784
856
|
|
|
@@ -792,8 +864,8 @@ const executor: SQLExecutor = {
|
|
|
792
864
|
}
|
|
793
865
|
|
|
794
866
|
const wallet = await Wallet.create({
|
|
795
|
-
identity:
|
|
796
|
-
arkServerUrl: 'https://
|
|
867
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
868
|
+
arkServerUrl: 'https://arkade.computer',
|
|
797
869
|
storage: {
|
|
798
870
|
walletRepository: new SQLiteWalletRepository(executor),
|
|
799
871
|
contractRepository: new SQLiteContractRepository(executor),
|
|
@@ -811,7 +883,7 @@ import { RealmWalletRepository, RealmContractRepository, ArkRealmSchemas } from
|
|
|
811
883
|
const realm = await Realm.open({ schema: [...ArkRealmSchemas, ...yourSchemas] })
|
|
812
884
|
const wallet = await Wallet.create({
|
|
813
885
|
identity,
|
|
814
|
-
arkServerUrl: 'https://
|
|
886
|
+
arkServerUrl: 'https://arkade.computer',
|
|
815
887
|
storage: {
|
|
816
888
|
walletRepository: new RealmWalletRepository(realm),
|
|
817
889
|
contractRepository: new RealmContractRepository(realm),
|
|
@@ -825,11 +897,11 @@ In the browser, the SDK defaults to IndexedDB repositories when no `storage`
|
|
|
825
897
|
is provided:
|
|
826
898
|
|
|
827
899
|
```typescript
|
|
828
|
-
import {
|
|
900
|
+
import { MnemonicIdentity, Wallet } from '@arkade-os/sdk'
|
|
829
901
|
|
|
830
902
|
const wallet = await Wallet.create({
|
|
831
|
-
identity:
|
|
832
|
-
arkServerUrl: 'https://
|
|
903
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
904
|
+
arkServerUrl: 'https://arkade.computer',
|
|
833
905
|
// Uses IndexedDB by default in the browser
|
|
834
906
|
})
|
|
835
907
|
```
|
|
@@ -841,14 +913,15 @@ For ephemeral storage (no persistence), pass the in-memory repositories:
|
|
|
841
913
|
|
|
842
914
|
```typescript
|
|
843
915
|
import {
|
|
916
|
+
MnemonicIdentity,
|
|
917
|
+
Wallet,
|
|
844
918
|
InMemoryWalletRepository,
|
|
845
|
-
InMemoryContractRepository
|
|
846
|
-
Wallet
|
|
919
|
+
InMemoryContractRepository
|
|
847
920
|
} from '@arkade-os/sdk'
|
|
848
921
|
|
|
849
922
|
const wallet = await Wallet.create({
|
|
850
|
-
identity,
|
|
851
|
-
arkServerUrl: 'https://
|
|
923
|
+
identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
924
|
+
arkServerUrl: 'https://arkade.computer',
|
|
852
925
|
storage: {
|
|
853
926
|
walletRepository: new InMemoryWalletRepository(),
|
|
854
927
|
contractRepository: new InMemoryContractRepository()
|
|
@@ -869,7 +942,7 @@ import { EventSource } from "eventsource";
|
|
|
869
942
|
(globalThis as any).EventSource = EventSource;
|
|
870
943
|
|
|
871
944
|
// Use dynamic import so the polyfill is set before the SDK evaluates
|
|
872
|
-
const { Wallet
|
|
945
|
+
const { Wallet } = await import("@arkade-os/sdk");
|
|
873
946
|
```
|
|
874
947
|
|
|
875
948
|
If you also need IndexedDB persistence (e.g. for `WalletRepository`), set up the shim before any SDK import:
|
|
@@ -895,16 +968,15 @@ See [`examples/node/multiple-wallets.ts`](examples/node/multiple-wallets.ts) for
|
|
|
895
968
|
For React Native and Expo applications where standard EventSource and fetch streaming may not work properly, use the Expo-compatible providers:
|
|
896
969
|
|
|
897
970
|
```typescript
|
|
898
|
-
import { Wallet,
|
|
971
|
+
import { Wallet, MnemonicIdentity } from '@arkade-os/sdk'
|
|
899
972
|
import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo'
|
|
900
973
|
|
|
901
|
-
const identity =
|
|
974
|
+
const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
|
|
902
975
|
|
|
903
976
|
const wallet = await Wallet.create({
|
|
904
977
|
identity: identity,
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
indexerProvider: new ExpoIndexerProvider('https://mutinynet.arkade.sh'), // For address subscriptions and VTXO updates
|
|
978
|
+
arkProvider: new ExpoArkProvider('https://arkade.computer'), // For settlement events and transactions streaming
|
|
979
|
+
indexerProvider: new ExpoIndexerProvider('https://arkade.computer'), // For address subscriptions and virtual output state updates
|
|
908
980
|
})
|
|
909
981
|
|
|
910
982
|
// use expo/fetch for streaming support (SSE)
|
|
@@ -916,7 +988,7 @@ const address = await wallet.getAddress()
|
|
|
916
988
|
Both ExpoArkProvider and ExpoIndexerProvider are available as adapters following the SDK's modular architecture pattern. This keeps the main SDK bundle clean while providing opt-in functionality for specific environments:
|
|
917
989
|
|
|
918
990
|
- **ExpoArkProvider**: Handles settlement events and transaction streaming using expo/fetch for Server-Sent Events
|
|
919
|
-
- **ExpoIndexerProvider**: Handles address subscriptions and
|
|
991
|
+
- **ExpoIndexerProvider**: Handles address subscriptions and virtual output state updates using expo/fetch for JSON streaming
|
|
920
992
|
|
|
921
993
|
For persistence in Expo/React Native, use the SQLite repository with `expo-sqlite`:
|
|
922
994
|
|
|
@@ -933,9 +1005,9 @@ const executor = {
|
|
|
933
1005
|
|
|
934
1006
|
const wallet = await Wallet.create({
|
|
935
1007
|
identity,
|
|
936
|
-
arkServerUrl: 'https://
|
|
937
|
-
arkProvider: new ExpoArkProvider('https://
|
|
938
|
-
indexerProvider: new ExpoIndexerProvider('https://
|
|
1008
|
+
arkServerUrl: 'https://arkade.computer',
|
|
1009
|
+
arkProvider: new ExpoArkProvider('https://arkade.computer'),
|
|
1010
|
+
indexerProvider: new ExpoIndexerProvider('https://arkade.computer'),
|
|
939
1011
|
storage: {
|
|
940
1012
|
walletRepository: new SQLiteWalletRepository(executor),
|
|
941
1013
|
contractRepository: new SQLiteContractRepository(executor),
|
|
@@ -958,7 +1030,7 @@ if (!global.crypto) global.crypto = {} as any;
|
|
|
958
1030
|
global.crypto.getRandomValues = Crypto.getRandomValues;
|
|
959
1031
|
|
|
960
1032
|
// Now import the SDK
|
|
961
|
-
import { Wallet,
|
|
1033
|
+
import { Wallet, MnemonicIdentity } from '@arkade-os/sdk';
|
|
962
1034
|
import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo';
|
|
963
1035
|
```
|
|
964
1036
|
|
|
@@ -966,7 +1038,7 @@ This is required for MuSig2 settlements and cryptographic operations.
|
|
|
966
1038
|
|
|
967
1039
|
### Contract Management
|
|
968
1040
|
|
|
969
|
-
Both `Wallet` and `ServiceWorkerWallet` use a `ContractManager` internally to watch for
|
|
1041
|
+
Both `Wallet` and `ServiceWorkerWallet` use a `ContractManager` internally to watch for virtual outputs. This provides resilient connection handling with automatic reconnection and failsafe polling - for your wallet's default address and any external contracts you register (Boltz swaps, HTLCs, etc.).
|
|
970
1042
|
|
|
971
1043
|
When you call `wallet.notifyIncomingFunds()` or use `waitForIncomingFunds()`, it uses the ContractManager under the hood, giving you automatic reconnection and failsafe polling for free - no code changes needed.
|
|
972
1044
|
|
|
@@ -997,10 +1069,10 @@ const contract = await manager.createContract({
|
|
|
997
1069
|
const unsubscribe = await manager.onContractEvent((event) => {
|
|
998
1070
|
switch (event.type) {
|
|
999
1071
|
case 'vtxo_received':
|
|
1000
|
-
console.log(`Received ${event.vtxos.length}
|
|
1072
|
+
console.log(`Received ${event.vtxos.length} virtual outputs to ${event.contractScript}`)
|
|
1001
1073
|
break
|
|
1002
1074
|
case 'vtxo_spent':
|
|
1003
|
-
console.log(`Spent
|
|
1075
|
+
console.log(`Spent virtual outputs from ${event.contractScript}`)
|
|
1004
1076
|
break
|
|
1005
1077
|
case 'contract_expired':
|
|
1006
1078
|
console.log(`Contract ${event.contractScript} expired`)
|
|
@@ -1011,7 +1083,7 @@ const unsubscribe = await manager.onContractEvent((event) => {
|
|
|
1011
1083
|
// Update contract data (e.g., set preimage when revealed)
|
|
1012
1084
|
await manager.updateContractParams(contract.script, { preimage: revealedPreimage })
|
|
1013
1085
|
|
|
1014
|
-
// Check spendable paths (requires a specific
|
|
1086
|
+
// Check spendable paths (requires a specific virtual output)
|
|
1015
1087
|
const [withVtxos] = await manager.getContractsWithVtxos({ script: contract.script })
|
|
1016
1088
|
const vtxo = withVtxos.vtxos[0]
|
|
1017
1089
|
const paths = manager.getSpendablePaths({
|
|
@@ -1025,17 +1097,17 @@ if (paths.length > 0) {
|
|
|
1025
1097
|
}
|
|
1026
1098
|
|
|
1027
1099
|
// Or list all possible paths for the current context (no spendability checks)
|
|
1028
|
-
const allPaths = manager.getAllSpendingPaths({
|
|
1100
|
+
const allPaths = await manager.getAllSpendingPaths({
|
|
1029
1101
|
contractScript: contract.script,
|
|
1030
1102
|
collaborative: true,
|
|
1031
1103
|
walletPubKey: myPubKey,
|
|
1032
1104
|
})
|
|
1033
1105
|
|
|
1034
|
-
//
|
|
1035
|
-
const
|
|
1106
|
+
// Fetch contracts together with their current virtual outputs
|
|
1107
|
+
const contractsWithVtxos = await manager.getContractsWithVtxos()
|
|
1036
1108
|
|
|
1037
|
-
//
|
|
1038
|
-
|
|
1109
|
+
// Force a full refresh from the indexer when needed
|
|
1110
|
+
await manager.refreshVtxos()
|
|
1039
1111
|
|
|
1040
1112
|
// Stop watching
|
|
1041
1113
|
unsubscribe()
|
|
@@ -1051,7 +1123,7 @@ The watcher features:
|
|
|
1051
1123
|
Access low-level data management through repositories:
|
|
1052
1124
|
|
|
1053
1125
|
```typescript
|
|
1054
|
-
//
|
|
1126
|
+
// Virtual output management (automatically cached for performance)
|
|
1055
1127
|
const addr = await wallet.getAddress()
|
|
1056
1128
|
const vtxos = await wallet.walletRepository.getVtxos(addr)
|
|
1057
1129
|
await wallet.walletRepository.saveVtxos(addr, vtxos)
|
|
@@ -1069,7 +1141,7 @@ await wallet.contractRepository.saveToContractCollection(
|
|
|
1069
1141
|
const swaps = await wallet.contractRepository.getContractCollection('swaps')
|
|
1070
1142
|
```
|
|
1071
1143
|
|
|
1072
|
-
_For complete API documentation, visit our [
|
|
1144
|
+
_For complete API documentation, visit our [TypeDoc documentation](https://arkade-os.github.io/ts-sdk/)._
|
|
1073
1145
|
|
|
1074
1146
|
## Development
|
|
1075
1147
|
|