@arkade-os/sdk 0.3.0-alpha.7 → 0.3.0-alpha.8
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 +51 -0
- package/dist/cjs/adapters/expo.js +8 -0
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/providers/expoArk.js +237 -0
- package/dist/cjs/providers/expoIndexer.js +194 -0
- package/dist/cjs/providers/indexer.js +3 -1
- package/dist/cjs/utils/arkTransaction.js +13 -0
- package/dist/cjs/wallet/index.js +1 -1
- package/dist/cjs/wallet/serviceWorker/utils.js +0 -9
- package/dist/cjs/wallet/serviceWorker/worker.js +14 -17
- package/dist/cjs/wallet/utils.js +11 -0
- package/dist/cjs/wallet/wallet.js +69 -51
- package/dist/esm/adapters/expo.js +3 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/providers/expoArk.js +200 -0
- package/dist/esm/providers/expoIndexer.js +157 -0
- package/dist/esm/providers/indexer.js +3 -1
- package/dist/esm/utils/arkTransaction.js +13 -1
- package/dist/esm/wallet/index.js +1 -1
- package/dist/esm/wallet/serviceWorker/utils.js +0 -8
- package/dist/esm/wallet/serviceWorker/worker.js +15 -18
- package/dist/esm/wallet/utils.js +8 -0
- package/dist/esm/wallet/wallet.js +70 -52
- package/dist/types/adapters/expo.d.ts +4 -0
- package/dist/types/index.d.ts +5 -5
- package/dist/types/providers/ark.d.ts +136 -2
- package/dist/types/providers/expoArk.d.ts +22 -0
- package/dist/types/providers/expoIndexer.d.ts +26 -0
- package/dist/types/providers/indexer.d.ts +8 -0
- package/dist/types/utils/arkTransaction.d.ts +3 -1
- package/dist/types/wallet/index.d.ts +44 -6
- package/dist/types/wallet/serviceWorker/utils.d.ts +0 -2
- package/dist/types/wallet/utils.d.ts +2 -0
- package/dist/types/wallet/wallet.d.ts +9 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -288,6 +288,55 @@ const wallet = await Wallet.create({
|
|
|
288
288
|
})
|
|
289
289
|
```
|
|
290
290
|
|
|
291
|
+
### Using with Expo/React Native
|
|
292
|
+
|
|
293
|
+
For React Native and Expo applications where standard EventSource and fetch streaming may not work properly, use the Expo-compatible providers:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { Wallet, SingleKey } from '@arkade-os/sdk'
|
|
297
|
+
import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo'
|
|
298
|
+
|
|
299
|
+
const identity = SingleKey.fromHex('your_private_key_hex')
|
|
300
|
+
|
|
301
|
+
const wallet = await Wallet.create({
|
|
302
|
+
identity: identity,
|
|
303
|
+
esploraUrl: 'https://mutinynet.com/api',
|
|
304
|
+
arkProvider: new ExpoArkProvider('https://mutinynet.arkade.sh'), // For settlement events and transactions streaming
|
|
305
|
+
indexerProvider: new ExpoIndexerProvider('https://mutinynet.arkade.sh'), // For address subscriptions and VTXO updates
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// use expo/fetch for streaming support (SSE)
|
|
309
|
+
// All other wallet functionality remains the same
|
|
310
|
+
const balance = await wallet.getBalance()
|
|
311
|
+
const address = await wallet.getAddress()
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
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:
|
|
315
|
+
|
|
316
|
+
- **ExpoArkProvider**: Handles settlement events and transaction streaming using expo/fetch for Server-Sent Events
|
|
317
|
+
- **ExpoIndexerProvider**: Handles address subscriptions and VTXO updates using expo/fetch for JSON streaming
|
|
318
|
+
|
|
319
|
+
#### Crypto Polyfill Requirement
|
|
320
|
+
|
|
321
|
+
Install `expo-crypto` and polyfill `crypto.getRandomValues()` at the top of your app entry point:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
npx expo install expo-crypto
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// App.tsx or index.js - MUST be first import
|
|
329
|
+
import * as Crypto from 'expo-crypto';
|
|
330
|
+
if (!global.crypto) global.crypto = {} as any;
|
|
331
|
+
global.crypto.getRandomValues = Crypto.getRandomValues;
|
|
332
|
+
|
|
333
|
+
// Now import the SDK
|
|
334
|
+
import { Wallet, SingleKey } from '@arkade-os/sdk';
|
|
335
|
+
import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo';
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
This is required for MuSig2 settlements and cryptographic operations.
|
|
339
|
+
|
|
291
340
|
### Repository Pattern
|
|
292
341
|
|
|
293
342
|
Access low-level data management through repositories:
|
|
@@ -311,6 +360,8 @@ await wallet.contractRepository.saveToContractCollection(
|
|
|
311
360
|
const swaps = await wallet.contractRepository.getContractCollection('swaps')
|
|
312
361
|
```
|
|
313
362
|
|
|
363
|
+
_For complete API documentation, visit our [TypeScript documentation](https://arkade-os.github.io/ts-sdk/)._
|
|
364
|
+
|
|
314
365
|
## Development
|
|
315
366
|
|
|
316
367
|
### Requirements
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExpoIndexerProvider = exports.ExpoArkProvider = void 0;
|
|
4
|
+
// Expo adapter for React Native/Expo environments
|
|
5
|
+
var expoArk_1 = require("../providers/expoArk");
|
|
6
|
+
Object.defineProperty(exports, "ExpoArkProvider", { enumerable: true, get: function () { return expoArk_1.ExpoArkProvider; } });
|
|
7
|
+
var expoIndexer_1 = require("../providers/expoIndexer");
|
|
8
|
+
Object.defineProperty(exports, "ExpoIndexerProvider", { enumerable: true, get: function () { return expoIndexer_1.ExpoIndexerProvider; } });
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.waitForIncomingFunds = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
3
|
+
exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
4
4
|
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
5
5
|
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_js_1.Transaction; } });
|
|
6
6
|
const singleKey_1 = require("./identity/singleKey");
|
|
@@ -48,6 +48,7 @@ Object.defineProperty(exports, "CSVMultisigTapscript", { enumerable: true, get:
|
|
|
48
48
|
Object.defineProperty(exports, "decodeTapscript", { enumerable: true, get: function () { return tapscript_1.decodeTapscript; } });
|
|
49
49
|
Object.defineProperty(exports, "MultisigTapscript", { enumerable: true, get: function () { return tapscript_1.MultisigTapscript; } });
|
|
50
50
|
const arkTransaction_1 = require("./utils/arkTransaction");
|
|
51
|
+
Object.defineProperty(exports, "hasBoardingTxExpired", { enumerable: true, get: function () { return arkTransaction_1.hasBoardingTxExpired; } });
|
|
51
52
|
Object.defineProperty(exports, "buildOffchainTx", { enumerable: true, get: function () { return arkTransaction_1.buildOffchainTx; } });
|
|
52
53
|
const unknownFields_1 = require("./utils/unknownFields");
|
|
53
54
|
Object.defineProperty(exports, "VtxoTaprootTree", { enumerable: true, get: function () { return unknownFields_1.VtxoTaprootTree; } });
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ExpoArkProvider = void 0;
|
|
37
|
+
const ark_1 = require("./ark");
|
|
38
|
+
/**
|
|
39
|
+
* Expo-compatible Ark provider implementation using expo/fetch for SSE support.
|
|
40
|
+
* This provider works specifically in React Native/Expo environments where
|
|
41
|
+
* standard EventSource is not available but expo/fetch provides SSE capabilities.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { ExpoArkProvider } from '@arkade-os/sdk/providers/expo';
|
|
46
|
+
*
|
|
47
|
+
* const provider = new ExpoArkProvider('https://ark.example.com');
|
|
48
|
+
* const info = await provider.getInfo();
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
class ExpoArkProvider extends ark_1.RestArkProvider {
|
|
52
|
+
constructor(serverUrl) {
|
|
53
|
+
super(serverUrl);
|
|
54
|
+
}
|
|
55
|
+
async *getEventStream(signal, topics) {
|
|
56
|
+
// Dynamic import to avoid bundling expo/fetch in non-Expo environments
|
|
57
|
+
let expoFetch = fetch; // Default to standard fetch
|
|
58
|
+
try {
|
|
59
|
+
const expoFetchModule = await Promise.resolve().then(() => __importStar(require("expo/fetch")));
|
|
60
|
+
// expo/fetch returns a compatible fetch function but with different types
|
|
61
|
+
expoFetch = expoFetchModule.fetch;
|
|
62
|
+
console.debug("Using expo/fetch for SSE");
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Fall back to standard fetch if expo/fetch is not available
|
|
66
|
+
console.warn("Using standard fetch instead of expo/fetch. " +
|
|
67
|
+
"Streaming may not be fully supported in some environments.", error);
|
|
68
|
+
}
|
|
69
|
+
const url = `${this.serverUrl}/v1/batch/events`;
|
|
70
|
+
const queryParams = topics.length > 0
|
|
71
|
+
? `?${topics.map((topic) => `topics=${encodeURIComponent(topic)}`).join("&")}`
|
|
72
|
+
: "";
|
|
73
|
+
while (!signal?.aborted) {
|
|
74
|
+
// Create a new AbortController for this specific fetch attempt
|
|
75
|
+
// to prevent accumulating listeners on the parent signal
|
|
76
|
+
const fetchController = new AbortController();
|
|
77
|
+
const cleanup = () => fetchController.abort();
|
|
78
|
+
signal?.addEventListener("abort", cleanup, { once: true });
|
|
79
|
+
try {
|
|
80
|
+
const response = await expoFetch(url + queryParams, {
|
|
81
|
+
headers: {
|
|
82
|
+
Accept: "text/event-stream",
|
|
83
|
+
},
|
|
84
|
+
signal: fetchController.signal,
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`Unexpected status ${response.status} when fetching event stream`);
|
|
88
|
+
}
|
|
89
|
+
if (!response.body) {
|
|
90
|
+
throw new Error("Response body is null");
|
|
91
|
+
}
|
|
92
|
+
const reader = response.body.getReader();
|
|
93
|
+
const decoder = new TextDecoder();
|
|
94
|
+
let buffer = "";
|
|
95
|
+
while (!signal?.aborted) {
|
|
96
|
+
const { done, value } = await reader.read();
|
|
97
|
+
if (done) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
// Append new data to buffer and split by newlines
|
|
101
|
+
buffer += decoder.decode(value, { stream: true });
|
|
102
|
+
const lines = buffer.split("\n");
|
|
103
|
+
// Process all complete lines
|
|
104
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
105
|
+
const line = lines[i].trim();
|
|
106
|
+
if (!line)
|
|
107
|
+
continue;
|
|
108
|
+
try {
|
|
109
|
+
// Parse SSE format: "data: {json}"
|
|
110
|
+
if (line.startsWith("data:")) {
|
|
111
|
+
const jsonStr = line.substring(5).trim();
|
|
112
|
+
if (!jsonStr)
|
|
113
|
+
continue;
|
|
114
|
+
const data = JSON.parse(jsonStr);
|
|
115
|
+
// Handle different response structures
|
|
116
|
+
// v8 mesh API might wrap in {result: ...} or send directly
|
|
117
|
+
const eventData = data.result || data;
|
|
118
|
+
// Skip heartbeat messages
|
|
119
|
+
if (eventData.heartbeat !== undefined) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const event = this.parseSettlementEvent(eventData);
|
|
123
|
+
if (event) {
|
|
124
|
+
yield event;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
console.error("Failed to parse event:", line);
|
|
130
|
+
console.error("Parse error:", err);
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Keep the last partial line in the buffer
|
|
135
|
+
buffer = lines[lines.length - 1];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
143
|
+
// these timeouts are set by expo/fetch function
|
|
144
|
+
if ((0, ark_1.isFetchTimeoutError)(error)) {
|
|
145
|
+
console.debug("Timeout error ignored");
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
console.error("Event stream error:", error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
// Clean up the abort listener
|
|
153
|
+
signal?.removeEventListener("abort", cleanup);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async *getTransactionsStream(signal) {
|
|
158
|
+
// Dynamic import to avoid bundling expo/fetch in non-Expo environments
|
|
159
|
+
let expoFetch = fetch; // Default to standard fetch
|
|
160
|
+
try {
|
|
161
|
+
const expoFetchModule = await Promise.resolve().then(() => __importStar(require("expo/fetch")));
|
|
162
|
+
// expo/fetch returns a compatible fetch function but with different types
|
|
163
|
+
expoFetch = expoFetchModule.fetch;
|
|
164
|
+
console.debug("Using expo/fetch for transaction stream");
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// Fall back to standard fetch if expo/fetch is not available
|
|
168
|
+
console.warn("Using standard fetch instead of expo/fetch. " +
|
|
169
|
+
"Streaming may not be fully supported in some environments.", error);
|
|
170
|
+
}
|
|
171
|
+
const url = `${this.serverUrl}/v1/txs`;
|
|
172
|
+
while (!signal?.aborted) {
|
|
173
|
+
// Create a new AbortController for this specific fetch attempt
|
|
174
|
+
// to prevent accumulating listeners on the parent signal
|
|
175
|
+
const fetchController = new AbortController();
|
|
176
|
+
const cleanup = () => fetchController.abort();
|
|
177
|
+
signal?.addEventListener("abort", cleanup, { once: true });
|
|
178
|
+
try {
|
|
179
|
+
const response = await expoFetch(url, {
|
|
180
|
+
headers: {
|
|
181
|
+
Accept: "text/event-stream",
|
|
182
|
+
},
|
|
183
|
+
signal: fetchController.signal,
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(`Unexpected status ${response.status} when fetching transaction stream`);
|
|
187
|
+
}
|
|
188
|
+
if (!response.body) {
|
|
189
|
+
throw new Error("Response body is null");
|
|
190
|
+
}
|
|
191
|
+
const reader = response.body.getReader();
|
|
192
|
+
const decoder = new TextDecoder();
|
|
193
|
+
let buffer = "";
|
|
194
|
+
while (!signal?.aborted) {
|
|
195
|
+
const { done, value } = await reader.read();
|
|
196
|
+
if (done) {
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
// Append new data to buffer and split by newlines
|
|
200
|
+
buffer += decoder.decode(value, { stream: true });
|
|
201
|
+
const lines = buffer.split("\n");
|
|
202
|
+
// Process all complete lines
|
|
203
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
204
|
+
const line = lines[i].trim();
|
|
205
|
+
if (!line)
|
|
206
|
+
continue;
|
|
207
|
+
const data = JSON.parse(line);
|
|
208
|
+
const txNotification = this.parseTransactionNotification(data.result);
|
|
209
|
+
if (txNotification) {
|
|
210
|
+
yield txNotification;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Keep the last partial line in the buffer
|
|
214
|
+
buffer = lines[lines.length - 1];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
222
|
+
// these timeouts are set by expo/fetch function
|
|
223
|
+
if ((0, ark_1.isFetchTimeoutError)(error)) {
|
|
224
|
+
console.debug("Timeout error ignored");
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
console.error("Address subscription error:", error);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
// Clean up the abort listener
|
|
232
|
+
signal?.removeEventListener("abort", cleanup);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.ExpoArkProvider = ExpoArkProvider;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ExpoIndexerProvider = void 0;
|
|
37
|
+
const indexer_1 = require("./indexer");
|
|
38
|
+
const ark_1 = require("./ark");
|
|
39
|
+
// Helper function to convert Vtxo to VirtualCoin (same as in indexer.ts)
|
|
40
|
+
function convertVtxo(vtxo) {
|
|
41
|
+
return {
|
|
42
|
+
txid: vtxo.outpoint.txid,
|
|
43
|
+
vout: vtxo.outpoint.vout,
|
|
44
|
+
value: Number(vtxo.amount),
|
|
45
|
+
status: {
|
|
46
|
+
confirmed: !vtxo.isSwept && !vtxo.isPreconfirmed,
|
|
47
|
+
},
|
|
48
|
+
virtualStatus: {
|
|
49
|
+
state: vtxo.isSwept
|
|
50
|
+
? "swept"
|
|
51
|
+
: vtxo.isPreconfirmed
|
|
52
|
+
? "preconfirmed"
|
|
53
|
+
: "settled",
|
|
54
|
+
commitmentTxIds: vtxo.commitmentTxids,
|
|
55
|
+
batchExpiry: vtxo.expiresAt
|
|
56
|
+
? Number(vtxo.expiresAt) * 1000
|
|
57
|
+
: undefined,
|
|
58
|
+
},
|
|
59
|
+
spentBy: vtxo.spentBy ?? "",
|
|
60
|
+
settledBy: vtxo.settledBy,
|
|
61
|
+
arkTxId: vtxo.arkTxid,
|
|
62
|
+
createdAt: new Date(Number(vtxo.createdAt) * 1000),
|
|
63
|
+
isUnrolled: vtxo.isUnrolled,
|
|
64
|
+
isSpent: vtxo.isSpent,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Expo-compatible Indexer provider implementation using expo/fetch for streaming support.
|
|
69
|
+
* This provider works specifically in React Native/Expo environments where
|
|
70
|
+
* standard fetch streaming may not work properly but expo/fetch provides streaming capabilities.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo';
|
|
75
|
+
*
|
|
76
|
+
* const provider = new ExpoIndexerProvider('https://indexer.example.com');
|
|
77
|
+
* const vtxos = await provider.getVtxos({ scripts: ['script1'] });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
class ExpoIndexerProvider extends indexer_1.RestIndexerProvider {
|
|
81
|
+
constructor(serverUrl) {
|
|
82
|
+
super(serverUrl);
|
|
83
|
+
}
|
|
84
|
+
async *getSubscription(subscriptionId, abortSignal) {
|
|
85
|
+
// Detect if we're running in React Native/Expo environment
|
|
86
|
+
const isReactNative = typeof navigator !== "undefined" &&
|
|
87
|
+
navigator.product === "ReactNative";
|
|
88
|
+
// Dynamic import to avoid bundling expo/fetch in non-Expo environments
|
|
89
|
+
let expoFetch = fetch; // Default to standard fetch
|
|
90
|
+
try {
|
|
91
|
+
const expoFetchModule = await Promise.resolve().then(() => __importStar(require("expo/fetch")));
|
|
92
|
+
// expo/fetch returns a compatible fetch function but with different types
|
|
93
|
+
expoFetch = expoFetchModule.fetch;
|
|
94
|
+
console.debug("Using expo/fetch for indexer subscription");
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// In React Native/Expo, expo/fetch is required for proper streaming support
|
|
98
|
+
if (isReactNative) {
|
|
99
|
+
throw new Error("expo/fetch is unavailable in React Native environment. " +
|
|
100
|
+
"Please ensure expo/fetch is installed and properly configured. " +
|
|
101
|
+
"Streaming support may not work with standard fetch in React Native.");
|
|
102
|
+
}
|
|
103
|
+
// In non-RN environments, fall back to standard fetch but warn about potential streaming issues
|
|
104
|
+
console.warn("Using standard fetch instead of expo/fetch. " +
|
|
105
|
+
"Streaming may not be fully supported in some environments.", error);
|
|
106
|
+
}
|
|
107
|
+
const url = `${this.serverUrl}/v1/indexer/script/subscription/${subscriptionId}`;
|
|
108
|
+
while (!abortSignal.aborted) {
|
|
109
|
+
try {
|
|
110
|
+
const res = await expoFetch(url, {
|
|
111
|
+
headers: {
|
|
112
|
+
Accept: "text/event-stream",
|
|
113
|
+
"Content-Type": "application/json",
|
|
114
|
+
},
|
|
115
|
+
signal: abortSignal,
|
|
116
|
+
});
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw new Error(`Unexpected status ${res.status} when subscribing to address updates`);
|
|
119
|
+
}
|
|
120
|
+
// Check if response is the expected content type
|
|
121
|
+
const contentType = res.headers.get("content-type");
|
|
122
|
+
if (contentType &&
|
|
123
|
+
!contentType.includes("text/event-stream") &&
|
|
124
|
+
!contentType.includes("application/json")) {
|
|
125
|
+
throw new Error(`Unexpected content-type: ${contentType}. Expected text/event-stream or application/json`);
|
|
126
|
+
}
|
|
127
|
+
if (!res.body) {
|
|
128
|
+
throw new Error("Response body is null");
|
|
129
|
+
}
|
|
130
|
+
const reader = res.body.getReader();
|
|
131
|
+
const decoder = new TextDecoder();
|
|
132
|
+
let buffer = "";
|
|
133
|
+
while (!abortSignal.aborted) {
|
|
134
|
+
const { done, value } = await reader.read();
|
|
135
|
+
if (done) {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
buffer += decoder.decode(value, { stream: true });
|
|
139
|
+
const lines = buffer.split("\n");
|
|
140
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
141
|
+
const line = lines[i].trim();
|
|
142
|
+
if (!line)
|
|
143
|
+
continue;
|
|
144
|
+
try {
|
|
145
|
+
// Parse SSE format: "data: {json}"
|
|
146
|
+
if (line.startsWith("data:")) {
|
|
147
|
+
const jsonStr = line.substring(5).trim();
|
|
148
|
+
if (!jsonStr)
|
|
149
|
+
continue;
|
|
150
|
+
const data = JSON.parse(jsonStr);
|
|
151
|
+
// Handle new v8 proto format with heartbeat or event
|
|
152
|
+
if (data.heartbeat !== undefined) {
|
|
153
|
+
// Skip heartbeat messages
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// Process event messages
|
|
157
|
+
if (data.event) {
|
|
158
|
+
yield {
|
|
159
|
+
txid: data.event.txid,
|
|
160
|
+
scripts: data.event.scripts || [],
|
|
161
|
+
newVtxos: (data.event.newVtxos || []).map(convertVtxo),
|
|
162
|
+
spentVtxos: (data.event.spentVtxos || []).map(convertVtxo),
|
|
163
|
+
sweptVtxos: (data.event.sweptVtxos || []).map(convertVtxo),
|
|
164
|
+
tx: data.event.tx,
|
|
165
|
+
checkpointTxs: data.event.checkpointTxs,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (parseError) {
|
|
171
|
+
console.error("Failed to parse subscription response:", parseError);
|
|
172
|
+
throw parseError;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
buffer = lines[lines.length - 1];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
183
|
+
// these timeouts are set by expo/fetch function
|
|
184
|
+
if ((0, ark_1.isFetchTimeoutError)(error)) {
|
|
185
|
+
console.debug("Timeout error ignored");
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
console.error("Subscription error:", error);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.ExpoIndexerProvider = ExpoIndexerProvider;
|
|
@@ -176,6 +176,7 @@ class RestIndexerProvider {
|
|
|
176
176
|
scripts: data.event.scripts || [],
|
|
177
177
|
newVtxos: (data.event.newVtxos || []).map(convertVtxo),
|
|
178
178
|
spentVtxos: (data.event.spentVtxos || []).map(convertVtxo),
|
|
179
|
+
sweptVtxos: (data.event.sweptVtxos || []).map(convertVtxo),
|
|
179
180
|
tx: data.event.tx,
|
|
180
181
|
checkpointTxs: data.event.checkpointTxs,
|
|
181
182
|
};
|
|
@@ -329,7 +330,7 @@ class RestIndexerProvider {
|
|
|
329
330
|
});
|
|
330
331
|
if (!res.ok) {
|
|
331
332
|
const errorText = await res.text();
|
|
332
|
-
|
|
333
|
+
console.warn(`Failed to unsubscribe to scripts: ${errorText}`);
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
}
|
|
@@ -358,6 +359,7 @@ function convertVtxo(vtxo) {
|
|
|
358
359
|
arkTxId: vtxo.arkTxid,
|
|
359
360
|
createdAt: new Date(Number(vtxo.createdAt) * 1000),
|
|
360
361
|
isUnrolled: vtxo.isUnrolled,
|
|
362
|
+
isSpent: vtxo.isSpent,
|
|
361
363
|
};
|
|
362
364
|
}
|
|
363
365
|
// Unexported namespace for type guards only
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildOffchainTx = buildOffchainTx;
|
|
4
|
+
exports.hasBoardingTxExpired = hasBoardingTxExpired;
|
|
4
5
|
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
5
6
|
const tapscript_1 = require("../script/tapscript");
|
|
6
7
|
const base_1 = require("../script/base");
|
|
@@ -106,3 +107,15 @@ const nLocktimeMinSeconds = 500000000n;
|
|
|
106
107
|
function isSeconds(locktime) {
|
|
107
108
|
return locktime >= nLocktimeMinSeconds;
|
|
108
109
|
}
|
|
110
|
+
function hasBoardingTxExpired(coin, boardingTimelock) {
|
|
111
|
+
if (!coin.status.block_time)
|
|
112
|
+
return false;
|
|
113
|
+
if (boardingTimelock.value === 0n)
|
|
114
|
+
return true;
|
|
115
|
+
if (boardingTimelock.type !== "blocks")
|
|
116
|
+
return false; // TODO: handle get chain tip
|
|
117
|
+
// validate expiry in terms of seconds
|
|
118
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
119
|
+
const blockTime = BigInt(Math.floor(coin.status.block_time));
|
|
120
|
+
return blockTime + boardingTimelock.value <= now;
|
|
121
|
+
}
|
package/dist/cjs/wallet/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var TxType;
|
|
|
10
10
|
TxType["TxReceived"] = "RECEIVED";
|
|
11
11
|
})(TxType || (exports.TxType = TxType = {}));
|
|
12
12
|
function isSpendable(vtxo) {
|
|
13
|
-
return vtxo.
|
|
13
|
+
return !vtxo.isSpent;
|
|
14
14
|
}
|
|
15
15
|
function isRecoverable(vtxo) {
|
|
16
16
|
return vtxo.virtualStatus.state === "swept" && isSpendable(vtxo);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.setupServiceWorker = setupServiceWorker;
|
|
4
|
-
exports.extendVirtualCoin = extendVirtualCoin;
|
|
5
4
|
/**
|
|
6
5
|
* setupServiceWorker sets up the service worker.
|
|
7
6
|
* @param path - the path to the service worker script
|
|
@@ -48,11 +47,3 @@ async function setupServiceWorker(path) {
|
|
|
48
47
|
navigator.serviceWorker.addEventListener("error", onError);
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
|
-
function extendVirtualCoin(wallet, vtxo) {
|
|
52
|
-
return {
|
|
53
|
-
...vtxo,
|
|
54
|
-
forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
|
|
55
|
-
intentTapLeafScript: wallet.offchainTapscript.exit(),
|
|
56
|
-
tapTree: wallet.offchainTapscript.encode(),
|
|
57
|
-
};
|
|
58
|
-
}
|