@accesly/react 1.3.0 → 1.3.2
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 +37 -213
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -45
- package/dist/index.d.ts +20 -45
- package/dist/index.js +37 -213
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Environment, CognitoConfig, AuthClient, SessionStorage, DeviceStore, TelemetrySink, TokenManager, AccesslyEndpoints, AuthStatus, CredentialRecord, EncryptedEnvelope } from '@accesly/core';
|
|
1
|
+
import { Environment, CognitoConfig, AuthClient, SessionStorage, DeviceStore, TelemetrySink, TokenManager, AccesslyEndpoints, AuthStatus, CredentialRecord, EncryptedEnvelope, WalletHistoryItem } from '@accesly/core';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -817,69 +817,44 @@ interface UseWalletActivityResult {
|
|
|
817
817
|
}
|
|
818
818
|
declare function useWalletActivity(walletAddress?: string | null, opts?: UseWalletActivityOptions): UseWalletActivityResult;
|
|
819
819
|
|
|
820
|
-
/**
|
|
821
|
-
* Cliente y decoder de la **Stellar Expert public API** (indexer free, full
|
|
822
|
-
* retention, CORS abierto). Es la fuente primaria de `useWalletHistory`.
|
|
823
|
-
*
|
|
824
|
-
* Endpoints usados:
|
|
825
|
-
* - GET /contract/{walletAddress}/events — Smart Account events (rotaciones).
|
|
826
|
-
* - GET /contract/{xlmSac}/events?topics=transfer
|
|
827
|
-
* — Todos los transfers del SAC. Filtramos client-side por wallet.
|
|
828
|
-
* - GET /contract/{walletAddress} — Metadata del wallet (creación).
|
|
829
|
-
*
|
|
830
|
-
* Rate limit anonymous: ~1 req/s. Para zaramos cross-tab via BroadcastChannel
|
|
831
|
-
* en el hook que consume este módulo.
|
|
832
|
-
*/
|
|
833
|
-
|
|
834
|
-
type StellarExpertNetwork = 'testnet' | 'mainnet';
|
|
835
|
-
|
|
836
820
|
/**
|
|
837
821
|
* `useWalletHistory(walletAddress?, opts?)` — historial completo de la wallet
|
|
838
|
-
*
|
|
839
|
-
*
|
|
822
|
+
* pre-decodificado desde Stellar Expert (indexer free, full retention).
|
|
823
|
+
*
|
|
824
|
+
* El backend de Accesly **proxea** las requests a Stellar Expert porque SE
|
|
825
|
+
* bloquea CORS desde browsers (Cloudflare retorna 403 en cross-origin). El
|
|
826
|
+
* proxy hace el call server-side, decodea topics + amounts, y devuelve items
|
|
827
|
+
* tipados listos para renderizar.
|
|
840
828
|
*
|
|
841
|
-
*
|
|
842
|
-
* -
|
|
843
|
-
*
|
|
844
|
-
* - Cache en `localStorage` per-wallet con TTL 12h → return instantáneo entre
|
|
845
|
-
* page reloads y route changes.
|
|
829
|
+
* Features:
|
|
830
|
+
* - Cache en `localStorage` per-wallet con TTL 12h → render instantáneo en
|
|
831
|
+
* reloads + navegación.
|
|
846
832
|
* - Polling cada 30s para nuevos events. Pausa si tab oculta.
|
|
847
|
-
* - `BroadcastChannel` cross-tab para compartir
|
|
848
|
-
*
|
|
849
|
-
* - Optimistic updates: el SDK
|
|
850
|
-
*
|
|
851
|
-
* esperar el indexing de SE (~30-60s típico).
|
|
852
|
-
* - Override de Soroban RPC: pasa `{ sorobanRpcUrl, sorobanRpcAuth }` para
|
|
853
|
-
* que el tail (últimos eventos) venga via RPC con tu API key — sin lag.
|
|
833
|
+
* - `BroadcastChannel` cross-tab para compartir fetches — solo UN tab hace
|
|
834
|
+
* el request, los demás escuchan el resultado vía canal.
|
|
835
|
+
* - Optimistic updates: el SDK inserta el item al instante cuando `tx.send`
|
|
836
|
+
* confirma, sin esperar el indexing de SE (~30-60s típico).
|
|
854
837
|
*/
|
|
855
838
|
|
|
856
839
|
/**
|
|
857
840
|
* Inyecta un item de history "optimistically" — útil cuando acabás de hacer
|
|
858
841
|
* `tx.send` y querés que aparezca al instante sin esperar el indexing de SE.
|
|
859
|
-
* El item queda
|
|
860
|
-
* (porque ya está en el feed real) o hasta `clearOptimistic(walletAddress)`.
|
|
861
|
-
*
|
|
862
|
-
* Usado internamente por `tx.send`; expuesto al integrador para casos custom.
|
|
842
|
+
* El item queda hasta que el próximo fetch confirma que ya está en el feed.
|
|
863
843
|
*/
|
|
864
|
-
declare function historyOptimisticPush(walletAddress: string, item: WalletActivityItem): void;
|
|
844
|
+
declare function historyOptimisticPush(walletAddress: string, item: WalletHistoryItem | WalletActivityItem): void;
|
|
865
845
|
declare function historyClearOptimistic(walletAddress: string): void;
|
|
866
846
|
interface UseWalletHistoryOptions {
|
|
867
|
-
/** `'testnet'` (default) o `'mainnet'`. */
|
|
868
|
-
readonly network?: StellarExpertNetwork;
|
|
869
847
|
/** Intervalo de poll para nuevos events (ms). Default 30s, 0 desactiva. */
|
|
870
848
|
readonly pollIntervalMs?: number;
|
|
871
849
|
/**
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
850
|
+
* Cuántos transfers del XLM_SAC scan-ear por fetch. Default 50, max 500
|
|
851
|
+
* (5 paginated calls). En testnet hay millones de transfers globalmente; si
|
|
852
|
+
* tu wallet tiene pocos transfers, sube este número para encontrarlos.
|
|
875
853
|
*/
|
|
876
|
-
readonly sorobanRpcUrl?: string;
|
|
877
|
-
readonly sorobanRpcAuth?: string;
|
|
878
|
-
/** Cantidad de transfers del XLM_SAC a scan-ear por fetch. Default 50. */
|
|
879
854
|
readonly transferScanLimit?: number;
|
|
880
855
|
}
|
|
881
856
|
interface UseWalletHistoryResult {
|
|
882
|
-
readonly events: readonly
|
|
857
|
+
readonly events: readonly WalletHistoryItem[];
|
|
883
858
|
readonly isLoading: boolean;
|
|
884
859
|
readonly error: Error | null;
|
|
885
860
|
readonly hasMore: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Environment, CognitoConfig, AuthClient, SessionStorage, DeviceStore, TelemetrySink, TokenManager, AccesslyEndpoints, AuthStatus, CredentialRecord, EncryptedEnvelope } from '@accesly/core';
|
|
1
|
+
import { Environment, CognitoConfig, AuthClient, SessionStorage, DeviceStore, TelemetrySink, TokenManager, AccesslyEndpoints, AuthStatus, CredentialRecord, EncryptedEnvelope, WalletHistoryItem } from '@accesly/core';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -817,69 +817,44 @@ interface UseWalletActivityResult {
|
|
|
817
817
|
}
|
|
818
818
|
declare function useWalletActivity(walletAddress?: string | null, opts?: UseWalletActivityOptions): UseWalletActivityResult;
|
|
819
819
|
|
|
820
|
-
/**
|
|
821
|
-
* Cliente y decoder de la **Stellar Expert public API** (indexer free, full
|
|
822
|
-
* retention, CORS abierto). Es la fuente primaria de `useWalletHistory`.
|
|
823
|
-
*
|
|
824
|
-
* Endpoints usados:
|
|
825
|
-
* - GET /contract/{walletAddress}/events — Smart Account events (rotaciones).
|
|
826
|
-
* - GET /contract/{xlmSac}/events?topics=transfer
|
|
827
|
-
* — Todos los transfers del SAC. Filtramos client-side por wallet.
|
|
828
|
-
* - GET /contract/{walletAddress} — Metadata del wallet (creación).
|
|
829
|
-
*
|
|
830
|
-
* Rate limit anonymous: ~1 req/s. Para zaramos cross-tab via BroadcastChannel
|
|
831
|
-
* en el hook que consume este módulo.
|
|
832
|
-
*/
|
|
833
|
-
|
|
834
|
-
type StellarExpertNetwork = 'testnet' | 'mainnet';
|
|
835
|
-
|
|
836
820
|
/**
|
|
837
821
|
* `useWalletHistory(walletAddress?, opts?)` — historial completo de la wallet
|
|
838
|
-
*
|
|
839
|
-
*
|
|
822
|
+
* pre-decodificado desde Stellar Expert (indexer free, full retention).
|
|
823
|
+
*
|
|
824
|
+
* El backend de Accesly **proxea** las requests a Stellar Expert porque SE
|
|
825
|
+
* bloquea CORS desde browsers (Cloudflare retorna 403 en cross-origin). El
|
|
826
|
+
* proxy hace el call server-side, decodea topics + amounts, y devuelve items
|
|
827
|
+
* tipados listos para renderizar.
|
|
840
828
|
*
|
|
841
|
-
*
|
|
842
|
-
* -
|
|
843
|
-
*
|
|
844
|
-
* - Cache en `localStorage` per-wallet con TTL 12h → return instantáneo entre
|
|
845
|
-
* page reloads y route changes.
|
|
829
|
+
* Features:
|
|
830
|
+
* - Cache en `localStorage` per-wallet con TTL 12h → render instantáneo en
|
|
831
|
+
* reloads + navegación.
|
|
846
832
|
* - Polling cada 30s para nuevos events. Pausa si tab oculta.
|
|
847
|
-
* - `BroadcastChannel` cross-tab para compartir
|
|
848
|
-
*
|
|
849
|
-
* - Optimistic updates: el SDK
|
|
850
|
-
*
|
|
851
|
-
* esperar el indexing de SE (~30-60s típico).
|
|
852
|
-
* - Override de Soroban RPC: pasa `{ sorobanRpcUrl, sorobanRpcAuth }` para
|
|
853
|
-
* que el tail (últimos eventos) venga via RPC con tu API key — sin lag.
|
|
833
|
+
* - `BroadcastChannel` cross-tab para compartir fetches — solo UN tab hace
|
|
834
|
+
* el request, los demás escuchan el resultado vía canal.
|
|
835
|
+
* - Optimistic updates: el SDK inserta el item al instante cuando `tx.send`
|
|
836
|
+
* confirma, sin esperar el indexing de SE (~30-60s típico).
|
|
854
837
|
*/
|
|
855
838
|
|
|
856
839
|
/**
|
|
857
840
|
* Inyecta un item de history "optimistically" — útil cuando acabás de hacer
|
|
858
841
|
* `tx.send` y querés que aparezca al instante sin esperar el indexing de SE.
|
|
859
|
-
* El item queda
|
|
860
|
-
* (porque ya está en el feed real) o hasta `clearOptimistic(walletAddress)`.
|
|
861
|
-
*
|
|
862
|
-
* Usado internamente por `tx.send`; expuesto al integrador para casos custom.
|
|
842
|
+
* El item queda hasta que el próximo fetch confirma que ya está en el feed.
|
|
863
843
|
*/
|
|
864
|
-
declare function historyOptimisticPush(walletAddress: string, item: WalletActivityItem): void;
|
|
844
|
+
declare function historyOptimisticPush(walletAddress: string, item: WalletHistoryItem | WalletActivityItem): void;
|
|
865
845
|
declare function historyClearOptimistic(walletAddress: string): void;
|
|
866
846
|
interface UseWalletHistoryOptions {
|
|
867
|
-
/** `'testnet'` (default) o `'mainnet'`. */
|
|
868
|
-
readonly network?: StellarExpertNetwork;
|
|
869
847
|
/** Intervalo de poll para nuevos events (ms). Default 30s, 0 desactiva. */
|
|
870
848
|
readonly pollIntervalMs?: number;
|
|
871
849
|
/**
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
850
|
+
* Cuántos transfers del XLM_SAC scan-ear por fetch. Default 50, max 500
|
|
851
|
+
* (5 paginated calls). En testnet hay millones de transfers globalmente; si
|
|
852
|
+
* tu wallet tiene pocos transfers, sube este número para encontrarlos.
|
|
875
853
|
*/
|
|
876
|
-
readonly sorobanRpcUrl?: string;
|
|
877
|
-
readonly sorobanRpcAuth?: string;
|
|
878
|
-
/** Cantidad de transfers del XLM_SAC a scan-ear por fetch. Default 50. */
|
|
879
854
|
readonly transferScanLimit?: number;
|
|
880
855
|
}
|
|
881
856
|
interface UseWalletHistoryResult {
|
|
882
|
-
readonly events: readonly
|
|
857
|
+
readonly events: readonly WalletHistoryItem[];
|
|
883
858
|
readonly isLoading: boolean;
|
|
884
859
|
readonly error: Error | null;
|
|
885
860
|
readonly hasMore: boolean;
|
package/dist/index.js
CHANGED
|
@@ -86,170 +86,6 @@ var init_sorobanDeployStatus = __esm({
|
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
// src/hooks/stellarExpert.ts
|
|
90
|
-
async function fetchContractEvents(contractId, opts) {
|
|
91
|
-
const params = new URLSearchParams();
|
|
92
|
-
params.set("limit", String(opts.limit ?? 50));
|
|
93
|
-
params.set("order", opts.order ?? "desc");
|
|
94
|
-
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
95
|
-
if (opts.topics) params.set("topics", opts.topics);
|
|
96
|
-
const url = `${SE_BASE}/${opts.network}/contract/${contractId}/events?${params.toString()}`;
|
|
97
|
-
const res = await fetch(url, opts.signal ? { signal: opts.signal } : {});
|
|
98
|
-
if (!res.ok) {
|
|
99
|
-
throw new Error(`StellarExpert ${res.status} ${res.statusText}`);
|
|
100
|
-
}
|
|
101
|
-
return await res.json();
|
|
102
|
-
}
|
|
103
|
-
async function fetchContractMeta(contractId, network, signal) {
|
|
104
|
-
const url = `${SE_BASE}/${network}/contract/${contractId}`;
|
|
105
|
-
try {
|
|
106
|
-
const res = await fetch(url, signal ? { signal } : {});
|
|
107
|
-
if (!res.ok) return null;
|
|
108
|
-
return await res.json();
|
|
109
|
-
} catch {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
function decodeTransferAmount(bodyXdr) {
|
|
114
|
-
if (!bodyXdr) return "0";
|
|
115
|
-
try {
|
|
116
|
-
const bytes = base64ToBytes(bodyXdr);
|
|
117
|
-
if (bytes.length < 20) return "0";
|
|
118
|
-
let hi = 0n;
|
|
119
|
-
for (let i = 8; i < 16; i += 1) hi = hi << 8n | BigInt(bytes[i] ?? 0);
|
|
120
|
-
let lo = 0n;
|
|
121
|
-
for (let i = 16; i < 24; i += 1) lo = lo << 8n | BigInt(bytes[i] ?? 0);
|
|
122
|
-
const value = hi << 64n | lo;
|
|
123
|
-
return value.toString();
|
|
124
|
-
} catch {
|
|
125
|
-
return "0";
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function base64ToBytes(s) {
|
|
129
|
-
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(s, "base64"));
|
|
130
|
-
const bin = globalThis.atob(s);
|
|
131
|
-
const arr = new Uint8Array(bin.length);
|
|
132
|
-
for (let i = 0; i < bin.length; i += 1) arr[i] = bin.charCodeAt(i);
|
|
133
|
-
return arr;
|
|
134
|
-
}
|
|
135
|
-
function decodeSEEvent(ev, walletAddress, xlmSac, txHashByEventId) {
|
|
136
|
-
const t0 = ev.topics[0];
|
|
137
|
-
if (!t0) return null;
|
|
138
|
-
const timestamp = new Date(ev.ts * 1e3).toISOString();
|
|
139
|
-
const txHash = txHashByEventId.get(ev.id) ?? ev.id.split("-")[0] ?? "";
|
|
140
|
-
if (ev.contract === walletAddress) {
|
|
141
|
-
if (t0 === "signer_rotated") {
|
|
142
|
-
return {
|
|
143
|
-
type: "signer-rotated",
|
|
144
|
-
txHash,
|
|
145
|
-
ledger: ev.ts,
|
|
146
|
-
// SE no expone ledger directamente — usamos timestamp
|
|
147
|
-
timestamp,
|
|
148
|
-
newOwnerEd25519Hex: ""
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
if (ev.contract === xlmSac && t0 === "transfer") {
|
|
154
|
-
const from = ev.topics[1];
|
|
155
|
-
const to = ev.topics[2];
|
|
156
|
-
if (!from || !to) return null;
|
|
157
|
-
const amountStroops = decodeTransferAmount(ev.bodyXdr);
|
|
158
|
-
if (from === walletAddress && to !== walletAddress) {
|
|
159
|
-
return {
|
|
160
|
-
type: "transfer-out",
|
|
161
|
-
txHash,
|
|
162
|
-
ledger: ev.ts,
|
|
163
|
-
timestamp,
|
|
164
|
-
to,
|
|
165
|
-
amountStroops
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
if (to === walletAddress && from !== walletAddress) {
|
|
169
|
-
return {
|
|
170
|
-
type: "transfer-in",
|
|
171
|
-
txHash,
|
|
172
|
-
ledger: ev.ts,
|
|
173
|
-
timestamp,
|
|
174
|
-
from,
|
|
175
|
-
amountStroops
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
async function fetchWalletHistory(walletAddress, opts) {
|
|
182
|
-
const xlmSac = opts.network === "mainnet" ? XLM_SAC_MAINNET : XLM_SAC_TESTNET;
|
|
183
|
-
const limit = 50;
|
|
184
|
-
const transferLimit = opts.transferScanLimit ?? 50;
|
|
185
|
-
const [smartAccountResp, transfersResp] = await Promise.all([
|
|
186
|
-
fetchContractEvents(walletAddress, {
|
|
187
|
-
network: opts.network,
|
|
188
|
-
limit,
|
|
189
|
-
order: "desc",
|
|
190
|
-
...opts.smartAccountCursor ? { cursor: opts.smartAccountCursor } : {},
|
|
191
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
192
|
-
}).catch((err) => {
|
|
193
|
-
console.warn("[stellarExpert] smart account events failed", err);
|
|
194
|
-
return { _embedded: { records: [] } };
|
|
195
|
-
}),
|
|
196
|
-
fetchContractEvents(xlmSac, {
|
|
197
|
-
network: opts.network,
|
|
198
|
-
limit: transferLimit,
|
|
199
|
-
order: "desc",
|
|
200
|
-
topics: "transfer",
|
|
201
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
202
|
-
}).catch((err) => {
|
|
203
|
-
console.warn("[stellarExpert] xlm sac events failed", err);
|
|
204
|
-
return { _embedded: { records: [] } };
|
|
205
|
-
})
|
|
206
|
-
]);
|
|
207
|
-
const txHashByEventId = /* @__PURE__ */ new Map();
|
|
208
|
-
const items = [];
|
|
209
|
-
const saRecords = smartAccountResp._embedded?.records ?? [];
|
|
210
|
-
const txRecords = transfersResp._embedded?.records ?? [];
|
|
211
|
-
for (const ev of saRecords) {
|
|
212
|
-
const decoded = decodeSEEvent(ev, walletAddress, xlmSac, txHashByEventId);
|
|
213
|
-
if (decoded) items.push(decoded);
|
|
214
|
-
}
|
|
215
|
-
for (const ev of txRecords) {
|
|
216
|
-
const decoded = decodeSEEvent(ev, walletAddress, xlmSac, txHashByEventId);
|
|
217
|
-
if (decoded) items.push(decoded);
|
|
218
|
-
}
|
|
219
|
-
if (!opts.smartAccountCursor) {
|
|
220
|
-
const meta = await fetchContractMeta(walletAddress, opts.network, opts.signal);
|
|
221
|
-
if (meta) {
|
|
222
|
-
items.push({
|
|
223
|
-
type: "wallet-created",
|
|
224
|
-
txHash: "",
|
|
225
|
-
ledger: meta.created,
|
|
226
|
-
timestamp: new Date(meta.created * 1e3).toISOString()
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
items.sort((a, b) => (b.ledger ?? 0) - (a.ledger ?? 0));
|
|
231
|
-
return {
|
|
232
|
-
items,
|
|
233
|
-
cursors: {
|
|
234
|
-
smartAccount: extractCursor(smartAccountResp),
|
|
235
|
-
transfers: extractCursor(transfersResp)
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
function extractCursor(resp) {
|
|
240
|
-
const records = resp._embedded?.records;
|
|
241
|
-
if (!records || records.length === 0) return null;
|
|
242
|
-
return records[records.length - 1]?.paging_token ?? records[records.length - 1]?.id ?? null;
|
|
243
|
-
}
|
|
244
|
-
var SE_BASE, XLM_SAC_TESTNET, XLM_SAC_MAINNET;
|
|
245
|
-
var init_stellarExpert = __esm({
|
|
246
|
-
"src/hooks/stellarExpert.ts"() {
|
|
247
|
-
SE_BASE = "https://api.stellar.expert/explorer";
|
|
248
|
-
XLM_SAC_TESTNET = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
|
|
249
|
-
XLM_SAC_MAINNET = "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA";
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
89
|
// src/hooks/useWalletHistory.ts
|
|
254
90
|
var useWalletHistory_exports = {};
|
|
255
91
|
__export(useWalletHistory_exports, {
|
|
@@ -311,14 +147,10 @@ function subscribeOptimistic(walletAddress, listener) {
|
|
|
311
147
|
function useWalletHistory(walletAddress, opts = {}) {
|
|
312
148
|
const { wallet, _internal } = useAccesly();
|
|
313
149
|
const username = _internal.username;
|
|
314
|
-
const network = opts.network ?? inferNetwork(_internal.env);
|
|
315
150
|
const [resolvedAddress, setResolvedAddress] = useState(walletAddress ?? null);
|
|
316
151
|
const [events, setEvents] = useState([]);
|
|
317
152
|
const [optimistic, setOptimistic] = useState([]);
|
|
318
|
-
const [cursors, setCursors] = useState({
|
|
319
|
-
smartAccount: null,
|
|
320
|
-
transfers: null
|
|
321
|
-
});
|
|
153
|
+
const [cursors, setCursors] = useState({ smartAccount: null, transfers: null });
|
|
322
154
|
const [isLoading, setIsLoading] = useState(true);
|
|
323
155
|
const [error, setError] = useState(null);
|
|
324
156
|
const [hasMore, setHasMore] = useState(true);
|
|
@@ -351,6 +183,8 @@ function useWalletHistory(walletAddress, opts = {}) {
|
|
|
351
183
|
setOptimistic(optimisticItems.get(resolvedAddress) ?? []);
|
|
352
184
|
return subscribeOptimistic(resolvedAddress, setOptimistic);
|
|
353
185
|
}, [resolvedAddress]);
|
|
186
|
+
const endpointsRef = useStableRef(_internal.endpoints);
|
|
187
|
+
const transferScanLimit = opts.transferScanLimit ?? 1500;
|
|
354
188
|
useEffect(() => {
|
|
355
189
|
if (!resolvedAddress) {
|
|
356
190
|
setIsLoading(false);
|
|
@@ -376,12 +210,11 @@ function useWalletHistory(walletAddress, opts = {}) {
|
|
|
376
210
|
}
|
|
377
211
|
void (async () => {
|
|
378
212
|
try {
|
|
379
|
-
const result = await
|
|
380
|
-
|
|
381
|
-
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
213
|
+
const result = await endpointsRef.current.walletHistory(resolvedAddress, {
|
|
214
|
+
transferScanLimit
|
|
382
215
|
});
|
|
383
216
|
if (cancelled) return;
|
|
384
|
-
const deduped = dedupItems(result.
|
|
217
|
+
const deduped = dedupItems(result.events);
|
|
385
218
|
setEvents(deduped);
|
|
386
219
|
setCursors(result.cursors);
|
|
387
220
|
setIsLoading(false);
|
|
@@ -405,35 +238,31 @@ function useWalletHistory(walletAddress, opts = {}) {
|
|
|
405
238
|
cancelled = true;
|
|
406
239
|
channel?.close();
|
|
407
240
|
};
|
|
408
|
-
}, [resolvedAddress,
|
|
241
|
+
}, [resolvedAddress, transferScanLimit, endpointsRef]);
|
|
409
242
|
const interval = opts.pollIntervalMs ?? POLL_INTERVAL_MS;
|
|
410
243
|
useEffect(() => {
|
|
411
244
|
if (!resolvedAddress || interval === 0) return void 0;
|
|
412
245
|
const tick = async () => {
|
|
413
246
|
if (typeof document !== "undefined" && document.hidden) return;
|
|
414
247
|
try {
|
|
415
|
-
const result = await
|
|
416
|
-
|
|
417
|
-
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
248
|
+
const result = await endpointsRef.current.walletHistory(resolvedAddress, {
|
|
249
|
+
transferScanLimit
|
|
418
250
|
});
|
|
419
|
-
setEvents((prev) => mergeAndDedup(prev, result.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const listeners = optimisticListeners.get(resolvedAddress);
|
|
428
|
-
if (listeners) for (const fn of listeners) fn(optimisticsRemaining);
|
|
429
|
-
}
|
|
251
|
+
setEvents((prev) => mergeAndDedup(prev, result.events));
|
|
252
|
+
const realTxHashes = new Set(result.events.map((it) => it.eventToid));
|
|
253
|
+
const current = optimisticItems.get(resolvedAddress) ?? [];
|
|
254
|
+
const remaining = current.filter((it) => !realTxHashes.has(it.eventToid));
|
|
255
|
+
if (remaining.length !== current.length) {
|
|
256
|
+
optimisticItems.set(resolvedAddress, remaining);
|
|
257
|
+
const listeners = optimisticListeners.get(resolvedAddress);
|
|
258
|
+
if (listeners) for (const fn of listeners) fn(remaining);
|
|
430
259
|
}
|
|
431
260
|
} catch {
|
|
432
261
|
}
|
|
433
262
|
};
|
|
434
263
|
const id = setInterval(tick, interval);
|
|
435
264
|
return () => clearInterval(id);
|
|
436
|
-
}, [resolvedAddress,
|
|
265
|
+
}, [resolvedAddress, interval, transferScanLimit, endpointsRef]);
|
|
437
266
|
const loadMoreImpl = useCallback(async () => {
|
|
438
267
|
if (!resolvedAddress) return;
|
|
439
268
|
if (!cursors.smartAccount && !cursors.transfers) {
|
|
@@ -441,27 +270,26 @@ function useWalletHistory(walletAddress, opts = {}) {
|
|
|
441
270
|
return;
|
|
442
271
|
}
|
|
443
272
|
try {
|
|
444
|
-
const result = await
|
|
445
|
-
network,
|
|
273
|
+
const result = await endpointsRef.current.walletHistory(resolvedAddress, {
|
|
446
274
|
...cursors.smartAccount ? { smartAccountCursor: cursors.smartAccount } : {},
|
|
447
|
-
...
|
|
275
|
+
...cursors.transfers ? { transfersCursor: cursors.transfers } : {},
|
|
276
|
+
transferScanLimit
|
|
448
277
|
});
|
|
449
|
-
setEvents((prev) => mergeAndDedup(prev, result.
|
|
278
|
+
setEvents((prev) => mergeAndDedup(prev, result.events));
|
|
450
279
|
setCursors(result.cursors);
|
|
451
280
|
setHasMore(result.cursors.smartAccount !== null || result.cursors.transfers !== null);
|
|
452
281
|
} catch (err) {
|
|
453
282
|
setError(err);
|
|
454
283
|
}
|
|
455
|
-
}, [resolvedAddress,
|
|
284
|
+
}, [resolvedAddress, cursors, transferScanLimit, endpointsRef]);
|
|
456
285
|
const refreshImpl = useCallback(async () => {
|
|
457
286
|
if (!resolvedAddress) return;
|
|
458
287
|
setIsLoading(true);
|
|
459
288
|
try {
|
|
460
|
-
const result = await
|
|
461
|
-
|
|
462
|
-
...opts.transferScanLimit ? { transferScanLimit: opts.transferScanLimit } : {}
|
|
289
|
+
const result = await endpointsRef.current.walletHistory(resolvedAddress, {
|
|
290
|
+
transferScanLimit
|
|
463
291
|
});
|
|
464
|
-
const deduped = dedupItems(result.
|
|
292
|
+
const deduped = dedupItems(result.events);
|
|
465
293
|
setEvents(deduped);
|
|
466
294
|
setCursors(result.cursors);
|
|
467
295
|
setError(null);
|
|
@@ -475,7 +303,7 @@ function useWalletHistory(walletAddress, opts = {}) {
|
|
|
475
303
|
} finally {
|
|
476
304
|
setIsLoading(false);
|
|
477
305
|
}
|
|
478
|
-
}, [resolvedAddress,
|
|
306
|
+
}, [resolvedAddress, transferScanLimit, endpointsRef]);
|
|
479
307
|
const combined = [...optimistic, ...events];
|
|
480
308
|
return {
|
|
481
309
|
events: combined,
|
|
@@ -486,19 +314,16 @@ function useWalletHistory(walletAddress, opts = {}) {
|
|
|
486
314
|
refresh: refreshImpl
|
|
487
315
|
};
|
|
488
316
|
}
|
|
489
|
-
function inferNetwork(env) {
|
|
490
|
-
return env === "prod" ? "mainnet" : "testnet";
|
|
491
|
-
}
|
|
492
317
|
function dedupItems(items) {
|
|
493
318
|
const seen = /* @__PURE__ */ new Set();
|
|
494
319
|
const out = [];
|
|
495
320
|
for (const item of items) {
|
|
496
|
-
const key = `${item.type}:${item.
|
|
321
|
+
const key = `${item.type}:${item.eventToid}:${item.ledger}`;
|
|
497
322
|
if (seen.has(key)) continue;
|
|
498
323
|
seen.add(key);
|
|
499
324
|
out.push(item);
|
|
500
325
|
}
|
|
501
|
-
out.sort((a, b) =>
|
|
326
|
+
out.sort((a, b) => b.ledger - a.ledger);
|
|
502
327
|
return out;
|
|
503
328
|
}
|
|
504
329
|
function mergeAndDedup(prev, fresh) {
|
|
@@ -508,7 +333,6 @@ var POLL_INTERVAL_MS, CACHE_TTL_MS, CACHE_KEY_PREFIX, BROADCAST_CHANNEL_PREFIX,
|
|
|
508
333
|
var init_useWalletHistory = __esm({
|
|
509
334
|
"src/hooks/useWalletHistory.ts"() {
|
|
510
335
|
init_useAccesly();
|
|
511
|
-
init_stellarExpert();
|
|
512
336
|
POLL_INTERVAL_MS = 3e4;
|
|
513
337
|
CACHE_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
514
338
|
CACHE_KEY_PREFIX = "accesly:history:";
|
|
@@ -981,8 +805,8 @@ function useAccesly() {
|
|
|
981
805
|
const sessionPlaintext = unwrapSessionFragment2(wrappedF2, ephemeral.privateKey).plaintext;
|
|
982
806
|
const fragmentF2Wire = JSON.parse(new TextDecoder().decode(sessionPlaintext));
|
|
983
807
|
const fragmentF2Envelope = {
|
|
984
|
-
nonce:
|
|
985
|
-
ciphertext:
|
|
808
|
+
nonce: base64ToBytes(fragmentF2Wire.nonce),
|
|
809
|
+
ciphertext: base64ToBytes(fragmentF2Wire.ciphertext)
|
|
986
810
|
};
|
|
987
811
|
const reconstructed = reconstructFromPlainAndEncrypted({
|
|
988
812
|
fragmentF1Plain: input.fragmentF1Plain,
|
|
@@ -1061,18 +885,18 @@ function useAccesly() {
|
|
|
1061
885
|
"recovery.reconstructSeed: la wallet fue creada antes de Fase 1 y no tiene F2 cipher-bound a recoveryKey. No es recuperable v\xEDa OTP."
|
|
1062
886
|
);
|
|
1063
887
|
}
|
|
1064
|
-
const recoverySalt =
|
|
888
|
+
const recoverySalt = base64ToBytes(frag.recoverySalt);
|
|
1065
889
|
const recoveryKey = deriveRecoveryKey({
|
|
1066
890
|
password: input.cognitoPassword,
|
|
1067
891
|
salt: recoverySalt
|
|
1068
892
|
});
|
|
1069
893
|
const f2Envelope = {
|
|
1070
|
-
ciphertext:
|
|
1071
|
-
nonce:
|
|
894
|
+
ciphertext: base64ToBytes(frag.fragmentF2Recovery.ciphertext),
|
|
895
|
+
nonce: base64ToBytes(frag.fragmentF2Recovery.nonce)
|
|
1072
896
|
};
|
|
1073
897
|
const f3Envelope = {
|
|
1074
|
-
ciphertext:
|
|
1075
|
-
nonce:
|
|
898
|
+
ciphertext: base64ToBytes(frag.fragmentF3Encrypted.ciphertext),
|
|
899
|
+
nonce: base64ToBytes(frag.fragmentF3Encrypted.nonce)
|
|
1076
900
|
};
|
|
1077
901
|
const seedResult = reconstructKey({
|
|
1078
902
|
fragments: [
|
|
@@ -1308,7 +1132,7 @@ function base64FromBytes(bytes) {
|
|
|
1308
1132
|
for (let i = 0; i < bytes.length; i += 1) bin += String.fromCharCode(bytes[i] ?? 0);
|
|
1309
1133
|
return globalThis.btoa(bin);
|
|
1310
1134
|
}
|
|
1311
|
-
function
|
|
1135
|
+
function base64ToBytes(s) {
|
|
1312
1136
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(s, "base64"));
|
|
1313
1137
|
const bin = globalThis.atob(s);
|
|
1314
1138
|
const arr = new Uint8Array(bin.length);
|