Skip to content

Commit

Permalink
feat: Beacon responds with Error to dApp
Browse files Browse the repository at this point in the history
  • Loading branch information
dianasavvatina committed Jan 21, 2025
1 parent 26ce960 commit 74bb429
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 42 deletions.
15 changes: 11 additions & 4 deletions apps/web/src/components/SendFlow/Beacon/useSignWithBeacon.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BeaconMessageType, type OperationResponseInput } from "@airgap/beacon-wallet";
import { BeaconErrorType, BeaconMessageType, type OperationResponseInput } from "@airgap/beacon-wallet";
import { type TezosToolkit } from "@taquito/taquito";
import { useDynamicModalContext } from "@umami/components";
import { executeOperations, totalFee } from "@umami/core";
import { WalletClient, useAsyncActionHandler } from "@umami/state";
import { getErrorContext } from "@umami/utils";
import { useForm } from "react-hook-form";

import { SuccessStep } from "../SuccessStep";
Expand Down Expand Up @@ -34,9 +35,15 @@ export const useSignWithBeacon = ({

return openWith(<SuccessStep hash={opHash} />);
},
error => ({
description: `Failed to confirm Beacon operation: ${error.message}`,
})
error => {
const context = getErrorContext(error);
void WalletClient.respond({
id: headerProps.requestId.id.toString(),
type: BeaconMessageType.Error,
errorType: BeaconErrorType.UNKNOWN_ERROR,
});
return { description: `Failed to confirm Beacon operation: ${context.description}` };
}
);

