@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.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import React, { createContext, useContext, useMemo, useCallback, useState, useEffect, useRef } from 'react';
2
2
  import { QueryClient, QueryClientProvider, useMutation, useQueryClient, useQuery } from '@tanstack/react-query';
3
- import { ChipiSDK, decryptPrivateKey, encryptPrivateKey } from '@chipi-stack/backend';
3
+ import { ChipiSDK, decryptPrivateKey, encryptPrivateKey, buildJwtAppleSubEnvelopeFromTokenResponse, buildWebAuthnEnvelopeFromAssertion, buildEip191EnvelopeFromSignature, buildEd25519Envelope, buildStarkEnvelope, OP_SET_THRESHOLD, OP_ROTATE_OWNER, OP_REMOVE_OWNER, OP_ADD_OWNER, ROLE_GUARDIAN, ROLE_OWNER, DEFAULT_OP_EXPIRY_SECONDS, TIMELOCK_SECONDS, secondsUntilExecutable, isReadyToExecute, buildFinalizeRecoveryCall, buildCancelRecoveryCall, buildInitiateRecoveryCall, buildCancelPendingOpCall, buildExecuteSetThresholdCall, buildProposeSetThresholdCall, buildExecuteRotateOwnerCall, buildProposeRotateOwnerCall, buildExecuteRemoveOwnerCall, buildProposeRemoveOwnerCall, buildExecuteAddOwnerCall, buildProposeAddOwnerCall, assertValidV2Snip12Envelope, buildThresholdEnvelope, MIN_THRESHOLD_OWNERS, estimateThresholdGasOverhead, parseThresholdEnvelope } from '@chipi-stack/backend';
4
4
  import { createWalletPasskey, getWalletEncryptKey } from '@chipi-stack/chipi-passkey';
5
- import { jsx } from 'react/jsx-runtime';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
6
  import { ChipiApiError } from '@chipi-stack/shared';
7
7
  import { OnChainTxStatus, Chain, ChainToken } from '@chipi-stack/types';
8
8
  export { Chain, ChainToken } from '@chipi-stack/types';
@@ -712,6 +712,25 @@ function useUpdateWalletEncryption() {
712
712
  reset: mutation.reset
713
713
  };
714
714
  }
