Skip to content

Commit

Permalink
fix: WalletConnect hangs if request modal gets hidden
Browse files Browse the repository at this point in the history
  • Loading branch information
dianasavvatina committed Feb 10, 2025
1 parent 34c48a0 commit 6146d55
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 21 deletions.
2 changes: 1 addition & 1 deletion apps/web/src/components/AccountCard/AccountCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const AccountCard = () => {
background={color("100")}
account={currentAccount}
id="account-tile"
onClick={() => openWith(<AccountSelectorModal />)}
onClick={() => openWith(<AccountSelectorModal />, { canBeOverriden: true })}
>
<IconButton
alignSelf="center"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const Header = () => {
<AccountTile
background={color("100")}
account={currentAccount}
onClick={() => openWith(<AccountSelectorModal />)}
onClick={() => openWith(<AccountSelectorModal />, { canBeOverriden: true })}
size="xs"
/>
</SlideFade>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const useSignWithBeacon = ({
};
await WalletClient.respond(response);

return openWith(<SuccessStep hash={opHash} />);
return openWith(<SuccessStep hash={opHash} />, { canBeOverriden: true });
},
(error: any) => {
const context = getErrorContext(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ export const useSignWithWalletConnect = ({
} catch (error: any) {
const errorContext = getErrorContext(error);
await openWith(
<SuccessStep dAppNotificationError={errorContext.description} hash={opHash} />
<SuccessStep dAppNotificationError={errorContext.description} hash={opHash} />,
{ canBeOverriden: true }
);
error.processed = true; // no toast for this error
throw error;
}
return openWith(<SuccessStep hash={opHash} />);
return openWith(<SuccessStep hash={opHash} />, { canBeOverriden: true });
},
(error: { message: any }) => ({
description: `Failed to perform WalletConnect operation: ${error.message}`,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/SendFlow/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export const useSignPageHelpers = (
{ ...operations, estimates: form.watch("executeParams") },
tezosToolkit
);
await openWith(<SuccessStep hash={operation.opHash} />);
await openWith(<SuccessStep hash={operation.opHash} />, { canBeOverriden: true });
return operation;
});

Expand Down
11 changes: 7 additions & 4 deletions apps/web/src/components/WalletConnect/SessionProposalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const SessionProposalModal = ({
const toggleWcPeerListUpdated = useToggleWcPeerListUpdated();
const color = useColor();

const { onClose } = useDynamicModalContext();
const { goBack } = useDynamicModalContext();
const { isLoading, handleAsyncAction } = useAsyncActionHandler();

const verifyContext: Verify.Context = proposal.verifyContext;
Expand Down Expand Up @@ -82,13 +82,16 @@ export const SessionProposalModal = ({
});
console.log("WC session approved", session);
toggleWcPeerListUpdated();
onClose();

// this modal could hide existing request modal; now when we are done, it's time to bring it back. otherwise, WalletConenct will wait forever
goBack();
});

const onReject = () =>
handleAsyncAction(async () => {
// close immediately assuming that the user wants to get rid of the modal
onClose();
// Close immediately assuming that the user wants to get rid of the modal.
// This modal could hide existing request modal; now when we are done, it's time to bring it back. otherwise, WalletConenct will wait forever
goBack();
console.log("WC session rejected");
await walletKit.rejectSession({
id: proposal.id,
Expand Down
19 changes: 15 additions & 4 deletions apps/web/src/components/WalletConnect/WalletConnectProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type EventEmitter from "events";
import { type NetworkType } from "@airgap/beacon-wallet";
import { useToast } from "@chakra-ui/react";
import { type WalletKitTypes } from "@reown/walletkit";
import { useDynamicModalContext } from "@umami/components";
import { useDynamicModal, useDynamicModalContext } from "@umami/components";
import {
createWalletKit,
useAsyncActionHandler,
Expand Down Expand Up @@ -31,7 +31,8 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
const walletKitState = useRef<WalletKitState>(WalletKitState.NOT_INITIALIZED);
const eventEmitters = useRef<EventEmitter[]>([]);
const { handleAsyncActionUnsafe } = useAsyncActionHandler();
const { openWith } = useDynamicModalContext();
const { openWith, isOpen, canBeOverriden } = useDynamicModalContext();
const { content } = useDynamicModal();
const toggleWcPeerListUpdated = useToggleWcPeerListUpdated();
const toast = useToast();

Expand Down Expand Up @@ -64,6 +65,12 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
);
}

if (isOpen && !canBeOverriden) {
const errMessage = `Rejected session proposal from dApp ${proposal.params.proposer.metadata.name}. Wallet is busy waiting for user answer for the previous request`;
console.error(errMessage, content);
throw new WalletConnectError(errMessage, WcErrorCode.WALLET_BUSY, null);
}

await openWith(<SessionProposalModal network={network} proposal={proposal} />, {});
}).catch(async () => {
// dApp is waiting so we need to notify it
Expand All @@ -72,7 +79,7 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
reason: getSdkError("UNSUPPORTED_CHAINS"),
});
}),
[availableNetworks, openWith, handleAsyncActionUnsafe]
[handleAsyncActionUnsafe, availableNetworks, isOpen, canBeOverriden, openWith, content]
);

const onSessionDelete = useCallback(
Expand All @@ -98,6 +105,10 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
}

const session = activeSessions[event.topic];
if (isOpen && !canBeOverriden) {
const errMessage = `Rejected request from dApp ${session.peer.metadata.name}. Wallet is busy waiting for user answer for the previous request`;
throw new WalletConnectError(errMessage, WcErrorCode.WALLET_BUSY, null);
}
toast({
description: `Session request from dApp ${session.peer.metadata.name}`,
status: "info",
Expand All @@ -109,7 +120,7 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
// dApp is waiting so we need to notify it
await walletKit.respondSessionRequest({ topic, response });
}),
[handleAsyncActionUnsafe, handleWcRequest, toast]
[handleAsyncActionUnsafe, handleWcRequest, isOpen, canBeOverriden, toast]
);

useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/beacon/PermissionRequestModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const PermissionRequestModal = ({ request }: { request: PermissionRequest
const color = useColor();
const addConnectionToBeaconSlice = useAddBeaconConnection();
const getAccount = useGetImplicitAccount();
const { onClose } = useDynamicModalContext();
const { goBack } = useDynamicModalContext();
const { handleAsyncAction } = useAsyncActionHandler();
const form = useForm<{ address: string }>({
mode: "onBlur",
Expand All @@ -69,7 +69,7 @@ export const PermissionRequestModal = ({ request }: { request: PermissionRequest
await WalletClient.respond(response);

addConnectionToBeaconSlice(request.senderId, account.address.pkh, request.network.type);
}).finally(onClose);
}).finally(goBack);

return (
<ModalContent>
Expand Down
15 changes: 10 additions & 5 deletions apps/web/src/components/beacon/useHandleBeaconMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
useRemoveBeaconPeerBySenderId,
} from "@umami/state";
import { type Network } from "@umami/tezos";
import { BeaconError, getErrorContext } from "@umami/utils";
import { BeaconError, CustomError, getErrorContext } from "@umami/utils";

import { PermissionRequestModal } from "./PermissionRequestModal";
import { SignPayloadRequestModal } from "../common/SignPayloadRequestModal";
Expand All @@ -34,7 +34,7 @@ import {
* estimate the fee and open the SingleSignPage only if it succeeds
*/
export const useHandleBeaconMessage = () => {
const { openWith } = useDynamicModalContext();
const { openWith, isOpen, canBeOverriden } = useDynamicModalContext();
const { handleAsyncAction } = useAsyncActionHandler();
const getAccount = useGetOwnedAccountSafe();
const getImplicitAccount = useGetImplicitAccount();
Expand All @@ -48,18 +48,17 @@ export const useHandleBeaconMessage = () => {
errorType: BeaconErrorType,
errorData?: any
) => {
console.info("[respondWithError]", messageId, errorType, errorData);
if (messageId !== lastProcessedMessageId) {
lastProcessedMessageId = messageId;
console.info("[respondWithError] sending response", errorType);
console.info("[respondWithError] sending response", messageId, errorType, errorData);
await WalletClient.respond({
id: messageId,
type: BeaconMessageType.Error,
errorType,
errorData,
});
} else {
console.info("[respondWithError] response already sent", errorType);
console.info("[respondWithError] response already sent", messageId, errorType, errorData);
}
};

Expand Down Expand Up @@ -90,6 +89,12 @@ export const useHandleBeaconMessage = () => {
let modal;
let onClose;

if (isOpen && !canBeOverriden) {
const errMessage = `Rejected request from dApp ${message.appMetadata.name}. Wallet is busy waiting for user answer for the previous request`;
console.error(errMessage);
throw new CustomError(errMessage);
}

// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
switch (message.type) {
case BeaconMessageType.PermissionRequest: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ interface DynamicDisclosureContextType {
props?: ThemingProps & {
onClose?: () => void | Promise<void>;
closeOnEsc?: boolean;
canBeOverriden?: boolean;
}
) => Promise<void>;
onClose: () => void;
isOpen: boolean;
goBack: () => void;
goToIndex: (index: number) => void;
hasPrevious: boolean;
canBeOverriden: boolean;
formValues: Record<string, any>;
allFormValues: RefObject<Record<string, any>>;
}
Expand All @@ -42,6 +44,7 @@ const defaultContextValue = {
goToIndex: () => {},
isOpen: false,
hasPrevious: false,
canBeOverriden: false,
formValues: {},
allFormValues: { current: {} },
};
Expand All @@ -64,6 +67,7 @@ type DisclosureStackItem = {
props: ThemingProps & {
onClose: () => void | Promise<void>;
closeOnEsc?: boolean;
canBeOverriden?: boolean; // protects WalletConnect and Beacon modals from being overriden
};
formValues: Record<string, any>;
};
Expand All @@ -89,6 +93,7 @@ export const useDynamicDisclosure = () => {
props: ThemingProps & {
onClose?: () => void | Promise<void>;
closeOnEsc?: boolean;
canBeOverriden?: boolean;
} = {}
) => {
const onClose = () => {
Expand Down Expand Up @@ -146,6 +151,7 @@ export const useDynamicDisclosure = () => {
formValues: currentItem?.formValues || {},
hasPrevious: stackRef.current.length > 1,
allFormValues,
canBeOverriden: currentItem?.props.canBeOverriden || false,
};
};

Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/ErrorContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export enum WcErrorCode {
REJECTED_BY_CHAIN = 4009,
DELEGATE_UNCHANGED = 4010,
UNKNOWN_ERROR = 4011,
WALLET_BUSY = 4012,
}

// Converts a known L1 error message to a more user-friendly one
Expand Down

0 comments on commit 6146d55

Please sign in to comment.