@chipi-stack/chipi-react 14.4.0 → 14.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode } from 'react';
3
- import { ChipiSDK } from '@chipi-stack/backend';
2
+ import React, { ReactNode } from 'react';
3
+ import { ChipiSDK, NewOwnerInput } from '@chipi-stack/backend';
4
4
  import { ChipiSDKConfig } from '@chipi-stack/types';
5
5
  export * from '@chipi-stack/types';
6
6
  export { Chain, ChainToken } from '@chipi-stack/types';
7
- export { ChipiWalletData, SessionState, SessionWallet, UseChipiSessionConfig, UseChipiSessionReturn, UseChipiWalletConfig, UseChipiWalletReturn, UseTransferConfig, UseX402PaymentConfig, UseX402PaymentReturn, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useMigrateWalletToPasskey, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useSyncOnChainTransfers, useTransfer, useUpdateWalletEncryption, useX402Payment } from './hooks.mjs';
7
+ export { ChipiWalletData, SessionState, SessionWallet, ThresholdSignState, UseChipiSessionConfig, UseChipiSessionReturn, UseChipiWalletConfig, UseChipiWalletReturn, UseGuardianRecoveryReturn, UseShhhSignerReturn, UseThresholdSignOptions, UseThresholdSignReturn, UseTransferConfig, UseX402PaymentConfig, UseX402PaymentReturn, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useGuardianRecovery, useMigrateWalletToPasskey, useMigrateWalletToShhh, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useShhhSigner, useSyncOnChainTransfers, useThresholdSign, useTransfer, useUpdateWalletEncryption, useX402Payment } from './hooks.mjs';
8
8
  import '@tanstack/react-query';
9
9
 