return {
Expand Down
61 changes: 26 additions & 35 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 { CustomError } from "@umami/utils";
import { CustomError, getErrorContext } from "@umami/utils";

import { PermissionRequestModal } from "./PermissionRequestModal";
import { SignPayloadRequestModal } from "../common/SignPayloadRequestModal";
Expand All @@ -41,6 +41,20 @@ export const useHandleBeaconMessage = () => {
const findNetwork = useFindNetwork();
const removePeer = useRemoveBeaconPeerBySenderId();

// Beacon SDK expects errorData for TRANSACTION_INVALID_ERROR only and as an array of RPC errors
const respondWithError = async (
messageId: string,
errorType: BeaconErrorType,
errorData?: any
) => {
await WalletClient.respond({
id: messageId,
type: BeaconMessageType.Error,
errorType,
errorData,
});
};

// we should confirm that we support the network that the beacon request is coming from
const checkNetwork = ({
id: messageId,
Expand All @@ -52,11 +66,7 @@ export const useHandleBeaconMessage = () => {
const network = findNetwork(beaconNetwork.type);

if (!network) {
void WalletClient.respond({
id: messageId,
type: BeaconMessageType.Error,
errorType: BeaconErrorType.NETWORK_NOT_SUPPORTED,
});
void respondWithError(messageId, BeaconErrorType.NETWORK_NOT_SUPPORTED);
throw new CustomError(
`Got Beacon request from an unknown network: ${JSON.stringify(
beaconNetwork
Expand All @@ -79,11 +89,7 @@ export const useHandleBeaconMessage = () => {
checkNetwork(message);
modal = <PermissionRequestModal request={message} />;
onClose = async () => {
await WalletClient.respond({
id: message.id,
type: BeaconMessageType.Error,
errorType: BeaconErrorType.NOT_GRANTED_ERROR,
});
await respondWithError(message.id, BeaconErrorType.NOT_GRANTED_ERROR);
await removePeer(message.senderId);
};
break;
Expand All @@ -100,23 +106,15 @@ export const useHandleBeaconMessage = () => {
};
modal = <SignPayloadRequestModal opts={signPayloadProps} />;
onClose = async () => {
await WalletClient.respond({
id: message.id,
type: BeaconMessageType.Error,
errorType: BeaconErrorType.ABORTED_ERROR,
});
await respondWithError(message.id, BeaconErrorType.ABORTED_ERROR);
};
break;
}
case BeaconMessageType.OperationRequest: {
const network = checkNetwork(message);
const signer = getAccount(message.sourceAddress);
if (!signer) {
void WalletClient.respond({
id: message.id,
type: BeaconMessageType.Error,
errorType: BeaconErrorType.NO_PRIVATE_KEY_FOUND_ERROR,
});
void respondWithError(message.id, BeaconErrorType.NO_PRIVATE_KEY_FOUND_ERROR);
throw new CustomError(`Unknown account: ${message.sourceAddress}`);
}

Expand All @@ -141,33 +139,26 @@ export const useHandleBeaconMessage = () => {
} else {
modal = <BatchSignPage {...signProps} {...message.operationDetails} />;
}
onClose = () =>
WalletClient.respond({
id: message.id,
type: BeaconMessageType.Error,
errorType: BeaconErrorType.ABORTED_ERROR,
});
onClose = () => respondWithError(message.id, BeaconErrorType.ABORTED_ERROR);

break;
}
default: {
// TODO: Open a modal with an unknown operation instead

void WalletClient.respond({
id: message.id,
type: BeaconMessageType.Error,
errorType: BeaconErrorType.UNKNOWN_ERROR,
});
void respondWithError(message.id, BeaconErrorType.UNKNOWN_ERROR);

throw new CustomError(`Unknown Beacon message type: ${message.type}`);
}
}

return openWith(modal, { onClose });
},
error => ({
description: `Error while processing Beacon request: ${error.message}`,
})
error => {
const context = getErrorContext(error);
void respondWithError(message.id, BeaconErrorType.UNKNOWN_ERROR);
return { description: `Error while processing Beacon request: ${context.description}` };
}
);
};
};
12 changes: 12 additions & 0 deletions packages/test-utils/src/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
jest.mock("@walletconnect/core", () => ({
Core: jest.fn().mockImplementation(config => ({
projectId: config.projectId,
})),
}));
jest.mock("@reown/walletkit", () => ({
WalletKit: jest.fn(),
}));
jest.mock("@walletconnect/utils", () => ({
WalletConnect: jest.fn(),
}));

5 changes: 2 additions & 3 deletions packages/utils/src/ErrorContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const getErrorContext = (error: any, silent: boolean = false): ErrorConte
"Something went wrong. Please try again. Contact support if the issue persists.";
let description = defaultDescription;
let technicalDetails: any = undefined;
let code: number = WcErrorCode.INTERNAL_ERROR;
let code: WcErrorCode | number = WcErrorCode.INTERNAL_ERROR;
const errorMessage = typeof error === "string" ? error : error.message;

let stacktrace = "";
Expand Down Expand Up @@ -175,7 +175,6 @@ export const getErrorContext = (error: any, silent: boolean = false): ErrorConte
const plainMessage = stripHtmlTags(error.message);
description = `HTTP request failed for ${error.url} (${error.status}) ${httpError}`;
code = error.status;
console.log("HTTP ERROR", error);
if (code === 500) {
description = `${description}\nDetails: ${plainMessage}`;
}
Expand All @@ -186,7 +185,7 @@ export const getErrorContext = (error: any, silent: boolean = false): ErrorConte
}

if (!silent) {
console.warn("Request failed", code, description, technicalDetails, error);
console.error("Request failed", code, description, technicalDetails, error);
}

return {
Expand Down
11 changes: 11 additions & 0 deletions packages/utils/src/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
jest.mock("@walletconnect/core", () => ({
Core: jest.fn().mockImplementation(config => ({
projectId: config.projectId,
})),
}));
jest.mock("@reown/walletkit", () => ({
WalletKit: jest.fn(),
}));
jest.mock("@walletconnect/utils", () => ({
WalletConnect: jest.fn(),
}));

0 comments on commit 74bb429

Please sign in to comment.