@horus-wallet/sdk-react 0.3.0-beta.1 → 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/dist/index.cjs +218 -24
- package/dist/index.d.cts +163 -7
- package/dist/index.d.ts +163 -7
- package/dist/index.js +214 -24
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -29,12 +29,16 @@ __export(index_exports, {
|
|
|
29
29
|
useChain: () => useChain,
|
|
30
30
|
useCreateWallet: () => useCreateWallet,
|
|
31
31
|
useExportWallet: () => useExportWallet,
|
|
32
|
+
useGenerateMcpKey: () => useGenerateMcpKey,
|
|
32
33
|
useHorusAuth: () => useHorusAuth,
|
|
34
|
+
useMcpKeys: () => useMcpKeys,
|
|
35
|
+
useRevokeMcpKey: () => useRevokeMcpKey,
|
|
33
36
|
useSendTransaction: () => useSendTransaction,
|
|
34
37
|
useSignMessage: () => useSignMessage,
|
|
35
38
|
useSignTypedData: () => useSignTypedData,
|
|
36
39
|
useSwitchChain: () => useSwitchChain,
|
|
37
40
|
useTransfer: () => useTransfer,
|
|
41
|
+
useUpdateMcpKey: () => useUpdateMcpKey,
|
|
38
42
|
useUser: () => useUser,
|
|
39
43
|
useWallets: () => useWallets
|
|
40
44
|
});
|
|
@@ -190,10 +194,10 @@ var DEFAULT_API_BASE = "https://api.horuswallet.com";
|
|
|
190
194
|
var DEFAULT_AUTH_PAGE = "https://auth.horuswallet.com";
|
|
191
195
|
var REFRESH_LEAD_SECONDS = 60;
|
|
192
196
|
var DEFAULT_AUTO_PROVISION = {
|
|
193
|
-
network: "
|
|
197
|
+
network: "ETHEREUM",
|
|
194
198
|
networkType: "MAINNET"
|
|
195
199
|
};
|
|
196
|
-
var DEFAULT_CHAIN = { network: "
|
|
200
|
+
var DEFAULT_CHAIN = { network: "ETHEREUM", networkType: "MAINNET" };
|
|
197
201
|
var AUTO_PROVISION_STORAGE_KEY = "horus.autoProvisioned.localIds";
|
|
198
202
|
function HorusProvider(props) {
|
|
199
203
|
const {
|
|
@@ -218,6 +222,10 @@ function HorusProvider(props) {
|
|
|
218
222
|
const setChain = (0, import_react2.useCallback)((chain) => {
|
|
219
223
|
setCurrentChain(chain);
|
|
220
224
|
}, []);
|
|
225
|
+
const [mcpKeysVersion, setMcpKeysVersion] = (0, import_react2.useState)(0);
|
|
226
|
+
const revalidateMcpKeys = (0, import_react2.useCallback)(() => {
|
|
227
|
+
setMcpKeysVersion((v) => v + 1);
|
|
228
|
+
}, []);
|
|
221
229
|
const setTokens = (0, import_react2.useCallback)((tokens) => {
|
|
222
230
|
tokensRef.current = tokens;
|
|
223
231
|
if (tokens) {
|
|
@@ -271,6 +279,58 @@ function HorusProvider(props) {
|
|
|
271
279
|
setState({ status: "unauthenticated" });
|
|
272
280
|
}
|
|
273
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]);
|
|
274
334
|
(0, import_react2.useEffect)(() => {
|
|
275
335
|
if (typeof window === "undefined") return;
|
|
276
336
|
const onStorage = (ev) => {
|
|
@@ -305,15 +365,9 @@ function HorusProvider(props) {
|
|
|
305
365
|
let cancelled = false;
|
|
306
366
|
(async () => {
|
|
307
367
|
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
368
|
await http.post("/createWallet", autoProvisionWallet);
|
|
316
369
|
if (cancelled) return;
|
|
370
|
+
markAutoProvisioned(localId);
|
|
317
371
|
revalidateWallets();
|
|
318
372
|
} catch (err) {
|
|
319
373
|
console.warn("@horus-wallet/sdk-react: auto-provision wallet failed", err);
|
|
@@ -347,7 +401,9 @@ function HorusProvider(props) {
|
|
|
347
401
|
walletsVersion,
|
|
348
402
|
revalidateWallets,
|
|
349
403
|
currentChain,
|
|
350
|
-
setChain
|
|
404
|
+
setChain,
|
|
405
|
+
mcpKeysVersion,
|
|
406
|
+
revalidateMcpKeys
|
|
351
407
|
}),
|
|
352
408
|
[
|
|
353
409
|
state,
|
|
@@ -359,7 +415,9 @@ function HorusProvider(props) {
|
|
|
359
415
|
walletsVersion,
|
|
360
416
|
revalidateWallets,
|
|
361
417
|
currentChain,
|
|
362
|
-
setChain
|
|
418
|
+
setChain,
|
|
419
|
+
mcpKeysVersion,
|
|
420
|
+
revalidateMcpKeys
|
|
363
421
|
]
|
|
364
422
|
);
|
|
365
423
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HorusContext.Provider, { value: ctx, children });
|
|
@@ -374,6 +432,7 @@ function userFromTokens(t) {
|
|
|
374
432
|
providerId: t.providerId
|
|
375
433
|
};
|
|
376
434
|
}
|
|
435
|
+
var consumedMagicLinkCodes = /* @__PURE__ */ new Set();
|
|
377
436
|
var memoryAutoProvisioned = /* @__PURE__ */ new Set();
|
|
378
437
|
function readAutoProvisionedSet() {
|
|
379
438
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -422,6 +481,9 @@ function buildAuthUrl(p, state) {
|
|
|
422
481
|
url.searchParams.set("appKey", p.appId);
|
|
423
482
|
url.searchParams.set("mode", p.mode);
|
|
424
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
|
+
}
|
|
425
487
|
const b = p.branding;
|
|
426
488
|
if (b) {
|
|
427
489
|
if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
|
|
@@ -549,6 +611,7 @@ function openPopupFlow(params) {
|
|
|
549
611
|
}
|
|
550
612
|
const expectedOrigin = new URL(params.baseUrl).origin;
|
|
551
613
|
let settled = false;
|
|
614
|
+
let lastError = null;
|
|
552
615
|
const cleanup = () => {
|
|
553
616
|
window.removeEventListener("message", onMessage);
|
|
554
617
|
clearInterval(poll);
|
|
@@ -579,17 +642,11 @@ function openPopupFlow(params) {
|
|
|
579
642
|
popup.close();
|
|
580
643
|
} catch {
|
|
581
644
|
}
|
|
582
|
-
reject(new Error("User cancelled sign-in."));
|
|
645
|
+
reject(new Error(lastError ?? "User cancelled sign-in."));
|
|
583
646
|
return;
|
|
584
647
|
case "error":
|
|
585
648
|
if (settled) return;
|
|
586
|
-
|
|
587
|
-
cleanup();
|
|
588
|
-
try {
|
|
589
|
-
popup.close();
|
|
590
|
-
} catch {
|
|
591
|
-
}
|
|
592
|
-
reject(new Error(body.message || "Sign-in failed."));
|
|
649
|
+
lastError = body.message || "Sign-in failed.";
|
|
593
650
|
return;
|
|
594
651
|
}
|
|
595
652
|
};
|
|
@@ -598,7 +655,7 @@ function openPopupFlow(params) {
|
|
|
598
655
|
if (popup.closed && !settled) {
|
|
599
656
|
settled = true;
|
|
600
657
|
cleanup();
|
|
601
|
-
reject(new Error("Auth popup was closed before sign-in completed."));
|
|
658
|
+
reject(new Error(lastError ?? "Auth popup was closed before sign-in completed."));
|
|
602
659
|
}
|
|
603
660
|
}, 400);
|
|
604
661
|
});
|
|
@@ -696,13 +753,20 @@ function useCreateWallet() {
|
|
|
696
753
|
// src/hooks/useSwitchChain.ts
|
|
697
754
|
var import_react6 = require("react");
|
|
698
755
|
var SUPPORTED_CHAINS = [
|
|
699
|
-
|
|
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.
|
|
700
760
|
"ETHEREUM",
|
|
701
761
|
"BASE",
|
|
702
762
|
"POLYGON",
|
|
703
763
|
"BSC",
|
|
704
764
|
"ARBITRUM",
|
|
705
765
|
"OPTIMISM",
|
|
766
|
+
"TELOS",
|
|
767
|
+
"CHILIZ",
|
|
768
|
+
"FLARE",
|
|
769
|
+
// Non-EVM chains.
|
|
706
770
|
"BITCOIN",
|
|
707
771
|
"ICP",
|
|
708
772
|
"CASPER",
|
|
@@ -933,6 +997,7 @@ var defaultDialogStyle = {
|
|
|
933
997
|
function HorusAuthModal(props) {
|
|
934
998
|
const {
|
|
935
999
|
flow,
|
|
1000
|
+
enabledMethods,
|
|
936
1001
|
phone,
|
|
937
1002
|
open,
|
|
938
1003
|
onClose,
|
|
@@ -955,11 +1020,12 @@ function HorusAuthModal(props) {
|
|
|
955
1020
|
baseUrl: authPageUrl,
|
|
956
1021
|
mode: "iframe",
|
|
957
1022
|
branding,
|
|
958
|
-
phone
|
|
1023
|
+
phone,
|
|
1024
|
+
enabledMethods
|
|
959
1025
|
},
|
|
960
1026
|
stateRef.current
|
|
961
1027
|
);
|
|
962
|
-
}, [open, flow, appId, authPageUrl, branding, phone]);
|
|
1028
|
+
}, [open, flow, appId, authPageUrl, branding, phone, enabledMethods]);
|
|
963
1029
|
const expectedOrigin = (0, import_react12.useMemo)(() => {
|
|
964
1030
|
try {
|
|
965
1031
|
return new URL(authPageUrl).origin;
|
|
@@ -1005,7 +1071,6 @@ function HorusAuthModal(props) {
|
|
|
1005
1071
|
return;
|
|
1006
1072
|
case "error":
|
|
1007
1073
|
onError?.(new Error(body.message ?? "Sign-in failed."));
|
|
1008
|
-
onClose();
|
|
1009
1074
|
return;
|
|
1010
1075
|
}
|
|
1011
1076
|
};
|
|
@@ -1213,6 +1278,131 @@ function useExportWallet() {
|
|
|
1213
1278
|
);
|
|
1214
1279
|
return { reveal, pending, error };
|
|
1215
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
|
+
}
|
|
1216
1406
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1217
1407
|
0 && (module.exports = {
|
|
1218
1408
|
HorusAuthModal,
|
|
@@ -1224,12 +1414,16 @@ function useExportWallet() {
|
|
|
1224
1414
|
useChain,
|
|
1225
1415
|
useCreateWallet,
|
|
1226
1416
|
useExportWallet,
|
|
1417
|
+
useGenerateMcpKey,
|
|
1227
1418
|
useHorusAuth,
|
|
1419
|
+
useMcpKeys,
|
|
1420
|
+
useRevokeMcpKey,
|
|
1228
1421
|
useSendTransaction,
|
|
1229
1422
|
useSignMessage,
|
|
1230
1423
|
useSignTypedData,
|
|
1231
1424
|
useSwitchChain,
|
|
1232
1425
|
useTransfer,
|
|
1426
|
+
useUpdateMcpKey,
|
|
1233
1427
|
useUser,
|
|
1234
1428
|
useWallets
|
|
1235
1429
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -107,10 +107,16 @@ interface HorusProviderConfig {
|
|
|
107
107
|
autoRefresh?: boolean;
|
|
108
108
|
/**
|
|
109
109
|
* Auto-create a wallet for the user on first sign-in if they don't
|
|
110
|
-
* already have one. Defaults to `{ network: '
|
|
110
|
+
* already have one. Defaults to `{ network: 'ETHEREUM', networkType: 'MAINNET' }`
|
|
111
111
|
* to match Privy's behavior — new users land in your app with a
|
|
112
112
|
* wallet already provisioned, no extra round-trip required.
|
|
113
113
|
*
|
|
114
|
+
* `network` must be a specific chain name (ETHEREUM, BASE, POLYGON,
|
|
115
|
+
* BITCOIN, ICP, CASPER, …) — the API rejects family labels like
|
|
116
|
+
* `'EVM'`. The wallet is stored by family on the backend, so a
|
|
117
|
+
* wallet provisioned as ETHEREUM is reachable via BASE / POLYGON /
|
|
118
|
+
* etc. at sign time (same key, different chain selector).
|
|
119
|
+
*
|
|
114
120
|
* Set to `false` to opt out (your app must call `useCreateWallet`
|
|
115
121
|
* explicitly to provision wallets).
|
|
116
122
|
*
|
|
@@ -125,7 +131,7 @@ interface HorusProviderConfig {
|
|
|
125
131
|
};
|
|
126
132
|
/**
|
|
127
133
|
* Initial active chain exposed via `useSwitchChain()` / `useChain()`.
|
|
128
|
-
* Defaults to `{ network: '
|
|
134
|
+
* Defaults to `{ network: 'ETHEREUM', networkType: 'MAINNET' }`.
|
|
129
135
|
*
|
|
130
136
|
* Partners can read the current chain in their UI (e.g., to highlight
|
|
131
137
|
* the active network in a switcher) and pass it explicitly when
|
|
@@ -324,7 +330,7 @@ declare function useCreateWallet(): UseCreateWalletResult;
|
|
|
324
330
|
* as readonly so a partner who pins on a specific value gets a TS
|
|
325
331
|
* error when we drop a chain rather than a silent runtime miss.
|
|
326
332
|
*/
|
|
327
|
-
declare const SUPPORTED_CHAINS: readonly ["
|
|
333
|
+
declare const SUPPORTED_CHAINS: readonly ["ETHEREUM", "BASE", "POLYGON", "BSC", "ARBITRUM", "OPTIMISM", "TELOS", "CHILIZ", "FLARE", "BITCOIN", "ICP", "CASPER", "AETERNITY"];
|
|
328
334
|
type SupportedChain = (typeof SUPPORTED_CHAINS)[number];
|
|
329
335
|
interface UseSwitchChainResult {
|
|
330
336
|
/** Currently-active chain selector. */
|
|
@@ -541,7 +547,15 @@ declare function HorusLoginButton({ flow, phone, onAuthenticated, onError, child
|
|
|
541
547
|
* Not exported to partners; internal-only.
|
|
542
548
|
*/
|
|
543
549
|
|
|
544
|
-
type AuthFlow = 'google' | 'email_link' | 'phone';
|
|
550
|
+
type AuthFlow = 'google' | 'email_link' | 'phone' | 'unified';
|
|
551
|
+
/**
|
|
552
|
+
* Subset of methods the `unified` chooser may surface. Aligns with the
|
|
553
|
+
* auth-page's `HorusUnifiedMethod`. Includes `email_password` because
|
|
554
|
+
* the unified flow exposes it as a button even though direct
|
|
555
|
+
* `?flow=email_password` from the SDK isn't useful (that path is
|
|
556
|
+
* already headless via `useHorusAuth().loginWithEmail`).
|
|
557
|
+
*/
|
|
558
|
+
type UnifiedMethod = 'google' | 'email_password' | 'email_link' | 'phone';
|
|
545
559
|
|
|
546
560
|
/**
|
|
547
561
|
* `<HorusAuthModal>` — inline auth flow.
|
|
@@ -577,9 +591,26 @@ type AuthFlow = 'google' | 'email_link' | 'phone';
|
|
|
577
591
|
*/
|
|
578
592
|
|
|
579
593
|
interface HorusAuthModalProps {
|
|
580
|
-
/**
|
|
594
|
+
/**
|
|
595
|
+
* Which sign-in flow to render in the iframe. Use `'unified'` for
|
|
596
|
+
* the Privy-style "one button → chooser modal" pattern: the iframe
|
|
597
|
+
* lands on a chooser screen with one button per enabled method,
|
|
598
|
+
* then transitions to whichever flow the end-user picks.
|
|
599
|
+
*/
|
|
581
600
|
flow: AuthFlow;
|
|
582
|
-
/**
|
|
601
|
+
/**
|
|
602
|
+
* Only applies when `flow === 'unified'`. Comma-joined into the URL
|
|
603
|
+
* as `&methods=...` so the chooser only shows what you list. Omit
|
|
604
|
+
* to surface all four (google, email_password, email_link, phone).
|
|
605
|
+
*
|
|
606
|
+
* <HorusAuthModal
|
|
607
|
+
* flow="unified"
|
|
608
|
+
* enabledMethods={['google', 'email_password']}
|
|
609
|
+
* open onClose={...}
|
|
610
|
+
* />
|
|
611
|
+
*/
|
|
612
|
+
enabledMethods?: UnifiedMethod[];
|
|
613
|
+
/** Optional phone-flow pre-fill (when `flow === 'phone'`). */
|
|
583
614
|
phone?: string;
|
|
584
615
|
/** Controlled open state — partner toggles to show/hide the modal. */
|
|
585
616
|
open: boolean;
|
|
@@ -703,6 +734,131 @@ interface UseExportWalletResult {
|
|
|
703
734
|
}
|
|
704
735
|
declare function useExportWallet(): UseExportWalletResult;
|
|
705
736
|
|
|
737
|
+
/**
|
|
738
|
+
* MCP / agentic key management hooks.
|
|
739
|
+
*
|
|
740
|
+
* Mints, lists, revokes, and updates the "agent keys" that an end-user
|
|
741
|
+
* issues to an MCP runtime (Claude Desktop, custom agent, etc.) so the
|
|
742
|
+
* agent can act on the user's behalf — sign messages, transfer tokens,
|
|
743
|
+
* call partner-allowed tools — without the agent ever seeing the
|
|
744
|
+
* underlying wallet credentials.
|
|
745
|
+
*
|
|
746
|
+
* Scope is the wallet-OWNER side: minting + lifecycle. The actual
|
|
747
|
+
* JSON-RPC tool execution endpoint (`POST /mcp`) is consumed by MCP
|
|
748
|
+
* runtimes directly with the minted key; partner React code never
|
|
749
|
+
* calls it.
|
|
750
|
+
*
|
|
751
|
+
* Wire shapes mirror the Node SDK's `client.mcp.*` exactly so the
|
|
752
|
+
* two stay in sync.
|
|
753
|
+
*/
|
|
754
|
+
interface GenerateMcpKeyInput {
|
|
755
|
+
/** Display name shown in `listKeys` (and in any partner UI). */
|
|
756
|
+
name: string;
|
|
757
|
+
/** Wallet network this key signs against. */
|
|
758
|
+
network: string;
|
|
759
|
+
walletIndex?: number;
|
|
760
|
+
/** ISO-8601 expiry. The backend mirrors this to the row's TTL attribute. */
|
|
761
|
+
expiresAt?: string;
|
|
762
|
+
/** Optional IP allow-list (exact IPs only — no CIDRs). */
|
|
763
|
+
allowedIps?: string[];
|
|
764
|
+
/** Optional whitelist of MCP tool names this key may invoke. */
|
|
765
|
+
allowedTools?: string[];
|
|
766
|
+
/** Per-key rate cap. Backend default ~600 RPM. */
|
|
767
|
+
maxRequestsPerMinute?: number;
|
|
768
|
+
/**
|
|
769
|
+
* Wraps the wallet password so the key holder can sign without
|
|
770
|
+
* re-prompting. Required if the wallet is password-protected.
|
|
771
|
+
*/
|
|
772
|
+
mcpPassword?: string;
|
|
773
|
+
}
|
|
774
|
+
interface GenerateMcpKeyResponse {
|
|
775
|
+
/** PLAINTEXT — returned exactly once at creation. Never recoverable. */
|
|
776
|
+
key: string;
|
|
777
|
+
name: string;
|
|
778
|
+
prefix: string;
|
|
779
|
+
wallet_index: number;
|
|
780
|
+
network: string;
|
|
781
|
+
wallet_address?: string;
|
|
782
|
+
expires_at?: string;
|
|
783
|
+
allowed_ips: string[];
|
|
784
|
+
allowed_tools: string[];
|
|
785
|
+
max_requests_per_minute: number;
|
|
786
|
+
created_at: string;
|
|
787
|
+
message: string;
|
|
788
|
+
}
|
|
789
|
+
interface McpKeyMetadata {
|
|
790
|
+
prefix: string;
|
|
791
|
+
name: string;
|
|
792
|
+
wallet_index: number;
|
|
793
|
+
network: string;
|
|
794
|
+
expires_at?: string;
|
|
795
|
+
last_used_at?: string;
|
|
796
|
+
created_at: string;
|
|
797
|
+
allowed_ips: string[];
|
|
798
|
+
allowed_tools: string[];
|
|
799
|
+
max_requests_per_minute: number;
|
|
800
|
+
}
|
|
801
|
+
interface UpdateMcpKeyInput {
|
|
802
|
+
expiresAt?: string;
|
|
803
|
+
allowedIps?: string[];
|
|
804
|
+
allowedTools?: string[];
|
|
805
|
+
maxRequestsPerMinute?: number;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* `useGenerateMcpKey()` — mint a new MCP key.
|
|
809
|
+
*
|
|
810
|
+
* const { generate, pending, error, lastResult } = useGenerateMcpKey();
|
|
811
|
+
* const result = await generate({ name: 'Claude Desktop', network: 'ETHEREUM' });
|
|
812
|
+
* // Show `result.key` to the user immediately — it is never returned again.
|
|
813
|
+
*
|
|
814
|
+
* After a successful call, `lastResult` holds the response so partners
|
|
815
|
+
* can render a one-time secret banner without threading the value
|
|
816
|
+
* through their own state. It's cleared on the next `generate()`.
|
|
817
|
+
*/
|
|
818
|
+
declare function useGenerateMcpKey(): {
|
|
819
|
+
generate: (input: GenerateMcpKeyInput) => Promise<GenerateMcpKeyResponse>;
|
|
820
|
+
pending: boolean;
|
|
821
|
+
error: Error | undefined;
|
|
822
|
+
lastResult: GenerateMcpKeyResponse | null;
|
|
823
|
+
clearLastResult: () => void;
|
|
824
|
+
};
|
|
825
|
+
/**
|
|
826
|
+
* `useMcpKeys()` — read the current user's MCP key list.
|
|
827
|
+
*
|
|
828
|
+
* const { ready, keys, refresh, error } = useMcpKeys();
|
|
829
|
+
*
|
|
830
|
+
* Mutating hooks (`useGenerateMcpKey`, `useRevokeMcpKey`, `useUpdateMcpKey`)
|
|
831
|
+
* each bump an internal version counter so this hook re-fetches
|
|
832
|
+
* automatically after every change — no manual `refresh()` needed.
|
|
833
|
+
*/
|
|
834
|
+
declare function useMcpKeys(): {
|
|
835
|
+
ready: boolean;
|
|
836
|
+
keys: McpKeyMetadata[];
|
|
837
|
+
refresh: () => Promise<void>;
|
|
838
|
+
error: Error | undefined;
|
|
839
|
+
};
|
|
840
|
+
/** `useRevokeMcpKey()` — revoke a key by its prefix. Idempotent on the backend. */
|
|
841
|
+
declare function useRevokeMcpKey(): {
|
|
842
|
+
revoke: (prefix: string) => Promise<{
|
|
843
|
+
revoked: boolean;
|
|
844
|
+
message: string;
|
|
845
|
+
}>;
|
|
846
|
+
pending: boolean;
|
|
847
|
+
error: Error | undefined;
|
|
848
|
+
};
|
|
849
|
+
/**
|
|
850
|
+
* `useUpdateMcpKey()` — patch a key's policy fields (expiry, allowed
|
|
851
|
+
* IPs, allowed tools, RPM cap). Returns `{ updated, message }`.
|
|
852
|
+
*/
|
|
853
|
+
declare function useUpdateMcpKey(): {
|
|
854
|
+
update: (prefix: string, updates: UpdateMcpKeyInput) => Promise<{
|
|
855
|
+
updated: boolean;
|
|
856
|
+
message: string;
|
|
857
|
+
}>;
|
|
858
|
+
pending: boolean;
|
|
859
|
+
error: Error | undefined;
|
|
860
|
+
};
|
|
861
|
+
|
|
706
862
|
/**
|
|
707
863
|
* Fetch wrapper for browser-direct calls to the Horus API.
|
|
708
864
|
*
|
|
@@ -729,4 +885,4 @@ declare class HorusHttpError extends Error {
|
|
|
729
885
|
constructor(status: number, message: string, body: unknown, code?: string);
|
|
730
886
|
}
|
|
731
887
|
|
|
732
|
-
export { type AuthState, type AuthTokens, type ChainSelector, type CreateWalletInput, type CreateWalletResult, type Eip712TypedData, type ExportWalletInput, type ExportWalletResult, HorusAuthModal, type HorusAuthModalProps, type HorusBranding, HorusHttpError, HorusLoginButton, type HorusLoginButtonProps, HorusProvider, type HorusProviderConfig, type HorusProviderProps, HorusRevealModal, type HorusRevealModalProps, type NativeTransferInput, type NetworkType, SUPPORTED_CHAINS, type SendTransactionInput, type SignMessageInput, type SignTypedDataInput, type SupportedChain, type TokenTransferInput, type UseHorusAuthResult, type User, type WalletDescriptor, useChain, useCreateWallet, useExportWallet, useHorusAuth, useSendTransaction, useSignMessage, useSignTypedData, useSwitchChain, useTransfer, useUser, useWallets };
|
|
888
|
+
export { type AuthState, type AuthTokens, type ChainSelector, type CreateWalletInput, type CreateWalletResult, type Eip712TypedData, type ExportWalletInput, type ExportWalletResult, type GenerateMcpKeyInput, type GenerateMcpKeyResponse, HorusAuthModal, type HorusAuthModalProps, type HorusBranding, HorusHttpError, HorusLoginButton, type HorusLoginButtonProps, HorusProvider, type HorusProviderConfig, type HorusProviderProps, HorusRevealModal, type HorusRevealModalProps, type McpKeyMetadata, type NativeTransferInput, type NetworkType, SUPPORTED_CHAINS, type SendTransactionInput, type SignMessageInput, type SignTypedDataInput, type SupportedChain, type TokenTransferInput, type UnifiedMethod, type UpdateMcpKeyInput, type UseHorusAuthResult, type User, type WalletDescriptor, useChain, useCreateWallet, useExportWallet, useGenerateMcpKey, useHorusAuth, useMcpKeys, useRevokeMcpKey, useSendTransaction, useSignMessage, useSignTypedData, useSwitchChain, useTransfer, useUpdateMcpKey, useUser, useWallets };
|
package/dist/index.d.ts
CHANGED
|
@@ -107,10 +107,16 @@ interface HorusProviderConfig {
|
|
|
107
107
|
autoRefresh?: boolean;
|
|
108
108
|
/**
|
|
109
109
|
* Auto-create a wallet for the user on first sign-in if they don't
|
|
110
|
-
* already have one. Defaults to `{ network: '
|
|
110
|
+
* already have one. Defaults to `{ network: 'ETHEREUM', networkType: 'MAINNET' }`
|
|
111
111
|
* to match Privy's behavior — new users land in your app with a
|
|
112
112
|
* wallet already provisioned, no extra round-trip required.
|
|
113
113
|
*
|
|
114
|
+
* `network` must be a specific chain name (ETHEREUM, BASE, POLYGON,
|
|
115
|
+
* BITCOIN, ICP, CASPER, …) — the API rejects family labels like
|
|
116
|
+
* `'EVM'`. The wallet is stored by family on the backend, so a
|
|
117
|
+
* wallet provisioned as ETHEREUM is reachable via BASE / POLYGON /
|
|
118
|
+
* etc. at sign time (same key, different chain selector).
|
|
119
|
+
*
|
|
114
120
|
* Set to `false` to opt out (your app must call `useCreateWallet`
|
|
115
121
|
* explicitly to provision wallets).
|
|
116
122
|
*
|
|
@@ -125,7 +131,7 @@ interface HorusProviderConfig {
|
|
|
125
131
|
};
|
|
126
132
|
/**
|
|
127
133
|
* Initial active chain exposed via `useSwitchChain()` / `useChain()`.
|
|
128
|
-
* Defaults to `{ network: '
|
|
134
|
+
* Defaults to `{ network: 'ETHEREUM', networkType: 'MAINNET' }`.
|
|
129
135
|
*
|
|
130
136
|
* Partners can read the current chain in their UI (e.g., to highlight
|
|
131
137
|
* the active network in a switcher) and pass it explicitly when
|
|
@@ -324,7 +330,7 @@ declare function useCreateWallet(): UseCreateWalletResult;
|
|
|
324
330
|
* as readonly so a partner who pins on a specific value gets a TS
|
|
325
331
|
* error when we drop a chain rather than a silent runtime miss.
|
|
326
332
|
*/
|
|
327
|
-
declare const SUPPORTED_CHAINS: readonly ["
|
|
333
|
+
declare const SUPPORTED_CHAINS: readonly ["ETHEREUM", "BASE", "POLYGON", "BSC", "ARBITRUM", "OPTIMISM", "TELOS", "CHILIZ", "FLARE", "BITCOIN", "ICP", "CASPER", "AETERNITY"];
|
|
328
334
|
type SupportedChain = (typeof SUPPORTED_CHAINS)[number];
|
|
329
335
|
interface UseSwitchChainResult {
|
|
330
336
|
/** Currently-active chain selector. */
|
|
@@ -541,7 +547,15 @@ declare function HorusLoginButton({ flow, phone, onAuthenticated, onError, child
|
|
|
541
547
|
* Not exported to partners; internal-only.
|
|
542
548
|
*/
|
|
543
549
|
|
|
544
|
-
type AuthFlow = 'google' | 'email_link' | 'phone';
|
|
550
|
+
type AuthFlow = 'google' | 'email_link' | 'phone' | 'unified';
|
|
551
|
+
/**
|
|
552
|
+
* Subset of methods the `unified` chooser may surface. Aligns with the
|
|
553
|
+
* auth-page's `HorusUnifiedMethod`. Includes `email_password` because
|
|
554
|
+
* the unified flow exposes it as a button even though direct
|
|
555
|
+
* `?flow=email_password` from the SDK isn't useful (that path is
|
|
556
|
+
* already headless via `useHorusAuth().loginWithEmail`).
|
|
557
|
+
*/
|
|
558
|
+
type UnifiedMethod = 'google' | 'email_password' | 'email_link' | 'phone';
|
|
545
559
|
|
|
546
560
|
/**
|
|
547
561
|
* `<HorusAuthModal>` — inline auth flow.
|
|
@@ -577,9 +591,26 @@ type AuthFlow = 'google' | 'email_link' | 'phone';
|
|
|
577
591
|
*/
|
|
578
592
|
|
|
579
593
|
interface HorusAuthModalProps {
|
|
580
|
-
/**
|
|
594
|
+
/**
|
|
595
|
+
* Which sign-in flow to render in the iframe. Use `'unified'` for
|
|
596
|
+
* the Privy-style "one button → chooser modal" pattern: the iframe
|
|
597
|
+
* lands on a chooser screen with one button per enabled method,
|
|
598
|
+
* then transitions to whichever flow the end-user picks.
|
|
599
|
+
*/
|
|
581
600
|
flow: AuthFlow;
|
|
582
|
-
/**
|
|
601
|
+
/**
|
|
602
|
+
* Only applies when `flow === 'unified'`. Comma-joined into the URL
|
|
603
|
+
* as `&methods=...` so the chooser only shows what you list. Omit
|
|
604
|
+
* to surface all four (google, email_password, email_link, phone).
|
|
605
|
+
*
|
|
606
|
+
* <HorusAuthModal
|
|
607
|
+
* flow="unified"
|
|
608
|
+
* enabledMethods={['google', 'email_password']}
|
|
609
|
+
* open onClose={...}
|
|
610
|
+
* />
|
|
611
|
+
*/
|
|
612
|
+
enabledMethods?: UnifiedMethod[];
|
|
613
|
+
/** Optional phone-flow pre-fill (when `flow === 'phone'`). */
|
|
583
614
|
phone?: string;
|
|
584
615
|
/** Controlled open state — partner toggles to show/hide the modal. */
|
|
585
616
|
open: boolean;
|
|
@@ -703,6 +734,131 @@ interface UseExportWalletResult {
|
|
|
703
734
|
}
|
|
704
735
|
declare function useExportWallet(): UseExportWalletResult;
|
|
705
736
|
|
|
737
|
+
/**
|
|
738
|
+
* MCP / agentic key management hooks.
|
|
739
|
+
*
|
|
740
|
+
* Mints, lists, revokes, and updates the "agent keys" that an end-user
|
|
741
|
+
* issues to an MCP runtime (Claude Desktop, custom agent, etc.) so the
|
|
742
|
+
* agent can act on the user's behalf — sign messages, transfer tokens,
|
|
743
|
+
* call partner-allowed tools — without the agent ever seeing the
|
|
744
|
+
* underlying wallet credentials.
|
|
745
|
+
*
|
|
746
|
+
* Scope is the wallet-OWNER side: minting + lifecycle. The actual
|
|
747
|
+
* JSON-RPC tool execution endpoint (`POST /mcp`) is consumed by MCP
|
|
748
|
+
* runtimes directly with the minted key; partner React code never
|
|
749
|
+
* calls it.
|
|
750
|
+
*
|
|
751
|
+
* Wire shapes mirror the Node SDK's `client.mcp.*` exactly so the
|
|
752
|
+
* two stay in sync.
|
|
753
|
+
*/
|
|
754
|
+
interface GenerateMcpKeyInput {
|
|
755
|
+
/** Display name shown in `listKeys` (and in any partner UI). */
|
|
756
|
+
name: string;
|
|
757
|
+
/** Wallet network this key signs against. */
|
|
758
|
+
network: string;
|
|
759
|
+
walletIndex?: number;
|
|
760
|
+
/** ISO-8601 expiry. The backend mirrors this to the row's TTL attribute. */
|
|
761
|
+
expiresAt?: string;
|
|
762
|
+
/** Optional IP allow-list (exact IPs only — no CIDRs). */
|
|
763
|
+
allowedIps?: string[];
|
|
764
|
+
/** Optional whitelist of MCP tool names this key may invoke. */
|
|
765
|
+
allowedTools?: string[];
|
|
766
|
+
/** Per-key rate cap. Backend default ~600 RPM. */
|
|
767
|
+
maxRequestsPerMinute?: number;
|
|
768
|
+
/**
|
|
769
|
+
* Wraps the wallet password so the key holder can sign without
|
|
770
|
+
* re-prompting. Required if the wallet is password-protected.
|
|
771
|
+
*/
|
|
772
|
+
mcpPassword?: string;
|
|
773
|
+
}
|
|
774
|
+
interface GenerateMcpKeyResponse {
|
|
775
|
+
/** PLAINTEXT — returned exactly once at creation. Never recoverable. */
|
|
776
|
+
key: string;
|
|
777
|
+
name: string;
|
|
778
|
+
prefix: string;
|
|
779
|
+
wallet_index: number;
|
|
780
|
+
network: string;
|
|
781
|
+
wallet_address?: string;
|
|
782
|
+
expires_at?: string;
|
|
783
|
+
allowed_ips: string[];
|
|
784
|
+
allowed_tools: string[];
|
|
785
|
+
max_requests_per_minute: number;
|
|
786
|
+
created_at: string;
|
|
787
|
+
message: string;
|
|
788
|
+
}
|
|
789
|
+
interface McpKeyMetadata {
|
|
790
|
+
prefix: string;
|
|
791
|
+
name: string;
|
|
792
|
+
wallet_index: number;
|
|
793
|
+
network: string;
|
|
794
|
+
expires_at?: string;
|
|
795
|
+
last_used_at?: string;
|
|
796
|
+
created_at: string;
|
|
797
|
+
allowed_ips: string[];
|
|
798
|
+
allowed_tools: string[];
|
|
799
|
+
max_requests_per_minute: number;
|
|
800
|
+
}
|
|
801
|
+
interface UpdateMcpKeyInput {
|
|
802
|
+
expiresAt?: string;
|
|
803
|
+
allowedIps?: string[];
|
|
804
|
+
allowedTools?: string[];
|
|
805
|
+
maxRequestsPerMinute?: number;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* `useGenerateMcpKey()` — mint a new MCP key.
|
|
809
|
+
*
|
|
810
|
+
* const { generate, pending, error, lastResult } = useGenerateMcpKey();
|
|
811
|
+
* const result = await generate({ name: 'Claude Desktop', network: 'ETHEREUM' });
|
|
812
|
+
* // Show `result.key` to the user immediately — it is never returned again.
|
|
813
|
+
*
|
|
814
|
+
* After a successful call, `lastResult` holds the response so partners
|
|
815
|
+
* can render a one-time secret banner without threading the value
|
|
816
|
+
* through their own state. It's cleared on the next `generate()`.
|
|
817
|
+
*/
|
|
818
|
+
declare function useGenerateMcpKey(): {
|
|
819
|
+
generate: (input: GenerateMcpKeyInput) => Promise<GenerateMcpKeyResponse>;
|
|
820
|
+
pending: boolean;
|
|
821
|
+
error: Error | undefined;
|
|
822
|
+
lastResult: GenerateMcpKeyResponse | null;
|
|
823
|
+
clearLastResult: () => void;
|
|
824
|
+
};
|
|
825
|
+
/**
|
|
826
|
+
* `useMcpKeys()` — read the current user's MCP key list.
|
|
827
|
+
*
|
|
828
|
+
* const { ready, keys, refresh, error } = useMcpKeys();
|
|
829
|
+
*
|
|
830
|
+
* Mutating hooks (`useGenerateMcpKey`, `useRevokeMcpKey`, `useUpdateMcpKey`)
|
|
831
|
+
* each bump an internal version counter so this hook re-fetches
|
|
832
|
+
* automatically after every change — no manual `refresh()` needed.
|
|
833
|
+
*/
|
|
834
|
+
declare function useMcpKeys(): {
|
|
835
|
+
ready: boolean;
|
|
836
|
+
keys: McpKeyMetadata[];
|
|
837
|
+
refresh: () => Promise<void>;
|
|
838
|
+
error: Error | undefined;
|
|
839
|
+
};
|
|
840
|
+
/** `useRevokeMcpKey()` — revoke a key by its prefix. Idempotent on the backend. */
|
|
841
|
+
declare function useRevokeMcpKey(): {
|
|
842
|
+
revoke: (prefix: string) => Promise<{
|
|
843
|
+
revoked: boolean;
|
|
844
|
+
message: string;
|
|
845
|
+
}>;
|
|
846
|
+
pending: boolean;
|
|
847
|
+
error: Error | undefined;
|
|
848
|
+
};
|
|
849
|
+
/**
|
|
850
|
+
* `useUpdateMcpKey()` — patch a key's policy fields (expiry, allowed
|
|
851
|
+
* IPs, allowed tools, RPM cap). Returns `{ updated, message }`.
|
|
852
|
+
*/
|
|
853
|
+
declare function useUpdateMcpKey(): {
|
|
854
|
+
update: (prefix: string, updates: UpdateMcpKeyInput) => Promise<{
|
|
855
|
+
updated: boolean;
|
|
856
|
+
message: string;
|
|
857
|
+
}>;
|
|
858
|
+
pending: boolean;
|
|
859
|
+
error: Error | undefined;
|
|
860
|
+
};
|
|
861
|
+
|
|
706
862
|
/**
|
|
707
863
|
* Fetch wrapper for browser-direct calls to the Horus API.
|
|
708
864
|
*
|
|
@@ -729,4 +885,4 @@ declare class HorusHttpError extends Error {
|
|
|
729
885
|
constructor(status: number, message: string, body: unknown, code?: string);
|
|
730
886
|
}
|
|
731
887
|
|
|
732
|
-
export { type AuthState, type AuthTokens, type ChainSelector, type CreateWalletInput, type CreateWalletResult, type Eip712TypedData, type ExportWalletInput, type ExportWalletResult, HorusAuthModal, type HorusAuthModalProps, type HorusBranding, HorusHttpError, HorusLoginButton, type HorusLoginButtonProps, HorusProvider, type HorusProviderConfig, type HorusProviderProps, HorusRevealModal, type HorusRevealModalProps, type NativeTransferInput, type NetworkType, SUPPORTED_CHAINS, type SendTransactionInput, type SignMessageInput, type SignTypedDataInput, type SupportedChain, type TokenTransferInput, type UseHorusAuthResult, type User, type WalletDescriptor, useChain, useCreateWallet, useExportWallet, useHorusAuth, useSendTransaction, useSignMessage, useSignTypedData, useSwitchChain, useTransfer, useUser, useWallets };
|
|
888
|
+
export { type AuthState, type AuthTokens, type ChainSelector, type CreateWalletInput, type CreateWalletResult, type Eip712TypedData, type ExportWalletInput, type ExportWalletResult, type GenerateMcpKeyInput, type GenerateMcpKeyResponse, HorusAuthModal, type HorusAuthModalProps, type HorusBranding, HorusHttpError, HorusLoginButton, type HorusLoginButtonProps, HorusProvider, type HorusProviderConfig, type HorusProviderProps, HorusRevealModal, type HorusRevealModalProps, type McpKeyMetadata, type NativeTransferInput, type NetworkType, SUPPORTED_CHAINS, type SendTransactionInput, type SignMessageInput, type SignTypedDataInput, type SupportedChain, type TokenTransferInput, type UnifiedMethod, type UpdateMcpKeyInput, type UseHorusAuthResult, type User, type WalletDescriptor, useChain, useCreateWallet, useExportWallet, useGenerateMcpKey, useHorusAuth, useMcpKeys, useRevokeMcpKey, useSendTransaction, useSignMessage, useSignTypedData, useSwitchChain, useTransfer, useUpdateMcpKey, useUser, useWallets };
|
package/dist/index.js
CHANGED
|
@@ -148,10 +148,10 @@ var DEFAULT_API_BASE = "https://api.horuswallet.com";
|
|
|
148
148
|
var DEFAULT_AUTH_PAGE = "https://auth.horuswallet.com";
|
|
149
149
|
var REFRESH_LEAD_SECONDS = 60;
|
|
150
150
|
var DEFAULT_AUTO_PROVISION = {
|
|
151
|
-
network: "
|
|
151
|
+
network: "ETHEREUM",
|
|
152
152
|
networkType: "MAINNET"
|
|
153
153
|
};
|
|
154
|
-
var DEFAULT_CHAIN = { network: "
|
|
154
|
+
var DEFAULT_CHAIN = { network: "ETHEREUM", networkType: "MAINNET" };
|
|
155
155
|
var AUTO_PROVISION_STORAGE_KEY = "horus.autoProvisioned.localIds";
|
|
156
156
|
function HorusProvider(props) {
|
|
157
157
|
const {
|
|
@@ -176,6 +176,10 @@ function HorusProvider(props) {
|
|
|
176
176
|
const setChain = useCallback((chain) => {
|
|
177
177
|
setCurrentChain(chain);
|
|
178
178
|
}, []);
|
|
179
|
+
const [mcpKeysVersion, setMcpKeysVersion] = useState(0);
|
|
180
|
+
const revalidateMcpKeys = useCallback(() => {
|
|
181
|
+
setMcpKeysVersion((v) => v + 1);
|
|
182
|
+
}, []);
|
|
179
183
|
const setTokens = useCallback((tokens) => {
|
|
180
184
|
tokensRef.current = tokens;
|
|
181
185
|
if (tokens) {
|
|
@@ -229,6 +233,58 @@ function HorusProvider(props) {
|
|
|
229
233
|
setState({ status: "unauthenticated" });
|
|
230
234
|
}
|
|
231
235
|
}, []);
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
if (typeof window === "undefined") return;
|
|
238
|
+
const params = new URLSearchParams(window.location.search);
|
|
239
|
+
const mode = params.get("mode");
|
|
240
|
+
const oobCode = params.get("oobCode");
|
|
241
|
+
if (mode !== "signIn" || !oobCode) return;
|
|
242
|
+
if (consumedMagicLinkCodes.has(oobCode)) return;
|
|
243
|
+
consumedMagicLinkCodes.add(oobCode);
|
|
244
|
+
const email = params.get("email") ?? (typeof window.localStorage !== "undefined" ? window.localStorage.getItem("horus.signinEmail") : null) ?? "";
|
|
245
|
+
if (!email) {
|
|
246
|
+
console.warn(
|
|
247
|
+
"@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."
|
|
248
|
+
);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
void (async () => {
|
|
252
|
+
try {
|
|
253
|
+
const stripUrlParams = () => {
|
|
254
|
+
const u = new URL(window.location.href);
|
|
255
|
+
["mode", "oobCode", "apiKey", "continueUrl", "lang", "email"].forEach(
|
|
256
|
+
(k) => u.searchParams.delete(k)
|
|
257
|
+
);
|
|
258
|
+
window.history.replaceState({}, "", u.toString());
|
|
259
|
+
if (window.localStorage) {
|
|
260
|
+
window.localStorage.removeItem("horus.signinEmail");
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const raw = await fetch(joinUrl(apiBase, "/auth/email-link/verify"), {
|
|
264
|
+
method: "POST",
|
|
265
|
+
headers: {
|
|
266
|
+
"Content-Type": "application/json",
|
|
267
|
+
"x-horus-key": appId
|
|
268
|
+
},
|
|
269
|
+
body: JSON.stringify({ email, oobCode })
|
|
270
|
+
});
|
|
271
|
+
if (!raw.ok) {
|
|
272
|
+
const body = await raw.text();
|
|
273
|
+
console.warn(
|
|
274
|
+
`@horus-wallet/sdk-react: magic-link verify failed (HTTP ${raw.status}): ${body}`
|
|
275
|
+
);
|
|
276
|
+
stripUrlParams();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const json = await raw.json();
|
|
280
|
+
const stamped = stampExpiry(json);
|
|
281
|
+
setTokens(stamped);
|
|
282
|
+
stripUrlParams();
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.warn("@horus-wallet/sdk-react: magic-link verify threw", err);
|
|
285
|
+
}
|
|
286
|
+
})();
|
|
287
|
+
}, [apiBase, appId, setTokens]);
|
|
232
288
|
useEffect(() => {
|
|
233
289
|
if (typeof window === "undefined") return;
|
|
234
290
|
const onStorage = (ev) => {
|
|
@@ -263,15 +319,9 @@ function HorusProvider(props) {
|
|
|
263
319
|
let cancelled = false;
|
|
264
320
|
(async () => {
|
|
265
321
|
try {
|
|
266
|
-
const existing = await http.get("/getWallet");
|
|
267
|
-
if (cancelled) return;
|
|
268
|
-
const hasAny = Object.values(existing?.wallets ?? {}).some(
|
|
269
|
-
(group) => Array.isArray(group?.wallets) && group.wallets.length > 0
|
|
270
|
-
);
|
|
271
|
-
markAutoProvisioned(localId);
|
|
272
|
-
if (hasAny) return;
|
|
273
322
|
await http.post("/createWallet", autoProvisionWallet);
|
|
274
323
|
if (cancelled) return;
|
|
324
|
+
markAutoProvisioned(localId);
|
|
275
325
|
revalidateWallets();
|
|
276
326
|
} catch (err) {
|
|
277
327
|
console.warn("@horus-wallet/sdk-react: auto-provision wallet failed", err);
|
|
@@ -305,7 +355,9 @@ function HorusProvider(props) {
|
|
|
305
355
|
walletsVersion,
|
|
306
356
|
revalidateWallets,
|
|
307
357
|
currentChain,
|
|
308
|
-
setChain
|
|
358
|
+
setChain,
|
|
359
|
+
mcpKeysVersion,
|
|
360
|
+
revalidateMcpKeys
|
|
309
361
|
}),
|
|
310
362
|
[
|
|
311
363
|
state,
|
|
@@ -317,7 +369,9 @@ function HorusProvider(props) {
|
|
|
317
369
|
walletsVersion,
|
|
318
370
|
revalidateWallets,
|
|
319
371
|
currentChain,
|
|
320
|
-
setChain
|
|
372
|
+
setChain,
|
|
373
|
+
mcpKeysVersion,
|
|
374
|
+
revalidateMcpKeys
|
|
321
375
|
]
|
|
322
376
|
);
|
|
323
377
|
return /* @__PURE__ */ jsx(HorusContext.Provider, { value: ctx, children });
|
|
@@ -332,6 +386,7 @@ function userFromTokens(t) {
|
|
|
332
386
|
providerId: t.providerId
|
|
333
387
|
};
|
|
334
388
|
}
|
|
389
|
+
var consumedMagicLinkCodes = /* @__PURE__ */ new Set();
|
|
335
390
|
var memoryAutoProvisioned = /* @__PURE__ */ new Set();
|
|
336
391
|
function readAutoProvisionedSet() {
|
|
337
392
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -380,6 +435,9 @@ function buildAuthUrl(p, state) {
|
|
|
380
435
|
url.searchParams.set("appKey", p.appId);
|
|
381
436
|
url.searchParams.set("mode", p.mode);
|
|
382
437
|
if (p.phone) url.searchParams.set("phone", p.phone);
|
|
438
|
+
if (p.flow === "unified" && p.enabledMethods && p.enabledMethods.length > 0) {
|
|
439
|
+
url.searchParams.set("methods", p.enabledMethods.join(","));
|
|
440
|
+
}
|
|
383
441
|
const b = p.branding;
|
|
384
442
|
if (b) {
|
|
385
443
|
if (b.logoUrl) url.searchParams.set("b_logo", b.logoUrl);
|
|
@@ -507,6 +565,7 @@ function openPopupFlow(params) {
|
|
|
507
565
|
}
|
|
508
566
|
const expectedOrigin = new URL(params.baseUrl).origin;
|
|
509
567
|
let settled = false;
|
|
568
|
+
let lastError = null;
|
|
510
569
|
const cleanup = () => {
|
|
511
570
|
window.removeEventListener("message", onMessage);
|
|
512
571
|
clearInterval(poll);
|
|
@@ -537,17 +596,11 @@ function openPopupFlow(params) {
|
|
|
537
596
|
popup.close();
|
|
538
597
|
} catch {
|
|
539
598
|
}
|
|
540
|
-
reject(new Error("User cancelled sign-in."));
|
|
599
|
+
reject(new Error(lastError ?? "User cancelled sign-in."));
|
|
541
600
|
return;
|
|
542
601
|
case "error":
|
|
543
602
|
if (settled) return;
|
|
544
|
-
|
|
545
|
-
cleanup();
|
|
546
|
-
try {
|
|
547
|
-
popup.close();
|
|
548
|
-
} catch {
|
|
549
|
-
}
|
|
550
|
-
reject(new Error(body.message || "Sign-in failed."));
|
|
603
|
+
lastError = body.message || "Sign-in failed.";
|
|
551
604
|
return;
|
|
552
605
|
}
|
|
553
606
|
};
|
|
@@ -556,7 +609,7 @@ function openPopupFlow(params) {
|
|
|
556
609
|
if (popup.closed && !settled) {
|
|
557
610
|
settled = true;
|
|
558
611
|
cleanup();
|
|
559
|
-
reject(new Error("Auth popup was closed before sign-in completed."));
|
|
612
|
+
reject(new Error(lastError ?? "Auth popup was closed before sign-in completed."));
|
|
560
613
|
}
|
|
561
614
|
}, 400);
|
|
562
615
|
});
|
|
@@ -654,13 +707,20 @@ function useCreateWallet() {
|
|
|
654
707
|
// src/hooks/useSwitchChain.ts
|
|
655
708
|
import { useCallback as useCallback5 } from "react";
|
|
656
709
|
var SUPPORTED_CHAINS = [
|
|
657
|
-
|
|
710
|
+
// EVM-family chains the backend accepts as `network` selectors.
|
|
711
|
+
// `EVM` itself is intentionally NOT here — it's a family label, not
|
|
712
|
+
// a chain, and the API rejects it on /createWallet, /signMessage, etc.
|
|
713
|
+
// Pick a specific chain when interacting with the API.
|
|
658
714
|
"ETHEREUM",
|
|
659
715
|
"BASE",
|
|
660
716
|
"POLYGON",
|
|
661
717
|
"BSC",
|
|
662
718
|
"ARBITRUM",
|
|
663
719
|
"OPTIMISM",
|
|
720
|
+
"TELOS",
|
|
721
|
+
"CHILIZ",
|
|
722
|
+
"FLARE",
|
|
723
|
+
// Non-EVM chains.
|
|
664
724
|
"BITCOIN",
|
|
665
725
|
"ICP",
|
|
666
726
|
"CASPER",
|
|
@@ -891,6 +951,7 @@ var defaultDialogStyle = {
|
|
|
891
951
|
function HorusAuthModal(props) {
|
|
892
952
|
const {
|
|
893
953
|
flow,
|
|
954
|
+
enabledMethods,
|
|
894
955
|
phone,
|
|
895
956
|
open,
|
|
896
957
|
onClose,
|
|
@@ -913,11 +974,12 @@ function HorusAuthModal(props) {
|
|
|
913
974
|
baseUrl: authPageUrl,
|
|
914
975
|
mode: "iframe",
|
|
915
976
|
branding,
|
|
916
|
-
phone
|
|
977
|
+
phone,
|
|
978
|
+
enabledMethods
|
|
917
979
|
},
|
|
918
980
|
stateRef.current
|
|
919
981
|
);
|
|
920
|
-
}, [open, flow, appId, authPageUrl, branding, phone]);
|
|
982
|
+
}, [open, flow, appId, authPageUrl, branding, phone, enabledMethods]);
|
|
921
983
|
const expectedOrigin = useMemo2(() => {
|
|
922
984
|
try {
|
|
923
985
|
return new URL(authPageUrl).origin;
|
|
@@ -963,7 +1025,6 @@ function HorusAuthModal(props) {
|
|
|
963
1025
|
return;
|
|
964
1026
|
case "error":
|
|
965
1027
|
onError?.(new Error(body.message ?? "Sign-in failed."));
|
|
966
|
-
onClose();
|
|
967
1028
|
return;
|
|
968
1029
|
}
|
|
969
1030
|
};
|
|
@@ -1171,6 +1232,131 @@ function useExportWallet() {
|
|
|
1171
1232
|
);
|
|
1172
1233
|
return { reveal, pending, error };
|
|
1173
1234
|
}
|
|
1235
|
+
|
|
1236
|
+
// src/hooks/useMcp.ts
|
|
1237
|
+
import { useCallback as useCallback13, useEffect as useEffect5, useState as useState10 } from "react";
|
|
1238
|
+
function useGenerateMcpKey() {
|
|
1239
|
+
const { http, walletsVersion } = useHorusContext();
|
|
1240
|
+
void walletsVersion;
|
|
1241
|
+
const [pending, setPending] = useState10(false);
|
|
1242
|
+
const [error, setError] = useState10(void 0);
|
|
1243
|
+
const [lastResult, setLastResult] = useState10(null);
|
|
1244
|
+
const generate = useCallback13(
|
|
1245
|
+
async (input) => {
|
|
1246
|
+
setPending(true);
|
|
1247
|
+
setError(void 0);
|
|
1248
|
+
try {
|
|
1249
|
+
const response = await http.post("/mcp/keys/generate", {
|
|
1250
|
+
name: input.name,
|
|
1251
|
+
network: input.network,
|
|
1252
|
+
wallet_index: typeof input.walletIndex === "number" ? input.walletIndex : 0,
|
|
1253
|
+
expires_at: input.expiresAt,
|
|
1254
|
+
allowed_ips: input.allowedIps,
|
|
1255
|
+
allowed_tools: input.allowedTools,
|
|
1256
|
+
max_requests_per_minute: input.maxRequestsPerMinute,
|
|
1257
|
+
mcp_password: input.mcpPassword
|
|
1258
|
+
});
|
|
1259
|
+
setLastResult(response);
|
|
1260
|
+
return response;
|
|
1261
|
+
} catch (err) {
|
|
1262
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1263
|
+
setError(e);
|
|
1264
|
+
throw e;
|
|
1265
|
+
} finally {
|
|
1266
|
+
setPending(false);
|
|
1267
|
+
}
|
|
1268
|
+
},
|
|
1269
|
+
[http]
|
|
1270
|
+
);
|
|
1271
|
+
return { generate, pending, error, lastResult, clearLastResult: () => setLastResult(null) };
|
|
1272
|
+
}
|
|
1273
|
+
function useMcpKeys() {
|
|
1274
|
+
const { http, state, mcpKeysVersion } = useHorusContext();
|
|
1275
|
+
const [keys, setKeys] = useState10([]);
|
|
1276
|
+
const [ready, setReady] = useState10(false);
|
|
1277
|
+
const [error, setError] = useState10(void 0);
|
|
1278
|
+
const load = useCallback13(async () => {
|
|
1279
|
+
if (state.status !== "authenticated") {
|
|
1280
|
+
setKeys([]);
|
|
1281
|
+
setReady(true);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
setReady(false);
|
|
1285
|
+
setError(void 0);
|
|
1286
|
+
try {
|
|
1287
|
+
const response = await http.get("/mcp/keys");
|
|
1288
|
+
setKeys(response?.keys ?? []);
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1291
|
+
} finally {
|
|
1292
|
+
setReady(true);
|
|
1293
|
+
}
|
|
1294
|
+
}, [http, state.status]);
|
|
1295
|
+
useEffect5(() => {
|
|
1296
|
+
void load();
|
|
1297
|
+
}, [load, mcpKeysVersion]);
|
|
1298
|
+
return { ready, keys, refresh: load, error };
|
|
1299
|
+
}
|
|
1300
|
+
function useRevokeMcpKey() {
|
|
1301
|
+
const { http, revalidateMcpKeys } = useHorusContext();
|
|
1302
|
+
const [pending, setPending] = useState10(false);
|
|
1303
|
+
const [error, setError] = useState10(void 0);
|
|
1304
|
+
const revoke = useCallback13(
|
|
1305
|
+
async (prefix) => {
|
|
1306
|
+
if (!prefix) throw new Error("useRevokeMcpKey: prefix is required");
|
|
1307
|
+
setPending(true);
|
|
1308
|
+
setError(void 0);
|
|
1309
|
+
try {
|
|
1310
|
+
const response = await http.del(
|
|
1311
|
+
`/mcp/keys/${encodeURIComponent(prefix)}`
|
|
1312
|
+
);
|
|
1313
|
+
revalidateMcpKeys();
|
|
1314
|
+
return response;
|
|
1315
|
+
} catch (err) {
|
|
1316
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1317
|
+
setError(e);
|
|
1318
|
+
throw e;
|
|
1319
|
+
} finally {
|
|
1320
|
+
setPending(false);
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
[http, revalidateMcpKeys]
|
|
1324
|
+
);
|
|
1325
|
+
return { revoke, pending, error };
|
|
1326
|
+
}
|
|
1327
|
+
function useUpdateMcpKey() {
|
|
1328
|
+
const { http, revalidateMcpKeys } = useHorusContext();
|
|
1329
|
+
const [pending, setPending] = useState10(false);
|
|
1330
|
+
const [error, setError] = useState10(void 0);
|
|
1331
|
+
const update = useCallback13(
|
|
1332
|
+
async (prefix, updates) => {
|
|
1333
|
+
if (!prefix) throw new Error("useUpdateMcpKey: prefix is required");
|
|
1334
|
+
setPending(true);
|
|
1335
|
+
setError(void 0);
|
|
1336
|
+
try {
|
|
1337
|
+
const response = await http.put(
|
|
1338
|
+
`/mcp/keys/${encodeURIComponent(prefix)}`,
|
|
1339
|
+
{
|
|
1340
|
+
expires_at: updates.expiresAt,
|
|
1341
|
+
allowed_ips: updates.allowedIps,
|
|
1342
|
+
allowed_tools: updates.allowedTools,
|
|
1343
|
+
max_requests_per_minute: updates.maxRequestsPerMinute
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
revalidateMcpKeys();
|
|
1347
|
+
return response;
|
|
1348
|
+
} catch (err) {
|
|
1349
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1350
|
+
setError(e);
|
|
1351
|
+
throw e;
|
|
1352
|
+
} finally {
|
|
1353
|
+
setPending(false);
|
|
1354
|
+
}
|
|
1355
|
+
},
|
|
1356
|
+
[http, revalidateMcpKeys]
|
|
1357
|
+
);
|
|
1358
|
+
return { update, pending, error };
|
|
1359
|
+
}
|
|
1174
1360
|
export {
|
|
1175
1361
|
HorusAuthModal,
|
|
1176
1362
|
HorusHttpError,
|
|
@@ -1181,12 +1367,16 @@ export {
|
|
|
1181
1367
|
useChain,
|
|
1182
1368
|
useCreateWallet,
|
|
1183
1369
|
useExportWallet,
|
|
1370
|
+
useGenerateMcpKey,
|
|
1184
1371
|
useHorusAuth,
|
|
1372
|
+
useMcpKeys,
|
|
1373
|
+
useRevokeMcpKey,
|
|
1185
1374
|
useSendTransaction,
|
|
1186
1375
|
useSignMessage,
|
|
1187
1376
|
useSignTypedData,
|
|
1188
1377
|
useSwitchChain,
|
|
1189
1378
|
useTransfer,
|
|
1379
|
+
useUpdateMcpKey,
|
|
1190
1380
|
useUser,
|
|
1191
1381
|
useWallets
|
|
1192
1382
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@horus-wallet/sdk-react",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.2",
|
|
4
4
|
"description": "React bindings for the Horus embedded-wallet SDK — provider + hooks + drop-in components for partners to ship wallet UX in minutes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Horus Wallet <info@horuswallet.com>",
|