@chipi-stack/chipi-react 14.3.1 → 14.6.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/hooks.d.mts +430 -3
- package/dist/hooks.d.ts +430 -3
- package/dist/hooks.js +162 -0
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.mjs +160 -2
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.d.mts +78 -4
- package/dist/index.d.ts +78 -4
- package/dist/index.js +331 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +329 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
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
|
-
|
|
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
|
-
|
|
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;
|