@use-stellar/vue 0.0.0-stage → 0.2.3
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/LICENSE +134 -0
- package/README.md +187 -2
- package/dist/chunk-7ROX7ZPL.mjs +123 -0
- package/dist/chunk-7ROX7ZPL.mjs.map +1 -0
- package/dist/index.d.mts +212 -0
- package/dist/index.d.ts +212 -0
- package/dist/index.js +829 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +713 -0
- package/dist/index.mjs.map +1 -0
- package/dist/nuxt/module.d.mts +15 -0
- package/dist/nuxt/module.d.ts +15 -0
- package/dist/nuxt/module.js +29 -0
- package/dist/nuxt/module.js.map +1 -0
- package/dist/nuxt/module.mjs +23 -0
- package/dist/nuxt/module.mjs.map +1 -0
- package/dist/nuxt/runtime/plugin.d.mts +24 -0
- package/dist/nuxt/runtime/plugin.d.ts +24 -0
- package/dist/nuxt/runtime/plugin.js +140 -0
- package/dist/nuxt/runtime/plugin.js.map +1 -0
- package/dist/nuxt/runtime/plugin.mjs +28 -0
- package/dist/nuxt/runtime/plugin.mjs.map +1 -0
- package/dist/plugin-bXIRDH8_.d.mts +84 -0
- package/dist/plugin-bXIRDH8_.d.ts +84 -0
- package/dist/test-utils.d.mts +27 -0
- package/dist/test-utils.d.ts +27 -0
- package/dist/test-utils.js +97 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/test-utils.mjs +22 -0
- package/dist/test-utils.mjs.map +1 -0
- package/package.json +132 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vue = require('vue');
|
|
4
|
+
var core = require('use-stellar/core');
|
|
5
|
+
|
|
6
|
+
// src/plugin.ts
|
|
7
|
+
|
|
8
|
+
// src/types.ts
|
|
9
|
+
var NETWORK_PASSPHRASES = {
|
|
10
|
+
testnet: "Test SDF Network ; September 2015",
|
|
11
|
+
mainnet: "Public Global Stellar Network ; September 2015",
|
|
12
|
+
futurenet: "Test SDF Future Network ; October 2022"
|
|
13
|
+
};
|
|
14
|
+
var NETWORK_CONFIGS = {
|
|
15
|
+
testnet: {
|
|
16
|
+
network: "testnet",
|
|
17
|
+
horizonUrl: "https://horizon-testnet.stellar.org",
|
|
18
|
+
sorobanUrl: "https://soroban-testnet.stellar.org",
|
|
19
|
+
networkPassphrase: NETWORK_PASSPHRASES.testnet
|
|
20
|
+
},
|
|
21
|
+
mainnet: {
|
|
22
|
+
network: "mainnet",
|
|
23
|
+
horizonUrl: "https://horizon.stellar.org",
|
|
24
|
+
sorobanUrl: "https://soroban.stellar.org",
|
|
25
|
+
networkPassphrase: NETWORK_PASSPHRASES.mainnet
|
|
26
|
+
},
|
|
27
|
+
futurenet: {
|
|
28
|
+
network: "futurenet",
|
|
29
|
+
horizonUrl: "https://horizon-futurenet.stellar.org",
|
|
30
|
+
sorobanUrl: "https://rpc-futurenet.stellar.org",
|
|
31
|
+
networkPassphrase: NETWORK_PASSPHRASES.futurenet
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/plugin.ts
|
|
36
|
+
var STELLAR_RUNTIME_KEY = /* @__PURE__ */ Symbol("use-stellar/vue:runtime");
|
|
37
|
+
var DISCONNECTED_WALLET = {
|
|
38
|
+
connected: false,
|
|
39
|
+
connecting: false,
|
|
40
|
+
address: null,
|
|
41
|
+
network: null,
|
|
42
|
+
wallet: null,
|
|
43
|
+
walletName: null,
|
|
44
|
+
error: null,
|
|
45
|
+
walletNetwork: null,
|
|
46
|
+
walletNetworkPassphrase: null
|
|
47
|
+
};
|
|
48
|
+
function resolveNetworkConfig(network, override) {
|
|
49
|
+
const builtIn = NETWORK_CONFIGS[network];
|
|
50
|
+
if (!override) return builtIn;
|
|
51
|
+
return {
|
|
52
|
+
network,
|
|
53
|
+
horizonUrl: override.horizonUrl ?? builtIn.horizonUrl,
|
|
54
|
+
sorobanUrl: override.sorobanUrl ?? builtIn.sorobanUrl,
|
|
55
|
+
networkPassphrase: override.networkPassphrase ?? builtIn.networkPassphrase
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function createStellarRuntime(options = {}) {
|
|
59
|
+
const network = options.network ?? "testnet";
|
|
60
|
+
const walletAddress = vue.shallowRef(options.walletAddress ?? null);
|
|
61
|
+
const runtime = {
|
|
62
|
+
network,
|
|
63
|
+
networkConfig: resolveNetworkConfig(network, options.networkConfig),
|
|
64
|
+
queryStore: new core.QueryStore(options.queryConfig),
|
|
65
|
+
wallet: { ...DISCONNECTED_WALLET, address: options.walletAddress ?? null },
|
|
66
|
+
setWallet(wallet) {
|
|
67
|
+
runtime.wallet = wallet;
|
|
68
|
+
walletAddress.value = wallet.address;
|
|
69
|
+
},
|
|
70
|
+
walletAddress,
|
|
71
|
+
setWalletAddress(address) {
|
|
72
|
+
walletAddress.value = address;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return runtime;
|
|
76
|
+
}
|
|
77
|
+
function createStellarPlugin(options = {}) {
|
|
78
|
+
return {
|
|
79
|
+
install(app) {
|
|
80
|
+
app.provide(STELLAR_RUNTIME_KEY, createStellarRuntime(options));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function useStellarRuntime() {
|
|
85
|
+
const runtime = vue.inject(STELLAR_RUNTIME_KEY);
|
|
86
|
+
if (!runtime) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
"@use-stellar/vue: no plugin installed. Call app.use(createStellarPlugin()) before using any composable."
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return runtime;
|
|
92
|
+
}
|
|
93
|
+
function useQuery(options) {
|
|
94
|
+
const data = vue.shallowRef(null);
|
|
95
|
+
const loading = vue.ref(false);
|
|
96
|
+
const error = vue.shallowRef(null);
|
|
97
|
+
const updatedAt = vue.ref(null);
|
|
98
|
+
let observer = null;
|
|
99
|
+
let unsubscribe = null;
|
|
100
|
+
const resolved = vue.computed(() => ({
|
|
101
|
+
queryKey: options.queryKey(),
|
|
102
|
+
queryFn: options.queryFn,
|
|
103
|
+
store: options.store,
|
|
104
|
+
enabled: options.enabled?.() ?? true,
|
|
105
|
+
staleTime: vue.toValue(options.staleTime),
|
|
106
|
+
maxRetries: vue.toValue(options.maxRetries)
|
|
107
|
+
}));
|
|
108
|
+
function project(snapshot) {
|
|
109
|
+
data.value = snapshot.data;
|
|
110
|
+
loading.value = snapshot.loading;
|
|
111
|
+
error.value = snapshot.error;
|
|
112
|
+
updatedAt.value = snapshot.updatedAt;
|
|
113
|
+
}
|
|
114
|
+
function start() {
|
|
115
|
+
if (observer) return;
|
|
116
|
+
observer = core.createQueryObserver(resolved.value);
|
|
117
|
+
unsubscribe = observer.subscribe(project);
|
|
118
|
+
project(observer.getSnapshot());
|
|
119
|
+
}
|
|
120
|
+
vue.watch(
|
|
121
|
+
() => [
|
|
122
|
+
core.serializeKey(resolved.value.queryKey),
|
|
123
|
+
resolved.value.enabled,
|
|
124
|
+
resolved.value.staleTime,
|
|
125
|
+
resolved.value.maxRetries
|
|
126
|
+
],
|
|
127
|
+
() => {
|
|
128
|
+
observer?.setOptions(resolved.value);
|
|
129
|
+
if (observer) project(observer.getSnapshot());
|
|
130
|
+
}
|
|
131
|
+
// Batched (the default "pre" flush), not "sync": inputs that change in
|
|
132
|
+
// the same tick — a contract id and a method, say — must produce one
|
|
133
|
+
// query for the final combination, not an extra request for every
|
|
134
|
+
// half-updated one in between.
|
|
135
|
+
);
|
|
136
|
+
if (vue.getCurrentInstance()) {
|
|
137
|
+
if (typeof globalThis.window === "undefined" && options.ssr) start();
|
|
138
|
+
else vue.onMounted(start);
|
|
139
|
+
} else {
|
|
140
|
+
start();
|
|
141
|
+
}
|
|
142
|
+
vue.onScopeDispose(() => {
|
|
143
|
+
unsubscribe?.();
|
|
144
|
+
observer?.destroy();
|
|
145
|
+
unsubscribe = null;
|
|
146
|
+
observer = null;
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
data,
|
|
150
|
+
loading,
|
|
151
|
+
error,
|
|
152
|
+
updatedAt,
|
|
153
|
+
rateLimitedUntil: vue.computed(() => observer?.getRateLimitedUntil() ?? null),
|
|
154
|
+
refetch: () => observer?.fetch({ force: true }) ?? Promise.resolve()
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/useBalance.ts
|
|
159
|
+
function matchesAsset(balance, asset) {
|
|
160
|
+
if (asset === "XLM") return balance.asset === "XLM";
|
|
161
|
+
if (typeof asset !== "object") return balance.asset === asset;
|
|
162
|
+
return typeof balance.asset === "object" && balance.asset.code === asset.code && balance.asset.issuer === asset.issuer;
|
|
163
|
+
}
|
|
164
|
+
function useBalance(options) {
|
|
165
|
+
const runtime = useStellarRuntime();
|
|
166
|
+
const address = vue.computed(() => vue.toValue(options.address)?.trim() || null);
|
|
167
|
+
const asset = options.asset ?? "XLM";
|
|
168
|
+
const query = useQuery({
|
|
169
|
+
store: runtime.queryStore,
|
|
170
|
+
queryKey: () => core.accountKey(
|
|
171
|
+
runtime.networkConfig.horizonUrl,
|
|
172
|
+
runtime.networkConfig.network,
|
|
173
|
+
address.value ?? ""
|
|
174
|
+
),
|
|
175
|
+
queryFn: () => core.fetchAccount(runtime.networkConfig, { address: address.value }),
|
|
176
|
+
enabled: () => address.value !== null,
|
|
177
|
+
staleTime: options.staleTime,
|
|
178
|
+
maxRetries: options.maxRetries
|
|
179
|
+
});
|
|
180
|
+
const balances = vue.computed(() => query.data.value?.balances ?? []);
|
|
181
|
+
return {
|
|
182
|
+
balance: vue.computed(
|
|
183
|
+
() => balances.value.find((item) => matchesAsset(item, asset))?.balance ?? null
|
|
184
|
+
),
|
|
185
|
+
balances,
|
|
186
|
+
loading: vue.computed(() => query.loading.value),
|
|
187
|
+
error: vue.computed(() => query.error.value),
|
|
188
|
+
refetch: query.refetch
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function unwrapArgs(value) {
|
|
192
|
+
if (!value) return [];
|
|
193
|
+
const raw = vue.toRaw(value);
|
|
194
|
+
return raw.map(
|
|
195
|
+
(argument) => argument !== null && typeof argument === "object" ? vue.toRaw(argument) : argument
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function useSorobanContract(options) {
|
|
199
|
+
const { networkConfig, queryStore } = useStellarRuntime();
|
|
200
|
+
const call = vue.computed(() => {
|
|
201
|
+
const contractId = vue.toValue(options.contractId)?.trim() ?? "";
|
|
202
|
+
const method = vue.toValue(options.method)?.trim() ?? "";
|
|
203
|
+
if (!contractId || !method) return null;
|
|
204
|
+
const spec = options.spec === void 0 ? void 0 : vue.toValue(options.spec);
|
|
205
|
+
const sourceAccount = (options.sourceAccount === void 0 ? void 0 : vue.toValue(options.sourceAccount))?.trim() || core.ANONYMOUS_SIMULATION_SOURCE;
|
|
206
|
+
return {
|
|
207
|
+
contractId,
|
|
208
|
+
method,
|
|
209
|
+
args: unwrapArgs(options.args === void 0 ? [] : vue.toValue(options.args)),
|
|
210
|
+
spec: spec ? vue.toRaw(spec) : void 0,
|
|
211
|
+
sourceAccount
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
const query = useQuery({
|
|
215
|
+
store: queryStore,
|
|
216
|
+
queryKey: () => {
|
|
217
|
+
const current = call.value;
|
|
218
|
+
if (!current) return ["soroban", "disabled"];
|
|
219
|
+
return core.sorobanContractKey(
|
|
220
|
+
networkConfig.sorobanUrl,
|
|
221
|
+
networkConfig.network,
|
|
222
|
+
current.contractId,
|
|
223
|
+
current.method,
|
|
224
|
+
core.argsKey(current.args),
|
|
225
|
+
current.sourceAccount,
|
|
226
|
+
current.spec ? "spec" : "raw"
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
queryFn: () => core.simulateContractCall(networkConfig, call.value),
|
|
230
|
+
enabled: () => call.value !== null,
|
|
231
|
+
staleTime: options.staleTime
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
data: vue.computed(() => query.data.value),
|
|
235
|
+
loading: vue.computed(() => query.loading.value),
|
|
236
|
+
error: vue.computed(
|
|
237
|
+
() => query.error.value ? core.toStellarError(query.error.value) : null
|
|
238
|
+
),
|
|
239
|
+
refetch: () => {
|
|
240
|
+
void query.refetch();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function useContractEvents(options) {
|
|
245
|
+
const { networkConfig } = useStellarRuntime();
|
|
246
|
+
const events = vue.shallowRef([]);
|
|
247
|
+
const latestLedgerState = vue.ref(null);
|
|
248
|
+
const loadingState = vue.ref(false);
|
|
249
|
+
const errorState = vue.shallowRef(null);
|
|
250
|
+
let activePoller = null;
|
|
251
|
+
let activeUnsubscribe = null;
|
|
252
|
+
const input = vue.computed(() => {
|
|
253
|
+
const contractIds = [...vue.toValue(options.contractIds)];
|
|
254
|
+
const rawTopics = options.topics === void 0 ? void 0 : vue.toValue(options.topics);
|
|
255
|
+
const topics = rawTopics?.map((topic) => [...topic]);
|
|
256
|
+
return {
|
|
257
|
+
contractIds,
|
|
258
|
+
contractKey: JSON.stringify(contractIds),
|
|
259
|
+
topics,
|
|
260
|
+
topicKey: JSON.stringify(topics ?? []),
|
|
261
|
+
startLedger: options.startLedger === void 0 ? void 0 : vue.toValue(options.startLedger),
|
|
262
|
+
interval: (options.interval === void 0 ? void 0 : vue.toValue(options.interval)) ?? core.DEFAULT_POLL_INTERVAL,
|
|
263
|
+
bufferSize: (options.bufferSize === void 0 ? void 0 : vue.toValue(options.bufferSize)) ?? core.DEFAULT_BUFFER_SIZE,
|
|
264
|
+
enabled: (options.enabled === void 0 ? void 0 : vue.toValue(options.enabled)) ?? true
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
function stopActivePoller() {
|
|
268
|
+
activeUnsubscribe?.();
|
|
269
|
+
activeUnsubscribe = null;
|
|
270
|
+
activePoller?.stop();
|
|
271
|
+
activePoller = null;
|
|
272
|
+
}
|
|
273
|
+
vue.watch(
|
|
274
|
+
() => [
|
|
275
|
+
input.value.contractKey,
|
|
276
|
+
input.value.topicKey,
|
|
277
|
+
input.value.startLedger,
|
|
278
|
+
input.value.interval,
|
|
279
|
+
input.value.bufferSize,
|
|
280
|
+
input.value.enabled
|
|
281
|
+
],
|
|
282
|
+
() => {
|
|
283
|
+
stopActivePoller();
|
|
284
|
+
const current = input.value;
|
|
285
|
+
if (!current.enabled || current.contractIds.length === 0) {
|
|
286
|
+
events.value = [];
|
|
287
|
+
latestLedgerState.value = null;
|
|
288
|
+
loadingState.value = false;
|
|
289
|
+
errorState.value = null;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const poller = core.createContractEventPoller(
|
|
293
|
+
{ sorobanUrl: networkConfig.sorobanUrl },
|
|
294
|
+
{
|
|
295
|
+
contractIds: current.contractIds,
|
|
296
|
+
topics: current.topics,
|
|
297
|
+
startLedger: current.startLedger,
|
|
298
|
+
interval: current.interval,
|
|
299
|
+
bufferSize: current.bufferSize
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
activePoller = poller;
|
|
303
|
+
activeUnsubscribe = poller.subscribe((snapshot) => {
|
|
304
|
+
events.value = snapshot.events;
|
|
305
|
+
latestLedgerState.value = snapshot.latestLedger;
|
|
306
|
+
loadingState.value = snapshot.loading;
|
|
307
|
+
errorState.value = snapshot.error;
|
|
308
|
+
});
|
|
309
|
+
poller.start();
|
|
310
|
+
},
|
|
311
|
+
{ immediate: true }
|
|
312
|
+
);
|
|
313
|
+
vue.onScopeDispose(stopActivePoller);
|
|
314
|
+
const latestLedger = vue.computed(() => latestLedgerState.value);
|
|
315
|
+
const loading = vue.computed(() => loadingState.value);
|
|
316
|
+
const error = vue.computed(() => errorState.value);
|
|
317
|
+
function clear() {
|
|
318
|
+
activePoller?.clear();
|
|
319
|
+
}
|
|
320
|
+
return { events, latestLedger, loading, error, clear };
|
|
321
|
+
}
|
|
322
|
+
function useSendPayment() {
|
|
323
|
+
const runtime = useStellarRuntime();
|
|
324
|
+
const loading = vue.ref(false);
|
|
325
|
+
const error = vue.shallowRef(null);
|
|
326
|
+
const result = vue.shallowRef(null);
|
|
327
|
+
let operationId = 0;
|
|
328
|
+
let disposed = false;
|
|
329
|
+
const isCurrent = (id) => !disposed && id === operationId;
|
|
330
|
+
async function send(options) {
|
|
331
|
+
if (!core.onlineManager.isOnline()) throw core.offlineError();
|
|
332
|
+
const id = ++operationId;
|
|
333
|
+
const previousError = error.value;
|
|
334
|
+
const previousResult = result.value;
|
|
335
|
+
loading.value = true;
|
|
336
|
+
error.value = null;
|
|
337
|
+
result.value = null;
|
|
338
|
+
try {
|
|
339
|
+
const outcome = await core.sendPayment(
|
|
340
|
+
{
|
|
341
|
+
network: runtime.network,
|
|
342
|
+
networkConfig: runtime.networkConfig,
|
|
343
|
+
wallet: runtime.wallet,
|
|
344
|
+
queryStore: runtime.queryStore
|
|
345
|
+
},
|
|
346
|
+
options
|
|
347
|
+
);
|
|
348
|
+
if (isCurrent(id)) result.value = outcome;
|
|
349
|
+
return outcome;
|
|
350
|
+
} catch (caught) {
|
|
351
|
+
if (caught instanceof core.SendPaymentAbortedError) {
|
|
352
|
+
return { hash: "", status: "failed", error: caught.message };
|
|
353
|
+
}
|
|
354
|
+
if (core.isPreflightError(caught)) {
|
|
355
|
+
if (isCurrent(id)) {
|
|
356
|
+
error.value = previousError;
|
|
357
|
+
result.value = previousResult;
|
|
358
|
+
}
|
|
359
|
+
throw caught;
|
|
360
|
+
}
|
|
361
|
+
const stellarError = caught;
|
|
362
|
+
if (isCurrent(id)) {
|
|
363
|
+
if (stellarError.code === "TX_TIMEOUT") {
|
|
364
|
+
result.value = { hash: stellarError.hash ?? "", status: "pending" };
|
|
365
|
+
} else if (stellarError.isSubmissionFailure) {
|
|
366
|
+
result.value = { hash: stellarError.hash ?? "", status: "failed" };
|
|
367
|
+
}
|
|
368
|
+
error.value = stellarError;
|
|
369
|
+
}
|
|
370
|
+
throw caught;
|
|
371
|
+
} finally {
|
|
372
|
+
if (isCurrent(id)) loading.value = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function reset() {
|
|
376
|
+
operationId += 1;
|
|
377
|
+
loading.value = false;
|
|
378
|
+
error.value = null;
|
|
379
|
+
result.value = null;
|
|
380
|
+
}
|
|
381
|
+
vue.onScopeDispose(() => {
|
|
382
|
+
disposed = true;
|
|
383
|
+
operationId += 1;
|
|
384
|
+
});
|
|
385
|
+
return { send, loading, error, result, reset };
|
|
386
|
+
}
|
|
387
|
+
function useAddTrustline() {
|
|
388
|
+
const runtime = useStellarRuntime();
|
|
389
|
+
const loading = vue.ref(false);
|
|
390
|
+
const error = vue.shallowRef(null);
|
|
391
|
+
const result = vue.shallowRef(null);
|
|
392
|
+
let operationId = 0;
|
|
393
|
+
let disposed = false;
|
|
394
|
+
const isCurrent = (id) => !disposed && id === operationId;
|
|
395
|
+
async function addTrustline(options) {
|
|
396
|
+
if (!core.onlineManager.isOnline()) throw core.offlineError();
|
|
397
|
+
const id = ++operationId;
|
|
398
|
+
const previousError = error.value;
|
|
399
|
+
const previousResult = result.value;
|
|
400
|
+
loading.value = true;
|
|
401
|
+
error.value = null;
|
|
402
|
+
result.value = null;
|
|
403
|
+
try {
|
|
404
|
+
const outcome = await core.addTrustline(
|
|
405
|
+
{
|
|
406
|
+
network: runtime.network,
|
|
407
|
+
networkConfig: runtime.networkConfig,
|
|
408
|
+
wallet: runtime.wallet,
|
|
409
|
+
queryStore: runtime.queryStore
|
|
410
|
+
},
|
|
411
|
+
options
|
|
412
|
+
);
|
|
413
|
+
if (isCurrent(id)) result.value = outcome;
|
|
414
|
+
return outcome;
|
|
415
|
+
} catch (caught) {
|
|
416
|
+
if (caught instanceof core.AddTrustlineAbortedError) {
|
|
417
|
+
return { hash: "", status: "failed" };
|
|
418
|
+
}
|
|
419
|
+
if (core.isAddTrustlinePreflightError(caught)) {
|
|
420
|
+
if (isCurrent(id)) {
|
|
421
|
+
error.value = previousError;
|
|
422
|
+
result.value = previousResult;
|
|
423
|
+
}
|
|
424
|
+
throw caught;
|
|
425
|
+
}
|
|
426
|
+
const stellarError = caught;
|
|
427
|
+
if (isCurrent(id)) {
|
|
428
|
+
if (stellarError.code === "TX_TIMEOUT") {
|
|
429
|
+
result.value = { hash: stellarError.hash ?? "", status: "pending" };
|
|
430
|
+
} else if (stellarError.isSubmissionFailure) {
|
|
431
|
+
result.value = { hash: stellarError.hash ?? "", status: "failed" };
|
|
432
|
+
}
|
|
433
|
+
error.value = stellarError;
|
|
434
|
+
}
|
|
435
|
+
throw caught;
|
|
436
|
+
} finally {
|
|
437
|
+
if (isCurrent(id)) loading.value = false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function reset() {
|
|
441
|
+
operationId += 1;
|
|
442
|
+
loading.value = false;
|
|
443
|
+
error.value = null;
|
|
444
|
+
result.value = null;
|
|
445
|
+
}
|
|
446
|
+
vue.onScopeDispose(() => {
|
|
447
|
+
disposed = true;
|
|
448
|
+
operationId += 1;
|
|
449
|
+
});
|
|
450
|
+
return { addTrustline, loading, error, result, reset };
|
|
451
|
+
}
|
|
452
|
+
function usePathPayment() {
|
|
453
|
+
const runtime = useStellarRuntime();
|
|
454
|
+
const loading = vue.ref(false);
|
|
455
|
+
const error = vue.shallowRef(null);
|
|
456
|
+
const result = vue.shallowRef(null);
|
|
457
|
+
let operationId = 0;
|
|
458
|
+
let disposed = false;
|
|
459
|
+
const isCurrent = (id) => !disposed && id === operationId;
|
|
460
|
+
async function pathPayment(options) {
|
|
461
|
+
if (!core.onlineManager.isOnline()) throw core.offlineError();
|
|
462
|
+
const id = ++operationId;
|
|
463
|
+
const previousError = error.value;
|
|
464
|
+
const previousResult = result.value;
|
|
465
|
+
loading.value = true;
|
|
466
|
+
error.value = null;
|
|
467
|
+
result.value = null;
|
|
468
|
+
try {
|
|
469
|
+
const outcome = await core.pathPayment(
|
|
470
|
+
{
|
|
471
|
+
network: runtime.network,
|
|
472
|
+
networkConfig: runtime.networkConfig,
|
|
473
|
+
wallet: runtime.wallet,
|
|
474
|
+
queryStore: runtime.queryStore
|
|
475
|
+
},
|
|
476
|
+
options
|
|
477
|
+
);
|
|
478
|
+
if (isCurrent(id)) result.value = outcome;
|
|
479
|
+
return outcome;
|
|
480
|
+
} catch (caught) {
|
|
481
|
+
if (core.isPathPaymentPreflightError(caught)) {
|
|
482
|
+
if (isCurrent(id)) {
|
|
483
|
+
error.value = previousError;
|
|
484
|
+
result.value = previousResult;
|
|
485
|
+
}
|
|
486
|
+
throw caught;
|
|
487
|
+
}
|
|
488
|
+
if (isCurrent(id)) error.value = caught;
|
|
489
|
+
throw caught;
|
|
490
|
+
} finally {
|
|
491
|
+
if (isCurrent(id)) loading.value = false;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function reset() {
|
|
495
|
+
operationId += 1;
|
|
496
|
+
loading.value = false;
|
|
497
|
+
error.value = null;
|
|
498
|
+
result.value = null;
|
|
499
|
+
}
|
|
500
|
+
vue.onScopeDispose(() => {
|
|
501
|
+
disposed = true;
|
|
502
|
+
operationId += 1;
|
|
503
|
+
});
|
|
504
|
+
return { pathPayment, loading, error, result, reset };
|
|
505
|
+
}
|
|
506
|
+
function useClaimableBalance(options = {}) {
|
|
507
|
+
const runtime = useStellarRuntime();
|
|
508
|
+
const address = vue.computed(() => {
|
|
509
|
+
const value = options.address === void 0 ? runtime.walletAddress.value : vue.toValue(options.address);
|
|
510
|
+
return value?.trim() || null;
|
|
511
|
+
});
|
|
512
|
+
const query = useQuery({
|
|
513
|
+
store: runtime.queryStore,
|
|
514
|
+
queryKey: () => core.claimableBalanceKey(
|
|
515
|
+
runtime.networkConfig.horizonUrl,
|
|
516
|
+
runtime.networkConfig.network,
|
|
517
|
+
address.value ?? ""
|
|
518
|
+
),
|
|
519
|
+
queryFn: () => core.fetchClaimableBalances(runtime.networkConfig, { address: address.value }),
|
|
520
|
+
enabled: () => address.value !== null,
|
|
521
|
+
staleTime: options.staleTime
|
|
522
|
+
});
|
|
523
|
+
return {
|
|
524
|
+
balances: vue.computed(() => query.data.value ?? []),
|
|
525
|
+
loading: vue.computed(() => query.loading.value),
|
|
526
|
+
error: vue.computed(() => query.error.value),
|
|
527
|
+
refetch: query.refetch
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function usePayments(options = {}) {
|
|
531
|
+
const runtime = useStellarRuntime();
|
|
532
|
+
const address = vue.computed(() => {
|
|
533
|
+
const value = options.address === void 0 ? runtime.walletAddress.value : vue.toValue(options.address);
|
|
534
|
+
return value?.trim() || null;
|
|
535
|
+
});
|
|
536
|
+
const limit = vue.computed(() => vue.toValue(options.limit) ?? 10);
|
|
537
|
+
const order = vue.computed(() => vue.toValue(options.order) ?? "desc");
|
|
538
|
+
const inputCursor = vue.computed(() => vue.toValue(options.cursor));
|
|
539
|
+
const activeCursor = vue.ref(inputCursor.value);
|
|
540
|
+
vue.watch(inputCursor, (cursor) => activeCursor.value = cursor, { flush: "sync" });
|
|
541
|
+
vue.watch(
|
|
542
|
+
[address, limit, order],
|
|
543
|
+
(_next, previous) => {
|
|
544
|
+
if (previous.length > 0) activeCursor.value = void 0;
|
|
545
|
+
},
|
|
546
|
+
{ flush: "sync" }
|
|
547
|
+
);
|
|
548
|
+
const queryKey = vue.computed(
|
|
549
|
+
() => address.value ? core.paymentsKey(
|
|
550
|
+
runtime.networkConfig.horizonUrl,
|
|
551
|
+
runtime.networkConfig.network,
|
|
552
|
+
address.value,
|
|
553
|
+
limit.value,
|
|
554
|
+
order.value,
|
|
555
|
+
activeCursor.value
|
|
556
|
+
) : ["payments", "disabled"]
|
|
557
|
+
);
|
|
558
|
+
const query = useQuery({
|
|
559
|
+
store: runtime.queryStore,
|
|
560
|
+
queryKey: () => queryKey.value,
|
|
561
|
+
queryFn: () => core.fetchPaymentsPage(runtime.networkConfig, {
|
|
562
|
+
address: address.value,
|
|
563
|
+
limit: limit.value,
|
|
564
|
+
order: order.value,
|
|
565
|
+
cursor: activeCursor.value
|
|
566
|
+
}),
|
|
567
|
+
enabled: () => address.value !== null,
|
|
568
|
+
maxRetries: options.maxRetries
|
|
569
|
+
});
|
|
570
|
+
const displayed = vue.shallowRef([]);
|
|
571
|
+
const nextPage = vue.shallowRef(null);
|
|
572
|
+
const prevPage = vue.shallowRef(null);
|
|
573
|
+
const pageHasNext = vue.ref(false);
|
|
574
|
+
const pageHasPrev = vue.ref(false);
|
|
575
|
+
const pageLoading = vue.ref(false);
|
|
576
|
+
const pageError = vue.shallowRef(null);
|
|
577
|
+
let requestId = 0;
|
|
578
|
+
function install(page) {
|
|
579
|
+
displayed.value = page.records;
|
|
580
|
+
nextPage.value = page.nextCursor;
|
|
581
|
+
prevPage.value = page.prevCursor;
|
|
582
|
+
pageHasNext.value = page.hasNext;
|
|
583
|
+
pageHasPrev.value = page.hasPrev;
|
|
584
|
+
pageError.value = null;
|
|
585
|
+
}
|
|
586
|
+
vue.watch(
|
|
587
|
+
query.data,
|
|
588
|
+
(page) => {
|
|
589
|
+
requestId += 1;
|
|
590
|
+
pageLoading.value = false;
|
|
591
|
+
if (page) install(page);
|
|
592
|
+
else {
|
|
593
|
+
displayed.value = [];
|
|
594
|
+
nextPage.value = null;
|
|
595
|
+
prevPage.value = null;
|
|
596
|
+
pageHasNext.value = false;
|
|
597
|
+
pageHasPrev.value = false;
|
|
598
|
+
pageError.value = null;
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
{ immediate: true, flush: "sync" }
|
|
602
|
+
);
|
|
603
|
+
vue.watch(
|
|
604
|
+
() => core.serializeKey(queryKey.value),
|
|
605
|
+
() => {
|
|
606
|
+
requestId += 1;
|
|
607
|
+
displayed.value = [];
|
|
608
|
+
nextPage.value = null;
|
|
609
|
+
prevPage.value = null;
|
|
610
|
+
pageHasNext.value = false;
|
|
611
|
+
pageHasPrev.value = false;
|
|
612
|
+
pageLoading.value = false;
|
|
613
|
+
pageError.value = null;
|
|
614
|
+
},
|
|
615
|
+
{ flush: "sync" }
|
|
616
|
+
);
|
|
617
|
+
async function navigate(direction) {
|
|
618
|
+
const load = direction === "next" ? nextPage.value : prevPage.value;
|
|
619
|
+
if (!load || !address.value) return;
|
|
620
|
+
const id = ++requestId;
|
|
621
|
+
pageLoading.value = true;
|
|
622
|
+
pageError.value = null;
|
|
623
|
+
try {
|
|
624
|
+
const response = await load();
|
|
625
|
+
if (id !== requestId || !address.value) return;
|
|
626
|
+
const hasMore = response.records.length > limit.value;
|
|
627
|
+
const records = hasMore ? response.records.slice(0, limit.value) : response.records;
|
|
628
|
+
const normalized = records.map((record) => core.normalizePayment(record, address.value));
|
|
629
|
+
if (normalized.length > 0) displayed.value = normalized;
|
|
630
|
+
nextPage.value = () => response.next();
|
|
631
|
+
prevPage.value = () => response.prev();
|
|
632
|
+
pageHasNext.value = direction === "next" ? hasMore : true;
|
|
633
|
+
pageHasPrev.value = direction === "prev" ? hasMore : true;
|
|
634
|
+
} catch (cause) {
|
|
635
|
+
if (id !== requestId) return;
|
|
636
|
+
pageError.value = core.toStellarError(cause);
|
|
637
|
+
} finally {
|
|
638
|
+
if (id === requestId) pageLoading.value = false;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
async function refetch() {
|
|
642
|
+
requestId += 1;
|
|
643
|
+
pageError.value = null;
|
|
644
|
+
await query.refetch();
|
|
645
|
+
}
|
|
646
|
+
const error = vue.computed(() => pageError.value ?? query.error.value);
|
|
647
|
+
return {
|
|
648
|
+
payments: vue.computed(() => displayed.value),
|
|
649
|
+
loading: vue.computed(() => pageLoading.value || query.loading.value),
|
|
650
|
+
error,
|
|
651
|
+
isStale: vue.computed(() => error.value !== null && displayed.value.length > 0),
|
|
652
|
+
refetch,
|
|
653
|
+
fetchNext: () => navigate("next"),
|
|
654
|
+
fetchPrev: () => navigate("prev"),
|
|
655
|
+
hasNext: vue.computed(() => pageHasNext.value),
|
|
656
|
+
hasPrev: vue.computed(() => pageHasPrev.value)
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function usePaymentHistory(options = {}) {
|
|
660
|
+
const payments = usePayments({
|
|
661
|
+
address: options.address,
|
|
662
|
+
limit: options.limit,
|
|
663
|
+
order: options.order,
|
|
664
|
+
cursor: options.cursor,
|
|
665
|
+
maxRetries: options.maxRetries
|
|
666
|
+
});
|
|
667
|
+
return {
|
|
668
|
+
...payments,
|
|
669
|
+
payments: vue.computed(
|
|
670
|
+
() => core.filterPayments(payments.payments.value, {
|
|
671
|
+
direction: vue.toValue(options.direction) ?? "all",
|
|
672
|
+
asset: vue.toValue(options.asset) ?? "all"
|
|
673
|
+
})
|
|
674
|
+
)
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function normalizeKey(key) {
|
|
678
|
+
return typeof key === "string" ? key : JSON.stringify(key);
|
|
679
|
+
}
|
|
680
|
+
var DEFAULT_STALE_TIME = 3e4;
|
|
681
|
+
var DEFAULT_GC_TIME = 3e5;
|
|
682
|
+
var VueQueryStore = class {
|
|
683
|
+
constructor(config = {}) {
|
|
684
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
685
|
+
this.entries = vue.reactive(/* @__PURE__ */ new Map());
|
|
686
|
+
this.staleTime = config.staleTime ?? DEFAULT_STALE_TIME;
|
|
687
|
+
this.gcTime = config.gcTime ?? DEFAULT_GC_TIME;
|
|
688
|
+
}
|
|
689
|
+
ensure(key) {
|
|
690
|
+
const existing = this.entries.get(key);
|
|
691
|
+
if (existing) return existing;
|
|
692
|
+
const entry = {
|
|
693
|
+
data: null,
|
|
694
|
+
updatedAt: null,
|
|
695
|
+
loading: false,
|
|
696
|
+
error: null,
|
|
697
|
+
promise: null,
|
|
698
|
+
subscribers: 0,
|
|
699
|
+
gcTimer: null
|
|
700
|
+
};
|
|
701
|
+
this.entries.set(key, entry);
|
|
702
|
+
return entry;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* The live, reactive entry for a key. Read its fields inside a `computed`
|
|
706
|
+
* or `watchEffect` — Vue tracks the read and re-runs when the store writes
|
|
707
|
+
* to it.
|
|
708
|
+
*/
|
|
709
|
+
getEntry(key) {
|
|
710
|
+
return this.ensure(key);
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Registers a subscriber for `key` and cancels any pending GC eviction.
|
|
714
|
+
* Returns the unsubscribe function — call it from `onCleanup` /
|
|
715
|
+
* `onScopeDispose` so an idle entry is eventually evicted.
|
|
716
|
+
*/
|
|
717
|
+
subscribe(key) {
|
|
718
|
+
const entry = this.ensure(key);
|
|
719
|
+
entry.subscribers += 1;
|
|
720
|
+
if (entry.gcTimer !== null) {
|
|
721
|
+
clearTimeout(entry.gcTimer);
|
|
722
|
+
entry.gcTimer = null;
|
|
723
|
+
}
|
|
724
|
+
return () => {
|
|
725
|
+
entry.subscribers = Math.max(0, entry.subscribers - 1);
|
|
726
|
+
if (entry.subscribers === 0) this.scheduleGc(key, entry);
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
/** Number of active subscribers for a key. Exposed for tests. */
|
|
730
|
+
subscriberCount(key) {
|
|
731
|
+
return this.entries.get(key)?.subscribers ?? 0;
|
|
732
|
+
}
|
|
733
|
+
scheduleGc(key, entry) {
|
|
734
|
+
if (this.gcTime === 0) {
|
|
735
|
+
this.entries.delete(key);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
entry.gcTimer = setTimeout(() => {
|
|
739
|
+
if (entry.subscribers === 0) this.entries.delete(key);
|
|
740
|
+
}, this.gcTime);
|
|
741
|
+
}
|
|
742
|
+
isFresh(key, staleTime) {
|
|
743
|
+
const entry = this.entries.get(key);
|
|
744
|
+
if (!entry?.updatedAt) return false;
|
|
745
|
+
return Date.now() - entry.updatedAt < (staleTime ?? this.staleTime);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Runs `queryFn` for `key`, deduplicated: a call made while a fetch for the
|
|
749
|
+
* same key is already in flight awaits that same promise instead of
|
|
750
|
+
* issuing a second request — this is what lets two Vue scopes reading the
|
|
751
|
+
* same query share one network call. Skipped entirely when cached data is
|
|
752
|
+
* still within `staleTime`, unless `force` is set.
|
|
753
|
+
*/
|
|
754
|
+
run(key, queryFn, opts = {}) {
|
|
755
|
+
const entry = this.ensure(key);
|
|
756
|
+
if (!opts.force) {
|
|
757
|
+
if (entry.promise) return entry.promise;
|
|
758
|
+
if (entry.data !== null && this.isFresh(key, opts.staleTime)) {
|
|
759
|
+
return Promise.resolve(entry.data);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
entry.loading = true;
|
|
763
|
+
entry.error = null;
|
|
764
|
+
const promise = queryFn().then(
|
|
765
|
+
(data) => {
|
|
766
|
+
entry.data = data;
|
|
767
|
+
entry.updatedAt = Date.now();
|
|
768
|
+
entry.loading = false;
|
|
769
|
+
entry.error = null;
|
|
770
|
+
entry.promise = null;
|
|
771
|
+
return data;
|
|
772
|
+
},
|
|
773
|
+
(err) => {
|
|
774
|
+
entry.loading = false;
|
|
775
|
+
entry.error = err;
|
|
776
|
+
entry.promise = null;
|
|
777
|
+
throw err;
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
entry.promise = promise;
|
|
781
|
+
return promise;
|
|
782
|
+
}
|
|
783
|
+
/** Marks a cached query stale so its next read fetches fresh data. */
|
|
784
|
+
invalidate(key) {
|
|
785
|
+
const entry = this.entries.get(normalizeKey(key));
|
|
786
|
+
if (entry) entry.updatedAt = null;
|
|
787
|
+
}
|
|
788
|
+
/** Empties the store. Useful in tests; not meant for app code. */
|
|
789
|
+
clear() {
|
|
790
|
+
for (const entry of this.entries.values()) {
|
|
791
|
+
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
792
|
+
}
|
|
793
|
+
this.entries.clear();
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
Object.defineProperty(exports, "ANONYMOUS_SIMULATION_SOURCE", {
|
|
798
|
+
enumerable: true,
|
|
799
|
+
get: function () { return core.ANONYMOUS_SIMULATION_SOURCE; }
|
|
800
|
+
});
|
|
801
|
+
Object.defineProperty(exports, "accountKey", {
|
|
802
|
+
enumerable: true,
|
|
803
|
+
get: function () { return core.accountKey; }
|
|
804
|
+
});
|
|
805
|
+
Object.defineProperty(exports, "claimableBalanceKey", {
|
|
806
|
+
enumerable: true,
|
|
807
|
+
get: function () { return core.claimableBalanceKey; }
|
|
808
|
+
});
|
|
809
|
+
Object.defineProperty(exports, "paymentsKey", {
|
|
810
|
+
enumerable: true,
|
|
811
|
+
get: function () { return core.paymentsKey; }
|
|
812
|
+
});
|
|
813
|
+
exports.NETWORK_CONFIGS = NETWORK_CONFIGS;
|
|
814
|
+
exports.STELLAR_RUNTIME_KEY = STELLAR_RUNTIME_KEY;
|
|
815
|
+
exports.VueQueryStore = VueQueryStore;
|
|
816
|
+
exports.createStellarPlugin = createStellarPlugin;
|
|
817
|
+
exports.createStellarRuntime = createStellarRuntime;
|
|
818
|
+
exports.useAddTrustline = useAddTrustline;
|
|
819
|
+
exports.useBalance = useBalance;
|
|
820
|
+
exports.useClaimableBalance = useClaimableBalance;
|
|
821
|
+
exports.useContractEvents = useContractEvents;
|
|
822
|
+
exports.usePathPayment = usePathPayment;
|
|
823
|
+
exports.usePaymentHistory = usePaymentHistory;
|
|
824
|
+
exports.usePayments = usePayments;
|
|
825
|
+
exports.useSendPayment = useSendPayment;
|
|
826
|
+
exports.useSorobanContract = useSorobanContract;
|
|
827
|
+
exports.useStellarRuntime = useStellarRuntime;
|
|
828
|
+
//# sourceMappingURL=index.js.map
|
|
829
|
+
//# sourceMappingURL=index.js.map
|