@horus-wallet/sdk-react 0.1.0-beta.2 → 0.3.0-beta.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/README.md +133 -174
- package/dist/connect.cjs +218 -0
- package/dist/connect.d.cts +155 -0
- package/dist/connect.d.ts +155 -0
- package/dist/connect.js +188 -0
- package/dist/index.cjs +811 -64
- package/dist/index.d.cts +605 -31
- package/dist/index.d.ts +605 -31
- package/dist/index.js +798 -64
- package/package.json +7 -2
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
HorusAuthModal: () => HorusAuthModal,
|
|
23
24
|
HorusHttpError: () => HorusHttpError,
|
|
24
25
|
HorusLoginButton: () => HorusLoginButton,
|
|
25
26
|
HorusProvider: () => HorusProvider,
|
|
27
|
+
HorusRevealModal: () => HorusRevealModal,
|
|
28
|
+
SUPPORTED_CHAINS: () => SUPPORTED_CHAINS,
|
|
29
|
+
useChain: () => useChain,
|
|
30
|
+
useCreateWallet: () => useCreateWallet,
|
|
31
|
+
useExportWallet: () => useExportWallet,
|
|
32
|
+
useGenerateMcpKey: () => useGenerateMcpKey,
|
|
26
33
|
useHorusAuth: () => useHorusAuth,
|
|
34
|
+
useMcpKeys: () => useMcpKeys,
|
|
35
|
+
useRevokeMcpKey: () => useRevokeMcpKey,
|
|
36
|
+
useSendTransaction: () => useSendTransaction,
|
|
27
37
|
useSignMessage: () => useSignMessage,
|
|
38
|
+
useSignTypedData: () => useSignTypedData,
|
|
39
|
+
useSwitchChain: () => useSwitchChain,
|
|
28
40
|
useTransfer: () => useTransfer,
|
|
41
|
+
useUpdateMcpKey: () => useUpdateMcpKey,
|
|
29
42
|
useUser: () => useUser,
|
|
30
43
|
useWallets: () => useWallets
|
|
31
44
|
});
|
|
@@ -122,19 +135,21 @@ function makeHttp(deps) {
|
|
|
122
135
|
return path.startsWith("/") ? `${base}${path}` : `${base}/${path}`;
|
|
123
136
|
};
|
|
124
137
|
async function rawRequest(method, path, body, options = {}) {
|
|
125
|
-
const headers = {
|
|
138
|
+
const headers = {
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
"x-horus-key": deps.appId
|
|
141
|
+
};
|
|
126
142
|
if (options.auth !== false) {
|
|
127
143
|
const tokens = deps.getTokens();
|
|
128
144
|
if (tokens?.idToken) {
|
|
129
|
-
headers["
|
|
145
|
+
headers["Authorization"] = `Bearer ${tokens.idToken}`;
|
|
130
146
|
}
|
|
131
147
|
}
|
|
132
148
|
const res = await fetch(url(path), {
|
|
133
149
|
method,
|
|
134
150
|
headers,
|
|
135
151
|
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
136
|
-
signal: options.signal
|
|
137
|
-
credentials: "same-origin"
|
|
152
|
+
signal: options.signal
|
|
138
153
|
});
|
|
139
154
|
const text = await res.text();
|
|
140
155
|
let parsed = void 0;
|
|
@@ -175,9 +190,15 @@ function makeHttp(deps) {
|
|
|
175
190
|
|
|
176
191
|
// src/HorusProvider.tsx
|
|
177
192
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
178
|
-
var DEFAULT_API_BASE = "
|
|
193
|
+
var DEFAULT_API_BASE = "https://api.horuswallet.com";
|
|
179
194
|
var DEFAULT_AUTH_PAGE = "https://auth.horuswallet.com";
|
|
180
195
|
var REFRESH_LEAD_SECONDS = 60;
|
|
196
|
+
var DEFAULT_AUTO_PROVISION = {
|
|
197
|
+
network: "ETHEREUM",
|
|
198
|
+
networkType: "MAINNET"
|
|
199
|
+
};
|
|
200
|
+
var DEFAULT_CHAIN = { network: "ETHEREUM", networkType: "MAINNET" };
|
|
201
|
+
var AUTO_PROVISION_STORAGE_KEY = "horus.autoProvisioned.localIds";
|
|
181
202
|
function HorusProvider(props) {
|
|
182
203
|
const {
|
|
183
204
|
appId,
|
|
@@ -186,11 +207,25 @@ function HorusProvider(props) {
|
|
|
186
207
|
branding,
|
|
187
208
|
tokenStorage = "localStorage",
|
|
188
209
|
autoRefresh = true,
|
|
210
|
+
autoProvisionWallet = DEFAULT_AUTO_PROVISION,
|
|
211
|
+
defaultChain = DEFAULT_CHAIN,
|
|
189
212
|
children
|
|
190
213
|
} = props;
|
|
191
214
|
const tokenStoreRef = (0, import_react2.useRef)(createTokenStore(tokenStorage));
|
|
192
215
|
const tokensRef = (0, import_react2.useRef)(null);
|
|
193
216
|
const [state, setState] = (0, import_react2.useState)({ status: "loading" });
|
|
217
|
+
const [walletsVersion, setWalletsVersion] = (0, import_react2.useState)(0);
|
|
218
|
+
const revalidateWallets = (0, import_react2.useCallback)(() => {
|
|
219
|
+
setWalletsVersion((v) => v + 1);
|
|
220
|
+
}, []);
|
|
221
|
+
const [currentChain, setCurrentChain] = (0, import_react2.useState)(defaultChain);
|
|
222
|
+
const setChain = (0, import_react2.useCallback)((chain) => {
|
|
223
|
+
setCurrentChain(chain);
|
|
224
|
+
}, []);
|
|
225
|
+
const [mcpKeysVersion, setMcpKeysVersion] = (0, import_react2.useState)(0);
|
|
226
|
+
const revalidateMcpKeys = (0, import_react2.useCallback)(() => {
|
|
227
|
+
setMcpKeysVersion((v) => v + 1);
|
|
228
|
+
}, []);
|
|
194
229
|
const setTokens = (0, import_react2.useCallback)((tokens) => {
|
|
195
230
|
tokensRef.current = tokens;
|
|
196
231
|
if (tokens) {
|
|
@@ -211,9 +246,11 @@ function HorusProvider(props) {
|
|
|
211
246
|
if (!cur?.refreshToken) throw new Error("no refresh token cached");
|
|
212
247
|
const fresh = await fetch(joinUrl(apiBase, "/auth/refresh"), {
|
|
213
248
|
method: "POST",
|
|
214
|
-
headers: {
|
|
215
|
-
|
|
216
|
-
|
|
249
|
+
headers: {
|
|
250
|
+
"Content-Type": "application/json",
|
|
251
|
+
"x-horus-key": appId
|
|
252
|
+
},
|
|
253
|
+
body: JSON.stringify({ refreshToken: cur.refreshToken })
|
|
217
254
|
});
|
|
218
255
|
if (!fresh.ok) throw new Error(`refresh failed: ${fresh.status}`);
|
|
219
256
|
const raw = await fresh.json();
|
|
@@ -223,11 +260,12 @@ function HorusProvider(props) {
|
|
|
223
260
|
};
|
|
224
261
|
return makeHttp({
|
|
225
262
|
apiBase,
|
|
263
|
+
appId,
|
|
226
264
|
getTokens: () => tokensRef.current,
|
|
227
265
|
refreshTokens: refreshOnce,
|
|
228
266
|
autoRefresh
|
|
229
267
|
});
|
|
230
|
-
}, [apiBase, autoRefresh, setTokens]);
|
|
268
|
+
}, [apiBase, appId, autoRefresh, setTokens]);
|
|
231
269
|
(0, import_react2.useEffect)(() => {
|
|
232
270
|
const stored = tokenStoreRef.current.read();
|
|
233
271
|
if (stored) {
|
|
@@ -241,6 +279,58 @@ function HorusProvider(props) {
|
|
|
241
279
|
setState({ status: "unauthenticated" });
|
|
242
280
|
}
|
|
243
281
|
}, []);
|
|
282
|
+
(0, import_react2.useEffect)(() => {
|
|
283
|
+
if (typeof window === "undefined") return;
|
|
284
|
+
const params = new URLSearchParams(window.location.search);
|
|
285
|
+
const mode = params.get("mode");
|
|
286
|
+
const oobCode = params.get("oobCode");
|
|
287
|
+
if (mode !== "signIn" || !oobCode) return;
|
|
288
|
+
if (consumedMagicLinkCodes.has(oobCode)) return;
|
|
289
|
+
consumedMagicLinkCodes.add(oobCode);
|
|
290
|
+
const email = params.get("email") ?? (typeof window.localStorage !== "undefined" ? window.localStorage.getItem("horus.signinEmail") : null) ?? "";
|
|
291
|
+
if (!email) {
|
|
292
|
+
console.warn(
|
|
293
|
+
"@horus-wallet/sdk-react: detected a magic-link return but no email is available. Pass `email` in the continueUrl or set localStorage.horus.signinEmail at send time."
|
|
294
|
+
);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
void (async () => {
|
|
298
|
+
try {
|
|
299
|
+
const stripUrlParams = () => {
|
|
300
|
+
const u = new URL(window.location.href);
|
|
301
|
+
["mode", "oobCode", "apiKey", "continueUrl", "lang", "email"].forEach(
|
|
302
|
+
(k) => u.searchParams.delete(k)
|
|
303
|
+
);
|
|
304
|
+
window.history.replaceState({}, "", u.toString());
|
|
305
|
+
if (window.localStorage) {
|
|
306
|
+
window.localStorage.removeItem("horus.signinEmail");
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const raw = await fetch(joinUrl(apiBase, "/auth/email-link/verify"), {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: {
|
|
312
|
+
"Content-Type": "application/json",
|
|
313
|
+
"x-horus-key": appId
|
|
314
|
+
},
|
|
315
|
+
body: JSON.stringify({ email, oobCode })
|
|
316
|
+
});
|
|
317
|
+
if (!raw.ok) {
|
|
318
|
+
const body = await raw.text();
|
|
319
|
+
console.warn(
|
|
320
|
+
`@horus-wallet/sdk-react: magic-link verify failed (HTTP ${raw.status}): ${body}`
|
|
321
|
+
);
|
|
322
|
+
stripUrlParams();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const json = await raw.json();
|
|
326
|
+
const stamped = stampExpiry(json);
|
|
327
|
+
setTokens(stamped);
|
|
328
|
+
stripUrlParams();
|
|
329
|
+
} catch (err) {
|
|
330
|
+
console.warn("@horus-wallet/sdk-react: magic-link verify threw", err);
|
|
331
|
+
}
|
|
332
|
+
})();
|
|
333
|
+
}, [apiBase, appId, setTokens]);
|
|
244
334
|
(0, import_react2.useEffect)(() => {
|
|
245
335
|
if (typeof window === "undefined") return;
|
|
246
336
|
const onStorage = (ev) => {
|
|
@@ -266,6 +356,27 @@ function HorusProvider(props) {
|
|
|
266
356
|
}, refreshIn);
|
|
267
357
|
return () => clearTimeout(handle);
|
|
268
358
|
}, [state, autoRefresh, http, setTokens]);
|
|
359
|
+
(0, import_react2.useEffect)(() => {
|
|
360
|
+
if (state.status !== "authenticated") return;
|
|
361
|
+
if (!autoProvisionWallet) return;
|
|
362
|
+
const localId = state.tokens.localId;
|
|
363
|
+
if (!localId) return;
|
|
364
|
+
if (alreadyAutoProvisioned(localId)) return;
|
|
365
|
+
let cancelled = false;
|
|
366
|
+
(async () => {
|
|
367
|
+
try {
|
|
368
|
+
await http.post("/createWallet", autoProvisionWallet);
|
|
369
|
+
if (cancelled) return;
|
|
370
|
+
markAutoProvisioned(localId);
|
|
371
|
+
revalidateWallets();
|
|
372
|
+
} catch (err) {
|
|
373
|
+
console.warn("@horus-wallet/sdk-react: auto-provision wallet failed", err);
|
|
374
|
+
}
|
|
375
|
+
})();
|
|
376
|
+
return () => {
|
|
377
|
+
cancelled = true;
|
|
378
|
+
};
|
|
379
|
+
}, [state, autoProvisionWallet, http, revalidateWallets]);
|
|
269
380
|
const ctx = (0, import_react2.useMemo)(
|
|
270
381
|
() => ({
|
|
271
382
|
state,
|
|
@@ -286,9 +397,28 @@ function HorusProvider(props) {
|
|
|
286
397
|
const stamped = stampExpiry(raw);
|
|
287
398
|
setTokens(stamped);
|
|
288
399
|
return stamped;
|
|
289
|
-
}
|
|
400
|
+
},
|
|
401
|
+
walletsVersion,
|
|
402
|
+
revalidateWallets,
|
|
403
|
+
currentChain,
|
|
404
|
+
setChain,
|
|
405
|
+
mcpKeysVersion,
|
|
406
|
+
revalidateMcpKeys
|
|
290
407
|
}),
|
|
291
|
-
[
|
|
408
|
+
[
|
|
409
|
+
state,
|
|
410
|
+
http,
|
|
411
|
+
authPageUrl,
|
|
412
|
+
appId,
|
|
413
|
+
branding,
|
|
414
|
+
setTokens,
|
|
415
|
+
walletsVersion,
|
|
416
|
+
revalidateWallets,
|
|
417
|
+
currentChain,
|
|
418
|
+
setChain,
|
|
419
|
+
mcpKeysVersion,
|
|
420
|
+
revalidateMcpKeys
|
|
421
|
+
]
|
|
292
422
|
);
|
|
293
423
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HorusContext.Provider, { value: ctx, children });
|
|
294
424
|
}
|
|
@@ -302,6 +432,37 @@ function userFromTokens(t) {
|
|
|
302
432
|
providerId: t.providerId
|
|
303
433
|
};
|
|
304
434
|
}
|
|
435
|
+
var consumedMagicLinkCodes = /* @__PURE__ */ new Set();
|
|
436
|
+
var memoryAutoProvisioned = /* @__PURE__ */ new Set();
|
|
437
|
+
function readAutoProvisionedSet() {
|
|
438
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
439
|
+
return memoryAutoProvisioned;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
const raw = window.localStorage.getItem(AUTO_PROVISION_STORAGE_KEY);
|
|
443
|
+
if (!raw) return /* @__PURE__ */ new Set();
|
|
444
|
+
const parsed = JSON.parse(raw);
|
|
445
|
+
return new Set(Array.isArray(parsed) ? parsed : []);
|
|
446
|
+
} catch {
|
|
447
|
+
return /* @__PURE__ */ new Set();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function alreadyAutoProvisioned(localId) {
|
|
451
|
+
return readAutoProvisionedSet().has(localId);
|
|
452
|
+
}
|
|
453
|
+
function markAutoProvisioned(localId) {
|
|
454
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
455
|
+
memoryAutoProvisioned.add(localId);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
const set = readAutoProvisionedSet();
|
|
460
|
+
set.add(localId);
|
|
461
|
+
window.localStorage.setItem(AUTO_PROVISION_STORAGE_KEY, JSON.stringify([...set]));
|
|
462
|
+
} catch {
|
|
463
|
+
memoryAutoProvisioned.add(localId);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
305
466
|
function joinUrl(base, path) {
|
|
306
467
|
const b = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
307
468
|
const p = path.startsWith("/") ? path : `/${path}`;
|
|
@@ -310,6 +471,39 @@ function joinUrl(base, path) {
|
|
|
310
471
|
|
|
311
472
|
// src/hooks/useHorusAuth.ts
|
|
312
473
|
var import_react3 = require("react");
|
|
474
|
+
|
|
475
|
+
// src/internal/authUrl.ts
|
|
476
|
+
function buildAuthUrl(p, state) {
|
|
477
|
+
const url = new URL(p.baseUrl);
|
|
478
|
+
url.searchParams.set("flow", p.flow);
|
|
479
|
+
url.searchParams.set("origin", window.location.origin);
|
|
480
|
+
url.searchParams.set("state", state);
|
|
481
|
+
url.searchParams.set("appKey", p.appId);
|
|
482
|
+
url.searchParams.set("mode", p.mode);
|
|
483
|
+
if (p.phone) url.searchParams.set("phone", p.phone);
|
|
484
|
+
if (p.flow === "unified" && p.enabledMethods && p.enabledMethods.length > 0) {
|
|
485
|
+
url.searchParams.set("methods", p.enabledMethods.join(","));
|
|
486
|
+
}
|
|
487
|
+
const b = p.branding;
|
|
488
|
+
if (b) {
|
|
489
|
+
if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
|
|
490
|
+
if (b.brandName) url.searchParams.set("b_name", b.brandName);
|
|
491
|
+
if (b.primaryColor) url.searchParams.set("b_color", b.primaryColor);
|
|
492
|
+
if (b.backgroundColor) url.searchParams.set("b_bg", b.backgroundColor);
|
|
493
|
+
if (b.fontFamily) url.searchParams.set("b_font", b.fontFamily);
|
|
494
|
+
if (b.termsUrl) url.searchParams.set("b_terms", b.termsUrl);
|
|
495
|
+
if (b.privacyUrl) url.searchParams.set("b_privacy", b.privacyUrl);
|
|
496
|
+
if (b.showPoweredByHorus === false) url.searchParams.set("b_poweredby", "0");
|
|
497
|
+
}
|
|
498
|
+
return url.toString();
|
|
499
|
+
}
|
|
500
|
+
function randomState() {
|
|
501
|
+
const bytes = new Uint8Array(16);
|
|
502
|
+
crypto.getRandomValues(bytes);
|
|
503
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/hooks/useHorusAuth.ts
|
|
313
507
|
function useHorusAuth() {
|
|
314
508
|
const ctx = useHorusContext();
|
|
315
509
|
const { state, http, signIn, signOut, refreshTokens, authPageUrl, appId, branding } = ctx;
|
|
@@ -409,7 +603,7 @@ function openPopupFlow(params) {
|
|
|
409
603
|
return;
|
|
410
604
|
}
|
|
411
605
|
const state = randomState();
|
|
412
|
-
const url = buildAuthUrl(params, state);
|
|
606
|
+
const url = buildAuthUrl({ ...params, mode: "popup" }, state);
|
|
413
607
|
const popup = window.open(url, "horus-auth", defaultPopupFeatures());
|
|
414
608
|
if (!popup) {
|
|
415
609
|
reject(new Error("Popup blocked \u2014 call from a click handler so the browser allows it."));
|
|
@@ -417,6 +611,7 @@ function openPopupFlow(params) {
|
|
|
417
611
|
}
|
|
418
612
|
const expectedOrigin = new URL(params.baseUrl).origin;
|
|
419
613
|
let settled = false;
|
|
614
|
+
let lastError = null;
|
|
420
615
|
const cleanup = () => {
|
|
421
616
|
window.removeEventListener("message", onMessage);
|
|
422
617
|
clearInterval(poll);
|
|
@@ -447,17 +642,11 @@ function openPopupFlow(params) {
|
|
|
447
642
|
popup.close();
|
|
448
643
|
} catch {
|
|
449
644
|
}
|
|
450
|
-
reject(new Error("User cancelled sign-in."));
|
|
645
|
+
reject(new Error(lastError ?? "User cancelled sign-in."));
|
|
451
646
|
return;
|
|
452
647
|
case "error":
|
|
453
648
|
if (settled) return;
|
|
454
|
-
|
|
455
|
-
cleanup();
|
|
456
|
-
try {
|
|
457
|
-
popup.close();
|
|
458
|
-
} catch {
|
|
459
|
-
}
|
|
460
|
-
reject(new Error(body.message || "Sign-in failed."));
|
|
649
|
+
lastError = body.message || "Sign-in failed.";
|
|
461
650
|
return;
|
|
462
651
|
}
|
|
463
652
|
};
|
|
@@ -466,31 +655,11 @@ function openPopupFlow(params) {
|
|
|
466
655
|
if (popup.closed && !settled) {
|
|
467
656
|
settled = true;
|
|
468
657
|
cleanup();
|
|
469
|
-
reject(new Error("Auth popup was closed before sign-in completed."));
|
|
658
|
+
reject(new Error(lastError ?? "Auth popup was closed before sign-in completed."));
|
|
470
659
|
}
|
|
471
660
|
}, 400);
|
|
472
661
|
});
|
|
473
662
|
}
|
|
474
|
-
function buildAuthUrl(p, state) {
|
|
475
|
-
const url = new URL(p.baseUrl);
|
|
476
|
-
url.searchParams.set("flow", p.flow);
|
|
477
|
-
url.searchParams.set("origin", window.location.origin);
|
|
478
|
-
url.searchParams.set("state", state);
|
|
479
|
-
url.searchParams.set("appKey", p.appId);
|
|
480
|
-
if (p.phone) url.searchParams.set("phone", p.phone);
|
|
481
|
-
const b = p.branding;
|
|
482
|
-
if (b) {
|
|
483
|
-
if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
|
|
484
|
-
if (b.brandName) url.searchParams.set("b_name", b.brandName);
|
|
485
|
-
if (b.primaryColor) url.searchParams.set("b_color", b.primaryColor);
|
|
486
|
-
if (b.backgroundColor) url.searchParams.set("b_bg", b.backgroundColor);
|
|
487
|
-
if (b.fontFamily) url.searchParams.set("b_font", b.fontFamily);
|
|
488
|
-
if (b.termsUrl) url.searchParams.set("b_terms", b.termsUrl);
|
|
489
|
-
if (b.privacyUrl) url.searchParams.set("b_privacy", b.privacyUrl);
|
|
490
|
-
if (b.showPoweredByHorus === false) url.searchParams.set("b_poweredby", "0");
|
|
491
|
-
}
|
|
492
|
-
return url.toString();
|
|
493
|
-
}
|
|
494
663
|
function defaultPopupFeatures() {
|
|
495
664
|
const w = 480, h = 640;
|
|
496
665
|
if (typeof window === "undefined") return `width=${w},height=${h}`;
|
|
@@ -498,11 +667,6 @@ function defaultPopupFeatures() {
|
|
|
498
667
|
const top = Math.max(0, (window.innerHeight - h) / 2 + (window.screenY ?? 0));
|
|
499
668
|
return `width=${w},height=${h},left=${left},top=${top},popup=1`;
|
|
500
669
|
}
|
|
501
|
-
function randomState() {
|
|
502
|
-
const bytes = new Uint8Array(16);
|
|
503
|
-
crypto.getRandomValues(bytes);
|
|
504
|
-
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
505
|
-
}
|
|
506
670
|
|
|
507
671
|
// src/hooks/useUser.ts
|
|
508
672
|
function useUser() {
|
|
@@ -513,7 +677,7 @@ function useUser() {
|
|
|
513
677
|
// src/hooks/useWallets.ts
|
|
514
678
|
var import_react4 = require("react");
|
|
515
679
|
function useWallets() {
|
|
516
|
-
const { http, state } = useHorusContext();
|
|
680
|
+
const { http, state, walletsVersion } = useHorusContext();
|
|
517
681
|
const [wallets, setWallets] = (0, import_react4.useState)([]);
|
|
518
682
|
const [ready, setReady] = (0, import_react4.useState)(false);
|
|
519
683
|
const [error, setError] = (0, import_react4.useState)(void 0);
|
|
@@ -527,7 +691,7 @@ function useWallets() {
|
|
|
527
691
|
setError(void 0);
|
|
528
692
|
try {
|
|
529
693
|
const response = await http.get(
|
|
530
|
-
"/
|
|
694
|
+
"/getWallet"
|
|
531
695
|
);
|
|
532
696
|
const flat = [];
|
|
533
697
|
for (const [network, group] of Object.entries(response.wallets ?? {})) {
|
|
@@ -544,22 +708,94 @@ function useWallets() {
|
|
|
544
708
|
}, [http, state.status]);
|
|
545
709
|
(0, import_react4.useEffect)(() => {
|
|
546
710
|
void load();
|
|
547
|
-
}, [load]);
|
|
711
|
+
}, [load, walletsVersion]);
|
|
548
712
|
return { ready, wallets, refresh: load, error };
|
|
549
713
|
}
|
|
550
714
|
|
|
551
|
-
// src/hooks/
|
|
715
|
+
// src/hooks/useCreateWallet.ts
|
|
552
716
|
var import_react5 = require("react");
|
|
553
|
-
function
|
|
554
|
-
const { http } = useHorusContext();
|
|
717
|
+
function useCreateWallet() {
|
|
718
|
+
const { http, revalidateWallets } = useHorusContext();
|
|
555
719
|
const [pending, setPending] = (0, import_react5.useState)(false);
|
|
556
720
|
const [error, setError] = (0, import_react5.useState)(void 0);
|
|
557
|
-
const
|
|
721
|
+
const create = (0, import_react5.useCallback)(
|
|
722
|
+
async (input) => {
|
|
723
|
+
if (pending) {
|
|
724
|
+
const err = new Error(
|
|
725
|
+
"useCreateWallet: a previous create() call is still in flight"
|
|
726
|
+
);
|
|
727
|
+
setError(err);
|
|
728
|
+
throw err;
|
|
729
|
+
}
|
|
730
|
+
setPending(true);
|
|
731
|
+
setError(void 0);
|
|
732
|
+
try {
|
|
733
|
+
const response = await http.post("/createWallet", {
|
|
734
|
+
network: input.network,
|
|
735
|
+
networkType: input.networkType,
|
|
736
|
+
...input.password ? { password: input.password } : {}
|
|
737
|
+
});
|
|
738
|
+
revalidateWallets();
|
|
739
|
+
return response;
|
|
740
|
+
} catch (err) {
|
|
741
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
742
|
+
setError(e);
|
|
743
|
+
throw e;
|
|
744
|
+
} finally {
|
|
745
|
+
setPending(false);
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
[http, revalidateWallets, pending]
|
|
749
|
+
);
|
|
750
|
+
return { create, pending, error };
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// src/hooks/useSwitchChain.ts
|
|
754
|
+
var import_react6 = require("react");
|
|
755
|
+
var SUPPORTED_CHAINS = [
|
|
756
|
+
// EVM-family chains the backend accepts as `network` selectors.
|
|
757
|
+
// `EVM` itself is intentionally NOT here — it's a family label, not
|
|
758
|
+
// a chain, and the API rejects it on /createWallet, /signMessage, etc.
|
|
759
|
+
// Pick a specific chain when interacting with the API.
|
|
760
|
+
"ETHEREUM",
|
|
761
|
+
"BASE",
|
|
762
|
+
"POLYGON",
|
|
763
|
+
"BSC",
|
|
764
|
+
"ARBITRUM",
|
|
765
|
+
"OPTIMISM",
|
|
766
|
+
"TELOS",
|
|
767
|
+
"CHILIZ",
|
|
768
|
+
"FLARE",
|
|
769
|
+
// Non-EVM chains.
|
|
770
|
+
"BITCOIN",
|
|
771
|
+
"ICP",
|
|
772
|
+
"CASPER",
|
|
773
|
+
"AETERNITY"
|
|
774
|
+
];
|
|
775
|
+
function useSwitchChain() {
|
|
776
|
+
const { currentChain, setChain } = useHorusContext();
|
|
777
|
+
const switchChain = (0, import_react6.useCallback)(
|
|
778
|
+
(chain) => {
|
|
779
|
+
setChain(chain);
|
|
780
|
+
},
|
|
781
|
+
[setChain]
|
|
782
|
+
);
|
|
783
|
+
return { chain: currentChain, switchChain, supportedChains: SUPPORTED_CHAINS };
|
|
784
|
+
}
|
|
785
|
+
var useChain = useSwitchChain;
|
|
786
|
+
|
|
787
|
+
// src/hooks/useSignMessage.ts
|
|
788
|
+
var import_react7 = require("react");
|
|
789
|
+
function useSignMessage() {
|
|
790
|
+
const { http } = useHorusContext();
|
|
791
|
+
const [pending, setPending] = (0, import_react7.useState)(false);
|
|
792
|
+
const [error, setError] = (0, import_react7.useState)(void 0);
|
|
793
|
+
const signMessage = (0, import_react7.useCallback)(
|
|
558
794
|
async (input) => {
|
|
559
795
|
setPending(true);
|
|
560
796
|
setError(void 0);
|
|
561
797
|
try {
|
|
562
|
-
const response = await http.post("/
|
|
798
|
+
const response = await http.post("/signMessage", input);
|
|
563
799
|
return response.signature;
|
|
564
800
|
} catch (err) {
|
|
565
801
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -574,13 +810,79 @@ function useSignMessage() {
|
|
|
574
810
|
return { signMessage, pending, error };
|
|
575
811
|
}
|
|
576
812
|
|
|
813
|
+
// src/hooks/useSignTypedData.ts
|
|
814
|
+
var import_react8 = require("react");
|
|
815
|
+
function useSignTypedData() {
|
|
816
|
+
const { http } = useHorusContext();
|
|
817
|
+
const [pending, setPending] = (0, import_react8.useState)(false);
|
|
818
|
+
const [error, setError] = (0, import_react8.useState)(void 0);
|
|
819
|
+
const signTypedData = (0, import_react8.useCallback)(
|
|
820
|
+
async (input) => {
|
|
821
|
+
setPending(true);
|
|
822
|
+
setError(void 0);
|
|
823
|
+
try {
|
|
824
|
+
const response = await http.post("/signTypedData", {
|
|
825
|
+
network: input.network,
|
|
826
|
+
networkType: input.networkType,
|
|
827
|
+
typedData: input.typedData,
|
|
828
|
+
...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {},
|
|
829
|
+
...input.password ? { password: input.password } : {}
|
|
830
|
+
});
|
|
831
|
+
return response.signature;
|
|
832
|
+
} catch (err) {
|
|
833
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
834
|
+
setError(e);
|
|
835
|
+
throw e;
|
|
836
|
+
} finally {
|
|
837
|
+
setPending(false);
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
[http]
|
|
841
|
+
);
|
|
842
|
+
return { signTypedData, pending, error };
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// src/hooks/useSendTransaction.ts
|
|
846
|
+
var import_react9 = require("react");
|
|
847
|
+
function useSendTransaction() {
|
|
848
|
+
const { http } = useHorusContext();
|
|
849
|
+
const [pending, setPending] = (0, import_react9.useState)(false);
|
|
850
|
+
const [error, setError] = (0, import_react9.useState)(void 0);
|
|
851
|
+
const sendTransaction = (0, import_react9.useCallback)(
|
|
852
|
+
async (input) => {
|
|
853
|
+
setPending(true);
|
|
854
|
+
setError(void 0);
|
|
855
|
+
try {
|
|
856
|
+
return await http.post("/sendTransaction", {
|
|
857
|
+
network: input.network,
|
|
858
|
+
networkType: input.networkType,
|
|
859
|
+
to: input.to,
|
|
860
|
+
...input.value !== void 0 ? { value: String(input.value) } : {},
|
|
861
|
+
...input.data !== void 0 ? { data: input.data } : {},
|
|
862
|
+
...input.gasLimit !== void 0 ? { gasLimit: String(input.gasLimit) } : {},
|
|
863
|
+
...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {},
|
|
864
|
+
...input.password ? { password: input.password } : {}
|
|
865
|
+
});
|
|
866
|
+
} catch (err) {
|
|
867
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
868
|
+
setError(e);
|
|
869
|
+
throw e;
|
|
870
|
+
} finally {
|
|
871
|
+
setPending(false);
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
[http]
|
|
875
|
+
);
|
|
876
|
+
return { sendTransaction, pending, error };
|
|
877
|
+
}
|
|
878
|
+
|
|
577
879
|
// src/hooks/useTransfer.ts
|
|
578
|
-
var
|
|
880
|
+
var import_react10 = require("react");
|
|
579
881
|
function useTransfer() {
|
|
580
882
|
const { http } = useHorusContext();
|
|
581
|
-
const [pending, setPending] = (0,
|
|
582
|
-
const [error, setError] = (0,
|
|
583
|
-
const wrap = (0,
|
|
883
|
+
const [pending, setPending] = (0, import_react10.useState)(false);
|
|
884
|
+
const [error, setError] = (0, import_react10.useState)(void 0);
|
|
885
|
+
const wrap = (0, import_react10.useCallback)(
|
|
584
886
|
async (fn) => {
|
|
585
887
|
setPending(true);
|
|
586
888
|
setError(void 0);
|
|
@@ -596,18 +898,18 @@ function useTransfer() {
|
|
|
596
898
|
},
|
|
597
899
|
[]
|
|
598
900
|
);
|
|
599
|
-
const native = (0,
|
|
901
|
+
const native = (0, import_react10.useCallback)(
|
|
600
902
|
(input) => wrap(
|
|
601
|
-
() => http.post("/
|
|
903
|
+
() => http.post("/nativeTransfer", {
|
|
602
904
|
...input,
|
|
603
905
|
amount: typeof input.amount === "bigint" ? input.amount.toString() : String(input.amount)
|
|
604
906
|
})
|
|
605
907
|
),
|
|
606
908
|
[http, wrap]
|
|
607
909
|
);
|
|
608
|
-
const token = (0,
|
|
910
|
+
const token = (0, import_react10.useCallback)(
|
|
609
911
|
(input) => wrap(
|
|
610
|
-
() => http.post("/
|
|
912
|
+
() => http.post("/tokenTransfer", {
|
|
611
913
|
...input,
|
|
612
914
|
amount: typeof input.amount === "bigint" ? input.amount.toString() : String(input.amount)
|
|
613
915
|
})
|
|
@@ -618,7 +920,7 @@ function useTransfer() {
|
|
|
618
920
|
}
|
|
619
921
|
|
|
620
922
|
// src/components/HorusLoginButton.tsx
|
|
621
|
-
var
|
|
923
|
+
var import_react11 = require("react");
|
|
622
924
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
623
925
|
function HorusLoginButton({
|
|
624
926
|
flow = "google",
|
|
@@ -631,7 +933,7 @@ function HorusLoginButton({
|
|
|
631
933
|
...rest
|
|
632
934
|
}) {
|
|
633
935
|
const auth = useHorusAuth();
|
|
634
|
-
const [busy, setBusy] = (0,
|
|
936
|
+
const [busy, setBusy] = (0, import_react11.useState)(false);
|
|
635
937
|
const onClick = async () => {
|
|
636
938
|
if (busy) return;
|
|
637
939
|
setBusy(true);
|
|
@@ -669,14 +971,459 @@ function HorusLoginButton({
|
|
|
669
971
|
}
|
|
670
972
|
);
|
|
671
973
|
}
|
|
974
|
+
|
|
975
|
+
// src/components/HorusAuthModal.tsx
|
|
976
|
+
var import_react12 = require("react");
|
|
977
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
978
|
+
var defaultBackdropStyle = {
|
|
979
|
+
position: "fixed",
|
|
980
|
+
inset: 0,
|
|
981
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
982
|
+
display: "flex",
|
|
983
|
+
alignItems: "center",
|
|
984
|
+
justifyContent: "center",
|
|
985
|
+
zIndex: 2147483600
|
|
986
|
+
};
|
|
987
|
+
var defaultDialogStyle = {
|
|
988
|
+
width: "480px",
|
|
989
|
+
maxWidth: "95vw",
|
|
990
|
+
height: "640px",
|
|
991
|
+
maxHeight: "95vh",
|
|
992
|
+
background: "#fff",
|
|
993
|
+
borderRadius: "16px",
|
|
994
|
+
boxShadow: "0 24px 56px rgba(0, 0, 0, 0.32)",
|
|
995
|
+
overflow: "hidden"
|
|
996
|
+
};
|
|
997
|
+
function HorusAuthModal(props) {
|
|
998
|
+
const {
|
|
999
|
+
flow,
|
|
1000
|
+
enabledMethods,
|
|
1001
|
+
phone,
|
|
1002
|
+
open,
|
|
1003
|
+
onClose,
|
|
1004
|
+
onSuccess,
|
|
1005
|
+
onError,
|
|
1006
|
+
dialogStyle,
|
|
1007
|
+
backdropStyle
|
|
1008
|
+
} = props;
|
|
1009
|
+
const { appId, authPageUrl, branding, signIn } = useHorusContext();
|
|
1010
|
+
const stateRef = (0, import_react12.useRef)("");
|
|
1011
|
+
if (stateRef.current === "" || !open) {
|
|
1012
|
+
stateRef.current = randomState();
|
|
1013
|
+
}
|
|
1014
|
+
const iframeSrc = (0, import_react12.useMemo)(() => {
|
|
1015
|
+
if (!open) return "";
|
|
1016
|
+
return buildAuthUrl(
|
|
1017
|
+
{
|
|
1018
|
+
flow,
|
|
1019
|
+
appId,
|
|
1020
|
+
baseUrl: authPageUrl,
|
|
1021
|
+
mode: "iframe",
|
|
1022
|
+
branding,
|
|
1023
|
+
phone,
|
|
1024
|
+
enabledMethods
|
|
1025
|
+
},
|
|
1026
|
+
stateRef.current
|
|
1027
|
+
);
|
|
1028
|
+
}, [open, flow, appId, authPageUrl, branding, phone, enabledMethods]);
|
|
1029
|
+
const expectedOrigin = (0, import_react12.useMemo)(() => {
|
|
1030
|
+
try {
|
|
1031
|
+
return new URL(authPageUrl).origin;
|
|
1032
|
+
} catch {
|
|
1033
|
+
return "";
|
|
1034
|
+
}
|
|
1035
|
+
}, [authPageUrl]);
|
|
1036
|
+
(0, import_react12.useEffect)(() => {
|
|
1037
|
+
if (!open) return;
|
|
1038
|
+
const onMessage = (ev) => {
|
|
1039
|
+
if (ev.origin !== expectedOrigin) return;
|
|
1040
|
+
const data = ev.data;
|
|
1041
|
+
if (!data || data.channel !== "horus.auth" || data.version !== 1) return;
|
|
1042
|
+
if (data.state !== void 0 && data.state !== stateRef.current) return;
|
|
1043
|
+
const body = data.body;
|
|
1044
|
+
if (!body) return;
|
|
1045
|
+
switch (body.type) {
|
|
1046
|
+
case "success": {
|
|
1047
|
+
const stamped = stampExpiry({
|
|
1048
|
+
idToken: body.idToken,
|
|
1049
|
+
refreshToken: "",
|
|
1050
|
+
expiresIn: body.expiresAt ? Math.max(60, body.expiresAt - Math.floor(Date.now() / 1e3)) : 3600,
|
|
1051
|
+
localId: body.user?.uid ?? "",
|
|
1052
|
+
email: body.user?.email,
|
|
1053
|
+
displayName: body.user?.displayName,
|
|
1054
|
+
photoUrl: body.user?.photoURL,
|
|
1055
|
+
providerId: body.user?.providerId
|
|
1056
|
+
});
|
|
1057
|
+
signIn(stamped);
|
|
1058
|
+
onSuccess?.({
|
|
1059
|
+
uid: stamped.localId,
|
|
1060
|
+
email: stamped.email,
|
|
1061
|
+
emailVerified: stamped.emailVerified,
|
|
1062
|
+
displayName: stamped.displayName,
|
|
1063
|
+
photoUrl: stamped.photoUrl,
|
|
1064
|
+
providerId: stamped.providerId
|
|
1065
|
+
});
|
|
1066
|
+
onClose();
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
case "cancel":
|
|
1070
|
+
onClose();
|
|
1071
|
+
return;
|
|
1072
|
+
case "error":
|
|
1073
|
+
onError?.(new Error(body.message ?? "Sign-in failed."));
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
window.addEventListener("message", onMessage);
|
|
1078
|
+
return () => window.removeEventListener("message", onMessage);
|
|
1079
|
+
}, [open, expectedOrigin, signIn, onSuccess, onError, onClose]);
|
|
1080
|
+
(0, import_react12.useEffect)(() => {
|
|
1081
|
+
if (!open) return;
|
|
1082
|
+
const onKey = (ev) => {
|
|
1083
|
+
if (ev.key === "Escape") onClose();
|
|
1084
|
+
};
|
|
1085
|
+
window.addEventListener("keydown", onKey);
|
|
1086
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1087
|
+
}, [open, onClose]);
|
|
1088
|
+
(0, import_react12.useEffect)(() => {
|
|
1089
|
+
if (!open) return;
|
|
1090
|
+
if (typeof document === "undefined") return;
|
|
1091
|
+
const prev = document.body.style.overflow;
|
|
1092
|
+
document.body.style.overflow = "hidden";
|
|
1093
|
+
return () => {
|
|
1094
|
+
document.body.style.overflow = prev;
|
|
1095
|
+
};
|
|
1096
|
+
}, [open]);
|
|
1097
|
+
const backdropClick = (0, import_react12.useCallback)(
|
|
1098
|
+
(ev) => {
|
|
1099
|
+
if (ev.target === ev.currentTarget) onClose();
|
|
1100
|
+
},
|
|
1101
|
+
[onClose]
|
|
1102
|
+
);
|
|
1103
|
+
if (!open || typeof window === "undefined") return null;
|
|
1104
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1105
|
+
"div",
|
|
1106
|
+
{
|
|
1107
|
+
role: "dialog",
|
|
1108
|
+
"aria-modal": "true",
|
|
1109
|
+
"aria-label": "Sign in with Horus",
|
|
1110
|
+
style: { ...defaultBackdropStyle, ...backdropStyle },
|
|
1111
|
+
onClick: backdropClick,
|
|
1112
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultDialogStyle, ...dialogStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1113
|
+
"iframe",
|
|
1114
|
+
{
|
|
1115
|
+
src: iframeSrc,
|
|
1116
|
+
title: "Horus authentication",
|
|
1117
|
+
allow: "publickey-credentials-get; publickey-credentials-create; clipboard-write",
|
|
1118
|
+
style: { width: "100%", height: "100%", border: "none" }
|
|
1119
|
+
}
|
|
1120
|
+
) })
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// src/components/HorusRevealModal.tsx
|
|
1126
|
+
var import_react13 = require("react");
|
|
1127
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1128
|
+
var defaultBackdropStyle2 = {
|
|
1129
|
+
position: "fixed",
|
|
1130
|
+
inset: 0,
|
|
1131
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
1132
|
+
display: "flex",
|
|
1133
|
+
alignItems: "center",
|
|
1134
|
+
justifyContent: "center",
|
|
1135
|
+
zIndex: 2147483600
|
|
1136
|
+
};
|
|
1137
|
+
var defaultDialogStyle2 = {
|
|
1138
|
+
width: "480px",
|
|
1139
|
+
maxWidth: "95vw",
|
|
1140
|
+
height: "640px",
|
|
1141
|
+
maxHeight: "95vh",
|
|
1142
|
+
background: "#fff",
|
|
1143
|
+
borderRadius: "16px",
|
|
1144
|
+
boxShadow: "0 24px 56px rgba(0, 0, 0, 0.32)",
|
|
1145
|
+
overflow: "hidden"
|
|
1146
|
+
};
|
|
1147
|
+
function HorusRevealModal(props) {
|
|
1148
|
+
const { revealToken, open, onClose, onComplete, onError, dialogStyle, backdropStyle } = props;
|
|
1149
|
+
const { appId, authPageUrl, branding } = useHorusContext();
|
|
1150
|
+
const stateRef = (0, import_react13.useRef)("");
|
|
1151
|
+
if (stateRef.current === "" || !open) {
|
|
1152
|
+
stateRef.current = randomState();
|
|
1153
|
+
}
|
|
1154
|
+
const iframeSrc = (0, import_react13.useMemo)(() => {
|
|
1155
|
+
if (!open || !revealToken) return "";
|
|
1156
|
+
const baseUrl = buildAuthUrl(
|
|
1157
|
+
{
|
|
1158
|
+
flow: "reveal",
|
|
1159
|
+
appId,
|
|
1160
|
+
baseUrl: authPageUrl,
|
|
1161
|
+
mode: "iframe",
|
|
1162
|
+
branding
|
|
1163
|
+
},
|
|
1164
|
+
stateRef.current
|
|
1165
|
+
);
|
|
1166
|
+
const u = new URL(baseUrl);
|
|
1167
|
+
u.searchParams.set("token", revealToken);
|
|
1168
|
+
return u.toString();
|
|
1169
|
+
}, [open, revealToken, appId, authPageUrl, branding]);
|
|
1170
|
+
const expectedOrigin = (0, import_react13.useMemo)(() => {
|
|
1171
|
+
try {
|
|
1172
|
+
return new URL(authPageUrl).origin;
|
|
1173
|
+
} catch {
|
|
1174
|
+
return "";
|
|
1175
|
+
}
|
|
1176
|
+
}, [authPageUrl]);
|
|
1177
|
+
(0, import_react13.useEffect)(() => {
|
|
1178
|
+
if (!open) return;
|
|
1179
|
+
const onMessage = (ev) => {
|
|
1180
|
+
if (ev.origin !== expectedOrigin) return;
|
|
1181
|
+
const data = ev.data;
|
|
1182
|
+
if (!data || data.channel !== "horus.auth" || data.version !== 1) return;
|
|
1183
|
+
if (data.state !== void 0 && data.state !== stateRef.current) return;
|
|
1184
|
+
const body = data.body;
|
|
1185
|
+
if (!body) return;
|
|
1186
|
+
switch (body.type) {
|
|
1187
|
+
case "reveal_complete":
|
|
1188
|
+
onComplete?.(Boolean(body.viewed));
|
|
1189
|
+
onClose();
|
|
1190
|
+
return;
|
|
1191
|
+
case "cancel":
|
|
1192
|
+
onClose();
|
|
1193
|
+
return;
|
|
1194
|
+
case "error":
|
|
1195
|
+
onError?.(new Error(body.message ?? "Reveal failed."));
|
|
1196
|
+
onClose();
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
window.addEventListener("message", onMessage);
|
|
1201
|
+
return () => window.removeEventListener("message", onMessage);
|
|
1202
|
+
}, [open, expectedOrigin, onComplete, onClose, onError]);
|
|
1203
|
+
(0, import_react13.useEffect)(() => {
|
|
1204
|
+
if (!open) return;
|
|
1205
|
+
const onKey = (ev) => {
|
|
1206
|
+
if (ev.key === "Escape") onClose();
|
|
1207
|
+
};
|
|
1208
|
+
window.addEventListener("keydown", onKey);
|
|
1209
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1210
|
+
}, [open, onClose]);
|
|
1211
|
+
(0, import_react13.useEffect)(() => {
|
|
1212
|
+
if (!open) return;
|
|
1213
|
+
if (typeof document === "undefined") return;
|
|
1214
|
+
const prev = document.body.style.overflow;
|
|
1215
|
+
document.body.style.overflow = "hidden";
|
|
1216
|
+
return () => {
|
|
1217
|
+
document.body.style.overflow = prev;
|
|
1218
|
+
};
|
|
1219
|
+
}, [open]);
|
|
1220
|
+
const backdropClick = (0, import_react13.useCallback)(
|
|
1221
|
+
(ev) => {
|
|
1222
|
+
if (ev.target === ev.currentTarget) onClose();
|
|
1223
|
+
},
|
|
1224
|
+
[onClose]
|
|
1225
|
+
);
|
|
1226
|
+
if (!open || !revealToken || typeof window === "undefined") return null;
|
|
1227
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1228
|
+
"div",
|
|
1229
|
+
{
|
|
1230
|
+
role: "dialog",
|
|
1231
|
+
"aria-modal": "true",
|
|
1232
|
+
"aria-label": "Reveal wallet private key",
|
|
1233
|
+
style: { ...defaultBackdropStyle2, ...backdropStyle },
|
|
1234
|
+
onClick: backdropClick,
|
|
1235
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...defaultDialogStyle2, ...dialogStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1236
|
+
"iframe",
|
|
1237
|
+
{
|
|
1238
|
+
src: iframeSrc,
|
|
1239
|
+
title: "Horus wallet export",
|
|
1240
|
+
allow: "clipboard-write",
|
|
1241
|
+
style: { width: "100%", height: "100%", border: "none" }
|
|
1242
|
+
}
|
|
1243
|
+
) })
|
|
1244
|
+
}
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// src/hooks/useExportWallet.ts
|
|
1249
|
+
var import_react14 = require("react");
|
|
1250
|
+
function useExportWallet() {
|
|
1251
|
+
const { http } = useHorusContext();
|
|
1252
|
+
const [pending, setPending] = (0, import_react14.useState)(false);
|
|
1253
|
+
const [error, setError] = (0, import_react14.useState)(void 0);
|
|
1254
|
+
const reveal = (0, import_react14.useCallback)(
|
|
1255
|
+
async (input) => {
|
|
1256
|
+
setPending(true);
|
|
1257
|
+
setError(void 0);
|
|
1258
|
+
try {
|
|
1259
|
+
const response = await http.post(
|
|
1260
|
+
"/exportWallet/grant",
|
|
1261
|
+
{
|
|
1262
|
+
network: input.network,
|
|
1263
|
+
networkType: input.networkType,
|
|
1264
|
+
password: input.password,
|
|
1265
|
+
...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {}
|
|
1266
|
+
}
|
|
1267
|
+
);
|
|
1268
|
+
return response;
|
|
1269
|
+
} catch (err) {
|
|
1270
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1271
|
+
setError(e);
|
|
1272
|
+
throw e;
|
|
1273
|
+
} finally {
|
|
1274
|
+
setPending(false);
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
[http]
|
|
1278
|
+
);
|
|
1279
|
+
return { reveal, pending, error };
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// src/hooks/useMcp.ts
|
|
1283
|
+
var import_react15 = require("react");
|
|
1284
|
+
function useGenerateMcpKey() {
|
|
1285
|
+
const { http, walletsVersion } = useHorusContext();
|
|
1286
|
+
void walletsVersion;
|
|
1287
|
+
const [pending, setPending] = (0, import_react15.useState)(false);
|
|
1288
|
+
const [error, setError] = (0, import_react15.useState)(void 0);
|
|
1289
|
+
const [lastResult, setLastResult] = (0, import_react15.useState)(null);
|
|
1290
|
+
const generate = (0, import_react15.useCallback)(
|
|
1291
|
+
async (input) => {
|
|
1292
|
+
setPending(true);
|
|
1293
|
+
setError(void 0);
|
|
1294
|
+
try {
|
|
1295
|
+
const response = await http.post("/mcp/keys/generate", {
|
|
1296
|
+
name: input.name,
|
|
1297
|
+
network: input.network,
|
|
1298
|
+
wallet_index: typeof input.walletIndex === "number" ? input.walletIndex : 0,
|
|
1299
|
+
expires_at: input.expiresAt,
|
|
1300
|
+
allowed_ips: input.allowedIps,
|
|
1301
|
+
allowed_tools: input.allowedTools,
|
|
1302
|
+
max_requests_per_minute: input.maxRequestsPerMinute,
|
|
1303
|
+
mcp_password: input.mcpPassword
|
|
1304
|
+
});
|
|
1305
|
+
setLastResult(response);
|
|
1306
|
+
return response;
|
|
1307
|
+
} catch (err) {
|
|
1308
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1309
|
+
setError(e);
|
|
1310
|
+
throw e;
|
|
1311
|
+
} finally {
|
|
1312
|
+
setPending(false);
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
[http]
|
|
1316
|
+
);
|
|
1317
|
+
return { generate, pending, error, lastResult, clearLastResult: () => setLastResult(null) };
|
|
1318
|
+
}
|
|
1319
|
+
function useMcpKeys() {
|
|
1320
|
+
const { http, state, mcpKeysVersion } = useHorusContext();
|
|
1321
|
+
const [keys, setKeys] = (0, import_react15.useState)([]);
|
|
1322
|
+
const [ready, setReady] = (0, import_react15.useState)(false);
|
|
1323
|
+
const [error, setError] = (0, import_react15.useState)(void 0);
|
|
1324
|
+
const load = (0, import_react15.useCallback)(async () => {
|
|
1325
|
+
if (state.status !== "authenticated") {
|
|
1326
|
+
setKeys([]);
|
|
1327
|
+
setReady(true);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
setReady(false);
|
|
1331
|
+
setError(void 0);
|
|
1332
|
+
try {
|
|
1333
|
+
const response = await http.get("/mcp/keys");
|
|
1334
|
+
setKeys(response?.keys ?? []);
|
|
1335
|
+
} catch (err) {
|
|
1336
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1337
|
+
} finally {
|
|
1338
|
+
setReady(true);
|
|
1339
|
+
}
|
|
1340
|
+
}, [http, state.status]);
|
|
1341
|
+
(0, import_react15.useEffect)(() => {
|
|
1342
|
+
void load();
|
|
1343
|
+
}, [load, mcpKeysVersion]);
|
|
1344
|
+
return { ready, keys, refresh: load, error };
|
|
1345
|
+
}
|
|
1346
|
+
function useRevokeMcpKey() {
|
|
1347
|
+
const { http, revalidateMcpKeys } = useHorusContext();
|
|
1348
|
+
const [pending, setPending] = (0, import_react15.useState)(false);
|
|
1349
|
+
const [error, setError] = (0, import_react15.useState)(void 0);
|
|
1350
|
+
const revoke = (0, import_react15.useCallback)(
|
|
1351
|
+
async (prefix) => {
|
|
1352
|
+
if (!prefix) throw new Error("useRevokeMcpKey: prefix is required");
|
|
1353
|
+
setPending(true);
|
|
1354
|
+
setError(void 0);
|
|
1355
|
+
try {
|
|
1356
|
+
const response = await http.del(
|
|
1357
|
+
`/mcp/keys/${encodeURIComponent(prefix)}`
|
|
1358
|
+
);
|
|
1359
|
+
revalidateMcpKeys();
|
|
1360
|
+
return response;
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1363
|
+
setError(e);
|
|
1364
|
+
throw e;
|
|
1365
|
+
} finally {
|
|
1366
|
+
setPending(false);
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
[http, revalidateMcpKeys]
|
|
1370
|
+
);
|
|
1371
|
+
return { revoke, pending, error };
|
|
1372
|
+
}
|
|
1373
|
+
function useUpdateMcpKey() {
|
|
1374
|
+
const { http, revalidateMcpKeys } = useHorusContext();
|
|
1375
|
+
const [pending, setPending] = (0, import_react15.useState)(false);
|
|
1376
|
+
const [error, setError] = (0, import_react15.useState)(void 0);
|
|
1377
|
+
const update = (0, import_react15.useCallback)(
|
|
1378
|
+
async (prefix, updates) => {
|
|
1379
|
+
if (!prefix) throw new Error("useUpdateMcpKey: prefix is required");
|
|
1380
|
+
setPending(true);
|
|
1381
|
+
setError(void 0);
|
|
1382
|
+
try {
|
|
1383
|
+
const response = await http.put(
|
|
1384
|
+
`/mcp/keys/${encodeURIComponent(prefix)}`,
|
|
1385
|
+
{
|
|
1386
|
+
expires_at: updates.expiresAt,
|
|
1387
|
+
allowed_ips: updates.allowedIps,
|
|
1388
|
+
allowed_tools: updates.allowedTools,
|
|
1389
|
+
max_requests_per_minute: updates.maxRequestsPerMinute
|
|
1390
|
+
}
|
|
1391
|
+
);
|
|
1392
|
+
revalidateMcpKeys();
|
|
1393
|
+
return response;
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1396
|
+
setError(e);
|
|
1397
|
+
throw e;
|
|
1398
|
+
} finally {
|
|
1399
|
+
setPending(false);
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
[http, revalidateMcpKeys]
|
|
1403
|
+
);
|
|
1404
|
+
return { update, pending, error };
|
|
1405
|
+
}
|
|
672
1406
|
// Annotate the CommonJS export names for ESM import in node:
|
|
673
1407
|
0 && (module.exports = {
|
|
1408
|
+
HorusAuthModal,
|
|
674
1409
|
HorusHttpError,
|
|
675
1410
|
HorusLoginButton,
|
|
676
1411
|
HorusProvider,
|
|
1412
|
+
HorusRevealModal,
|
|
1413
|
+
SUPPORTED_CHAINS,
|
|
1414
|
+
useChain,
|
|
1415
|
+
useCreateWallet,
|
|
1416
|
+
useExportWallet,
|
|
1417
|
+
useGenerateMcpKey,
|
|
677
1418
|
useHorusAuth,
|
|
1419
|
+
useMcpKeys,
|
|
1420
|
+
useRevokeMcpKey,
|
|
1421
|
+
useSendTransaction,
|
|
678
1422
|
useSignMessage,
|
|
1423
|
+
useSignTypedData,
|
|
1424
|
+
useSwitchChain,
|
|
679
1425
|
useTransfer,
|
|
1426
|
+
useUpdateMcpKey,
|
|
680
1427
|
useUser,
|
|
681
1428
|
useWallets
|
|
682
1429
|
});
|