Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: WalletConnect integration, part 9, get accounts #2288

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
export const useSignWithBeacon = ({
operation,
headerProps,
requestId,
}: SdkSignPageProps): CalculatedSignProps => {
const { isLoading: isSigning, handleAsyncAction } = useAsyncActionHandler();
const { openWith } = useDynamicModalContext();
Expand All @@ -28,7 +27,7 @@ export const useSignWithBeacon = ({

const response: OperationResponseInput = {
type: BeaconMessageType.OperationResponse,
id: requestId.id.toString(),
id: headerProps.requestId.id.toString(),
transactionHash: opHash,
};
await WalletClient.respond(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
export const useSignWithWalletConnect = ({
operation,
headerProps,
requestId,
}: SdkSignPageProps): CalculatedSignProps => {
const { isLoading: isSigning, handleAsyncAction } = useAsyncActionHandler();
const { openWith } = useDynamicModalContext();

const form = useForm({ defaultValues: { executeParams: operation.estimates } });
const requestId = headerProps.requestId;

if (requestId.sdkType !== "walletconnect") {
return {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/components/SendFlow/common/BatchSignPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export const BatchSignPage = (
const beaconCalculatedProps = useSignWithBeacon({ ...signProps });
const walletConnectCalculatedProps = useSignWithWalletConnect({ ...signProps });
const calculatedProps =
signProps.requestId.sdkType === "beacon" ? beaconCalculatedProps : walletConnectCalculatedProps;
signProps.headerProps.requestId.sdkType === "beacon"
? beaconCalculatedProps
: walletConnectCalculatedProps;

const { isSigning, onSign, network, fee } = calculatedProps;
const { signer, operations } = signProps.operation;
Expand Down
63 changes: 36 additions & 27 deletions apps/web/src/components/SendFlow/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,49 @@ import { capitalize } from "lodash";

import { CodeSandboxIcon } from "../../../assets/icons";
import { useColor } from "../../../styles/useColor";
import { VerifyInfobox } from "../../WalletConnect/VerifyInfobox";
import { SignPageHeader } from "../SignPageHeader";
import { type SignHeaderProps } from "../utils";

export const Header = ({ headerProps }: { headerProps: SignHeaderProps }) => {
const color = useColor();

return (
<SignPageHeader>
<Flex alignItems="center" justifyContent="center" marginTop="10px">
<Heading marginRight="4px" color={color("700")} size="sm">
Network:
</Heading>
<Text color={color("700")} fontWeight="400" size="sm">
{capitalize(headerProps.network.name)}
</Text>
</Flex>
<>
<SignPageHeader>
<Flex alignItems="center" justifyContent="center" marginTop="10px">
<Heading marginRight="4px" color={color("700")} size="sm">
Network:
</Heading>
<Text color={color("700")} fontWeight="400" size="sm">
{capitalize(headerProps.network.name)}
</Text>
</Flex>

<Flex
alignItems="center"
marginTop="16px"
padding="15px"
borderRadius="4px"
backgroundColor={color("100")}
>
<AspectRatio width="30px" marginRight="12px" ratio={1}>
<Image
borderRadius="4px"
objectFit="cover"
fallback={<CodeSandboxIcon width="36px" height="36px" />}
src={headerProps.appIcon}
/>
</AspectRatio>
<Heading size="sm">{headerProps.appName}</Heading>
</Flex>
</SignPageHeader>
<Flex
alignItems="center"
marginTop="16px"
padding="15px"
borderRadius="4px"
backgroundColor={color("100")}
>
<AspectRatio width="30px" marginRight="12px" ratio={1}>
<Image
borderRadius="4px"
objectFit="cover"
fallback={<CodeSandboxIcon width="36px" height="36px" />}
src={headerProps.appIcon}
/>
</AspectRatio>
<Heading size="sm">{headerProps.appName}</Heading>
</Flex>
</SignPageHeader>
{headerProps.requestId.sdkType === "walletconnect" ? (
<VerifyInfobox
isScam={headerProps.isScam ?? false}
validationStatus={headerProps.validationStatus ?? "UNKNOWN"}
/>
) : null}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ const headerProps: SignHeaderProps = {
network: GHOSTNET,
appName: message.appMetadata.name,
appIcon: message.appMetadata.icon,
requestId: { sdkType: "beacon", id: message.id },
};
const signProps: SdkSignPageProps = {
headerProps: headerProps,
operation: operation,
requestId: { sdkType: "beacon", id: message.id },
};

jest.mock("@umami/core", () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ describe("<SingleSignPage />", () => {
network: GHOSTNET,
appName: message.appMetadata.name,
appIcon: message.appMetadata.icon,
requestId: { sdkType: "beacon", id: message.id },
};
store.dispatch(networksActions.setCurrent(GHOSTNET));

Expand All @@ -89,7 +90,6 @@ describe("<SingleSignPage />", () => {
const signProps: SdkSignPageProps = {
headerProps: headerProps,
operation: operation,
requestId: { sdkType: "beacon", id: message.id },
};

jest.mocked(useGetSecretKey).mockImplementation(() => () => Promise.resolve("secretKey"));
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/components/SendFlow/common/SingleSignPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const SingleSignPage = (signProps: SdkSignPageProps) => {
const beaconCalculatedProps = useSignWithBeacon({ ...signProps });
const walletConnectCalculatedProps = useSignWithWalletConnect({ ...signProps });
const calculatedProps =
signProps.requestId.sdkType === "beacon" ? beaconCalculatedProps : walletConnectCalculatedProps;
signProps.headerProps.requestId.sdkType === "beacon"
? beaconCalculatedProps
: walletConnectCalculatedProps;

switch (operationType) {
case "tez": {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/components/SendFlow/common/TezSignPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ describe("<TezSignPage />", () => {
network: GHOSTNET,
appName: message.appMetadata.name,
appIcon: message.appMetadata.icon,
requestId: { sdkType: "beacon", id: message.id },
};
const signProps: SdkSignPageProps = {
headerProps: headerProps,
operation: operation,
requestId: { sdkType: "beacon", id: message.id },
};

store.dispatch(networksActions.setCurrent(GHOSTNET));
Expand All @@ -73,6 +73,8 @@ describe("<TezSignPage />", () => {
expect(screen.getByText("Ghostnet")).toBeInTheDocument();
expect(screen.queryByText("Mainnet")).not.toBeInTheDocument();

expect(screen.queryByText("verifyinfobox")).not.toBeInTheDocument();

const signButton = screen.getByRole("button", {
name: "Confirm Transaction",
});
Expand Down
17 changes: 16 additions & 1 deletion apps/web/src/components/SendFlow/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { type SigningType } from "@airgap/beacon-wallet";
import { Button, type ButtonProps } from "@chakra-ui/react";
import { type TezosToolkit } from "@taquito/taquito";
import { useDynamicModalContext } from "@umami/components";
import {
type Account,
type AccountOperations,
type EstimatedAccountOperations,
type ImplicitAccount,
type Operation,
estimate,
executeOperations,
Expand Down Expand Up @@ -74,14 +76,27 @@ export type SignHeaderProps = {
network: Network;
appName: string;
appIcon?: string;
isScam?: boolean;
validationStatus?: "VALID" | "INVALID" | "UNKNOWN";
requestId: SignRequestId;
};

export type SdkSignPageProps = {
requestId: SignRequestId;
operation: EstimatedAccountOperations;
headerProps: SignHeaderProps;
};

export type SignPayloadProps = {
requestId: SignRequestId;
appName: string;
appIcon?: string;
payload: string;
isScam?: boolean;
validationStatus?: "VALID" | "INVALID" | "UNKNOWN";
signer: ImplicitAccount;
signingType: SigningType;
};

export const FormSubmitButton = ({ title = "Preview", ...props }: ButtonProps) => {
const {
formState: { isValid },
Expand Down
8 changes: 1 addition & 7 deletions apps/web/src/components/WalletConnect/ProjectInfoCard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Avatar, Box, Card, Flex, Icon, Link, Text } from "@chakra-ui/react";
import { Avatar, Box, Card, Link, Text } from "@chakra-ui/react";
import { type SignClientTypes } from "@walletconnect/types";

import { PencilIcon } from "../../assets/icons";

type Props = {
metadata: SignClientTypes.Metadata;
intention?: string;
Expand Down Expand Up @@ -38,10 +36,6 @@ export const ProjectInfoCard = ({ metadata, intention }: Props) => {
{url}
</Link>
</Box>
<Flex alignItems="center" justifyContent="center" marginTop="16px">
<Icon as={PencilIcon} verticalAlign="bottom" />
<Card marginLeft="8px">Cannot Verify: to be implemented</Card>
</Flex>
</Box>
);
};
10 changes: 7 additions & 3 deletions apps/web/src/components/WalletConnect/SessionProposalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
useToggleWcPeerListUpdated,
walletKit,
} from "@umami/state";
import { type SessionTypes } from "@walletconnect/types";
import { type SessionTypes, type Verify } from "@walletconnect/types";
import { buildApprovedNamespaces, getSdkError } from "@walletconnect/utils";
import { FormProvider, useForm } from "react-hook-form";

Expand All @@ -46,6 +46,10 @@ export const SessionProposalModal = ({
const { onClose } = useDynamicModalContext();
const { isLoading, handleAsyncAction } = useAsyncActionHandler();

const verifyContext: Verify.Context = proposal.verifyContext;
const isScam = verifyContext.verified.isScam ?? false;
const validationStatus = verifyContext.verified.validation;

const form = useForm<{ address: string }>({
mode: "onBlur",
});
Expand Down Expand Up @@ -97,6 +101,7 @@ export const SessionProposalModal = ({
<ModalBody>
<Card>
<ProjectInfoCard metadata={proposal.params.proposer.metadata} />
<VerifyInfobox isScam={isScam} validationStatus={validationStatus} />
<Divider />
<Box marginBottom="16px" fontSize="xl" fontWeight="semibold">
Requested permissions
Expand Down Expand Up @@ -132,7 +137,6 @@ export const SessionProposalModal = ({
<Text marginLeft="8px">{network}</Text>
</Box>
<Divider />
<VerifyInfobox />
</Card>
</ModalBody>
<ModalFooter>
Expand All @@ -141,7 +145,7 @@ export const SessionProposalModal = ({
</Button>
<Button
width="100%"
isDisabled={!isValid}
isDisabled={!isValid || isScam}
isLoading={isLoading}
loadingText="Approving..."
onClick={onApprove}
Expand Down
69 changes: 58 additions & 11 deletions apps/web/src/components/WalletConnect/VerifyInfobox.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,64 @@
import { Box, Card, HStack, Icon, VStack } from "@chakra-ui/react";

import { AlertCircleIcon } from "../../assets/icons";
import { AlertCircleIcon, AlertTriangleIcon, VerifiedIcon } from "../../assets/icons";

export const VerifyInfobox = () => (
<Box textAlign="center">
<VStack spacing="16px">
<HStack margin="auto">
<Icon as={AlertCircleIcon} verticalAlign="bottom" />
<Card marginLeft="8px">Unknown domain</Card>
</HStack>
<Box margin="auto">
<Card>This domain was not verified. To be implemented.</Card>
</Box>
export const VerifyInfobox = ({
isScam,
validationStatus,
}: {
isScam: boolean;
validationStatus: "UNKNOWN" | "INVALID" | "VALID";
}) => (
<Box textAlign="left" data-testid="verifyinfobox">
<VStack margin="auto" marginTop="16px" marginBottom="16px" spacing="16px">
{isScam ? (
<HStack
margin="auto"
padding="8px"
border="1px solid"
borderColor="red.500"
borderRadius="md"
>
<Icon as={AlertTriangleIcon} verticalAlign="bottom" />
<Card marginLeft="8px">
This domain is suspected to be a SCAM. Potential threat detected.
</Card>
</HStack>
) : validationStatus === "UNKNOWN" ? (
<HStack
margin="auto"
padding="8px"
border="1px solid"
borderColor="yellow.500"
borderRadius="md"
>
<Icon as={AlertCircleIcon} verticalAlign="bottom" />
<Card marginLeft="8px">This domain is unknown. Cannot verify it.</Card>
</HStack>
) : validationStatus === "INVALID" ? (
<HStack
margin="auto"
padding="8px"
border="1px solid"
borderColor="yellow.500"
borderRadius="md"
>
<Icon as={AlertTriangleIcon} verticalAlign="bottom" />
<Card marginLeft="8px">This domain is invalid. </Card>
</HStack>
) : (
// VALID
<HStack
margin="auto"
padding="8px"
border="1px solid"
borderColor="green.500"
borderRadius="md"
>
<Icon as={VerifiedIcon} verticalAlign="bottom" />
<Card marginLeft="8px">This domain is verified. </Card>
</HStack>
)}
</VStack>
</Box>
);
Loading
Loading