10
10
  interface PasskeyAdapter {
@@ -31,4 +31,78 @@ declare function ChipiProvider({ children, config, passkeyAdapter }: ChipiProvid
31
31
  */
32
32
  declare function useChipiContext(): ChipiContextValue;
33
33
 
34
- export { ChipiProvider, type PasskeyAdapter, useChipiContext };
34
+ /**
35
+ * SHHH V8.4 standalone recovery component (Wave B B6.2).
36
+ *
37
+ * Controlled + headless: chipi-react owns the phase state machine and
38
+ * renders semantic HTML with `data-chipi-recover-*` attributes for CSS
39
+ * targeting. The integrator brings styling and supplies the side-effect
40
+ * callbacks (sign + execute against the paymaster). The component does
41
+ * NOT read chain state; the caller passes `pendingOp` from their own
42
+ * chain-read transport (multicall / view selectors / indexer).
43
+ *
44
+ * Two flows:
45
+ *
46
+ * mode="add-device" propose_add_owner (48h) execute_add_owner
47
+ * mode="guardian-recovery" initiate_recovery (7d) finalize_recovery
48
+ *
49
+ * The wallet-connect ceremony that produces the new owner's pubkey
50
+ * (MetaMask connect, passkey enrollment, Apple Sign In) is intentionally
51
+ * out of scope: caller computes `newOwner` upstream and passes it in.
52
+ */
53
+ interface RecoverProps {
54
+ /** Which flow this component renders. */
55
+ mode: "add-device" | "guardian-recovery";
56
+ /** The SHHH wallet being modified. */
57
+ walletAddress: bigint | string;
58
+ /**
59
+ * The new owner to add (or rotate the wallet to). Caller computes
60
+ * this upstream from whichever wallet-connect ceremony they ran.
61
+ */
62
+ newOwner: NewOwnerInput;
63
+ /**
64
+ * Pending op state read from chain by the caller. `null` (or omitted)
65
+ * means no op in flight; the propose form is shown. When set, the
66
+ * component switches to the countdown / execute panel.
67
+ */
68
+ pendingOp?: {
69
+ opId: bigint | string;
70
+ /** UNIX seconds when the op becomes executable. */
71
+ validAfter: number | bigint;
72
+ } | null;
73
+ /**
74
+ * Submits the propose (add-device) or initiate (guardian-recovery)
75
+ * OE through whatever paymaster / sign path the integrator wires.
76
+ * Returns the resulting opId + validAfter so the component can
77
+ * transition to "awaiting timelock" without waiting for the next
78
+ * chain read.
79
+ */
80
+ onPropose: () => Promise<{
81
+ opId: bigint | string;
82
+ validAfter: number | bigint;
83
+ }>;
84
+ /**
85
+ * Submits the execute (add-device) or finalize (guardian-recovery)
86
+ * OE once the timelock has elapsed.
87
+ */
88
+ onExecute: (opId: bigint | string) => Promise<void>;
89
+ /**
90
+ * Optional cancel handler. When omitted the cancel button is hidden.
91
+ * Add-device uses `cancel_pending_op` (any owner); guardian-recovery
92
+ * uses `cancel_recovery` (the original owner, if they resurface).
93
+ */
94
+ onCancel?: (opId: bigint | string) => Promise<void>;
95
+ /**
96
+ * Optional className for the root element. Compose with the
97
+ * `data-chipi-recover-*` selectors for styling.
98
+ */
99
+ className?: string;
100
+ /**
101
+ * Clock override for tests / chain-time logic. Defaults to
102
+ * `Math.floor(Date.now() / 1000)`.
103
+ */
104
+ nowSecs?: number;
105
+ }
106
+ declare function Recover(props: RecoverProps): React.ReactElement;
107
+
108
+ export { ChipiProvider, type PasskeyAdapter, Recover, type RecoverProps, useChipiContext };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode } from 'react';
3
- import { ChipiSDK } from '@chipi-stack/backend';
2
+ import React, { ReactNode } from 'react';
3
+ import { ChipiSDK, NewOwnerInput } from '@chipi-stack/backend';
4
4
  import { ChipiSDKConfig } from '@chipi-stack/types';
5
5
  export * from '@chipi-stack/types';
6
6
  export { Chain, ChainToken } from '@chipi-stack/types';
7
- export { ChipiWalletData, SessionState, SessionWallet, UseChipiSessionConfig, UseChipiSessionReturn, UseChipiWalletConfig, UseChipiWalletReturn, UseTransferConfig, UseX402PaymentConfig, UseX402PaymentReturn, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useMigrateWalletToPasskey, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useSyncOnChainTransfers, useTransfer, useUpdateWalletEncryption, useX402Payment } from './hooks.js';
7
+ export { ChipiWalletData, SessionState, SessionWallet, ThresholdSignState, UseChipiSessionConfig, UseChipiSessionReturn, UseChipiWalletConfig, UseChipiWalletReturn, UseGuardianRecoveryReturn, UseShhhSignerReturn, UseThresholdSignOptions, UseThresholdSignReturn, UseTransferConfig, UseX402PaymentConfig, UseX402PaymentReturn, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useGuardianRecovery, useMigrateWalletToPasskey, useMigrateWalletToShhh, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useShhhSigner, useSyncOnChainTransfers, useThresholdSign, useTransfer, useUpdateWalletEncryption, useX402Payment } from './hooks.js';
8
8
  import '@tanstack/react-query';
9
9
 
10
10
  interface PasskeyAdapter {
@@ -31,4 +31,78 @@ declare function ChipiProvider({ children, config, passkeyAdapter }: ChipiProvid
31
31
  */
32
32
  declare function useChipiContext(): ChipiContextValue;
33
33
 
34
- export { ChipiProvider, type PasskeyAdapter, useChipiContext };
34
+ /**
35
+ * SHHH V8.4 standalone recovery component (Wave B B6.2).
36
+ *
37
+ * Controlled + headless: chipi-react owns the phase state machine and
38
+ * renders semantic HTML with `data-chipi-recover-*` attributes for CSS
39
+ * targeting. The integrator brings styling and supplies the side-effect
40
+ * callbacks (sign + execute against the paymaster). The component does
41
+ * NOT read chain state; the caller passes `pendingOp` from their own
42
+ * chain-read transport (multicall / view selectors / indexer).
43
+ *
44
+ * Two flows:
45
+ *
46
+ * mode="add-device" propose_add_owner (48h) execute_add_owner
47
+ * mode="guardian-recovery" initiate_recovery (7d) finalize_recovery
48
+ *
49
+ * The wallet-connect ceremony that produces the new owner's pubkey
50
+ * (MetaMask connect, passkey enrollment, Apple Sign In) is intentionally
51
+ * out of scope: caller computes `newOwner` upstream and passes it in.
52
+ */
53
+ interface RecoverProps {
54
+ /** Which flow this component renders. */
55
+ mode: "add-device" | "guardian-recovery";
56
+ /** The SHHH wallet being modified. */
57
+ walletAddress: bigint | string;
58
+ /**
59
+ * The new owner to add (or rotate the wallet to). Caller computes
60
+ * this upstream from whichever wallet-connect ceremony they ran.
61
+ */
62
+ newOwner: NewOwnerInput;
63
+ /**
64
+ * Pending op state read from chain by the caller. `null` (or omitted)
65
+ * means no op in flight; the propose form is shown. When set, the
66
+ * component switches to the countdown / execute panel.
67
+ */
68
+ pendingOp?: {
69
+ opId: bigint | string;
70
+ /** UNIX seconds when the op becomes executable. */
71
+ validAfter: number | bigint;
72
+ } | null;
73
+ /**
74
+ * Submits the propose (add-device) or initiate (guardian-recovery)
75
+ * OE through whatever paymaster / sign path the integrator wires.
76
+ * Returns the resulting opId + validAfter so the component can
77
+ * transition to "awaiting timelock" without waiting for the next
78
+ * chain read.
79
+ */
80
+ onPropose: () => Promise<{
81
+ opId: bigint | string;
82
+ validAfter: number | bigint;
83
+ }>;
84
+ /**
85
+ * Submits the execute (add-device) or finalize (guardian-recovery)
86
+ * OE once the timelock has elapsed.
87
+ */
88
+ onExecute: (opId: bigint | string) => Promise<void>;
89
+ /**
90
+ * Optional cancel handler. When omitted the cancel button is hidden.
91
+ * Add-device uses `cancel_pending_op` (any owner); guardian-recovery
92
+ * uses `cancel_recovery` (the original owner, if they resurface).
93
+ */
94
+ onCancel?: (opId: bigint | string) => Promise<void>;
95
+ /**
96
+ * Optional className for the root element. Compose with the
97
+ * `data-chipi-recover-*` selectors for styling.
98
+ */
99
+ className?: string;
100
+ /**
101
+ * Clock override for tests / chain-time logic. Defaults to
102
+ * `Math.floor(Date.now() / 1000)`.
103
+ */
104
+ nowSecs?: number;
105
+ }
106
+ declare function Recover(props: RecoverProps): React.ReactElement;
107
+
108
+ export { ChipiProvider, type PasskeyAdapter, Recover, type RecoverProps, useChipiContext };
package/dist/index.js CHANGED
@@ -717,6 +717,25 @@ function useUpdateWalletEncryption() {
717
717
  reset: mutation.reset
718
718
  };
719
719
  }
720
+ function useMigrateWalletToShhh() {
721
+ const { chipiSDK } = useChipiContext();
722
+ const mutation = reactQuery.useMutation({
723
+ mutationFn: async (input) => {
724
+ const { bearerToken, ...rest } = input;
725
+ return chipiSDK.wallets.migrateToShhh(rest, bearerToken);
726
+ }
727
+ });
728
+ return {
729
+ migrate: mutation.mutate,
730
+ migrateAsync: mutation.mutateAsync,
731
+ data: mutation.data,
732
+ isLoading: mutation.isPending,
733
+ isError: mutation.isError,
734
+ error: mutation.error,
735
+ isSuccess: mutation.isSuccess,
736
+ reset: mutation.reset
737
+ };
738
+ }
720
739
  function useTransfer(config) {
721
740
  const { chipiSDK, passkeyAdapter } = useChipiContext();
722
741
  const mutation = reactQuery.useMutation({
@@ -1602,6 +1621,313 @@ function useX402Payment(config) {
1602
1621
  paymentCount
1603
1622
  };
1604
1623
  }
1624
+ function useShhhSigner() {
1625
+ return React.useMemo(
1626
+ () => ({
1627
+ signWithStark: (params) => backend.buildStarkEnvelope({
1628
+ privateKey: params.privateKey,
1629
+ messageHash: params.messageHash
1630
+ }),
1631
+ signWithPhantom: (params) => backend.buildEd25519Envelope({
1632
+ privateKey: params.privateKey,
1633
+ messageHash: params.messageHash
1634
+ }),
1635
+ signWithMetaMask: (params) => backend.buildEip191EnvelopeFromSignature({
1636
+ signatureHex: params.signatureHex,
1637
+ pubkey: params.pubkey
1638
+ }),
1639
+ signWithPasskey: (params) => backend.buildWebAuthnEnvelopeFromAssertion({
1640
+ authenticatorData: params.authenticatorData,
1641
+ clientDataJSON: params.clientDataJSON,
1642
+ signatureDer: params.signatureDer,
1643
+ pubkey: params.pubkey,
1644
+ messageHash: params.messageHash,
1645
+ yParity: params.yParity ?? 0n
1646
+ }),
1647
+ signWithAppleIdToken: (params) => backend.buildJwtAppleSubEnvelopeFromTokenResponse({
1648
+ idToken: params.idToken,
1649
+ pubkey: params.pubkey,
1650
+ messageHash: params.messageHash
1651
+ })
1652
+ }),
1653
+ []
1654
+ );
1655
+ }
1656
+ function useGuardianRecovery() {
1657
+ return React.useMemo(
1658
+ () => ({
1659
+ buildProposeAddOwner: (args) => backend.buildProposeAddOwnerCall(args),
1660
+ buildExecuteAddOwner: (args) => backend.buildExecuteAddOwnerCall(args),
1661
+ buildProposeRemoveOwner: (args) => backend.buildProposeRemoveOwnerCall(args),
1662
+ buildExecuteRemoveOwner: (args) => backend.buildExecuteRemoveOwnerCall(args),
1663
+ buildProposeRotateOwner: (args) => backend.buildProposeRotateOwnerCall(args),
1664
+ buildExecuteRotateOwner: (args) => backend.buildExecuteRotateOwnerCall(args),
1665
+ buildProposeSetThreshold: (args) => backend.buildProposeSetThresholdCall(args),
1666
+ buildExecuteSetThreshold: (args) => backend.buildExecuteSetThresholdCall(args),
1667
+ buildCancelPendingOp: (args) => backend.buildCancelPendingOpCall(args),
1668
+ buildInitiateRecovery: (args) => backend.buildInitiateRecoveryCall(args),
1669
+ buildCancelRecovery: (args) => backend.buildCancelRecoveryCall(args),
1670
+ buildFinalizeRecovery: (args) => backend.buildFinalizeRecoveryCall(args),
1671
+ isReady: (validAfterSecs, nowSecs) => backend.isReadyToExecute(validAfterSecs, nowSecs ?? Math.floor(Date.now() / 1e3)),
1672
+ secondsRemaining: (validAfterSecs, nowSecs) => backend.secondsUntilExecutable(validAfterSecs, nowSecs ?? Math.floor(Date.now() / 1e3)),
1673
+ TIMELOCK_SECONDS: backend.TIMELOCK_SECONDS,
1674
+ DEFAULT_OP_EXPIRY_SECONDS: backend.DEFAULT_OP_EXPIRY_SECONDS,
1675
+ ROLE_OWNER: backend.ROLE_OWNER,
1676
+ ROLE_GUARDIAN: backend.ROLE_GUARDIAN,
1677
+ OP_ADD_OWNER: backend.OP_ADD_OWNER,
1678
+ OP_REMOVE_OWNER: backend.OP_REMOVE_OWNER,
1679
+ OP_ROTATE_OWNER: backend.OP_ROTATE_OWNER,
1680
+ OP_SET_THRESHOLD: backend.OP_SET_THRESHOLD
1681
+ }),
1682
+ []
1683
+ );
1684
+ }
1685
+ function useThresholdSign(opts) {
1686
+ const { threshold } = opts;
1687
+ const [collected, setCollected] = React.useState([]);
1688
+ const [signedOwnerIds, setSignedOwnerIds] = React.useState([]);
1689
+ const [error, setError] = React.useState(null);
1690
+ const signedOwnerIdsRef = React.useRef([]);
1691
+ const addEnvelope = React.useCallback((envelope) => {
1692
+ let ownerId;
1693
+ try {
1694
+ ownerId = backend.assertValidV2Snip12Envelope(envelope);
1695
+ } catch (e) {
1696
+ const err = e instanceof Error ? e : new Error(String(e));
1697
+ setError(err);
1698
+ return false;
1699
+ }
1700
+ if (signedOwnerIdsRef.current.some((id) => id === ownerId)) {
1701
+ const err = new Error(
1702
+ `THRESH: owner_id 0x${ownerId.toString(16)} has already signed; use removeEnvelope first if you want to replace their envelope.`
1703
+ );
1704
+ setError(err);
1705
+ return false;
1706
+ }
1707
+ signedOwnerIdsRef.current = [...signedOwnerIdsRef.current, ownerId];
1708
+ setCollected((prev) => [...prev, envelope]);
1709
+ setSignedOwnerIds(signedOwnerIdsRef.current);
1710
+ setError(null);
1711
+ return true;
1712
+ }, []);
1713
+ const removeEnvelope = React.useCallback((ownerId) => {
1714
+ const matchIndex = signedOwnerIdsRef.current.findIndex((id) => id === ownerId);
1715
+ if (matchIndex < 0) return;
1716
+ signedOwnerIdsRef.current = signedOwnerIdsRef.current.filter((id) => id !== ownerId);
1717
+ setCollected((prev) => prev.filter((_, i) => i !== matchIndex));
1718
+ setSignedOwnerIds(signedOwnerIdsRef.current);
1719
+ setError(null);
1720
+ }, []);
1721
+ const reset = React.useCallback(() => {
1722
+ signedOwnerIdsRef.current = [];
1723
+ setCollected([]);
1724
+ setSignedOwnerIds([]);
1725
+ setError(null);
1726
+ }, []);
1727
+ const finalize = React.useCallback(() => {
1728
+ if (collected.length < threshold) {
1729
+ throw new Error(
1730
+ `THRESH: cannot finalize \u2014 need ${threshold} envelopes, have ${collected.length}. Wait for more owners to sign.`
1731
+ );
1732
+ }
1733
+ return backend.buildThresholdEnvelope({ innerEnvelopes: collected });
1734
+ }, [collected, threshold]);
1735
+ const state = React.useMemo(
1736
+ () => ({
1737
+ threshold,
1738
+ collected,
1739
+ signedOwnerIds,
1740
+ isReady: collected.length >= threshold,
1741
+ error
1742
+ }),
1743
+ [threshold, collected, signedOwnerIds, error]
1744
+ );
1745
+ const stateless = React.useMemo(
1746
+ () => ({
1747
+ assemble: (args) => backend.buildThresholdEnvelope(args),
1748
+ parse: (args) => backend.parseThresholdEnvelope(args.envelope),
1749
+ estimateGasOverhead: (args) => backend.estimateThresholdGasOverhead(args.owners),
1750
+ MIN_THRESHOLD_OWNERS: backend.MIN_THRESHOLD_OWNERS
1751
+ }),
1752
+ []
1753
+ );
1754
+ return {
1755
+ ...stateless,
1756
+ state,
1757
+ addEnvelope,
1758
+ removeEnvelope,
1759
+ reset,
1760
+ finalize
1761
+ };
1762
+ }
1763
+ function deriveStaticPhase(pendingOp, executed, isReady, nowSecs) {
1764
+ if (executed) return "done";
1765
+ if (!pendingOp) return "form-idle";
1766
+ return isReady(pendingOp.validAfter, nowSecs) ? "ready-to-execute" : "awaiting-timelock";
1767
+ }
1768
+ function shortPubkey(bytes) {
1769
+ if (bytes.length === 0) return "(empty)";
1770
+ const first = bytes[0].toString(16);
1771
+ return first.length > 12 ? `0x${first.slice(0, 8)}\u2026${first.slice(-4)}` : `0x${first}`;
1772
+ }
1773
+ function Recover(props) {
1774
+ const { mode, walletAddress, newOwner, pendingOp, onCancel, className, nowSecs } = props;
1775
+ const recovery = useGuardianRecovery();
1776
+ const [executed, setExecuted] = React.useState(false);
1777
+ const [submittingAction, setSubmittingAction] = React.useState(
1778
+ null
1779
+ );
1780
+ const [error, setError] = React.useState(null);
1781
+ const [optimisticPendingOp, setOptimisticPendingOp] = React.useState(null);
1782
+ React.useEffect(() => {
1783
+ if (pendingOp) setOptimisticPendingOp(null);
1784
+ }, [pendingOp]);
1785
+ const activePendingOp = pendingOp ?? optimisticPendingOp ?? null;
1786
+ const [tick, setTick] = React.useState(0);
1787
+ React.useEffect(() => {
1788
+ if (!activePendingOp || executed) return;
1789
+ const ready = recovery.isReady(activePendingOp.validAfter, nowSecs);
1790
+ if (ready) return;
1791
+ const id = setInterval(() => setTick((t) => t + 1), 1e3);
1792
+ return () => clearInterval(id);
1793
+ }, [activePendingOp, executed, recovery, nowSecs, tick]);
1794
+ const phase = React.useMemo(() => {
1795
+ if (submittingAction === "propose") return "submitting-propose";
1796
+ if (submittingAction === "execute") return "submitting-execute";
1797
+ if (submittingAction === "cancel") return "submitting-cancel";
1798
+ return deriveStaticPhase(activePendingOp, executed, recovery.isReady, nowSecs);
1799
+ }, [submittingAction, activePendingOp, executed, recovery, nowSecs, tick]);
1800
+ const secondsRemaining = React.useMemo(() => {
1801
+ if (!activePendingOp) return 0;
1802
+ return recovery.secondsRemaining(activePendingOp.validAfter, nowSecs);
1803
+ }, [activePendingOp, recovery, nowSecs, tick]);
1804
+ const handlePropose = React.useCallback(async () => {
1805
+ setError(null);
1806
+ setSubmittingAction("propose");
1807
+ try {
1808
+ const result = await props.onPropose();
1809
+ setOptimisticPendingOp({
1810
+ opId: result.opId,
1811
+ validAfter: result.validAfter
1812
+ });
1813
+ } catch (e) {
1814
+ setError(e instanceof Error ? e : new Error(String(e)));
1815
+ } finally {
1816
+ setSubmittingAction(null);
1817
+ }
1818
+ }, [props]);
1819
+ const handleExecute = React.useCallback(async () => {
1820
+ if (!activePendingOp) return;
1821
+ setError(null);
1822
+ setSubmittingAction("execute");
1823
+ try {
1824
+ await props.onExecute(activePendingOp.opId);
1825
+ setExecuted(true);
1826
+ } catch (e) {
1827
+ setError(e instanceof Error ? e : new Error(String(e)));
1828
+ } finally {
1829
+ setSubmittingAction(null);
1830
+ }
1831
+ }, [activePendingOp, props]);
1832
+ const handleCancel = React.useCallback(async () => {
1833
+ if (!activePendingOp || !onCancel) return;
1834
+ setError(null);
1835
+ setSubmittingAction("cancel");
1836
+ try {
1837
+ await onCancel(activePendingOp.opId);
1838
+ } catch (e) {
1839
+ setError(e instanceof Error ? e : new Error(String(e)));
1840
+ } finally {
1841
+ setSubmittingAction(null);
1842
+ }
1843
+ }, [activePendingOp, onCancel]);
1844
+ const isAddDevice = mode === "add-device";
1845
+ const proposeLabel = isAddDevice ? "Propose new device" : "Initiate recovery";
1846
+ const executeLabel = isAddDevice ? "Execute add-owner" : "Finalize recovery";
1847
+ const timelockDescription = isAddDevice ? "48-hour timelock before this device becomes active." : "7-day timelock before the recovered wallet becomes active.";
1848
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1849
+ "div",
1850
+ {
1851
+ "data-chipi-recover-root": "",
1852
+ "data-chipi-recover-mode": mode,
1853
+ "data-chipi-recover-phase": phase,
1854
+ className,
1855
+ children: [
1856
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { "data-chipi-recover-header": "", children: [
1857
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-wallet-address": "", children: String(walletAddress) }),
1858
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-new-owner-kind": "", children: newOwner.kind }),
1859
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-new-owner-label": "", children: newOwner.label }),
1860
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-new-owner-pubkey": "", children: shortPubkey(newOwner.pubkeyBytes) })
1861
+ ] }),
1862
+ phase === "form-idle" && /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-chipi-recover-section": "propose", children: [
1863
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-description": "", children: timelockDescription }),
1864
+ /* @__PURE__ */ jsxRuntime.jsx(
1865
+ "button",
1866
+ {
1867
+ type: "button",
1868
+ "data-chipi-recover-action": "propose",
1869
+ onClick: handlePropose,
1870
+ disabled: submittingAction !== null,
1871
+ children: proposeLabel
1872
+ }
1873
+ )
1874
+ ] }),
1875
+ phase === "submitting-propose" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "propose", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "submitting", children: "Submitting\u2026" }) }),
1876
+ phase === "awaiting-timelock" && activePendingOp && /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-chipi-recover-section": "awaiting", children: [
1877
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "awaiting", children: timelockDescription }),
1878
+ /* @__PURE__ */ jsxRuntime.jsxs(
1879
+ "p",
1880
+ {
1881
+ "data-chipi-recover-countdown": "",
1882
+ "data-chipi-recover-seconds-remaining": String(secondsRemaining),
1883
+ children: [
1884
+ secondsRemaining,
1885
+ "s remaining"
1886
+ ]
1887
+ }
1888
+ ),
1889
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-op-id": "", children: String(activePendingOp.opId) }),
1890
+ onCancel && /* @__PURE__ */ jsxRuntime.jsx(
1891
+ "button",
1892
+ {
1893
+ type: "button",
1894
+ "data-chipi-recover-action": "cancel",
1895
+ onClick: handleCancel,
1896
+ disabled: submittingAction !== null,
1897
+ children: "Cancel pending op"
1898
+ }
1899
+ )
1900
+ ] }),
1901
+ (phase === "ready-to-execute" || phase === "submitting-execute") && activePendingOp && /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-chipi-recover-section": "execute", children: [
1902
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "ready", children: "Timelock elapsed." }),
1903
+ /* @__PURE__ */ jsxRuntime.jsx(
1904
+ "button",
1905
+ {
1906
+ type: "button",
1907
+ "data-chipi-recover-action": "execute",
1908
+ onClick: handleExecute,
1909
+ disabled: submittingAction !== null,
1910
+ children: executeLabel
1911
+ }
1912
+ ),
1913
+ onCancel && /* @__PURE__ */ jsxRuntime.jsx(
1914
+ "button",
1915
+ {
1916
+ type: "button",
1917
+ "data-chipi-recover-action": "cancel",
1918
+ onClick: handleCancel,
1919
+ disabled: submittingAction !== null,
1920
+ children: "Cancel pending op"
1921
+ }
1922
+ )
1923
+ ] }),
1924
+ phase === "submitting-cancel" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "cancel", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "submitting", children: "Cancelling\u2026" }) }),
1925
+ phase === "done" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "done", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "done", children: isAddDevice ? "New device added." : "Wallet recovered." }) }),
1926
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-error": "", role: "alert", children: error.message })
1927
+ ]
1928
+ }
1929
+ );
1930
+ }
1605
1931
 
