@horus-wallet/sdk-react 0.1.0-beta.2 → 0.3.0-beta.1
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 +608 -55
- package/dist/index.d.cts +449 -31
- package/dist/index.d.ts +449 -31
- package/dist/index.js +599 -55
- package/package.json +7 -2
package/dist/index.cjs
CHANGED
|
@@ -20,11 +20,20 @@ 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,
|
|
26
32
|
useHorusAuth: () => useHorusAuth,
|
|
33
|
+
useSendTransaction: () => useSendTransaction,
|
|
27
34
|
useSignMessage: () => useSignMessage,
|
|
35
|
+
useSignTypedData: () => useSignTypedData,
|
|
36
|
+
useSwitchChain: () => useSwitchChain,
|
|
28
37
|
useTransfer: () => useTransfer,
|
|
29
38
|
useUser: () => useUser,
|
|
30
39
|
useWallets: () => useWallets
|
|
@@ -122,19 +131,21 @@ function makeHttp(deps) {
|
|
|
122
131
|
return path.startsWith("/") ? `${base}${path}` : `${base}/${path}`;
|
|
123
132
|
};
|
|
124
133
|
async function rawRequest(method, path, body, options = {}) {
|
|
125
|
-
const headers = {
|
|
134
|
+
const headers = {
|
|
135
|
+
"Content-Type": "application/json",
|
|
136
|
+
"x-horus-key": deps.appId
|
|
137
|
+
};
|
|
126
138
|
if (options.auth !== false) {
|
|
127
139
|
const tokens = deps.getTokens();
|
|
128
140
|
if (tokens?.idToken) {
|
|
129
|
-
headers["
|
|
141
|
+
headers["Authorization"] = `Bearer ${tokens.idToken}`;
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
const res = await fetch(url(path), {
|
|
133
145
|
method,
|
|
134
146
|
headers,
|
|
135
147
|
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
136
|
-
signal: options.signal
|
|
137
|
-
credentials: "same-origin"
|
|
148
|
+
signal: options.signal
|
|
138
149
|
});
|
|
139
150
|
const text = await res.text();
|
|
140
151
|
let parsed = void 0;
|
|
@@ -175,9 +186,15 @@ function makeHttp(deps) {
|
|
|
175
186
|
|
|
176
187
|
// src/HorusProvider.tsx
|
|
177
188
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
178
|
-
var DEFAULT_API_BASE = "
|
|
189
|
+
var DEFAULT_API_BASE = "https://api.horuswallet.com";
|
|
179
190
|
var DEFAULT_AUTH_PAGE = "https://auth.horuswallet.com";
|
|
180
191
|
var REFRESH_LEAD_SECONDS = 60;
|
|
192
|
+
var DEFAULT_AUTO_PROVISION = {
|
|
193
|
+
network: "EVM",
|
|
194
|
+
networkType: "MAINNET"
|
|
195
|
+
};
|
|
196
|
+
var DEFAULT_CHAIN = { network: "EVM", networkType: "MAINNET" };
|
|
197
|
+
var AUTO_PROVISION_STORAGE_KEY = "horus.autoProvisioned.localIds";
|
|
181
198
|
function HorusProvider(props) {
|
|
182
199
|
const {
|
|
183
200
|
appId,
|
|
@@ -186,11 +203,21 @@ function HorusProvider(props) {
|
|
|
186
203
|
branding,
|
|
187
204
|
tokenStorage = "localStorage",
|
|
188
205
|
autoRefresh = true,
|
|
206
|
+
autoProvisionWallet = DEFAULT_AUTO_PROVISION,
|
|
207
|
+
defaultChain = DEFAULT_CHAIN,
|
|
189
208
|
children
|
|
190
209
|
} = props;
|
|
191
210
|
const tokenStoreRef = (0, import_react2.useRef)(createTokenStore(tokenStorage));
|
|
192
211
|
const tokensRef = (0, import_react2.useRef)(null);
|
|
193
212
|
const [state, setState] = (0, import_react2.useState)({ status: "loading" });
|
|
213
|
+
const [walletsVersion, setWalletsVersion] = (0, import_react2.useState)(0);
|
|
214
|
+
const revalidateWallets = (0, import_react2.useCallback)(() => {
|
|
215
|
+
setWalletsVersion((v) => v + 1);
|
|
216
|
+
}, []);
|
|
217
|
+
const [currentChain, setCurrentChain] = (0, import_react2.useState)(defaultChain);
|
|
218
|
+
const setChain = (0, import_react2.useCallback)((chain) => {
|
|
219
|
+
setCurrentChain(chain);
|
|
220
|
+
}, []);
|
|
194
221
|
const setTokens = (0, import_react2.useCallback)((tokens) => {
|
|
195
222
|
tokensRef.current = tokens;
|
|
196
223
|
if (tokens) {
|
|
@@ -211,9 +238,11 @@ function HorusProvider(props) {
|
|
|
211
238
|
if (!cur?.refreshToken) throw new Error("no refresh token cached");
|
|
212
239
|
const fresh = await fetch(joinUrl(apiBase, "/auth/refresh"), {
|
|
213
240
|
method: "POST",
|
|
214
|
-
headers: {
|
|
215
|
-
|
|
216
|
-
|
|
241
|
+
headers: {
|
|
242
|
+
"Content-Type": "application/json",
|
|
243
|
+
"x-horus-key": appId
|
|
244
|
+
},
|
|
245
|
+
body: JSON.stringify({ refreshToken: cur.refreshToken })
|
|
217
246
|
});
|
|
218
247
|
if (!fresh.ok) throw new Error(`refresh failed: ${fresh.status}`);
|
|
219
248
|
const raw = await fresh.json();
|
|
@@ -223,11 +252,12 @@ function HorusProvider(props) {
|
|
|
223
252
|
};
|
|
224
253
|
return makeHttp({
|
|
225
254
|
apiBase,
|
|
255
|
+
appId,
|
|
226
256
|
getTokens: () => tokensRef.current,
|
|
227
257
|
refreshTokens: refreshOnce,
|
|
228
258
|
autoRefresh
|
|
229
259
|
});
|
|
230
|
-
}, [apiBase, autoRefresh, setTokens]);
|
|
260
|
+
}, [apiBase, appId, autoRefresh, setTokens]);
|
|
231
261
|
(0, import_react2.useEffect)(() => {
|
|
232
262
|
const stored = tokenStoreRef.current.read();
|
|
233
263
|
if (stored) {
|
|
@@ -266,6 +296,33 @@ function HorusProvider(props) {
|
|
|
266
296
|
}, refreshIn);
|
|
267
297
|
return () => clearTimeout(handle);
|
|
268
298
|
}, [state, autoRefresh, http, setTokens]);
|
|
299
|
+
(0, import_react2.useEffect)(() => {
|
|
300
|
+
if (state.status !== "authenticated") return;
|
|
301
|
+
if (!autoProvisionWallet) return;
|
|
302
|
+
const localId = state.tokens.localId;
|
|
303
|
+
if (!localId) return;
|
|
304
|
+
if (alreadyAutoProvisioned(localId)) return;
|
|
305
|
+
let cancelled = false;
|
|
306
|
+
(async () => {
|
|
307
|
+
try {
|
|
308
|
+
const existing = await http.get("/getWallet");
|
|
309
|
+
if (cancelled) return;
|
|
310
|
+
const hasAny = Object.values(existing?.wallets ?? {}).some(
|
|
311
|
+
(group) => Array.isArray(group?.wallets) && group.wallets.length > 0
|
|
312
|
+
);
|
|
313
|
+
markAutoProvisioned(localId);
|
|
314
|
+
if (hasAny) return;
|
|
315
|
+
await http.post("/createWallet", autoProvisionWallet);
|
|
316
|
+
if (cancelled) return;
|
|
317
|
+
revalidateWallets();
|
|
318
|
+
} catch (err) {
|
|
319
|
+
console.warn("@horus-wallet/sdk-react: auto-provision wallet failed", err);
|
|
320
|
+
}
|
|
321
|
+
})();
|
|
322
|
+
return () => {
|
|
323
|
+
cancelled = true;
|
|
324
|
+
};
|
|
325
|
+
}, [state, autoProvisionWallet, http, revalidateWallets]);
|
|
269
326
|
const ctx = (0, import_react2.useMemo)(
|
|
270
327
|
() => ({
|
|
271
328
|
state,
|
|
@@ -286,9 +343,24 @@ function HorusProvider(props) {
|
|
|
286
343
|
const stamped = stampExpiry(raw);
|
|
287
344
|
setTokens(stamped);
|
|
288
345
|
return stamped;
|
|
289
|
-
}
|
|
346
|
+
},
|
|
347
|
+
walletsVersion,
|
|
348
|
+
revalidateWallets,
|
|
349
|
+
currentChain,
|
|
350
|
+
setChain
|
|
290
351
|
}),
|
|
291
|
-
[
|
|
352
|
+
[
|
|
353
|
+
state,
|
|
354
|
+
http,
|
|
355
|
+
authPageUrl,
|
|
356
|
+
appId,
|
|
357
|
+
branding,
|
|
358
|
+
setTokens,
|
|
359
|
+
walletsVersion,
|
|
360
|
+
revalidateWallets,
|
|
361
|
+
currentChain,
|
|
362
|
+
setChain
|
|
363
|
+
]
|
|
292
364
|
);
|
|
293
365
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HorusContext.Provider, { value: ctx, children });
|
|
294
366
|
}
|
|
@@ -302,6 +374,36 @@ function userFromTokens(t) {
|
|
|
302
374
|
providerId: t.providerId
|
|
303
375
|
};
|
|
304
376
|
}
|
|
377
|
+
var memoryAutoProvisioned = /* @__PURE__ */ new Set();
|
|
378
|
+
function readAutoProvisionedSet() {
|
|
379
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
380
|
+
return memoryAutoProvisioned;
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
const raw = window.localStorage.getItem(AUTO_PROVISION_STORAGE_KEY);
|
|
384
|
+
if (!raw) return /* @__PURE__ */ new Set();
|
|
385
|
+
const parsed = JSON.parse(raw);
|
|
386
|
+
return new Set(Array.isArray(parsed) ? parsed : []);
|
|
387
|
+
} catch {
|
|
388
|
+
return /* @__PURE__ */ new Set();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function alreadyAutoProvisioned(localId) {
|
|
392
|
+
return readAutoProvisionedSet().has(localId);
|
|
393
|
+
}
|
|
394
|
+
function markAutoProvisioned(localId) {
|
|
395
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
396
|
+
memoryAutoProvisioned.add(localId);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
const set = readAutoProvisionedSet();
|
|
401
|
+
set.add(localId);
|
|
402
|
+
window.localStorage.setItem(AUTO_PROVISION_STORAGE_KEY, JSON.stringify([...set]));
|
|
403
|
+
} catch {
|
|
404
|
+
memoryAutoProvisioned.add(localId);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
305
407
|
function joinUrl(base, path) {
|
|
306
408
|
const b = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
307
409
|
const p = path.startsWith("/") ? path : `/${path}`;
|
|
@@ -310,6 +412,36 @@ function joinUrl(base, path) {
|
|
|
310
412
|
|
|
311
413
|
// src/hooks/useHorusAuth.ts
|
|
312
414
|
var import_react3 = require("react");
|
|
415
|
+
|
|
416
|
+
// src/internal/authUrl.ts
|
|
417
|
+
function buildAuthUrl(p, state) {
|
|
418
|
+
const url = new URL(p.baseUrl);
|
|
419
|
+
url.searchParams.set("flow", p.flow);
|
|
420
|
+
url.searchParams.set("origin", window.location.origin);
|
|
421
|
+
url.searchParams.set("state", state);
|
|
422
|
+
url.searchParams.set("appKey", p.appId);
|
|
423
|
+
url.searchParams.set("mode", p.mode);
|
|
424
|
+
if (p.phone) url.searchParams.set("phone", p.phone);
|
|
425
|
+
const b = p.branding;
|
|
426
|
+
if (b) {
|
|
427
|
+
if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
|
|
428
|
+
if (b.brandName) url.searchParams.set("b_name", b.brandName);
|
|
429
|
+
if (b.primaryColor) url.searchParams.set("b_color", b.primaryColor);
|
|
430
|
+
if (b.backgroundColor) url.searchParams.set("b_bg", b.backgroundColor);
|
|
431
|
+
if (b.fontFamily) url.searchParams.set("b_font", b.fontFamily);
|
|
432
|
+
if (b.termsUrl) url.searchParams.set("b_terms", b.termsUrl);
|
|
433
|
+
if (b.privacyUrl) url.searchParams.set("b_privacy", b.privacyUrl);
|
|
434
|
+
if (b.showPoweredByHorus === false) url.searchParams.set("b_poweredby", "0");
|
|
435
|
+
}
|
|
436
|
+
return url.toString();
|
|
437
|
+
}
|
|
438
|
+
function randomState() {
|
|
439
|
+
const bytes = new Uint8Array(16);
|
|
440
|
+
crypto.getRandomValues(bytes);
|
|
441
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/hooks/useHorusAuth.ts
|
|
313
445
|
function useHorusAuth() {
|
|
314
446
|
const ctx = useHorusContext();
|
|
315
447
|
const { state, http, signIn, signOut, refreshTokens, authPageUrl, appId, branding } = ctx;
|
|
@@ -409,7 +541,7 @@ function openPopupFlow(params) {
|
|
|
409
541
|
return;
|
|
410
542
|
}
|
|
411
543
|
const state = randomState();
|
|
412
|
-
const url = buildAuthUrl(params, state);
|
|
544
|
+
const url = buildAuthUrl({ ...params, mode: "popup" }, state);
|
|
413
545
|
const popup = window.open(url, "horus-auth", defaultPopupFeatures());
|
|
414
546
|
if (!popup) {
|
|
415
547
|
reject(new Error("Popup blocked \u2014 call from a click handler so the browser allows it."));
|
|
@@ -471,26 +603,6 @@ function openPopupFlow(params) {
|
|
|
471
603
|
}, 400);
|
|
472
604
|
});
|
|
473
605
|
}
|
|
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
606
|
function defaultPopupFeatures() {
|
|
495
607
|
const w = 480, h = 640;
|
|
496
608
|
if (typeof window === "undefined") return `width=${w},height=${h}`;
|
|
@@ -498,11 +610,6 @@ function defaultPopupFeatures() {
|
|
|
498
610
|
const top = Math.max(0, (window.innerHeight - h) / 2 + (window.screenY ?? 0));
|
|
499
611
|
return `width=${w},height=${h},left=${left},top=${top},popup=1`;
|
|
500
612
|
}
|
|
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
613
|
|
|
507
614
|
// src/hooks/useUser.ts
|
|
508
615
|
function useUser() {
|
|
@@ -513,7 +620,7 @@ function useUser() {
|
|
|
513
620
|
// src/hooks/useWallets.ts
|
|
514
621
|
var import_react4 = require("react");
|
|
515
622
|
function useWallets() {
|
|
516
|
-
const { http, state } = useHorusContext();
|
|
623
|
+
const { http, state, walletsVersion } = useHorusContext();
|
|
517
624
|
const [wallets, setWallets] = (0, import_react4.useState)([]);
|
|
518
625
|
const [ready, setReady] = (0, import_react4.useState)(false);
|
|
519
626
|
const [error, setError] = (0, import_react4.useState)(void 0);
|
|
@@ -527,7 +634,7 @@ function useWallets() {
|
|
|
527
634
|
setError(void 0);
|
|
528
635
|
try {
|
|
529
636
|
const response = await http.get(
|
|
530
|
-
"/
|
|
637
|
+
"/getWallet"
|
|
531
638
|
);
|
|
532
639
|
const flat = [];
|
|
533
640
|
for (const [network, group] of Object.entries(response.wallets ?? {})) {
|
|
@@ -544,22 +651,87 @@ function useWallets() {
|
|
|
544
651
|
}, [http, state.status]);
|
|
545
652
|
(0, import_react4.useEffect)(() => {
|
|
546
653
|
void load();
|
|
547
|
-
}, [load]);
|
|
654
|
+
}, [load, walletsVersion]);
|
|
548
655
|
return { ready, wallets, refresh: load, error };
|
|
549
656
|
}
|
|
550
657
|
|
|
551
|
-
// src/hooks/
|
|
658
|
+
// src/hooks/useCreateWallet.ts
|
|
552
659
|
var import_react5 = require("react");
|
|
553
|
-
function
|
|
554
|
-
const { http } = useHorusContext();
|
|
660
|
+
function useCreateWallet() {
|
|
661
|
+
const { http, revalidateWallets } = useHorusContext();
|
|
555
662
|
const [pending, setPending] = (0, import_react5.useState)(false);
|
|
556
663
|
const [error, setError] = (0, import_react5.useState)(void 0);
|
|
557
|
-
const
|
|
664
|
+
const create = (0, import_react5.useCallback)(
|
|
558
665
|
async (input) => {
|
|
666
|
+
if (pending) {
|
|
667
|
+
const err = new Error(
|
|
668
|
+
"useCreateWallet: a previous create() call is still in flight"
|
|
669
|
+
);
|
|
670
|
+
setError(err);
|
|
671
|
+
throw err;
|
|
672
|
+
}
|
|
559
673
|
setPending(true);
|
|
560
674
|
setError(void 0);
|
|
561
675
|
try {
|
|
562
|
-
const response = await http.post("/
|
|
676
|
+
const response = await http.post("/createWallet", {
|
|
677
|
+
network: input.network,
|
|
678
|
+
networkType: input.networkType,
|
|
679
|
+
...input.password ? { password: input.password } : {}
|
|
680
|
+
});
|
|
681
|
+
revalidateWallets();
|
|
682
|
+
return response;
|
|
683
|
+
} catch (err) {
|
|
684
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
685
|
+
setError(e);
|
|
686
|
+
throw e;
|
|
687
|
+
} finally {
|
|
688
|
+
setPending(false);
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
[http, revalidateWallets, pending]
|
|
692
|
+
);
|
|
693
|
+
return { create, pending, error };
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/hooks/useSwitchChain.ts
|
|
697
|
+
var import_react6 = require("react");
|
|
698
|
+
var SUPPORTED_CHAINS = [
|
|
699
|
+
"EVM",
|
|
700
|
+
"ETHEREUM",
|
|
701
|
+
"BASE",
|
|
702
|
+
"POLYGON",
|
|
703
|
+
"BSC",
|
|
704
|
+
"ARBITRUM",
|
|
705
|
+
"OPTIMISM",
|
|
706
|
+
"BITCOIN",
|
|
707
|
+
"ICP",
|
|
708
|
+
"CASPER",
|
|
709
|
+
"AETERNITY"
|
|
710
|
+
];
|
|
711
|
+
function useSwitchChain() {
|
|
712
|
+
const { currentChain, setChain } = useHorusContext();
|
|
713
|
+
const switchChain = (0, import_react6.useCallback)(
|
|
714
|
+
(chain) => {
|
|
715
|
+
setChain(chain);
|
|
716
|
+
},
|
|
717
|
+
[setChain]
|
|
718
|
+
);
|
|
719
|
+
return { chain: currentChain, switchChain, supportedChains: SUPPORTED_CHAINS };
|
|
720
|
+
}
|
|
721
|
+
var useChain = useSwitchChain;
|
|
722
|
+
|
|
723
|
+
// src/hooks/useSignMessage.ts
|
|
724
|
+
var import_react7 = require("react");
|
|
725
|
+
function useSignMessage() {
|
|
726
|
+
const { http } = useHorusContext();
|
|
727
|
+
const [pending, setPending] = (0, import_react7.useState)(false);
|
|
728
|
+
const [error, setError] = (0, import_react7.useState)(void 0);
|
|
729
|
+
const signMessage = (0, import_react7.useCallback)(
|
|
730
|
+
async (input) => {
|
|
731
|
+
setPending(true);
|
|
732
|
+
setError(void 0);
|
|
733
|
+
try {
|
|
734
|
+
const response = await http.post("/signMessage", input);
|
|
563
735
|
return response.signature;
|
|
564
736
|
} catch (err) {
|
|
565
737
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -574,13 +746,79 @@ function useSignMessage() {
|
|
|
574
746
|
return { signMessage, pending, error };
|
|
575
747
|
}
|
|
576
748
|
|
|
749
|
+
// src/hooks/useSignTypedData.ts
|
|
750
|
+
var import_react8 = require("react");
|
|
751
|
+
function useSignTypedData() {
|
|
752
|
+
const { http } = useHorusContext();
|
|
753
|
+
const [pending, setPending] = (0, import_react8.useState)(false);
|
|
754
|
+
const [error, setError] = (0, import_react8.useState)(void 0);
|
|
755
|
+
const signTypedData = (0, import_react8.useCallback)(
|
|
756
|
+
async (input) => {
|
|
757
|
+
setPending(true);
|
|
758
|
+
setError(void 0);
|
|
759
|
+
try {
|
|
760
|
+
const response = await http.post("/signTypedData", {
|
|
761
|
+
network: input.network,
|
|
762
|
+
networkType: input.networkType,
|
|
763
|
+
typedData: input.typedData,
|
|
764
|
+
...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {},
|
|
765
|
+
...input.password ? { password: input.password } : {}
|
|
766
|
+
});
|
|
767
|
+
return response.signature;
|
|
768
|
+
} catch (err) {
|
|
769
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
770
|
+
setError(e);
|
|
771
|
+
throw e;
|
|
772
|
+
} finally {
|
|
773
|
+
setPending(false);
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
[http]
|
|
777
|
+
);
|
|
778
|
+
return { signTypedData, pending, error };
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// src/hooks/useSendTransaction.ts
|
|
782
|
+
var import_react9 = require("react");
|
|
783
|
+
function useSendTransaction() {
|
|
784
|
+
const { http } = useHorusContext();
|
|
785
|
+
const [pending, setPending] = (0, import_react9.useState)(false);
|
|
786
|
+
const [error, setError] = (0, import_react9.useState)(void 0);
|
|
787
|
+
const sendTransaction = (0, import_react9.useCallback)(
|
|
788
|
+
async (input) => {
|
|
789
|
+
setPending(true);
|
|
790
|
+
setError(void 0);
|
|
791
|
+
try {
|
|
792
|
+
return await http.post("/sendTransaction", {
|
|
793
|
+
network: input.network,
|
|
794
|
+
networkType: input.networkType,
|
|
795
|
+
to: input.to,
|
|
796
|
+
...input.value !== void 0 ? { value: String(input.value) } : {},
|
|
797
|
+
...input.data !== void 0 ? { data: input.data } : {},
|
|
798
|
+
...input.gasLimit !== void 0 ? { gasLimit: String(input.gasLimit) } : {},
|
|
799
|
+
...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {},
|
|
800
|
+
...input.password ? { password: input.password } : {}
|
|
801
|
+
});
|
|
802
|
+
} catch (err) {
|
|
803
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
804
|
+
setError(e);
|
|
805
|
+
throw e;
|
|
806
|
+
} finally {
|
|
807
|
+
setPending(false);
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
[http]
|
|
811
|
+
);
|
|
812
|
+
return { sendTransaction, pending, error };
|
|
813
|
+
}
|
|
814
|
+
|
|
577
815
|
// src/hooks/useTransfer.ts
|
|
578
|
-
var
|
|
816
|
+
var import_react10 = require("react");
|
|
579
817
|
function useTransfer() {
|
|
580
818
|
const { http } = useHorusContext();
|
|
581
|
-
const [pending, setPending] = (0,
|
|
582
|
-
const [error, setError] = (0,
|
|
583
|
-
const wrap = (0,
|
|
819
|
+
const [pending, setPending] = (0, import_react10.useState)(false);
|
|
820
|
+
const [error, setError] = (0, import_react10.useState)(void 0);
|
|
821
|
+
const wrap = (0, import_react10.useCallback)(
|
|
584
822
|
async (fn) => {
|
|
585
823
|
setPending(true);
|
|
586
824
|
setError(void 0);
|
|
@@ -596,18 +834,18 @@ function useTransfer() {
|
|
|
596
834
|
},
|
|
597
835
|
[]
|
|
598
836
|
);
|
|
599
|
-
const native = (0,
|
|
837
|
+
const native = (0, import_react10.useCallback)(
|
|
600
838
|
(input) => wrap(
|
|
601
|
-
() => http.post("/
|
|
839
|
+
() => http.post("/nativeTransfer", {
|
|
602
840
|
...input,
|
|
603
841
|
amount: typeof input.amount === "bigint" ? input.amount.toString() : String(input.amount)
|
|
604
842
|
})
|
|
605
843
|
),
|
|
606
844
|
[http, wrap]
|
|
607
845
|
);
|
|
608
|
-
const token = (0,
|
|
846
|
+
const token = (0, import_react10.useCallback)(
|
|
609
847
|
(input) => wrap(
|
|
610
|
-
() => http.post("/
|
|
848
|
+
() => http.post("/tokenTransfer", {
|
|
611
849
|
...input,
|
|
612
850
|
amount: typeof input.amount === "bigint" ? input.amount.toString() : String(input.amount)
|
|
613
851
|
})
|
|
@@ -618,7 +856,7 @@ function useTransfer() {
|
|
|
618
856
|
}
|
|
619
857
|
|
|
620
858
|
// src/components/HorusLoginButton.tsx
|
|
621
|
-
var
|
|
859
|
+
var import_react11 = require("react");
|
|
622
860
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
623
861
|
function HorusLoginButton({
|
|
624
862
|
flow = "google",
|
|
@@ -631,7 +869,7 @@ function HorusLoginButton({
|
|
|
631
869
|
...rest
|
|
632
870
|
}) {
|
|
633
871
|
const auth = useHorusAuth();
|
|
634
|
-
const [busy, setBusy] = (0,
|
|
872
|
+
const [busy, setBusy] = (0, import_react11.useState)(false);
|
|
635
873
|
const onClick = async () => {
|
|
636
874
|
if (busy) return;
|
|
637
875
|
setBusy(true);
|
|
@@ -669,13 +907,328 @@ function HorusLoginButton({
|
|
|
669
907
|
}
|
|
670
908
|
);
|
|
671
909
|
}
|
|
910
|
+
|
|
911
|
+
// src/components/HorusAuthModal.tsx
|
|
912
|
+
var import_react12 = require("react");
|
|
913
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
914
|
+
var defaultBackdropStyle = {
|
|
915
|
+
position: "fixed",
|
|
916
|
+
inset: 0,
|
|
917
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
918
|
+
display: "flex",
|
|
919
|
+
alignItems: "center",
|
|
920
|
+
justifyContent: "center",
|
|
921
|
+
zIndex: 2147483600
|
|
922
|
+
};
|
|
923
|
+
var defaultDialogStyle = {
|
|
924
|
+
width: "480px",
|
|
925
|
+
maxWidth: "95vw",
|
|
926
|
+
height: "640px",
|
|
927
|
+
maxHeight: "95vh",
|
|
928
|
+
background: "#fff",
|
|
929
|
+
borderRadius: "16px",
|
|
930
|
+
boxShadow: "0 24px 56px rgba(0, 0, 0, 0.32)",
|
|
931
|
+
overflow: "hidden"
|
|
932
|
+
};
|
|
933
|
+
function HorusAuthModal(props) {
|
|
934
|
+
const {
|
|
935
|
+
flow,
|
|
936
|
+
phone,
|
|
937
|
+
open,
|
|
938
|
+
onClose,
|
|
939
|
+
onSuccess,
|
|
940
|
+
onError,
|
|
941
|
+
dialogStyle,
|
|
942
|
+
backdropStyle
|
|
943
|
+
} = props;
|
|
944
|
+
const { appId, authPageUrl, branding, signIn } = useHorusContext();
|
|
945
|
+
const stateRef = (0, import_react12.useRef)("");
|
|
946
|
+
if (stateRef.current === "" || !open) {
|
|
947
|
+
stateRef.current = randomState();
|
|
948
|
+
}
|
|
949
|
+
const iframeSrc = (0, import_react12.useMemo)(() => {
|
|
950
|
+
if (!open) return "";
|
|
951
|
+
return buildAuthUrl(
|
|
952
|
+
{
|
|
953
|
+
flow,
|
|
954
|
+
appId,
|
|
955
|
+
baseUrl: authPageUrl,
|
|
956
|
+
mode: "iframe",
|
|
957
|
+
branding,
|
|
958
|
+
phone
|
|
959
|
+
},
|
|
960
|
+
stateRef.current
|
|
961
|
+
);
|
|
962
|
+
}, [open, flow, appId, authPageUrl, branding, phone]);
|
|
963
|
+
const expectedOrigin = (0, import_react12.useMemo)(() => {
|
|
964
|
+
try {
|
|
965
|
+
return new URL(authPageUrl).origin;
|
|
966
|
+
} catch {
|
|
967
|
+
return "";
|
|
968
|
+
}
|
|
969
|
+
}, [authPageUrl]);
|
|
970
|
+
(0, import_react12.useEffect)(() => {
|
|
971
|
+
if (!open) return;
|
|
972
|
+
const onMessage = (ev) => {
|
|
973
|
+
if (ev.origin !== expectedOrigin) return;
|
|
974
|
+
const data = ev.data;
|
|
975
|
+
if (!data || data.channel !== "horus.auth" || data.version !== 1) return;
|
|
976
|
+
if (data.state !== void 0 && data.state !== stateRef.current) return;
|
|
977
|
+
const body = data.body;
|
|
978
|
+
if (!body) return;
|
|
979
|
+
switch (body.type) {
|
|
980
|
+
case "success": {
|
|
981
|
+
const stamped = stampExpiry({
|
|
982
|
+
idToken: body.idToken,
|
|
983
|
+
refreshToken: "",
|
|
984
|
+
expiresIn: body.expiresAt ? Math.max(60, body.expiresAt - Math.floor(Date.now() / 1e3)) : 3600,
|
|
985
|
+
localId: body.user?.uid ?? "",
|
|
986
|
+
email: body.user?.email,
|
|
987
|
+
displayName: body.user?.displayName,
|
|
988
|
+
photoUrl: body.user?.photoURL,
|
|
989
|
+
providerId: body.user?.providerId
|
|
990
|
+
});
|
|
991
|
+
signIn(stamped);
|
|
992
|
+
onSuccess?.({
|
|
993
|
+
uid: stamped.localId,
|
|
994
|
+
email: stamped.email,
|
|
995
|
+
emailVerified: stamped.emailVerified,
|
|
996
|
+
displayName: stamped.displayName,
|
|
997
|
+
photoUrl: stamped.photoUrl,
|
|
998
|
+
providerId: stamped.providerId
|
|
999
|
+
});
|
|
1000
|
+
onClose();
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
case "cancel":
|
|
1004
|
+
onClose();
|
|
1005
|
+
return;
|
|
1006
|
+
case "error":
|
|
1007
|
+
onError?.(new Error(body.message ?? "Sign-in failed."));
|
|
1008
|
+
onClose();
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
window.addEventListener("message", onMessage);
|
|
1013
|
+
return () => window.removeEventListener("message", onMessage);
|
|
1014
|
+
}, [open, expectedOrigin, signIn, onSuccess, onError, onClose]);
|
|
1015
|
+
(0, import_react12.useEffect)(() => {
|
|
1016
|
+
if (!open) return;
|
|
1017
|
+
const onKey = (ev) => {
|
|
1018
|
+
if (ev.key === "Escape") onClose();
|
|
1019
|
+
};
|
|
1020
|
+
window.addEventListener("keydown", onKey);
|
|
1021
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1022
|
+
}, [open, onClose]);
|
|
1023
|
+
(0, import_react12.useEffect)(() => {
|
|
1024
|
+
if (!open) return;
|
|
1025
|
+
if (typeof document === "undefined") return;
|
|
1026
|
+
const prev = document.body.style.overflow;
|
|
1027
|
+
document.body.style.overflow = "hidden";
|
|
1028
|
+
return () => {
|
|
1029
|
+
document.body.style.overflow = prev;
|
|
1030
|
+
};
|
|
1031
|
+
}, [open]);
|
|
1032
|
+
const backdropClick = (0, import_react12.useCallback)(
|
|
1033
|
+
(ev) => {
|
|
1034
|
+
if (ev.target === ev.currentTarget) onClose();
|
|
1035
|
+
},
|
|
1036
|
+
[onClose]
|
|
1037
|
+
);
|
|
1038
|
+
if (!open || typeof window === "undefined") return null;
|
|
1039
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1040
|
+
"div",
|
|
1041
|
+
{
|
|
1042
|
+
role: "dialog",
|
|
1043
|
+
"aria-modal": "true",
|
|
1044
|
+
"aria-label": "Sign in with Horus",
|
|
1045
|
+
style: { ...defaultBackdropStyle, ...backdropStyle },
|
|
1046
|
+
onClick: backdropClick,
|
|
1047
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...defaultDialogStyle, ...dialogStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1048
|
+
"iframe",
|
|
1049
|
+
{
|
|
1050
|
+
src: iframeSrc,
|
|
1051
|
+
title: "Horus authentication",
|
|
1052
|
+
allow: "publickey-credentials-get; publickey-credentials-create; clipboard-write",
|
|
1053
|
+
style: { width: "100%", height: "100%", border: "none" }
|
|
1054
|
+
}
|
|
1055
|
+
) })
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// src/components/HorusRevealModal.tsx
|
|
1061
|
+
var import_react13 = require("react");
|
|
1062
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1063
|
+
var defaultBackdropStyle2 = {
|
|
1064
|
+
position: "fixed",
|
|
1065
|
+
inset: 0,
|
|
1066
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
1067
|
+
display: "flex",
|
|
1068
|
+
alignItems: "center",
|
|
1069
|
+
justifyContent: "center",
|
|
1070
|
+
zIndex: 2147483600
|
|
1071
|
+
};
|
|
1072
|
+
var defaultDialogStyle2 = {
|
|
1073
|
+
width: "480px",
|
|
1074
|
+
maxWidth: "95vw",
|
|
1075
|
+
height: "640px",
|
|
1076
|
+
maxHeight: "95vh",
|
|
1077
|
+
background: "#fff",
|
|
1078
|
+
borderRadius: "16px",
|
|
1079
|
+
boxShadow: "0 24px 56px rgba(0, 0, 0, 0.32)",
|
|
1080
|
+
overflow: "hidden"
|
|
1081
|
+
};
|
|
1082
|
+
function HorusRevealModal(props) {
|
|
1083
|
+
const { revealToken, open, onClose, onComplete, onError, dialogStyle, backdropStyle } = props;
|
|
1084
|
+
const { appId, authPageUrl, branding } = useHorusContext();
|
|
1085
|
+
const stateRef = (0, import_react13.useRef)("");
|
|
1086
|
+
if (stateRef.current === "" || !open) {
|
|
1087
|
+
stateRef.current = randomState();
|
|
1088
|
+
}
|
|
1089
|
+
const iframeSrc = (0, import_react13.useMemo)(() => {
|
|
1090
|
+
if (!open || !revealToken) return "";
|
|
1091
|
+
const baseUrl = buildAuthUrl(
|
|
1092
|
+
{
|
|
1093
|
+
flow: "reveal",
|
|
1094
|
+
appId,
|
|
1095
|
+
baseUrl: authPageUrl,
|
|
1096
|
+
mode: "iframe",
|
|
1097
|
+
branding
|
|
1098
|
+
},
|
|
1099
|
+
stateRef.current
|
|
1100
|
+
);
|
|
1101
|
+
const u = new URL(baseUrl);
|
|
1102
|
+
u.searchParams.set("token", revealToken);
|
|
1103
|
+
return u.toString();
|
|
1104
|
+
}, [open, revealToken, appId, authPageUrl, branding]);
|
|
1105
|
+
const expectedOrigin = (0, import_react13.useMemo)(() => {
|
|
1106
|
+
try {
|
|
1107
|
+
return new URL(authPageUrl).origin;
|
|
1108
|
+
} catch {
|
|
1109
|
+
return "";
|
|
1110
|
+
}
|
|
1111
|
+
}, [authPageUrl]);
|
|
1112
|
+
(0, import_react13.useEffect)(() => {
|
|
1113
|
+
if (!open) return;
|
|
1114
|
+
const onMessage = (ev) => {
|
|
1115
|
+
if (ev.origin !== expectedOrigin) return;
|
|
1116
|
+
const data = ev.data;
|
|
1117
|
+
if (!data || data.channel !== "horus.auth" || data.version !== 1) return;
|
|
1118
|
+
if (data.state !== void 0 && data.state !== stateRef.current) return;
|
|
1119
|
+
const body = data.body;
|
|
1120
|
+
if (!body) return;
|
|
1121
|
+
switch (body.type) {
|
|
1122
|
+
case "reveal_complete":
|
|
1123
|
+
onComplete?.(Boolean(body.viewed));
|
|
1124
|
+
onClose();
|
|
1125
|
+
return;
|
|
1126
|
+
case "cancel":
|
|
1127
|
+
onClose();
|
|
1128
|
+
return;
|
|
1129
|
+
case "error":
|
|
1130
|
+
onError?.(new Error(body.message ?? "Reveal failed."));
|
|
1131
|
+
onClose();
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
window.addEventListener("message", onMessage);
|
|
1136
|
+
return () => window.removeEventListener("message", onMessage);
|
|
1137
|
+
}, [open, expectedOrigin, onComplete, onClose, onError]);
|
|
1138
|
+
(0, import_react13.useEffect)(() => {
|
|
1139
|
+
if (!open) return;
|
|
1140
|
+
const onKey = (ev) => {
|
|
1141
|
+
if (ev.key === "Escape") onClose();
|
|
1142
|
+
};
|
|
1143
|
+
window.addEventListener("keydown", onKey);
|
|
1144
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1145
|
+
}, [open, onClose]);
|
|
1146
|
+
(0, import_react13.useEffect)(() => {
|
|
1147
|
+
if (!open) return;
|
|
1148
|
+
if (typeof document === "undefined") return;
|
|
1149
|
+
const prev = document.body.style.overflow;
|
|
1150
|
+
document.body.style.overflow = "hidden";
|
|
1151
|
+
return () => {
|
|
1152
|
+
document.body.style.overflow = prev;
|
|
1153
|
+
};
|
|
1154
|
+
}, [open]);
|
|
1155
|
+
const backdropClick = (0, import_react13.useCallback)(
|
|
1156
|
+
(ev) => {
|
|
1157
|
+
if (ev.target === ev.currentTarget) onClose();
|
|
1158
|
+
},
|
|
1159
|
+
[onClose]
|
|
1160
|
+
);
|
|
1161
|
+
if (!open || !revealToken || typeof window === "undefined") return null;
|
|
1162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1163
|
+
"div",
|
|
1164
|
+
{
|
|
1165
|
+
role: "dialog",
|
|
1166
|
+
"aria-modal": "true",
|
|
1167
|
+
"aria-label": "Reveal wallet private key",
|
|
1168
|
+
style: { ...defaultBackdropStyle2, ...backdropStyle },
|
|
1169
|
+
onClick: backdropClick,
|
|
1170
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...defaultDialogStyle2, ...dialogStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1171
|
+
"iframe",
|
|
1172
|
+
{
|
|
1173
|
+
src: iframeSrc,
|
|
1174
|
+
title: "Horus wallet export",
|
|
1175
|
+
allow: "clipboard-write",
|
|
1176
|
+
style: { width: "100%", height: "100%", border: "none" }
|
|
1177
|
+
}
|
|
1178
|
+
) })
|
|
1179
|
+
}
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// src/hooks/useExportWallet.ts
|
|
1184
|
+
var import_react14 = require("react");
|
|
1185
|
+
function useExportWallet() {
|
|
1186
|
+
const { http } = useHorusContext();
|
|
1187
|
+
const [pending, setPending] = (0, import_react14.useState)(false);
|
|
1188
|
+
const [error, setError] = (0, import_react14.useState)(void 0);
|
|
1189
|
+
const reveal = (0, import_react14.useCallback)(
|
|
1190
|
+
async (input) => {
|
|
1191
|
+
setPending(true);
|
|
1192
|
+
setError(void 0);
|
|
1193
|
+
try {
|
|
1194
|
+
const response = await http.post(
|
|
1195
|
+
"/exportWallet/grant",
|
|
1196
|
+
{
|
|
1197
|
+
network: input.network,
|
|
1198
|
+
networkType: input.networkType,
|
|
1199
|
+
password: input.password,
|
|
1200
|
+
...typeof input.walletIndex === "number" ? { walletIndex: input.walletIndex } : {}
|
|
1201
|
+
}
|
|
1202
|
+
);
|
|
1203
|
+
return response;
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1206
|
+
setError(e);
|
|
1207
|
+
throw e;
|
|
1208
|
+
} finally {
|
|
1209
|
+
setPending(false);
|
|
1210
|
+
}
|
|
1211
|
+
},
|
|
1212
|
+
[http]
|
|
1213
|
+
);
|
|
1214
|
+
return { reveal, pending, error };
|
|
1215
|
+
}
|
|
672
1216
|
// Annotate the CommonJS export names for ESM import in node:
|
|
673
1217
|
0 && (module.exports = {
|
|
1218
|
+
HorusAuthModal,
|
|
674
1219
|
HorusHttpError,
|
|
675
1220
|
HorusLoginButton,
|
|
676
1221
|
HorusProvider,
|
|
1222
|
+
HorusRevealModal,
|
|
1223
|
+
SUPPORTED_CHAINS,
|
|
1224
|
+
useChain,
|
|
1225
|
+
useCreateWallet,
|
|
1226
|
+
useExportWallet,
|
|
677
1227
|
useHorusAuth,
|
|
1228
|
+
useSendTransaction,
|
|
678
1229
|
useSignMessage,
|
|
1230
|
+
useSignTypedData,
|
|
1231
|
+
useSwitchChain,
|
|
679
1232
|
useTransfer,
|
|
680
1233
|
useUser,
|
|
681
1234
|
useWallets
|