715
+ function useMigrateWalletToShhh() {
716
+ const { chipiSDK } = useChipiContext();
717
+ const mutation = useMutation({
718
+ mutationFn: async (input) => {
719
+ const { bearerToken, ...rest } = input;
720
+ return chipiSDK.wallets.migrateToShhh(rest, bearerToken);
721
+ }
722
+ });
723
+ return {
724
+ migrate: mutation.mutate,
725
+ migrateAsync: mutation.mutateAsync,
726
+ data: mutation.data,
727
+ isLoading: mutation.isPending,
728
+ isError: mutation.isError,
729
+ error: mutation.error,
730
+ isSuccess: mutation.isSuccess,
731
+ reset: mutation.reset
732
+ };
733
+ }
715
734
  function useTransfer(config) {
716
735
  const { chipiSDK, passkeyAdapter } = useChipiContext();
717
736
  const mutation = useMutation({
@@ -1597,7 +1616,314 @@ function useX402Payment(config) {
1597
1616
  paymentCount
1598
1617
  };
1599
1618
  }
1619
+ function useShhhSigner() {
1620
+ return useMemo(
1621
+ () => ({
1622
+ signWithStark: (params) => buildStarkEnvelope({
1623
+ privateKey: params.privateKey,
1624
+ messageHash: params.messageHash
1625
+ }),
1626
+ signWithPhantom: (params) => buildEd25519Envelope({
1627
+ privateKey: params.privateKey,
1628
+ messageHash: params.messageHash
1629
+ }),
1630
+ signWithMetaMask: (params) => buildEip191EnvelopeFromSignature({
1631
+ signatureHex: params.signatureHex,
1632
+ pubkey: params.pubkey
1633
+ }),
1634
+ signWithPasskey: (params) => buildWebAuthnEnvelopeFromAssertion({
1635
+ authenticatorData: params.authenticatorData,
1636
+ clientDataJSON: params.clientDataJSON,
1637
+ signatureDer: params.signatureDer,
1638
+ pubkey: params.pubkey,
1639
+ messageHash: params.messageHash,
1640
+ yParity: params.yParity ?? 0n
1641
+ }),
1642
+ signWithAppleIdToken: (params) => buildJwtAppleSubEnvelopeFromTokenResponse({
1643
+ idToken: params.idToken,
1644
+ pubkey: params.pubkey,
1645
+ messageHash: params.messageHash
1646
+ })
1647
+ }),
1648
+ []
1649
+ );
1650
+ }
1651
+ function useGuardianRecovery() {
1652
+ return useMemo(
1653
+ () => ({
1654
+ buildProposeAddOwner: (args) => buildProposeAddOwnerCall(args),
1655
+ buildExecuteAddOwner: (args) => buildExecuteAddOwnerCall(args),
1656
+ buildProposeRemoveOwner: (args) => buildProposeRemoveOwnerCall(args),
1657
+ buildExecuteRemoveOwner: (args) => buildExecuteRemoveOwnerCall(args),
1658
+ buildProposeRotateOwner: (args) => buildProposeRotateOwnerCall(args),
1659
+ buildExecuteRotateOwner: (args) => buildExecuteRotateOwnerCall(args),
1660
+ buildProposeSetThreshold: (args) => buildProposeSetThresholdCall(args),
1661
+ buildExecuteSetThreshold: (args) => buildExecuteSetThresholdCall(args),
1662
+ buildCancelPendingOp: (args) => buildCancelPendingOpCall(args),
1663
+ buildInitiateRecovery: (args) => buildInitiateRecoveryCall(args),
1664
+ buildCancelRecovery: (args) => buildCancelRecoveryCall(args),
1665
+ buildFinalizeRecovery: (args) => buildFinalizeRecoveryCall(args),
1666
+ isReady: (validAfterSecs, nowSecs) => isReadyToExecute(validAfterSecs, nowSecs ?? Math.floor(Date.now() / 1e3)),
1667
+ secondsRemaining: (validAfterSecs, nowSecs) => secondsUntilExecutable(validAfterSecs, nowSecs ?? Math.floor(Date.now() / 1e3)),
1668
+ TIMELOCK_SECONDS,
1669
+ DEFAULT_OP_EXPIRY_SECONDS,
1670
+ ROLE_OWNER,
1671
+ ROLE_GUARDIAN,
1672
+ OP_ADD_OWNER,
1673
+ OP_REMOVE_OWNER,
1674
+ OP_ROTATE_OWNER,
1675
+ OP_SET_THRESHOLD
1676
+ }),
1677
+ []
1678
+ );
1679
+ }
1680
+ function useThresholdSign(opts) {
1681
+ const { threshold } = opts;
1682
+ const [collected, setCollected] = useState([]);
1683
+ const [signedOwnerIds, setSignedOwnerIds] = useState([]);
1684
+ const [error, setError] = useState(null);
1685
+ const signedOwnerIdsRef = useRef([]);
1686
+ const addEnvelope = useCallback((envelope) => {
1687
+ let ownerId;
1688
+ try {
1689
+ ownerId = assertValidV2Snip12Envelope(envelope);
1690
+ } catch (e) {
1691
+ const err = e instanceof Error ? e : new Error(String(e));
1692
+ setError(err);
1693
+ return false;
1694
+ }
1695
+ if (signedOwnerIdsRef.current.some((id) => id === ownerId)) {
1696
+ const err = new Error(
1697
+ `THRESH: owner_id 0x${ownerId.toString(16)} has already signed; use removeEnvelope first if you want to replace their envelope.`
1698
+ );
1699
+ setError(err);
1700
+ return false;
1701
+ }
1702
+ signedOwnerIdsRef.current = [...signedOwnerIdsRef.current, ownerId];
1703
+ setCollected((prev) => [...prev, envelope]);
1704
+ setSignedOwnerIds(signedOwnerIdsRef.current);
1705
+ setError(null);
1706
+ return true;
1707
+ }, []);
1708
+ const removeEnvelope = useCallback((ownerId) => {
1709
+ const matchIndex = signedOwnerIdsRef.current.findIndex((id) => id === ownerId);
1710
+ if (matchIndex < 0) return;
1711
+ signedOwnerIdsRef.current = signedOwnerIdsRef.current.filter((id) => id !== ownerId);
1712
+ setCollected((prev) => prev.filter((_, i) => i !== matchIndex));
1713
+ setSignedOwnerIds(signedOwnerIdsRef.current);
1714
+ setError(null);
1715
+ }, []);
1716
+ const reset = useCallback(() => {
1717
+ signedOwnerIdsRef.current = [];
1718
+ setCollected([]);
1719
+ setSignedOwnerIds([]);
1720
+ setError(null);
1721
+ }, []);
1722
+ const finalize = useCallback(() => {
1723
+ if (collected.length < threshold) {
1724
+ throw new Error(
1725
+ `THRESH: cannot finalize \u2014 need ${threshold} envelopes, have ${collected.length}. Wait for more owners to sign.`
1726
+ );
1727
+ }
1728
+ return buildThresholdEnvelope({ innerEnvelopes: collected });
1729
+ }, [collected, threshold]);
1730
+ const state = useMemo(
1731
+ () => ({
1732
+ threshold,
1733
+ collected,
1734
+ signedOwnerIds,
1735
+ isReady: collected.length >= threshold,
1736
+ error
1737
+ }),
1738
+ [threshold, collected, signedOwnerIds, error]
1739
+ );
1740
+ const stateless = useMemo(
1741
+ () => ({
1742
+ assemble: (args) => buildThresholdEnvelope(args),
1743
+ parse: (args) => parseThresholdEnvelope(args.envelope),
1744
+ estimateGasOverhead: (args) => estimateThresholdGasOverhead(args.owners),
1745
+ MIN_THRESHOLD_OWNERS
1746
+ }),
1747
+ []
1748
+ );
1749
+ return {
1750
+ ...stateless,
1751
+ state,
1752
+ addEnvelope,
1753
+ removeEnvelope,
1754
+ reset,
1755
+ finalize
1756
+ };
1757
+ }
1758
+ function deriveStaticPhase(pendingOp, executed, isReady, nowSecs) {
1759
+ if (executed) return "done";
1760
+ if (!pendingOp) return "form-idle";
1761
+ return isReady(pendingOp.validAfter, nowSecs) ? "ready-to-execute" : "awaiting-timelock";
1762
+ }
1763
+ function shortPubkey(bytes) {
1764
+ if (bytes.length === 0) return "(empty)";
1765
+ const first = bytes[0].toString(16);
1766
+ return first.length > 12 ? `0x${first.slice(0, 8)}\u2026${first.slice(-4)}` : `0x${first}`;
1767
+ }
1768
+ function Recover(props) {
1769
+ const { mode, walletAddress, newOwner, pendingOp, onCancel, className, nowSecs } = props;
1770
+ const recovery = useGuardianRecovery();
1771
+ const [executed, setExecuted] = useState(false);
1772
+ const [submittingAction, setSubmittingAction] = useState(
1773
+ null
1774
+ );
1775
+ const [error, setError] = useState(null);
1776
+ const [optimisticPendingOp, setOptimisticPendingOp] = useState(null);
1777
+ useEffect(() => {
1778
+ if (pendingOp) setOptimisticPendingOp(null);
1779
+ }, [pendingOp]);
1780
+ const activePendingOp = pendingOp ?? optimisticPendingOp ?? null;
1781
+ const [tick, setTick] = useState(0);
1782
+ useEffect(() => {
1783
+ if (!activePendingOp || executed) return;
1784
+ const ready = recovery.isReady(activePendingOp.validAfter, nowSecs);
1785
+ if (ready) return;
1786
+ const id = setInterval(() => setTick((t) => t + 1), 1e3);
1787
+ return () => clearInterval(id);
1788
+ }, [activePendingOp, executed, recovery, nowSecs, tick]);
1789
+ const phase = useMemo(() => {
1790
+ if (submittingAction === "propose") return "submitting-propose";
1791
+ if (submittingAction === "execute") return "submitting-execute";
1792
+ if (submittingAction === "cancel") return "submitting-cancel";
1793
+ return deriveStaticPhase(activePendingOp, executed, recovery.isReady, nowSecs);
1794
+ }, [submittingAction, activePendingOp, executed, recovery, nowSecs, tick]);
1795
+ const secondsRemaining = useMemo(() => {
1796
+ if (!activePendingOp) return 0;
1797
+ return recovery.secondsRemaining(activePendingOp.validAfter, nowSecs);
1798
+ }, [activePendingOp, recovery, nowSecs, tick]);
1799
+ const handlePropose = useCallback(async () => {
1800
+ setError(null);
1801
+ setSubmittingAction("propose");
1802
+ try {
1803
+ const result = await props.onPropose();
1804
+ setOptimisticPendingOp({
1805
+ opId: result.opId,
1806
+ validAfter: result.validAfter
1807
+ });
1808
+ } catch (e) {
1809
+ setError(e instanceof Error ? e : new Error(String(e)));
1810
+ } finally {
1811
+ setSubmittingAction(null);
1812
+ }
1813
+ }, [props]);
1814
+ const handleExecute = useCallback(async () => {
1815
+ if (!activePendingOp) return;
1816
+ setError(null);
1817
+ setSubmittingAction("execute");
1818
+ try {
1819
+ await props.onExecute(activePendingOp.opId);
1820
+ setExecuted(true);
1821
+ } catch (e) {
1822
+ setError(e instanceof Error ? e : new Error(String(e)));
1823
+ } finally {
1824
+ setSubmittingAction(null);
1825
+ }
1826
+ }, [activePendingOp, props]);
1827
+ const handleCancel = useCallback(async () => {
1828
+ if (!activePendingOp || !onCancel) return;
1829
+ setError(null);
1830
+ setSubmittingAction("cancel");
1831
+ try {
1832
+ await onCancel(activePendingOp.opId);
1833
+ } catch (e) {
1834
+ setError(e instanceof Error ? e : new Error(String(e)));
1835
+ } finally {
1836
+ setSubmittingAction(null);
1837
+ }
1838
+ }, [activePendingOp, onCancel]);
1839
+ const isAddDevice = mode === "add-device";
1840
+ const proposeLabel = isAddDevice ? "Propose new device" : "Initiate recovery";
1841
+ const executeLabel = isAddDevice ? "Execute add-owner" : "Finalize recovery";
1842
+ const timelockDescription = isAddDevice ? "48-hour timelock before this device becomes active." : "7-day timelock before the recovered wallet becomes active.";
1843
+ return /* @__PURE__ */ jsxs(
1844
+ "div",
1845
+ {
1846
+ "data-chipi-recover-root": "",
1847
+ "data-chipi-recover-mode": mode,
1848
+ "data-chipi-recover-phase": phase,
1849
+ className,
1850
+ children: [
1851
+ /* @__PURE__ */ jsxs("header", { "data-chipi-recover-header": "", children: [
1852
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-wallet-address": "", children: String(walletAddress) }),
1853
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-new-owner-kind": "", children: newOwner.kind }),
1854
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-new-owner-label": "", children: newOwner.label }),
1855
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-new-owner-pubkey": "", children: shortPubkey(newOwner.pubkeyBytes) })
1856
+ ] }),
1857
+ phase === "form-idle" && /* @__PURE__ */ jsxs("section", { "data-chipi-recover-section": "propose", children: [
1858
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-description": "", children: timelockDescription }),
1859
+ /* @__PURE__ */ jsx(
1860
+ "button",
1861
+ {
1862
+ type: "button",
1863
+ "data-chipi-recover-action": "propose",
1864
+ onClick: handlePropose,
1865
+ disabled: submittingAction !== null,
1866
+ children: proposeLabel
1867
+ }
1868
+ )
1869
+ ] }),
1870
+ phase === "submitting-propose" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "propose", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "submitting", children: "Submitting\u2026" }) }),
1871
+ phase === "awaiting-timelock" && activePendingOp && /* @__PURE__ */ jsxs("section", { "data-chipi-recover-section": "awaiting", children: [
1872
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "awaiting", children: timelockDescription }),
1873
+ /* @__PURE__ */ jsxs(
1874
+ "p",
1875
+ {
1876
+ "data-chipi-recover-countdown": "",
1877
+ "data-chipi-recover-seconds-remaining": String(secondsRemaining),
1878
+ children: [
1879
+ secondsRemaining,
1880
+ "s remaining"
1881
+ ]
1882
+ }
1883
+ ),
1884
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-op-id": "", children: String(activePendingOp.opId) }),
1885
+ onCancel && /* @__PURE__ */ jsx(
1886
+ "button",
1887
+ {
1888
+ type: "button",
1889
+ "data-chipi-recover-action": "cancel",
1890
+ onClick: handleCancel,
1891
+ disabled: submittingAction !== null,
1892
+ children: "Cancel pending op"
1893
+ }
1894
+ )
1895
+ ] }),
1896
+ (phase === "ready-to-execute" || phase === "submitting-execute") && activePendingOp && /* @__PURE__ */ jsxs("section", { "data-chipi-recover-section": "execute", children: [
1897
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "ready", children: "Timelock elapsed." }),
1898
+ /* @__PURE__ */ jsx(
1899
+ "button",
1900
+ {
1901
+ type: "button",
1902
+ "data-chipi-recover-action": "execute",
1903
+ onClick: handleExecute,
1904
+ disabled: submittingAction !== null,
1905
+ children: executeLabel
1906
+ }
1907
+ ),
1908
+ onCancel && /* @__PURE__ */ jsx(
1909
+ "button",
1910
+ {
1911
+ type: "button",
1912
+ "data-chipi-recover-action": "cancel",
1913
+ onClick: handleCancel,
1914
+ disabled: submittingAction !== null,
1915
+ children: "Cancel pending op"
1916
+ }
1917
+ )
1918
+ ] }),
1919
+ phase === "submitting-cancel" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "cancel", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "submitting", children: "Cancelling\u2026" }) }),
1920
+ phase === "done" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "done", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "done", children: isAddDevice ? "New device added." : "Wallet recovered." }) }),
1921
+ error && /* @__PURE__ */ jsx("p", { "data-chipi-recover-error": "", role: "alert", children: error.message })
1922
+ ]
1923
+ }
1924
+ );
1925
+ }
1600
1926
 
1601
- export { ChipiProvider, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiContext, useChipiSession, useChipiWallet, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useMigrateWalletToPasskey, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useSyncOnChainTransfers, useTransfer, useUpdateWalletEncryption, useX402Payment };
1927
+ export { ChipiProvider, Recover, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiContext, 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 };
1602
1928
  //# sourceMappingURL=index.mjs.map
1603
1929
  //# sourceMappingURL=index.mjs.map