1606
1932
  Object.defineProperty(exports, "Chain", {
1607
1933
  enumerable: true,
@@ -1612,6 +1938,7 @@ Object.defineProperty(exports, "ChainToken", {
1612
1938
  get: function () { return types.ChainToken; }
1613
1939
  });
1614
1940
  exports.ChipiProvider = ChipiProvider;
1941
+ exports.Recover = Recover;
1615
1942
  exports.useAddSessionKeyToContract = useAddSessionKeyToContract;
1616
1943
  exports.useApprove = useApprove;
1617
1944
  exports.useCallAnyContract = useCallAnyContract;
@@ -1630,11 +1957,15 @@ exports.useGetTransaction = useGetTransaction;
1630
1957
  exports.useGetTransactionList = useGetTransactionList;
1631
1958
  exports.useGetTransactionStatus = useGetTransactionStatus;
1632
1959
  exports.useGetWallet = useGetWallet;
1960
+ exports.useGuardianRecovery = useGuardianRecovery;
1633
1961
  exports.useMigrateWalletToPasskey = useMigrateWalletToPasskey;
1962
+ exports.useMigrateWalletToShhh = useMigrateWalletToShhh;
1634
1963
  exports.usePurchaseSku = usePurchaseSku;
1635
1964
  exports.useRecordSendTransaction = useRecordSendTransaction;
1636
1965
  exports.useRevokeSessionKey = useRevokeSessionKey;
1966
+ exports.useShhhSigner = useShhhSigner;
1637
1967
  exports.useSyncOnChainTransfers = useSyncOnChainTransfers;
1968
+ exports.useThresholdSign = useThresholdSign;
1638
1969
  exports.useTransfer = useTransfer;
1639
1970
  exports.useUpdateWalletEncryption = useUpdateWalletEncryption;
1640
1971
  exports.useX402Payment = useX402Payment;