@accesly/react 1.1.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.cjs +906 -357
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -59
- package/dist/index.d.ts +196 -59
- package/dist/index.js +892 -345
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,155 +1,526 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var core = require('@accesly/core');
|
|
4
3
|
var react = require('react');
|
|
4
|
+
var core = require('@accesly/core');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
var
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
exports.AcceslyContext = void 0;
|
|
17
|
+
var init_context = __esm({
|
|
18
|
+
"src/context.ts"() {
|
|
19
|
+
exports.AcceslyContext = react.createContext(null);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
9
22
|
|
|
10
23
|
// src/config.ts
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
exports.ENVIRONMENT_DEFAULTS = void 0;
|
|
25
|
+
var init_config = __esm({
|
|
26
|
+
"src/config.ts"() {
|
|
27
|
+
exports.ENVIRONMENT_DEFAULTS = {
|
|
28
|
+
dev: {
|
|
29
|
+
apiUrl: "https://3fki7eiio5.execute-api.us-east-1.amazonaws.com/dev",
|
|
30
|
+
walletStreamUrl: "https://ajlmn37thw7fxen3oyykbfmlrm0eecue.lambda-url.us-east-1.on.aws/",
|
|
31
|
+
cognito: {
|
|
32
|
+
region: "us-east-1",
|
|
33
|
+
userPoolId: "us-east-1_K2Nag1tB1",
|
|
34
|
+
userPoolClientId: "6r64diep7pne50sender4557jt"
|
|
35
|
+
},
|
|
36
|
+
stellar: {
|
|
37
|
+
networkPassphrase: "Test SDF Network ; September 2015",
|
|
38
|
+
horizonUrl: "https://horizon-testnet.stellar.org",
|
|
39
|
+
sorobanRpcUrl: "https://soroban-testnet.stellar.org",
|
|
40
|
+
// OZ Relayer channels-fund — see CloudServices-accesly/docs/Deployed_Resources_dev.md
|
|
41
|
+
deployerAddress: "GDRHSVLY3VCEHCHCSR5MZR2ALYLCERDDFT3ULCUIELGFVYHTZFCMNU4E",
|
|
42
|
+
// accesly-contracts Phase 1 deploy on Stellar testnet.
|
|
43
|
+
ed25519VerifierAddress: "CALVIIGIOMODZMWTMKZLSD4PZFFEPWQBSYERHUFM6MH5FLWKCHW4E4G5"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
staging: {
|
|
47
|
+
apiUrl: "https://api-staging.accesly.xyz",
|
|
48
|
+
walletStreamUrl: "",
|
|
49
|
+
cognito: {
|
|
50
|
+
region: "us-east-1",
|
|
51
|
+
userPoolId: "TBD-staging",
|
|
52
|
+
userPoolClientId: "TBD-staging"
|
|
53
|
+
},
|
|
54
|
+
stellar: {
|
|
55
|
+
networkPassphrase: "Test SDF Network ; September 2015",
|
|
56
|
+
horizonUrl: "https://horizon-testnet.stellar.org",
|
|
57
|
+
sorobanRpcUrl: "https://soroban-testnet.stellar.org",
|
|
58
|
+
deployerAddress: "GDRHSVLY3VCEHCHCSR5MZR2ALYLCERDDFT3ULCUIELGFVYHTZFCMNU4E",
|
|
59
|
+
ed25519VerifierAddress: "CALVIIGIOMODZMWTMKZLSD4PZFFEPWQBSYERHUFM6MH5FLWKCHW4E4G5"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
prod: {
|
|
63
|
+
apiUrl: "https://api.accesly.xyz",
|
|
64
|
+
walletStreamUrl: "",
|
|
65
|
+
cognito: {
|
|
66
|
+
region: "us-east-1",
|
|
67
|
+
userPoolId: "TBD-prod",
|
|
68
|
+
userPoolClientId: "TBD-prod"
|
|
69
|
+
},
|
|
70
|
+
stellar: {
|
|
71
|
+
networkPassphrase: "Public Global Stellar Network ; September 2015",
|
|
72
|
+
horizonUrl: "https://horizon.stellar.org",
|
|
73
|
+
sorobanRpcUrl: "https://soroban-rpc.mainnet.stellar.org",
|
|
74
|
+
deployerAddress: "TBD-prod",
|
|
75
|
+
ed25519VerifierAddress: "TBD-prod"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
function isSorobanDeployPendingError(err) {
|
|
82
|
+
if (!(err instanceof core.AccesslyApiError)) return false;
|
|
83
|
+
const haystack = `${err.message ?? ""} ${err.code ?? ""}`.toLowerCase();
|
|
84
|
+
return haystack.includes("txsorobaninvalid") || haystack.includes("soroban sendtransaction") || haystack.includes("soroban submit failed") || haystack.includes("scecexceededlimit") || haystack.includes("exceededlimit");
|
|
85
|
+
}
|
|
86
|
+
var init_sorobanDeployStatus = __esm({
|
|
87
|
+
"src/hooks/sorobanDeployStatus.ts"() {
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// src/hooks/stellarExpert.ts
|
|
92
|
+
async function fetchContractEvents(contractId, opts) {
|
|
93
|
+
const params = new URLSearchParams();
|
|
94
|
+
params.set("limit", String(opts.limit ?? 50));
|
|
95
|
+
params.set("order", opts.order ?? "desc");
|
|
96
|
+
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
97
|
+
if (opts.topics) params.set("topics", opts.topics);
|
|
98
|
+
const url = `${SE_BASE}/${opts.network}/contract/${contractId}/events?${params.toString()}`;
|
|
99
|
+
const res = await fetch(url, opts.signal ? { signal: opts.signal } : {});
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
throw new Error(`StellarExpert ${res.status} ${res.statusText}`);
|
|
102
|
+
}
|
|
103
|
+
return await res.json();
|
|
104
|
+
}
|
|
105
|
+
async function fetchContractMeta(contractId, network, signal) {
|
|
106
|
+
const url = `${SE_BASE}/${network}/contract/${contractId}`;
|
|
107
|
+
try {
|
|
108
|
+
const res = await fetch(url, signal ? { signal } : {});
|
|
109
|
+
if (!res.ok) return null;
|
|
110
|
+
return await res.json();
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function decodeTransferAmount(bodyXdr) {
|
|
116
|
+
if (!bodyXdr) return "0";
|
|
117
|
+
try {
|
|
118
|
+
const bytes = base64ToBytes(bodyXdr);
|
|
119
|
+
if (bytes.length < 20) return "0";
|
|
120
|
+
let hi = 0n;
|
|
121
|
+
for (let i = 8; i < 16; i += 1) hi = hi << 8n | BigInt(bytes[i] ?? 0);
|
|
122
|
+
let lo = 0n;
|
|
123
|
+
for (let i = 16; i < 24; i += 1) lo = lo << 8n | BigInt(bytes[i] ?? 0);
|
|
124
|
+
const value = hi << 64n | lo;
|
|
125
|
+
return value.toString();
|
|
126
|
+
} catch {
|
|
127
|
+
return "0";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function base64ToBytes(s) {
|
|
131
|
+
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(s, "base64"));
|
|
132
|
+
const bin = globalThis.atob(s);
|
|
133
|
+
const arr = new Uint8Array(bin.length);
|
|
134
|
+
for (let i = 0; i < bin.length; i += 1) arr[i] = bin.charCodeAt(i);
|
|
135
|
+
return arr;
|
|
136
|
+
}
|
|
137
|
+
function decodeSEEvent(ev, walletAddress, xlmSac, txHashByEventId) {
|
|
138
|
+
const t0 = ev.topics[0];
|
|
139
|
+
if (!t0) return null;
|
|
140
|
+
const timestamp = new Date(ev.ts * 1e3).toISOString();
|
|
141
|
+
const txHash = txHashByEventId.get(ev.id) ?? ev.id.split("-")[0] ?? "";
|
|
142
|
+
if (ev.contract === walletAddress) {
|
|
143
|
+
if (t0 === "signer_rotated") {
|
|
144
|
+
return {
|
|
145
|
+
type: "signer-rotated",
|
|
146
|
+
txHash,
|
|
147
|
+
ledger: ev.ts,
|
|
148
|
+
// SE no expone ledger directamente — usamos timestamp
|
|
149
|
+
timestamp,
|
|
150
|
+
newOwnerEd25519Hex: ""
|
|
151
|
+
};
|
|
27
152
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
if (ev.contract === xlmSac && t0 === "transfer") {
|
|
156
|
+
const from = ev.topics[1];
|
|
157
|
+
const to = ev.topics[2];
|
|
158
|
+
if (!from || !to) return null;
|
|
159
|
+
const amountStroops = decodeTransferAmount(ev.bodyXdr);
|
|
160
|
+
if (from === walletAddress && to !== walletAddress) {
|
|
161
|
+
return {
|
|
162
|
+
type: "transfer-out",
|
|
163
|
+
txHash,
|
|
164
|
+
ledger: ev.ts,
|
|
165
|
+
timestamp,
|
|
166
|
+
to,
|
|
167
|
+
amountStroops
|
|
168
|
+
};
|
|
42
169
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
networkPassphrase: "Public Global Stellar Network ; September 2015",
|
|
53
|
-
horizonUrl: "https://horizon.stellar.org",
|
|
54
|
-
sorobanRpcUrl: "https://soroban-rpc.mainnet.stellar.org",
|
|
55
|
-
deployerAddress: "TBD-prod",
|
|
56
|
-
ed25519VerifierAddress: "TBD-prod"
|
|
170
|
+
if (to === walletAddress && from !== walletAddress) {
|
|
171
|
+
return {
|
|
172
|
+
type: "transfer-in",
|
|
173
|
+
txHash,
|
|
174
|
+
ledger: ev.ts,
|
|
175
|
+
timestamp,
|
|
176
|
+
from,
|
|
177
|
+
amountStroops
|
|
178
|
+
};
|
|
57
179
|
}
|
|
58
180
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
async function fetchWalletHistory(walletAddress, opts) {
|
|
184
|
+
const xlmSac = opts.network === "mainnet" ? XLM_SAC_MAINNET : XLM_SAC_TESTNET;
|
|
185
|
+
const limit = 50;
|
|
186
|
+
const transferLimit = opts.transferScanLimit ?? 50;
|
|
187
|
+
const [smartAccountResp, transfersResp] = await Promise.all([
|
|
188
|
+
fetchContractEvents(walletAddress, {
|
|
189
|
+
network: opts.network,
|
|
190
|
+
limit,
|
|
191
|
+
order: "desc",
|
|
192
|
+
...opts.smartAccountCursor ? { cursor: opts.smartAccountCursor } : {},
|
|
193
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
194
|
+
}).catch((err) => {
|
|
195
|
+
console.warn("[stellarExpert] smart account events failed", err);
|
|
196
|
+
return { _embedded: { records: [] } };
|
|
197
|
+
}),
|
|
198
|
+
fetchContractEvents(xlmSac, {
|
|
199
|
+
network: opts.network,
|
|
200
|
+
limit: transferLimit,
|
|
201
|
+
order: "desc",
|
|
202
|
+
topics: "transfer",
|
|
203
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
204
|
+
}).catch((err) => {
|
|
205
|
+
console.warn("[stellarExpert] xlm sac events failed", err);
|
|
206
|
+
return { _embedded: { records: [] } };
|
|
207
|
+
})
|
|
85
208
|
]);
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
209
|
+
const txHashByEventId = /* @__PURE__ */ new Map();
|
|
210
|
+
const items = [];
|
|
211
|
+
const saRecords = smartAccountResp._embedded?.records ?? [];
|
|
212
|
+
const txRecords = transfersResp._embedded?.records ?? [];
|
|
213
|
+
for (const ev of saRecords) {
|
|
214
|
+
const decoded = decodeSEEvent(ev, walletAddress, xlmSac, txHashByEventId);
|
|
215
|
+
if (decoded) items.push(decoded);
|
|
216
|
+
}
|
|
217
|
+
for (const ev of txRecords) {
|
|
218
|
+
const decoded = decodeSEEvent(ev, walletAddress, xlmSac, txHashByEventId);
|
|
219
|
+
if (decoded) items.push(decoded);
|
|
220
|
+
}
|
|
221
|
+
if (!opts.smartAccountCursor) {
|
|
222
|
+
const meta = await fetchContractMeta(walletAddress, opts.network, opts.signal);
|
|
223
|
+
if (meta) {
|
|
224
|
+
items.push({
|
|
225
|
+
type: "wallet-created",
|
|
226
|
+
txHash: "",
|
|
227
|
+
ledger: meta.created,
|
|
228
|
+
timestamp: new Date(meta.created * 1e3).toISOString()
|
|
229
|
+
});
|
|
97
230
|
}
|
|
98
|
-
}
|
|
231
|
+
}
|
|
232
|
+
items.sort((a, b) => (b.ledger ?? 0) - (a.ledger ?? 0));
|
|
233
|
+
return {
|
|
234
|
+
items,
|
|
235
|
+
cursors: {
|
|
236
|
+
smartAccount: extractCursor(smartAccountResp),
|
|
237
|
+
transfers: extractCursor(transfersResp)
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function extractCursor(resp) {
|
|
242
|
+
const records = resp._embedded?.records;
|
|
243
|
+
if (!records || records.length === 0) return null;
|
|
244
|
+
return records[records.length - 1]?.paging_token ?? records[records.length - 1]?.id ?? null;
|
|
245
|
+
}
|
|
246
|
+
var SE_BASE, XLM_SAC_TESTNET, XLM_SAC_MAINNET;
|
|
247
|
+
var init_stellarExpert = __esm({
|
|
248
|
+
"src/hooks/stellarExpert.ts"() {
|
|
249
|
+
SE_BASE = "https://api.stellar.expert/explorer";
|
|
250
|
+
XLM_SAC_TESTNET = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
|
|
251
|
+
XLM_SAC_MAINNET = "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA";
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// src/hooks/useWalletHistory.ts
|
|
256
|
+
var useWalletHistory_exports = {};
|
|
257
|
+
__export(useWalletHistory_exports, {
|
|
258
|
+
historyClearOptimistic: () => historyClearOptimistic,
|
|
259
|
+
historyOptimisticPush: () => historyOptimisticPush,
|
|
260
|
+
useWalletHistory: () => useWalletHistory
|
|
261
|
+
});
|
|
262
|
+
function useStableRef(value) {
|
|
263
|
+
const ref = react.useRef(value);
|
|
264
|
+
ref.current = value;
|
|
265
|
+
return ref;
|
|
266
|
+
}
|
|
267
|
+
function loadCache(walletAddress) {
|
|
268
|
+
if (typeof localStorage === "undefined") return null;
|
|
269
|
+
try {
|
|
270
|
+
const raw = localStorage.getItem(CACHE_KEY_PREFIX + walletAddress);
|
|
271
|
+
if (!raw) return null;
|
|
272
|
+
const parsed = JSON.parse(raw);
|
|
273
|
+
if (Date.now() - parsed.storedAt > CACHE_TTL_MS) return null;
|
|
274
|
+
return parsed;
|
|
275
|
+
} catch {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function saveCache(walletAddress, entry) {
|
|
280
|
+
if (typeof localStorage === "undefined") return;
|
|
281
|
+
try {
|
|
282
|
+
localStorage.setItem(CACHE_KEY_PREFIX + walletAddress, JSON.stringify(entry));
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function historyOptimisticPush(walletAddress, item) {
|
|
287
|
+
const current = optimisticItems.get(walletAddress) ?? [];
|
|
288
|
+
optimisticItems.set(walletAddress, [item, ...current]);
|
|
289
|
+
const listeners = optimisticListeners.get(walletAddress);
|
|
290
|
+
if (listeners) {
|
|
291
|
+
for (const fn of listeners) fn(optimisticItems.get(walletAddress) ?? []);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function historyClearOptimistic(walletAddress) {
|
|
295
|
+
optimisticItems.delete(walletAddress);
|
|
296
|
+
const listeners = optimisticListeners.get(walletAddress);
|
|
297
|
+
if (listeners) {
|
|
298
|
+
for (const fn of listeners) fn([]);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function subscribeOptimistic(walletAddress, listener) {
|
|
302
|
+
let set = optimisticListeners.get(walletAddress);
|
|
303
|
+
if (!set) {
|
|
304
|
+
set = /* @__PURE__ */ new Set();
|
|
305
|
+
optimisticListeners.set(walletAddress, set);
|
|
306
|
+
}
|
|
307
|
+
set.add(listener);
|
|
308
|
+
return () => {
|
|
309
|
+
set?.delete(listener);
|
|
310
|
+
if (set && set.size === 0) optimisticListeners.delete(walletAddress);
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function useWalletHistory(walletAddress, opts = {}) {
|
|
314
|
+
const { wallet, _internal } = useAccesly();
|
|
315
|
+
const username = _internal.username;
|
|
316
|
+
const network = opts.network ?? inferNetwork(_internal.env);
|
|
317
|
+
const [resolvedAddress, setResolvedAddress] = react.useState(walletAddress ?? null);
|
|
318
|
+
const [events, setEvents] = react.useState([]);
|
|
319
|
+
const [optimistic, setOptimistic] = react.useState([]);
|
|
320
|
+
const [cursors, setCursors] = react.useState({
|
|
321
|
+
smartAccount: null,
|
|
322
|
+
transfers: null
|
|
323
|
+
});
|
|
324
|
+
const [isLoading, setIsLoading] = react.useState(true);
|
|
325
|
+
const [error, setError] = react.useState(null);
|
|
326
|
+
const [hasMore, setHasMore] = react.useState(true);
|
|
327
|
+
const walletRef = useStableRef(wallet);
|
|
99
328
|
react.useEffect(() => {
|
|
100
|
-
|
|
101
|
-
|
|
329
|
+
if (walletAddress) {
|
|
330
|
+
setResolvedAddress(walletAddress);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (!username) {
|
|
334
|
+
setResolvedAddress(null);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
let cancelled = false;
|
|
338
|
+
void (async () => {
|
|
339
|
+
try {
|
|
340
|
+
const stored = await walletRef.current.getStoredCredential(username);
|
|
341
|
+
if (cancelled) return;
|
|
342
|
+
setResolvedAddress(stored?.walletAddress ?? null);
|
|
343
|
+
} catch {
|
|
344
|
+
if (!cancelled) setResolvedAddress(null);
|
|
345
|
+
}
|
|
346
|
+
})();
|
|
102
347
|
return () => {
|
|
103
|
-
|
|
348
|
+
cancelled = true;
|
|
104
349
|
};
|
|
105
|
-
}, [
|
|
106
|
-
|
|
107
|
-
()
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
350
|
+
}, [walletAddress, username, walletRef]);
|
|
351
|
+
react.useEffect(() => {
|
|
352
|
+
if (!resolvedAddress) return void 0;
|
|
353
|
+
setOptimistic(optimisticItems.get(resolvedAddress) ?? []);
|
|
354
|
+
return subscribeOptimistic(resolvedAddress, setOptimistic);
|
|
355
|
+
}, [resolvedAddress]);
|
|
356
|
+
react.useEffect(() => {
|
|
357
|
+
if (!resolvedAddress) {
|
|
358
|
+
setIsLoading(false);
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
const cached = loadCache(resolvedAddress);
|
|
362
|
+
if (cached) {
|
|
363
|
+
setEvents(cached.items);
|
|
364
|
+
setCursors(cached.cursors);
|
|
365
|
+
setIsLoading(false);
|
|
366
|
+
}
|
|
367
|
+
let cancelled = false;
|
|
368
|
+
const channel = typeof BroadcastChannel !== "undefined" ? new BroadcastChannel(BROADCAST_CHANNEL_PREFIX + resolvedAddress) : null;
|
|
369
|
+
if (channel) {
|
|
370
|
+
channel.onmessage = (ev) => {
|
|
371
|
+
const data = ev.data;
|
|
372
|
+
if (data && !cancelled) {
|
|
373
|
+
setEvents(data.items);
|
|
374
|
+
setCursors(data.cursors);
|
|
375
|
+
setIsLoading(false);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
void (async () => {
|
|
380
|
+
try {
|
|
381
|
+
const result = await fetchWalletHistory(resolvedAddress, {
|
|
382
|
+
network,
|
|
383
|
+
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
384
|
+
});
|
|
385
|
+
if (cancelled) return;
|
|
386
|
+
const deduped = dedupItems(result.items);
|
|
387
|
+
setEvents(deduped);
|
|
388
|
+
setCursors(result.cursors);
|
|
389
|
+
setIsLoading(false);
|
|
390
|
+
setError(null);
|
|
391
|
+
setHasMore(result.cursors.smartAccount !== null || result.cursors.transfers !== null);
|
|
392
|
+
const entry = {
|
|
393
|
+
items: deduped,
|
|
394
|
+
cursors: result.cursors,
|
|
395
|
+
storedAt: Date.now()
|
|
396
|
+
};
|
|
397
|
+
saveCache(resolvedAddress, entry);
|
|
398
|
+
channel?.postMessage(entry);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
if (!cancelled) {
|
|
401
|
+
setError(err);
|
|
402
|
+
setIsLoading(false);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
})();
|
|
406
|
+
return () => {
|
|
407
|
+
cancelled = true;
|
|
408
|
+
channel?.close();
|
|
409
|
+
};
|
|
410
|
+
}, [resolvedAddress, network, opts.transferScanLimit]);
|
|
411
|
+
const interval = opts.pollIntervalMs ?? POLL_INTERVAL_MS;
|
|
412
|
+
react.useEffect(() => {
|
|
413
|
+
if (!resolvedAddress || interval === 0) return void 0;
|
|
414
|
+
const tick = async () => {
|
|
415
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
416
|
+
try {
|
|
417
|
+
const result = await fetchWalletHistory(resolvedAddress, {
|
|
418
|
+
network,
|
|
419
|
+
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
420
|
+
});
|
|
421
|
+
setEvents((prev) => mergeAndDedup(prev, result.items));
|
|
422
|
+
if (resolvedAddress) {
|
|
423
|
+
const realTxHashes = new Set(result.items.map((it) => it.txHash));
|
|
424
|
+
const optimisticsRemaining = (optimisticItems.get(resolvedAddress) ?? []).filter(
|
|
425
|
+
(it) => !realTxHashes.has(it.txHash)
|
|
426
|
+
);
|
|
427
|
+
if (optimisticsRemaining.length !== (optimisticItems.get(resolvedAddress)?.length ?? 0)) {
|
|
428
|
+
optimisticItems.set(resolvedAddress, optimisticsRemaining);
|
|
429
|
+
const listeners = optimisticListeners.get(resolvedAddress);
|
|
430
|
+
if (listeners) for (const fn of listeners) fn(optimisticsRemaining);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
const id = setInterval(tick, interval);
|
|
437
|
+
return () => clearInterval(id);
|
|
438
|
+
}, [resolvedAddress, network, interval]);
|
|
439
|
+
const loadMoreImpl = react.useCallback(async () => {
|
|
440
|
+
if (!resolvedAddress) return;
|
|
441
|
+
if (!cursors.smartAccount && !cursors.transfers) {
|
|
442
|
+
setHasMore(false);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const result = await fetchWalletHistory(resolvedAddress, {
|
|
447
|
+
network,
|
|
448
|
+
...cursors.smartAccount ? { smartAccountCursor: cursors.smartAccount } : {},
|
|
449
|
+
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
450
|
+
});
|
|
451
|
+
setEvents((prev) => mergeAndDedup(prev, result.items));
|
|
452
|
+
setCursors(result.cursors);
|
|
453
|
+
setHasMore(result.cursors.smartAccount !== null || result.cursors.transfers !== null);
|
|
454
|
+
} catch (err) {
|
|
455
|
+
setError(err);
|
|
456
|
+
}
|
|
457
|
+
}, [resolvedAddress, network, cursors, opts.transferScanLimit]);
|
|
458
|
+
const refreshImpl = react.useCallback(async () => {
|
|
459
|
+
if (!resolvedAddress) return;
|
|
460
|
+
setIsLoading(true);
|
|
461
|
+
try {
|
|
462
|
+
const result = await fetchWalletHistory(resolvedAddress, {
|
|
463
|
+
network,
|
|
464
|
+
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
465
|
+
});
|
|
466
|
+
const deduped = dedupItems(result.items);
|
|
467
|
+
setEvents(deduped);
|
|
468
|
+
setCursors(result.cursors);
|
|
469
|
+
setError(null);
|
|
470
|
+
saveCache(resolvedAddress, {
|
|
471
|
+
items: deduped,
|
|
472
|
+
cursors: result.cursors,
|
|
473
|
+
storedAt: Date.now()
|
|
474
|
+
});
|
|
475
|
+
} catch (err) {
|
|
476
|
+
setError(err);
|
|
477
|
+
} finally {
|
|
478
|
+
setIsLoading(false);
|
|
479
|
+
}
|
|
480
|
+
}, [resolvedAddress, network, opts.transferScanLimit]);
|
|
481
|
+
const combined = [...optimistic, ...events];
|
|
482
|
+
return {
|
|
483
|
+
events: combined,
|
|
484
|
+
isLoading,
|
|
485
|
+
error,
|
|
486
|
+
hasMore,
|
|
487
|
+
loadMore: loadMoreImpl,
|
|
488
|
+
refresh: refreshImpl
|
|
489
|
+
};
|
|
124
490
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
if (tokens instanceof Promise) return "bootstrapping";
|
|
128
|
-
if (!tokens) return "anonymous";
|
|
129
|
-
return Date.now() + 5 * 60 * 1e3 >= tokens.expiresAt ? "expired" : "authenticated";
|
|
491
|
+
function inferNetwork(env) {
|
|
492
|
+
return env === "prod" ? "mainnet" : "testnet";
|
|
130
493
|
}
|
|
131
|
-
function
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
494
|
+
function dedupItems(items) {
|
|
495
|
+
const seen = /* @__PURE__ */ new Set();
|
|
496
|
+
const out = [];
|
|
497
|
+
for (const item of items) {
|
|
498
|
+
const key = `${item.type}:${item.txHash}:${item.ledger}`;
|
|
499
|
+
if (seen.has(key)) continue;
|
|
500
|
+
seen.add(key);
|
|
501
|
+
out.push(item);
|
|
502
|
+
}
|
|
503
|
+
out.sort((a, b) => (b.ledger ?? 0) - (a.ledger ?? 0));
|
|
504
|
+
return out;
|
|
135
505
|
}
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
const haystack = `${err.message ?? ""} ${err.code ?? ""}`.toLowerCase();
|
|
139
|
-
return haystack.includes("txsorobaninvalid") || haystack.includes("soroban sendtransaction") || haystack.includes("soroban submit failed") || haystack.includes("scecexceededlimit") || haystack.includes("exceededlimit");
|
|
506
|
+
function mergeAndDedup(prev, fresh) {
|
|
507
|
+
return dedupItems([...fresh, ...prev]);
|
|
140
508
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
509
|
+
var POLL_INTERVAL_MS, CACHE_TTL_MS, CACHE_KEY_PREFIX, BROADCAST_CHANNEL_PREFIX, optimisticItems, optimisticListeners;
|
|
510
|
+
var init_useWalletHistory = __esm({
|
|
511
|
+
"src/hooks/useWalletHistory.ts"() {
|
|
512
|
+
init_useAccesly();
|
|
513
|
+
init_stellarExpert();
|
|
514
|
+
POLL_INTERVAL_MS = 3e4;
|
|
515
|
+
CACHE_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
516
|
+
CACHE_KEY_PREFIX = "accesly:history:";
|
|
517
|
+
BROADCAST_CHANNEL_PREFIX = "accesly:history:";
|
|
518
|
+
optimisticItems = /* @__PURE__ */ new Map();
|
|
519
|
+
optimisticListeners = /* @__PURE__ */ new Map();
|
|
149
520
|
}
|
|
150
|
-
};
|
|
521
|
+
});
|
|
151
522
|
function useAccesly() {
|
|
152
|
-
const ctx = react.useContext(AcceslyContext);
|
|
523
|
+
const ctx = react.useContext(exports.AcceslyContext);
|
|
153
524
|
if (!ctx) {
|
|
154
525
|
throw new Error(
|
|
155
526
|
"useAccesly: missing <AcceslyProvider>. Wrap your app with <AcceslyProvider appId env>."
|
|
@@ -181,7 +552,7 @@ function useAccesly() {
|
|
|
181
552
|
[ctx]
|
|
182
553
|
);
|
|
183
554
|
const { hexToBytes, hexFromBytes } = react.useMemo(() => coderHelpers(), []);
|
|
184
|
-
const stellarConfig = ENVIRONMENT_DEFAULTS[ctx.env].stellar;
|
|
555
|
+
const stellarConfig = exports.ENVIRONMENT_DEFAULTS[ctx.env].stellar;
|
|
185
556
|
const wallet = react.useMemo(() => {
|
|
186
557
|
const c = ctx;
|
|
187
558
|
const postWallet = async (params) => {
|
|
@@ -612,8 +983,8 @@ function useAccesly() {
|
|
|
612
983
|
const sessionPlaintext = core.unwrapSessionFragment2(wrappedF2, ephemeral.privateKey).plaintext;
|
|
613
984
|
const fragmentF2Wire = JSON.parse(new TextDecoder().decode(sessionPlaintext));
|
|
614
985
|
const fragmentF2Envelope = {
|
|
615
|
-
nonce:
|
|
616
|
-
ciphertext:
|
|
986
|
+
nonce: base64ToBytes2(fragmentF2Wire.nonce),
|
|
987
|
+
ciphertext: base64ToBytes2(fragmentF2Wire.ciphertext)
|
|
617
988
|
};
|
|
618
989
|
const reconstructed = core.reconstructFromPlainAndEncrypted({
|
|
619
990
|
fragmentF1Plain: input.fragmentF1Plain,
|
|
@@ -631,6 +1002,24 @@ function useAccesly() {
|
|
|
631
1002
|
unsignedXdr: sim.unsignedXdr,
|
|
632
1003
|
signedAuthEntryXdr
|
|
633
1004
|
});
|
|
1005
|
+
try {
|
|
1006
|
+
const username = ctx.username;
|
|
1007
|
+
if (username) {
|
|
1008
|
+
const stored = await ctx.deviceStore.loadCredential(username);
|
|
1009
|
+
if (stored?.walletAddress) {
|
|
1010
|
+
const { historyOptimisticPush: historyOptimisticPush2 } = await Promise.resolve().then(() => (init_useWalletHistory(), useWalletHistory_exports));
|
|
1011
|
+
historyOptimisticPush2(stored.walletAddress, {
|
|
1012
|
+
type: "transfer-out",
|
|
1013
|
+
txHash: submit.txHash,
|
|
1014
|
+
ledger: Math.floor(Date.now() / 1e3),
|
|
1015
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1016
|
+
to: input.destinationAddress,
|
|
1017
|
+
amountStroops: input.amountStroops
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
} catch {
|
|
1022
|
+
}
|
|
634
1023
|
return {
|
|
635
1024
|
txHash: submit.txHash,
|
|
636
1025
|
status: submit.status,
|
|
@@ -674,18 +1063,18 @@ function useAccesly() {
|
|
|
674
1063
|
"recovery.reconstructSeed: la wallet fue creada antes de Fase 1 y no tiene F2 cipher-bound a recoveryKey. No es recuperable v\xEDa OTP."
|
|
675
1064
|
);
|
|
676
1065
|
}
|
|
677
|
-
const recoverySalt =
|
|
1066
|
+
const recoverySalt = base64ToBytes2(frag.recoverySalt);
|
|
678
1067
|
const recoveryKey = core.deriveRecoveryKey({
|
|
679
1068
|
password: input.cognitoPassword,
|
|
680
1069
|
salt: recoverySalt
|
|
681
1070
|
});
|
|
682
1071
|
const f2Envelope = {
|
|
683
|
-
ciphertext:
|
|
684
|
-
nonce:
|
|
1072
|
+
ciphertext: base64ToBytes2(frag.fragmentF2Recovery.ciphertext),
|
|
1073
|
+
nonce: base64ToBytes2(frag.fragmentF2Recovery.nonce)
|
|
685
1074
|
};
|
|
686
1075
|
const f3Envelope = {
|
|
687
|
-
ciphertext:
|
|
688
|
-
nonce:
|
|
1076
|
+
ciphertext: base64ToBytes2(frag.fragmentF3Encrypted.ciphertext),
|
|
1077
|
+
nonce: base64ToBytes2(frag.fragmentF3Encrypted.nonce)
|
|
689
1078
|
};
|
|
690
1079
|
const seedResult = core.reconstructKey({
|
|
691
1080
|
fragments: [
|
|
@@ -848,10 +1237,10 @@ function useAccesly() {
|
|
|
848
1237
|
const session = react.useMemo(
|
|
849
1238
|
() => ({
|
|
850
1239
|
async create() {
|
|
851
|
-
throw new NotImplementedYetError("session", "create");
|
|
1240
|
+
throw new exports.NotImplementedYetError("session", "create");
|
|
852
1241
|
},
|
|
853
1242
|
async revoke() {
|
|
854
|
-
throw new NotImplementedYetError("session", "revoke");
|
|
1243
|
+
throw new exports.NotImplementedYetError("session", "revoke");
|
|
855
1244
|
}
|
|
856
1245
|
}),
|
|
857
1246
|
[]
|
|
@@ -859,16 +1248,16 @@ function useAccesly() {
|
|
|
859
1248
|
const settings = react.useMemo(
|
|
860
1249
|
() => ({
|
|
861
1250
|
async addDevice() {
|
|
862
|
-
throw new NotImplementedYetError("settings", "addDevice");
|
|
1251
|
+
throw new exports.NotImplementedYetError("settings", "addDevice");
|
|
863
1252
|
},
|
|
864
1253
|
async removeDevice() {
|
|
865
|
-
throw new NotImplementedYetError("settings", "removeDevice");
|
|
1254
|
+
throw new exports.NotImplementedYetError("settings", "removeDevice");
|
|
866
1255
|
},
|
|
867
1256
|
async listDevices() {
|
|
868
|
-
throw new NotImplementedYetError("settings", "listDevices");
|
|
1257
|
+
throw new exports.NotImplementedYetError("settings", "listDevices");
|
|
869
1258
|
},
|
|
870
1259
|
async updateSpendingLimit() {
|
|
871
|
-
throw new NotImplementedYetError("settings", "updateSpendingLimit");
|
|
1260
|
+
throw new exports.NotImplementedYetError("settings", "updateSpendingLimit");
|
|
872
1261
|
}
|
|
873
1262
|
}),
|
|
874
1263
|
[]
|
|
@@ -876,13 +1265,13 @@ function useAccesly() {
|
|
|
876
1265
|
const yieldOps = react.useMemo(
|
|
877
1266
|
() => ({
|
|
878
1267
|
async invest() {
|
|
879
|
-
throw new NotImplementedYetError("yield", "invest");
|
|
1268
|
+
throw new exports.NotImplementedYetError("yield", "invest");
|
|
880
1269
|
},
|
|
881
1270
|
async redeem() {
|
|
882
|
-
throw new NotImplementedYetError("yield", "redeem");
|
|
1271
|
+
throw new exports.NotImplementedYetError("yield", "redeem");
|
|
883
1272
|
},
|
|
884
1273
|
async position() {
|
|
885
|
-
throw new NotImplementedYetError("yield", "position");
|
|
1274
|
+
throw new exports.NotImplementedYetError("yield", "position");
|
|
886
1275
|
}
|
|
887
1276
|
}),
|
|
888
1277
|
[]
|
|
@@ -921,15 +1310,245 @@ function base64FromBytes(bytes) {
|
|
|
921
1310
|
for (let i = 0; i < bytes.length; i += 1) bin += String.fromCharCode(bytes[i] ?? 0);
|
|
922
1311
|
return globalThis.btoa(bin);
|
|
923
1312
|
}
|
|
924
|
-
function
|
|
1313
|
+
function base64ToBytes2(s) {
|
|
925
1314
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(s, "base64"));
|
|
926
1315
|
const bin = globalThis.atob(s);
|
|
927
1316
|
const arr = new Uint8Array(bin.length);
|
|
928
1317
|
for (let i = 0; i < bin.length; i += 1) arr[i] = bin.charCodeAt(i);
|
|
929
1318
|
return arr;
|
|
930
1319
|
}
|
|
1320
|
+
exports.NotImplementedYetError = void 0;
|
|
1321
|
+
var init_useAccesly = __esm({
|
|
1322
|
+
"src/hooks/useAccesly.ts"() {
|
|
1323
|
+
init_context();
|
|
1324
|
+
init_config();
|
|
1325
|
+
init_sorobanDeployStatus();
|
|
1326
|
+
exports.NotImplementedYetError = class extends Error {
|
|
1327
|
+
constructor(namespace, method) {
|
|
1328
|
+
super(
|
|
1329
|
+
`${namespace}.${method}() is not implemented yet. This namespace ships in a later release; see docs/Handoff_Fase7.md for the roadmap.`
|
|
1330
|
+
);
|
|
1331
|
+
this.name = "NotImplementedYetError";
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
// src/provider.tsx
|
|
1338
|
+
init_context();
|
|
1339
|
+
init_config();
|
|
1340
|
+
function AcceslyProvider(props) {
|
|
1341
|
+
const defaults = exports.ENVIRONMENT_DEFAULTS[props.env];
|
|
1342
|
+
const apiUrl = props.apiUrl ?? defaults.apiUrl;
|
|
1343
|
+
const cognitoConfig = props.cognitoConfig ?? defaults.cognito;
|
|
1344
|
+
const telemetry = props.telemetry;
|
|
1345
|
+
const instances = react.useMemo(() => {
|
|
1346
|
+
const authClient = props.overrides?.authClient ?? new core.CognitoAuthClient(cognitoConfig);
|
|
1347
|
+
const sessionStorage = props.overrides?.sessionStorage ?? core.defaultSessionStorage();
|
|
1348
|
+
const deviceStore = props.overrides?.deviceStore ?? new core.InMemoryDeviceStore();
|
|
1349
|
+
const tokenManager = new core.TokenManager({ authClient, storage: sessionStorage });
|
|
1350
|
+
const apiClient = new core.AccesslyApiClient({
|
|
1351
|
+
baseUrl: apiUrl,
|
|
1352
|
+
getIdToken: () => tokenManager.getValidIdToken(),
|
|
1353
|
+
...telemetry ? { telemetry } : {}
|
|
1354
|
+
});
|
|
1355
|
+
const endpoints = new core.AccesslyEndpoints(apiClient);
|
|
1356
|
+
return { authClient, sessionStorage, deviceStore, tokenManager, endpoints };
|
|
1357
|
+
}, [
|
|
1358
|
+
apiUrl,
|
|
1359
|
+
cognitoConfig.region,
|
|
1360
|
+
cognitoConfig.userPoolId,
|
|
1361
|
+
cognitoConfig.userPoolClientId,
|
|
1362
|
+
props.overrides?.authClient,
|
|
1363
|
+
props.overrides?.sessionStorage,
|
|
1364
|
+
props.overrides?.deviceStore
|
|
1365
|
+
]);
|
|
1366
|
+
const [status, setStatus] = react.useState(() => initialStatus(instances.sessionStorage));
|
|
1367
|
+
const [username, setUsername] = react.useState(
|
|
1368
|
+
() => initialUsername(instances.sessionStorage)
|
|
1369
|
+
);
|
|
1370
|
+
const mountedRef = react.useRef(true);
|
|
1371
|
+
const refreshStatus = react.useCallback(async () => {
|
|
1372
|
+
const next = await instances.tokenManager.getStatus();
|
|
1373
|
+
const tokens = await Promise.resolve(instances.sessionStorage.load());
|
|
1374
|
+
if (mountedRef.current) {
|
|
1375
|
+
setStatus(next);
|
|
1376
|
+
setUsername(tokens?.username ?? null);
|
|
1377
|
+
}
|
|
1378
|
+
}, [instances]);
|
|
1379
|
+
react.useEffect(() => {
|
|
1380
|
+
mountedRef.current = true;
|
|
1381
|
+
void refreshStatus();
|
|
1382
|
+
return () => {
|
|
1383
|
+
mountedRef.current = false;
|
|
1384
|
+
};
|
|
1385
|
+
}, [instances]);
|
|
1386
|
+
const value = react.useMemo(
|
|
1387
|
+
() => ({
|
|
1388
|
+
appId: props.appId,
|
|
1389
|
+
env: props.env,
|
|
1390
|
+
apiUrl,
|
|
1391
|
+
cognitoConfig,
|
|
1392
|
+
authClient: instances.authClient,
|
|
1393
|
+
sessionStorage: instances.sessionStorage,
|
|
1394
|
+
tokenManager: instances.tokenManager,
|
|
1395
|
+
endpoints: instances.endpoints,
|
|
1396
|
+
deviceStore: instances.deviceStore,
|
|
1397
|
+
status,
|
|
1398
|
+
username,
|
|
1399
|
+
refreshStatus
|
|
1400
|
+
}),
|
|
1401
|
+
[props.appId, props.env, apiUrl, cognitoConfig, instances, status, username, refreshStatus]
|
|
1402
|
+
);
|
|
1403
|
+
return /* @__PURE__ */ jsxRuntime.jsx(exports.AcceslyContext.Provider, { value, children: props.children });
|
|
1404
|
+
}
|
|
1405
|
+
function initialStatus(storage) {
|
|
1406
|
+
const tokens = storage.load();
|
|
1407
|
+
if (tokens instanceof Promise) return "bootstrapping";
|
|
1408
|
+
if (!tokens) return "anonymous";
|
|
1409
|
+
return Date.now() + 5 * 60 * 1e3 >= tokens.expiresAt ? "expired" : "authenticated";
|
|
1410
|
+
}
|
|
1411
|
+
function initialUsername(storage) {
|
|
1412
|
+
const tokens = storage.load();
|
|
1413
|
+
if (tokens instanceof Promise) return null;
|
|
1414
|
+
return tokens?.username ?? null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/index.ts
|
|
1418
|
+
init_context();
|
|
1419
|
+
init_config();
|
|
1420
|
+
init_useAccesly();
|
|
1421
|
+
|
|
1422
|
+
// src/hooks/useWalletStatus.ts
|
|
1423
|
+
init_useAccesly();
|
|
1424
|
+
init_config();
|
|
1425
|
+
|
|
1426
|
+
// src/hooks/walletSubscription.ts
|
|
1427
|
+
var ACTIVITY_BUFFER_MAX = 50;
|
|
1428
|
+
var subscriptions = /* @__PURE__ */ new Map();
|
|
1429
|
+
function buildSubscriptionUrl(streamUrl, walletAddress) {
|
|
1430
|
+
const base = streamUrl.replace(/\/$/, "");
|
|
1431
|
+
return `${base}/?walletAddress=${encodeURIComponent(walletAddress)}`;
|
|
1432
|
+
}
|
|
1433
|
+
function openConnection(state) {
|
|
1434
|
+
if (state.eventSource) return;
|
|
1435
|
+
if (typeof EventSource === "undefined") return;
|
|
1436
|
+
let es;
|
|
1437
|
+
try {
|
|
1438
|
+
es = new EventSource(state.url, { withCredentials: false });
|
|
1439
|
+
} catch (err) {
|
|
1440
|
+
console.warn("[walletSubscription] EventSource construction failed", err);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
state.eventSource = es;
|
|
1444
|
+
es.addEventListener("status", (ev) => {
|
|
1445
|
+
try {
|
|
1446
|
+
const data = JSON.parse(ev.data);
|
|
1447
|
+
state.lastStatus = data;
|
|
1448
|
+
for (const listener of state.listeners.status) listener(data);
|
|
1449
|
+
} catch {
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
es.addEventListener("balance", (ev) => {
|
|
1453
|
+
try {
|
|
1454
|
+
const data = JSON.parse(ev.data);
|
|
1455
|
+
state.lastBalance = data;
|
|
1456
|
+
for (const listener of state.listeners.balance) listener(data);
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
es.addEventListener("activity", (ev) => {
|
|
1461
|
+
try {
|
|
1462
|
+
const data = JSON.parse(ev.data);
|
|
1463
|
+
const merged = [...data.events, ...state.activityBuffer];
|
|
1464
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1465
|
+
const deduped = [];
|
|
1466
|
+
for (const item of merged) {
|
|
1467
|
+
const key = `${item.txHash}:${item.ledger}`;
|
|
1468
|
+
if (seen.has(key)) continue;
|
|
1469
|
+
seen.add(key);
|
|
1470
|
+
deduped.push(item);
|
|
1471
|
+
if (deduped.length >= ACTIVITY_BUFFER_MAX) break;
|
|
1472
|
+
}
|
|
1473
|
+
state.activityBuffer = deduped;
|
|
1474
|
+
for (const listener of state.listeners.activity) {
|
|
1475
|
+
listener({ events: state.activityBuffer });
|
|
1476
|
+
}
|
|
1477
|
+
} catch {
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
es.addEventListener("close", () => {
|
|
1481
|
+
});
|
|
1482
|
+
es.onerror = () => {
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function closeConnection(state) {
|
|
1486
|
+
if (!state.eventSource) return;
|
|
1487
|
+
state.eventSource.close();
|
|
1488
|
+
state.eventSource = null;
|
|
1489
|
+
}
|
|
1490
|
+
function getOrCreateSubscription(streamUrl, walletAddress) {
|
|
1491
|
+
const existing = subscriptions.get(walletAddress);
|
|
1492
|
+
if (existing) return existing;
|
|
1493
|
+
const state = {
|
|
1494
|
+
walletAddress,
|
|
1495
|
+
url: buildSubscriptionUrl(streamUrl, walletAddress),
|
|
1496
|
+
eventSource: null,
|
|
1497
|
+
listeners: {
|
|
1498
|
+
status: /* @__PURE__ */ new Set(),
|
|
1499
|
+
balance: /* @__PURE__ */ new Set(),
|
|
1500
|
+
activity: /* @__PURE__ */ new Set()
|
|
1501
|
+
},
|
|
1502
|
+
lastStatus: null,
|
|
1503
|
+
lastBalance: null,
|
|
1504
|
+
activityBuffer: [],
|
|
1505
|
+
refCount: 0
|
|
1506
|
+
};
|
|
1507
|
+
subscriptions.set(walletAddress, state);
|
|
1508
|
+
return state;
|
|
1509
|
+
}
|
|
1510
|
+
function subscribeToWalletEvent(streamUrl, walletAddress, eventType, listener) {
|
|
1511
|
+
if (!streamUrl || typeof EventSource === "undefined") return null;
|
|
1512
|
+
const state = getOrCreateSubscription(streamUrl, walletAddress);
|
|
1513
|
+
state.refCount += 1;
|
|
1514
|
+
state.listeners[eventType].add(listener);
|
|
1515
|
+
if (eventType === "status" && state.lastStatus) {
|
|
1516
|
+
listener(state.lastStatus);
|
|
1517
|
+
} else if (eventType === "balance" && state.lastBalance) {
|
|
1518
|
+
listener(state.lastBalance);
|
|
1519
|
+
} else if (eventType === "activity" && state.activityBuffer.length > 0) {
|
|
1520
|
+
listener({
|
|
1521
|
+
events: state.activityBuffer
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
if (!state.eventSource) openConnection(state);
|
|
1525
|
+
return () => {
|
|
1526
|
+
state.listeners[eventType].delete(listener);
|
|
1527
|
+
state.refCount -= 1;
|
|
1528
|
+
if (state.refCount <= 0) {
|
|
1529
|
+
closeConnection(state);
|
|
1530
|
+
subscriptions.delete(walletAddress);
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
function closeAllWalletSubscriptions() {
|
|
1535
|
+
for (const state of subscriptions.values()) closeConnection(state);
|
|
1536
|
+
subscriptions.clear();
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// src/hooks/useWalletStatus.ts
|
|
931
1540
|
var POLL_BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 3e4];
|
|
932
1541
|
var STALE_THRESHOLD_MS = 6e4;
|
|
1542
|
+
function useStableRef2(value) {
|
|
1543
|
+
const ref = react.useRef(value);
|
|
1544
|
+
ref.current = value;
|
|
1545
|
+
return ref;
|
|
1546
|
+
}
|
|
1547
|
+
function deriveStatus(onChain) {
|
|
1548
|
+
if (onChain === true) return "on-chain";
|
|
1549
|
+
if (onChain === false) return "pending-deploy";
|
|
1550
|
+
return "unknown";
|
|
1551
|
+
}
|
|
933
1552
|
function useWalletStatus() {
|
|
934
1553
|
const { wallet, _internal } = useAccesly();
|
|
935
1554
|
const username = _internal.username;
|
|
@@ -938,147 +1557,75 @@ function useWalletStatus() {
|
|
|
938
1557
|
const [onChain, setOnChain] = react.useState(null);
|
|
939
1558
|
const [lastSuccessAt, setLastSuccessAt] = react.useState(0);
|
|
940
1559
|
const [isStale, setIsStale] = react.useState(false);
|
|
941
|
-
const
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
setStatus("no-wallet");
|
|
945
|
-
setWalletAddress(null);
|
|
946
|
-
setOnChain(null);
|
|
947
|
-
setLastSuccessAt(Date.now());
|
|
948
|
-
setIsStale(false);
|
|
949
|
-
return "no-wallet";
|
|
950
|
-
}
|
|
951
|
-
const next = res.onChain === true ? "on-chain" : res.onChain === false ? "pending-deploy" : "unknown";
|
|
952
|
-
setStatus(next);
|
|
953
|
-
setWalletAddress(res.walletAddress);
|
|
954
|
-
setOnChain(res.onChain);
|
|
955
|
-
setLastSuccessAt(Date.now());
|
|
956
|
-
setIsStale(false);
|
|
957
|
-
return next;
|
|
958
|
-
}, []);
|
|
1560
|
+
const envDefaults = exports.ENVIRONMENT_DEFAULTS[_internal.env];
|
|
1561
|
+
const streamUrl = envDefaults.walletStreamUrl;
|
|
1562
|
+
const walletRef = useStableRef2(wallet);
|
|
959
1563
|
const doFetch = react.useCallback(async () => {
|
|
960
1564
|
if (!username) return null;
|
|
961
1565
|
try {
|
|
962
|
-
const remote = await
|
|
963
|
-
if (!remote)
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1566
|
+
const remote = await walletRef.current.fetchRemote();
|
|
1567
|
+
if (!remote) {
|
|
1568
|
+
setStatus("no-wallet");
|
|
1569
|
+
setWalletAddress(null);
|
|
1570
|
+
setOnChain(null);
|
|
1571
|
+
setLastSuccessAt(Date.now());
|
|
1572
|
+
setIsStale(false);
|
|
1573
|
+
return "no-wallet";
|
|
1574
|
+
}
|
|
1575
|
+
const next = deriveStatus(remote.onChain);
|
|
1576
|
+
setStatus(next);
|
|
1577
|
+
setWalletAddress(remote.walletAddress);
|
|
1578
|
+
setOnChain(remote.onChain);
|
|
1579
|
+
setLastSuccessAt(Date.now());
|
|
1580
|
+
setIsStale(false);
|
|
1581
|
+
return next;
|
|
969
1582
|
} catch {
|
|
970
1583
|
return null;
|
|
971
1584
|
}
|
|
972
|
-
}, [username,
|
|
973
|
-
|
|
974
|
-
if (!username || typeof BroadcastChannel === "undefined") return void 0;
|
|
975
|
-
const channel = new BroadcastChannel(`accesly:wallet:${username}`);
|
|
976
|
-
channel.onmessage = (ev) => {
|
|
977
|
-
const data = ev.data;
|
|
978
|
-
if (data && (data.kind === "ok" || data.kind === "not-found")) {
|
|
979
|
-
apply(data);
|
|
980
|
-
}
|
|
981
|
-
};
|
|
982
|
-
return () => channel.close();
|
|
983
|
-
}, [username, apply]);
|
|
984
|
-
const broadcastIfAvailable = react.useCallback(
|
|
985
|
-
(res) => {
|
|
986
|
-
if (!username || typeof BroadcastChannel === "undefined") return;
|
|
987
|
-
try {
|
|
988
|
-
const channel = new BroadcastChannel(`accesly:wallet:${username}`);
|
|
989
|
-
channel.postMessage(res);
|
|
990
|
-
channel.close();
|
|
991
|
-
} catch {
|
|
992
|
-
}
|
|
993
|
-
},
|
|
994
|
-
[username]
|
|
995
|
-
);
|
|
1585
|
+
}, [username, walletRef]);
|
|
1586
|
+
const doFetchRef = useStableRef2(doFetch);
|
|
996
1587
|
react.useEffect(() => {
|
|
997
1588
|
if (!username) {
|
|
998
1589
|
setStatus("unknown");
|
|
999
1590
|
return void 0;
|
|
1000
1591
|
}
|
|
1001
1592
|
let cancelled = false;
|
|
1593
|
+
let unsubscribe = null;
|
|
1002
1594
|
let pollTimer = null;
|
|
1003
1595
|
let backoffIndex = 0;
|
|
1004
|
-
let eventSource = null;
|
|
1005
1596
|
const schedulePoll = (delayMs) => {
|
|
1006
1597
|
if (cancelled) return;
|
|
1007
1598
|
pollTimer = setTimeout(async () => {
|
|
1008
1599
|
if (cancelled) return;
|
|
1009
|
-
if (typeof document !== "undefined" && document.hidden)
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
broadcastIfAvailable({
|
|
1015
|
-
kind: next === "no-wallet" ? "not-found" : "ok",
|
|
1016
|
-
walletAddress: walletAddress ?? "",
|
|
1017
|
-
onChain
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
if (next !== "on-chain" && next !== "no-wallet") {
|
|
1021
|
-
backoffIndex = Math.min(backoffIndex + 1, POLL_BACKOFF_MS.length - 1);
|
|
1022
|
-
schedulePoll(POLL_BACKOFF_MS[backoffIndex]);
|
|
1023
|
-
}
|
|
1600
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
1601
|
+
const next = await doFetchRef.current();
|
|
1602
|
+
if (next === "on-chain" || next === "no-wallet") return;
|
|
1603
|
+
backoffIndex = Math.min(backoffIndex + 1, POLL_BACKOFF_MS.length - 1);
|
|
1604
|
+
schedulePoll(POLL_BACKOFF_MS[backoffIndex]);
|
|
1024
1605
|
}, delayMs);
|
|
1025
1606
|
};
|
|
1026
|
-
const startPolling = () => {
|
|
1027
|
-
backoffIndex = 0;
|
|
1028
|
-
schedulePoll(POLL_BACKOFF_MS[0]);
|
|
1029
|
-
};
|
|
1030
|
-
const stopPolling = () => {
|
|
1031
|
-
if (pollTimer) {
|
|
1032
|
-
clearTimeout(pollTimer);
|
|
1033
|
-
pollTimer = null;
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
const tryOpenSse = () => {
|
|
1037
|
-
if (typeof EventSource === "undefined") return false;
|
|
1038
|
-
try {
|
|
1039
|
-
const url = `${_internal.apiUrl.replace(/\/$/, "")}/wallets/stream`;
|
|
1040
|
-
eventSource = new EventSource(url, { withCredentials: false });
|
|
1041
|
-
eventSource.onmessage = (ev) => {
|
|
1042
|
-
try {
|
|
1043
|
-
const data = JSON.parse(ev.data);
|
|
1044
|
-
if (typeof data.walletAddress === "string") {
|
|
1045
|
-
const res = {
|
|
1046
|
-
kind: "ok",
|
|
1047
|
-
walletAddress: data.walletAddress,
|
|
1048
|
-
onChain: data.onChain ?? null
|
|
1049
|
-
};
|
|
1050
|
-
apply(res);
|
|
1051
|
-
broadcastIfAvailable(res);
|
|
1052
|
-
}
|
|
1053
|
-
} catch {
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
eventSource.onerror = () => {
|
|
1057
|
-
eventSource?.close();
|
|
1058
|
-
eventSource = null;
|
|
1059
|
-
startPolling();
|
|
1060
|
-
};
|
|
1061
|
-
return true;
|
|
1062
|
-
} catch {
|
|
1063
|
-
return false;
|
|
1064
|
-
}
|
|
1065
|
-
};
|
|
1066
1607
|
void (async () => {
|
|
1067
|
-
const
|
|
1608
|
+
const initial = await doFetchRef.current();
|
|
1068
1609
|
if (cancelled) return;
|
|
1069
|
-
if (
|
|
1070
|
-
|
|
1610
|
+
if (initial === "on-chain" || initial === "no-wallet") return;
|
|
1611
|
+
if (walletAddress) {
|
|
1612
|
+
unsubscribe = subscribeToWalletEvent(streamUrl, walletAddress, "status", (data) => {
|
|
1613
|
+
setWalletAddress(data.walletAddress);
|
|
1614
|
+
setOnChain(data.onChain);
|
|
1615
|
+
setStatus(deriveStatus(data.onChain));
|
|
1616
|
+
setLastSuccessAt(Date.now());
|
|
1617
|
+
setIsStale(false);
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
if (!unsubscribe) {
|
|
1621
|
+
backoffIndex = 0;
|
|
1622
|
+
schedulePoll(POLL_BACKOFF_MS[0]);
|
|
1071
1623
|
}
|
|
1072
|
-
const sseOpen = tryOpenSse();
|
|
1073
|
-
if (!sseOpen) startPolling();
|
|
1074
1624
|
})();
|
|
1075
1625
|
const onVisibilityChange = () => {
|
|
1076
1626
|
if (typeof document === "undefined") return;
|
|
1077
|
-
if (document.hidden) {
|
|
1078
|
-
|
|
1079
|
-
} else if (!eventSource && status !== "on-chain" && status !== "no-wallet") {
|
|
1080
|
-
void doFetch();
|
|
1081
|
-
startPolling();
|
|
1627
|
+
if (!document.hidden && !unsubscribe) {
|
|
1628
|
+
void doFetchRef.current();
|
|
1082
1629
|
}
|
|
1083
1630
|
};
|
|
1084
1631
|
if (typeof document !== "undefined") {
|
|
@@ -1089,33 +1636,27 @@ function useWalletStatus() {
|
|
|
1089
1636
|
setIsStale(true);
|
|
1090
1637
|
}
|
|
1091
1638
|
}, 3e4);
|
|
1092
|
-
refreshRef.current = async () => {
|
|
1093
|
-
const next = await doFetch();
|
|
1094
|
-
if (next === "on-chain" || next === "no-wallet") {
|
|
1095
|
-
stopPolling();
|
|
1096
|
-
} else {
|
|
1097
|
-
backoffIndex = 0;
|
|
1098
|
-
stopPolling();
|
|
1099
|
-
startPolling();
|
|
1100
|
-
}
|
|
1101
|
-
};
|
|
1102
1639
|
return () => {
|
|
1103
1640
|
cancelled = true;
|
|
1104
|
-
|
|
1641
|
+
if (unsubscribe) unsubscribe();
|
|
1642
|
+
if (pollTimer) clearTimeout(pollTimer);
|
|
1105
1643
|
clearInterval(staleTimer);
|
|
1106
|
-
eventSource?.close();
|
|
1107
1644
|
if (typeof document !== "undefined") {
|
|
1108
1645
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1109
1646
|
}
|
|
1110
1647
|
};
|
|
1111
|
-
}, [username,
|
|
1648
|
+
}, [username, streamUrl, walletAddress]);
|
|
1112
1649
|
const refresh = react.useCallback(async () => {
|
|
1113
|
-
await
|
|
1114
|
-
}, []);
|
|
1650
|
+
await doFetchRef.current();
|
|
1651
|
+
}, [doFetchRef]);
|
|
1115
1652
|
return { status, walletAddress, onChain, isStale, refresh };
|
|
1116
1653
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1654
|
+
|
|
1655
|
+
// src/hooks/useBalance.ts
|
|
1656
|
+
init_useAccesly();
|
|
1657
|
+
init_config();
|
|
1658
|
+
var POLL_FALLBACK_MS = 1e4;
|
|
1659
|
+
function useStableRef3(value) {
|
|
1119
1660
|
const ref = react.useRef(value);
|
|
1120
1661
|
ref.current = value;
|
|
1121
1662
|
return ref;
|
|
@@ -1130,8 +1671,7 @@ function useBalance(walletAddress) {
|
|
|
1130
1671
|
const [xlm, setXlm] = react.useState(null);
|
|
1131
1672
|
const [isLoading, setIsLoading] = react.useState(true);
|
|
1132
1673
|
const [error, setError] = react.useState(null);
|
|
1133
|
-
const
|
|
1134
|
-
const walletRef = useStableRef(wallet);
|
|
1674
|
+
const walletRef = useStableRef3(wallet);
|
|
1135
1675
|
react.useEffect(() => {
|
|
1136
1676
|
if (walletAddress) {
|
|
1137
1677
|
setResolvedAddress(walletAddress);
|
|
@@ -1155,11 +1695,11 @@ function useBalance(walletAddress) {
|
|
|
1155
1695
|
cancelled = true;
|
|
1156
1696
|
};
|
|
1157
1697
|
}, [walletAddress, username, walletRef]);
|
|
1158
|
-
const
|
|
1159
|
-
const
|
|
1698
|
+
const envDefaults = exports.ENVIRONMENT_DEFAULTS[_internal.env];
|
|
1699
|
+
const streamUrl = envDefaults.walletStreamUrl;
|
|
1700
|
+
const endpointsRef = useStableRef3(_internal.endpoints);
|
|
1701
|
+
const doFetchOnce = react.useCallback(async () => {
|
|
1160
1702
|
if (!resolvedAddress) return;
|
|
1161
|
-
if (fetchInFlight.current) return;
|
|
1162
|
-
fetchInFlight.current = true;
|
|
1163
1703
|
try {
|
|
1164
1704
|
const res = await endpointsRef.current.walletBalance(resolvedAddress);
|
|
1165
1705
|
setStroops(res.xlm.stroops);
|
|
@@ -1169,58 +1709,60 @@ function useBalance(walletAddress) {
|
|
|
1169
1709
|
setError(err);
|
|
1170
1710
|
} finally {
|
|
1171
1711
|
setIsLoading(false);
|
|
1172
|
-
fetchInFlight.current = false;
|
|
1173
1712
|
}
|
|
1174
1713
|
}, [resolvedAddress, endpointsRef]);
|
|
1175
|
-
const doFetchRef =
|
|
1714
|
+
const doFetchRef = useStableRef3(doFetchOnce);
|
|
1176
1715
|
react.useEffect(() => {
|
|
1177
1716
|
if (!resolvedAddress) {
|
|
1178
1717
|
setIsLoading(false);
|
|
1179
1718
|
return void 0;
|
|
1180
1719
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
const stopInterval = () => {
|
|
1191
|
-
if (interval) {
|
|
1192
|
-
clearInterval(interval);
|
|
1193
|
-
interval = null;
|
|
1720
|
+
const unsubscribe = subscribeToWalletEvent(
|
|
1721
|
+
streamUrl,
|
|
1722
|
+
resolvedAddress,
|
|
1723
|
+
"balance",
|
|
1724
|
+
(data) => {
|
|
1725
|
+
setStroops(data.stroops);
|
|
1726
|
+
setXlm(data.xlm);
|
|
1727
|
+
setError(null);
|
|
1728
|
+
setIsLoading(false);
|
|
1194
1729
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1730
|
+
);
|
|
1731
|
+
if (unsubscribe) {
|
|
1732
|
+
void doFetchRef.current();
|
|
1733
|
+
return unsubscribe;
|
|
1734
|
+
}
|
|
1735
|
+
void doFetchRef.current();
|
|
1736
|
+
const interval = setInterval(() => {
|
|
1737
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
1738
|
+
void doFetchRef.current();
|
|
1739
|
+
}, POLL_FALLBACK_MS);
|
|
1197
1740
|
const onVisibilityChange = () => {
|
|
1198
1741
|
if (typeof document === "undefined") return;
|
|
1199
|
-
if (document.hidden)
|
|
1200
|
-
stopInterval();
|
|
1201
|
-
} else {
|
|
1202
|
-
void doFetchRef.current();
|
|
1203
|
-
startInterval();
|
|
1204
|
-
}
|
|
1742
|
+
if (!document.hidden) void doFetchRef.current();
|
|
1205
1743
|
};
|
|
1206
1744
|
if (typeof document !== "undefined") {
|
|
1207
1745
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
1208
1746
|
}
|
|
1209
1747
|
return () => {
|
|
1210
|
-
|
|
1748
|
+
clearInterval(interval);
|
|
1211
1749
|
if (typeof document !== "undefined") {
|
|
1212
1750
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1213
1751
|
}
|
|
1214
1752
|
};
|
|
1215
|
-
}, [resolvedAddress, doFetchRef]);
|
|
1753
|
+
}, [resolvedAddress, streamUrl, doFetchRef]);
|
|
1216
1754
|
const refresh = react.useCallback(async () => {
|
|
1217
1755
|
await doFetchRef.current();
|
|
1218
1756
|
}, [doFetchRef]);
|
|
1219
1757
|
return { stroops, xlm, isLoading, error, refresh };
|
|
1220
1758
|
}
|
|
1221
|
-
|
|
1759
|
+
|
|
1760
|
+
// src/hooks/useWalletActivity.ts
|
|
1761
|
+
init_useAccesly();
|
|
1762
|
+
init_config();
|
|
1763
|
+
var POLL_FALLBACK_MS2 = 25e3;
|
|
1222
1764
|
var DEFAULT_LIMIT = 20;
|
|
1223
|
-
function
|
|
1765
|
+
function useStableRef4(value) {
|
|
1224
1766
|
const ref = react.useRef(value);
|
|
1225
1767
|
ref.current = value;
|
|
1226
1768
|
return ref;
|
|
@@ -1235,8 +1777,7 @@ function useWalletActivity(walletAddress, opts = {}) {
|
|
|
1235
1777
|
const [events, setEvents] = react.useState([]);
|
|
1236
1778
|
const [isLoading, setIsLoading] = react.useState(true);
|
|
1237
1779
|
const [error, setError] = react.useState(null);
|
|
1238
|
-
const
|
|
1239
|
-
const walletRef = useStableRef2(wallet);
|
|
1780
|
+
const walletRef = useStableRef4(wallet);
|
|
1240
1781
|
react.useEffect(() => {
|
|
1241
1782
|
if (walletAddress) {
|
|
1242
1783
|
setResolvedAddress(walletAddress);
|
|
@@ -1260,79 +1801,87 @@ function useWalletActivity(walletAddress, opts = {}) {
|
|
|
1260
1801
|
cancelled = true;
|
|
1261
1802
|
};
|
|
1262
1803
|
}, [walletAddress, username, walletRef]);
|
|
1263
|
-
const
|
|
1264
|
-
const
|
|
1804
|
+
const envDefaults = exports.ENVIRONMENT_DEFAULTS[_internal.env];
|
|
1805
|
+
const streamUrl = envDefaults.walletStreamUrl;
|
|
1806
|
+
const endpointsRef = useStableRef4(_internal.endpoints);
|
|
1807
|
+
const doFetchOnce = react.useCallback(async () => {
|
|
1265
1808
|
if (!resolvedAddress) return;
|
|
1266
|
-
if (fetchInFlight.current) return;
|
|
1267
|
-
fetchInFlight.current = true;
|
|
1268
1809
|
try {
|
|
1269
1810
|
const res = await endpointsRef.current.walletActivity(resolvedAddress, limit);
|
|
1270
|
-
|
|
1811
|
+
const adapted = [];
|
|
1812
|
+
for (const ev of res.events) {
|
|
1813
|
+
const conv = adaptRestEvent(ev);
|
|
1814
|
+
if (conv) adapted.push(conv);
|
|
1815
|
+
}
|
|
1816
|
+
setEvents(adapted.slice(0, limit));
|
|
1271
1817
|
setError(null);
|
|
1272
1818
|
} catch (err) {
|
|
1273
1819
|
setError(err);
|
|
1274
1820
|
} finally {
|
|
1275
1821
|
setIsLoading(false);
|
|
1276
|
-
fetchInFlight.current = false;
|
|
1277
1822
|
}
|
|
1278
1823
|
}, [resolvedAddress, limit, endpointsRef]);
|
|
1279
|
-
const doFetchRef =
|
|
1824
|
+
const doFetchRef = useStableRef4(doFetchOnce);
|
|
1280
1825
|
react.useEffect(() => {
|
|
1281
1826
|
if (!resolvedAddress) {
|
|
1282
1827
|
setIsLoading(false);
|
|
1283
1828
|
return void 0;
|
|
1284
1829
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
};
|
|
1294
|
-
const stop = () => {
|
|
1295
|
-
if (interval) {
|
|
1296
|
-
clearInterval(interval);
|
|
1297
|
-
interval = null;
|
|
1830
|
+
const unsubscribe = subscribeToWalletEvent(
|
|
1831
|
+
streamUrl,
|
|
1832
|
+
resolvedAddress,
|
|
1833
|
+
"activity",
|
|
1834
|
+
(data) => {
|
|
1835
|
+
setEvents(data.events.slice(0, limit));
|
|
1836
|
+
setError(null);
|
|
1837
|
+
setIsLoading(false);
|
|
1298
1838
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
if (document.hidden) stop();
|
|
1304
|
-
else {
|
|
1305
|
-
void doFetchRef.current();
|
|
1306
|
-
start();
|
|
1307
|
-
}
|
|
1308
|
-
};
|
|
1309
|
-
if (typeof document !== "undefined") {
|
|
1310
|
-
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
1839
|
+
);
|
|
1840
|
+
if (unsubscribe) {
|
|
1841
|
+
void doFetchRef.current();
|
|
1842
|
+
return unsubscribe;
|
|
1311
1843
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
if (typeof document !== "undefined")
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}, [resolvedAddress, doFetchRef]);
|
|
1844
|
+
void doFetchRef.current();
|
|
1845
|
+
const interval = setInterval(() => {
|
|
1846
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
1847
|
+
void doFetchRef.current();
|
|
1848
|
+
}, POLL_FALLBACK_MS2);
|
|
1849
|
+
return () => clearInterval(interval);
|
|
1850
|
+
}, [resolvedAddress, streamUrl, limit, doFetchRef]);
|
|
1319
1851
|
const refresh = react.useCallback(async () => {
|
|
1320
1852
|
await doFetchRef.current();
|
|
1321
1853
|
}, [doFetchRef]);
|
|
1322
1854
|
return { events, isLoading, error, refresh };
|
|
1323
1855
|
}
|
|
1856
|
+
function adaptRestEvent(ev) {
|
|
1857
|
+
const t0 = ev.topics[0];
|
|
1858
|
+
if (typeof t0 !== "string") return null;
|
|
1859
|
+
if (t0 === "SignerRotated") {
|
|
1860
|
+
return {
|
|
1861
|
+
type: "signer-rotated",
|
|
1862
|
+
txHash: ev.txHash,
|
|
1863
|
+
ledger: ev.ledger,
|
|
1864
|
+
timestamp: ev.timestamp,
|
|
1865
|
+
newOwnerEd25519Hex: typeof ev.value === "string" ? ev.value : ""
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
return null;
|
|
1869
|
+
}
|
|
1324
1870
|
|
|
1325
1871
|
// src/index.ts
|
|
1872
|
+
init_useWalletHistory();
|
|
1326
1873
|
var REACT_ADAPTER_VERSION = "0.0.0";
|
|
1327
1874
|
|
|
1328
|
-
exports.AcceslyContext = AcceslyContext;
|
|
1329
1875
|
exports.AcceslyProvider = AcceslyProvider;
|
|
1330
|
-
exports.ENVIRONMENT_DEFAULTS = ENVIRONMENT_DEFAULTS;
|
|
1331
|
-
exports.NotImplementedYetError = NotImplementedYetError;
|
|
1332
1876
|
exports.REACT_ADAPTER_VERSION = REACT_ADAPTER_VERSION;
|
|
1877
|
+
exports.closeAllWalletSubscriptions = closeAllWalletSubscriptions;
|
|
1878
|
+
exports.historyClearOptimistic = historyClearOptimistic;
|
|
1879
|
+
exports.historyOptimisticPush = historyOptimisticPush;
|
|
1880
|
+
exports.subscribeToWalletEvent = subscribeToWalletEvent;
|
|
1333
1881
|
exports.useAccesly = useAccesly;
|
|
1334
1882
|
exports.useBalance = useBalance;
|
|
1335
1883
|
exports.useWalletActivity = useWalletActivity;
|
|
1884
|
+
exports.useWalletHistory = useWalletHistory;
|
|
1336
1885
|
exports.useWalletStatus = useWalletStatus;
|
|
1337
1886
|
//# sourceMappingURL=index.cjs.map
|
|
1338
1887
|
//# sourceMappingURL=index.cjs.map
|