diff --git a/apps/web/src/components/SendFlow/Beacon/BatchSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/BatchSignPage.tsx index 3eb8e9833..aec74b54c 100644 --- a/apps/web/src/components/SendFlow/Beacon/BatchSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/BatchSignPage.tsx @@ -1,3 +1,4 @@ +import { type PartialTezosOperation } from "@airgap/beacon-wallet"; import { Accordion, AccordionButton, @@ -12,28 +13,36 @@ import { ModalFooter, Text, } from "@chakra-ui/react"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { useColor } from "../../../styles/useColor"; import { AddressTile } from "../../AddressTile/AddressTile"; import { JsValueWrap } from "../../JsValueWrap"; +import { useSignWithBeacon } from "../Beacon/useSignWithBeacon"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type SdkSignPageProps } from "../utils"; -export const BatchSignPage = ({ operation, message }: BeaconSignPageProps) => { - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); +export const BatchSignPage = ( + signProps: SdkSignPageProps, + operationDetails: PartialTezosOperation[] +) => { const color = useColor(); - const { signer } = operation; - const transactionCount = operation.operations.length; + + const beaconCalculatedProps = useSignWithBeacon({ ...signProps }); + const calculatedProps = beaconCalculatedProps; + + const { isSigning, onSign, network, fee } = calculatedProps; + const { signer, operations } = signProps.operation; + const transactionCount = operations.length; + const form = useForm({ defaultValues: { executeParams: signProps.operation.estimates } }); return (
-
+
@@ -45,11 +54,7 @@ export const BatchSignPage = ({ operation, message }: BeaconSignPageProps) => { - + diff --git a/apps/web/src/components/SendFlow/Beacon/BeaconSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/BeaconSignPage.tsx index 8d4049d32..4f7034684 100644 --- a/apps/web/src/components/SendFlow/Beacon/BeaconSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/BeaconSignPage.tsx @@ -1,39 +1,43 @@ import { CustomError } from "@umami/utils"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; +import { type SdkSignPageProps } from "../utils"; import { ContractCallSignPage } from "./ContractCallSignPage"; import { DelegationSignPage } from "./DelegationSignPage"; import { FinalizeUnstakeSignPage } from "./FinalizeUnstakeSignPage"; import { OriginationOperationSignPage } from "./OriginationOperationSignPage"; import { StakeSignPage } from "./StakeSignPage"; -import { TezSignPage as BeaconTezSignPage } from "./TezSignPage"; +import { TezSignPage } from "./TezSignPage"; import { UndelegationSignPage } from "./UndelegationSignPage"; import { UnstakeSignPage } from "./UnstakeSignPage"; +import { useSignWithBeacon } from "../Beacon/useSignWithBeacon"; -export const BeaconSignPage = ({ operation, message }: BeaconSignPageProps) => { - const operationType = operation.operations[0].type; +export const SingleSignPage = (signProps: SdkSignPageProps) => { + const operationType = signProps.operation.operations[0].type; + + const beaconCalculatedProps = useSignWithBeacon({ ...signProps }); + const calculatedProps = beaconCalculatedProps; switch (operationType) { case "tez": { - return ; + return ; } case "contract_call": { - return ; + return ; } case "delegation": { - return ; + return ; } case "undelegation": { - return ; + return ; } case "contract_origination": - return ; + return ; case "stake": - return ; + return ; case "unstake": - return ; + return ; case "finalize_unstake": - return ; + return ; /** * FA1/2 are impossible to get here because we don't parse them * instead we get a generic contract call diff --git a/apps/web/src/components/SendFlow/Beacon/BeaconSignPageProps.ts b/apps/web/src/components/SendFlow/Beacon/BeaconSignPageProps.ts index f55a03b77..e69de29bb 100644 --- a/apps/web/src/components/SendFlow/Beacon/BeaconSignPageProps.ts +++ b/apps/web/src/components/SendFlow/Beacon/BeaconSignPageProps.ts @@ -1,7 +0,0 @@ -import { type OperationRequestOutput } from "@airgap/beacon-wallet"; -import { type EstimatedAccountOperations } from "@umami/core"; - -export type BeaconSignPageProps = { - operation: EstimatedAccountOperations; - message: OperationRequestOutput; -}; diff --git a/apps/web/src/components/SendFlow/Beacon/ContractCallSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/ContractCallSignPage.tsx index 3e04c9850..9af917244 100644 --- a/apps/web/src/components/SendFlow/Beacon/ContractCallSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/ContractCallSignPage.tsx @@ -12,11 +12,9 @@ import { ModalFooter, } from "@chakra-ui/react"; import { type ContractCall } from "@umami/core"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { useColor } from "../../../styles/useColor"; import { AddressTile } from "../../AddressTile/AddressTile"; import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion"; @@ -24,22 +22,30 @@ import { TezTile } from "../../AssetTiles/TezTile"; import { JsValueWrap } from "../../JsValueWrap"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const ContractCallSignPage = ({ operation, message }: BeaconSignPageProps) => { +export const ContractCallSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { const { amount: mutezAmount, contract, entrypoint, args, } = operation.operations[0] as ContractCall; - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); const color = useColor(); + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); return ( -
+
diff --git a/apps/web/src/components/SendFlow/Beacon/DelegationSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/DelegationSignPage.tsx index 8659bbab8..8ab37e438 100644 --- a/apps/web/src/components/SendFlow/Beacon/DelegationSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/DelegationSignPage.tsx @@ -1,25 +1,31 @@ import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react"; import { type Delegation } from "@umami/core"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { AddressTile } from "../../AddressTile/AddressTile"; import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const DelegationSignPage = ({ operation, message }: BeaconSignPageProps) => { +export const DelegationSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { const { recipient } = operation.operations[0] as Delegation; - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); return ( -
+
From diff --git a/apps/web/src/components/SendFlow/Beacon/FinalizeUnstakeSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/FinalizeUnstakeSignPage.tsx index 3334c1fe2..cd1e081e8 100644 --- a/apps/web/src/components/SendFlow/Beacon/FinalizeUnstakeSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/FinalizeUnstakeSignPage.tsx @@ -1,17 +1,23 @@ import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react"; import { useAccountTotalFinalizableUnstakeAmount } from "@umami/state"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { AddressTile } from "../../AddressTile/AddressTile"; import { TezTile } from "../../AssetTiles/TezTile"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const FinalizeUnstakeSignPage = ({ operation, message }: BeaconSignPageProps) => { - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); +export const FinalizeUnstakeSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); const totalFinalizableAmount = useAccountTotalFinalizableUnstakeAmount( operation.signer.address.pkh ); @@ -19,7 +25,7 @@ export const FinalizeUnstakeSignPage = ({ operation, message }: BeaconSignPagePr -
+
diff --git a/apps/web/src/components/SendFlow/Beacon/Header.tsx b/apps/web/src/components/SendFlow/Beacon/Header.tsx index 5621f06eb..c032a227d 100644 --- a/apps/web/src/components/SendFlow/Beacon/Header.tsx +++ b/apps/web/src/components/SendFlow/Beacon/Header.tsx @@ -1,12 +1,12 @@ -import { type OperationRequestOutput } from "@airgap/beacon-wallet"; import { AspectRatio, Flex, Heading, Image, Text } from "@chakra-ui/react"; import { capitalize } from "lodash"; import { CodeSandboxIcon } from "../../../assets/icons"; import { useColor } from "../../../styles/useColor"; import { SignPageHeader } from "../SignPageHeader"; +import { type SignHeaderProps } from "../utils"; -export const Header = ({ message }: { message: OperationRequestOutput }) => { +export const Header = ({ headerProps }: { headerProps: SignHeaderProps }) => { const color = useColor(); return ( @@ -16,7 +16,7 @@ export const Header = ({ message }: { message: OperationRequestOutput }) => { Network: - {capitalize(message.network.type)} + {capitalize(headerProps.network.name)} @@ -32,10 +32,10 @@ export const Header = ({ message }: { message: OperationRequestOutput }) => { borderRadius="4px" objectFit="cover" fallback={} - src={message.appMetadata.icon} + src={headerProps.appIcon} /> - {message.appMetadata.name} + {headerProps.appName} ); diff --git a/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.test.tsx b/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.test.tsx index 29a6fd128..93d91bb5b 100644 --- a/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.test.tsx +++ b/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.test.tsx @@ -6,7 +6,6 @@ import { WalletClient, useGetSecretKey } from "@umami/state"; import { executeParams } from "@umami/test-utils"; import { GHOSTNET, makeToolkit, prettyTezAmount } from "@umami/tezos"; -import { OriginationOperationSignPage } from "./OriginationOperationSignPage"; import { act, dynamicModalContextMock, @@ -16,6 +15,8 @@ import { waitFor, } from "../../../testUtils"; import { SuccessStep } from "../SuccessStep"; +import { type SdkSignPageProps, type SignHeaderProps } from "../utils"; +import { SingleSignPage } from "./BeaconSignPage"; const message = { id: "messageid", @@ -30,6 +31,16 @@ const operation = { operations: [mockContractOrigination(0)], estimates: [executeParams({ fee: 123 })], }; +const headerProps: SignHeaderProps = { + network: GHOSTNET, + appName: message.appMetadata.name, + appIcon: message.appMetadata.icon, +}; +const signProps: SdkSignPageProps = { + headerProps: headerProps, + operation: operation, + requestId: { sdkType: "beacon", id: message.id }, +}; jest.mock("@umami/core", () => ({ ...jest.requireActual("@umami/core"), @@ -48,7 +59,7 @@ jest.mock("@umami/state", () => ({ describe("", () => { it("renders fee", async () => { - await renderInModal(); + await renderInModal(); await waitFor(() => expect(screen.getByText(prettyTezAmount(123))).toBeVisible()); }); @@ -62,13 +73,15 @@ describe("", () => { jest.mocked(executeOperations).mockResolvedValue({ opHash: "ophash" } as BatchWalletOperation); jest.spyOn(WalletClient, "respond").mockResolvedValue(); - await renderInModal(); - - await act(() => user.type(screen.getByLabelText("Password"), "Password")); + await renderInModal(); const signButton = screen.getByRole("button", { name: "Confirm Transaction", }); + await waitFor(() => expect(signButton).toBeDisabled()); + + await act(() => user.type(screen.getByLabelText("Password"), "ThisIsAPassword")); + await waitFor(() => expect(signButton).toBeEnabled()); await act(() => user.click(signButton)); diff --git a/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.tsx index e4176156c..bed82f9c3 100644 --- a/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/OriginationOperationSignPage.tsx @@ -17,22 +17,29 @@ import { } from "@chakra-ui/react"; import { type ContractOrigination } from "@umami/core"; import { capitalize } from "lodash"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { CodeSandboxIcon } from "../../../assets/icons"; import { useColor } from "../../../styles/useColor"; import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion"; import { JsValueWrap } from "../../JsValueWrap"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const OriginationOperationSignPage = ({ operation, message }: BeaconSignPageProps) => { - const { isSigning, onSign, network, form, fee } = useSignWithBeacon(operation, message); +export const OriginationOperationSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { const color = useColor(); const { code, storage } = operation.operations[0] as ContractOrigination; + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); + return ( @@ -46,7 +53,7 @@ export const OriginationOperationSignPage = ({ operation, message }: BeaconSignP Network: - {capitalize(message.network.type)} + {capitalize(headerProps.network.name)} @@ -64,10 +71,10 @@ export const OriginationOperationSignPage = ({ operation, message }: BeaconSignP borderRadius="4px" objectFit="cover" fallback={} - src={message.appMetadata.icon} + src={headerProps.appIcon} /> - {message.appMetadata.name} + {headerProps.appName} diff --git a/apps/web/src/components/SendFlow/Beacon/StakeSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/StakeSignPage.tsx index b91824870..237dfe4b8 100644 --- a/apps/web/src/components/SendFlow/Beacon/StakeSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/StakeSignPage.tsx @@ -1,25 +1,31 @@ import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react"; import { type Stake } from "@umami/core"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { AddressTile } from "../../AddressTile/AddressTile"; import { TezTile } from "../../AssetTiles/TezTile"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const StakeSignPage = ({ operation, message }: BeaconSignPageProps) => { +export const StakeSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { const { amount: mutezAmount } = operation.operations[0] as Stake; - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); return ( -
+
diff --git a/apps/web/src/components/SendFlow/Beacon/TezSignPage.test.tsx b/apps/web/src/components/SendFlow/Beacon/TezSignPage.test.tsx index 175abab10..78b23f5ba 100644 --- a/apps/web/src/components/SendFlow/Beacon/TezSignPage.test.tsx +++ b/apps/web/src/components/SendFlow/Beacon/TezSignPage.test.tsx @@ -3,9 +3,8 @@ import type { BatchWalletOperation } from "@taquito/taquito/dist/types/wallet/ba import { executeOperations, mockImplicitAccount, mockTezOperation } from "@umami/core"; import { WalletClient, makeStore, networksActions, useGetSecretKey } from "@umami/state"; import { executeParams } from "@umami/test-utils"; -import { GHOSTNET, MAINNET, makeToolkit } from "@umami/tezos"; +import { GHOSTNET, makeToolkit } from "@umami/tezos"; -import { TezSignPage } from "./TezSignPage"; import { act, dynamicModalContextMock, @@ -15,6 +14,8 @@ import { waitFor, } from "../../../testUtils"; import { SuccessStep } from "../SuccessStep"; +import { type SdkSignPageProps, type SignHeaderProps } from "../utils"; +import { SingleSignPage } from "./BeaconSignPage"; jest.mock("@umami/core", () => ({ ...jest.requireActual("@umami/core"), @@ -50,22 +51,35 @@ describe("", () => { operations: [mockTezOperation(0)], estimates: [executeParams({ fee: 123 })], }; - store.dispatch(networksActions.setCurrent(MAINNET)); + const headerProps: SignHeaderProps = { + network: GHOSTNET, + appName: message.appMetadata.name, + appIcon: message.appMetadata.icon, + }; + const signProps: SdkSignPageProps = { + headerProps: headerProps, + operation: operation, + requestId: { sdkType: "beacon", id: message.id }, + }; + + store.dispatch(networksActions.setCurrent(GHOSTNET)); jest.mocked(useGetSecretKey).mockImplementation(() => () => Promise.resolve("secretKey")); jest.mocked(executeOperations).mockResolvedValue({ opHash: "ophash" } as BatchWalletOperation); jest.spyOn(WalletClient, "respond").mockResolvedValue(); - await renderInModal(, store); + await renderInModal(, store); expect(screen.getByText("Ghostnet")).toBeInTheDocument(); expect(screen.queryByText("Mainnet")).not.toBeInTheDocument(); - await act(() => user.type(screen.getByLabelText("Password"), "Password")); - const signButton = screen.getByRole("button", { name: "Confirm Transaction", }); + await waitFor(() => expect(signButton).toBeDisabled()); + + await act(() => user.type(screen.getByLabelText("Password"), "ThisIsAPassword")); + await waitFor(() => expect(signButton).toBeEnabled()); await act(() => user.click(signButton)); diff --git a/apps/web/src/components/SendFlow/Beacon/TezSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/TezSignPage.tsx index cdd0969c1..bbae332e6 100644 --- a/apps/web/src/components/SendFlow/Beacon/TezSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/TezSignPage.tsx @@ -1,26 +1,31 @@ import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react"; import { type TezTransfer } from "@umami/core"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { AddressTile } from "../../AddressTile/AddressTile"; import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion"; import { TezTile } from "../../AssetTiles/TezTile"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; - -export const TezSignPage = ({ operation, message }: BeaconSignPageProps) => { +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; + +export const TezSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { const { amount: mutezAmount, recipient } = operation.operations[0] as TezTransfer; - - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); return ( - + -
+
diff --git a/apps/web/src/components/SendFlow/Beacon/UndelegationSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/UndelegationSignPage.tsx index 8c50bbdc5..c7094ff07 100644 --- a/apps/web/src/components/SendFlow/Beacon/UndelegationSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/UndelegationSignPage.tsx @@ -1,22 +1,28 @@ import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { AddressTile } from "../../AddressTile/AddressTile"; import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const UndelegationSignPage = ({ operation, message }: BeaconSignPageProps) => { - const { isSigning, onSign, network, form, fee } = useSignWithBeacon(operation, message); +export const UndelegationSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); return ( -
+
From diff --git a/apps/web/src/components/SendFlow/Beacon/UnstakeSignPage.tsx b/apps/web/src/components/SendFlow/Beacon/UnstakeSignPage.tsx index c3804a2b3..3bbb878cf 100644 --- a/apps/web/src/components/SendFlow/Beacon/UnstakeSignPage.tsx +++ b/apps/web/src/components/SendFlow/Beacon/UnstakeSignPage.tsx @@ -1,25 +1,31 @@ import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react"; import { type Unstake } from "@umami/core"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; -import { type BeaconSignPageProps } from "./BeaconSignPageProps"; import { Header } from "./Header"; -import { useSignWithBeacon } from "./useSignWithBeacon"; import { AddressTile } from "../../AddressTile/AddressTile"; import { TezTile } from "../../AssetTiles/TezTile"; import { SignButton } from "../SignButton"; import { SignPageFee } from "../SignPageFee"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const UnstakeSignPage = ({ operation, message }: BeaconSignPageProps) => { +export const UnstakeSignPage = ({ + operation, + headerProps, + isSigning, + onSign, + network, + fee, +}: SdkSignPageProps & CalculatedSignProps) => { const { amount: mutezAmount } = operation.operations[0] as Unstake; - const { isSigning, onSign, network, fee, form } = useSignWithBeacon(operation, message); + const form = useForm({ defaultValues: { executeParams: operation.estimates } }); return ( -
+
diff --git a/apps/web/src/components/SendFlow/Beacon/useSignWithBeacon.tsx b/apps/web/src/components/SendFlow/Beacon/useSignWithBeacon.tsx index 1b822a35c..7def3c8f6 100644 --- a/apps/web/src/components/SendFlow/Beacon/useSignWithBeacon.tsx +++ b/apps/web/src/components/SendFlow/Beacon/useSignWithBeacon.tsx @@ -1,23 +1,20 @@ -import { - BeaconMessageType, - type OperationRequestOutput, - type OperationResponseInput, -} from "@airgap/beacon-wallet"; +import { BeaconMessageType, type OperationResponseInput } from "@airgap/beacon-wallet"; import { type TezosToolkit } from "@taquito/taquito"; import { useDynamicModalContext } from "@umami/components"; -import { type EstimatedAccountOperations, executeOperations, totalFee } from "@umami/core"; -import { WalletClient, useAsyncActionHandler, useFindNetwork } from "@umami/state"; +import { executeOperations, totalFee } from "@umami/core"; +import { WalletClient, useAsyncActionHandler } from "@umami/state"; import { useForm } from "react-hook-form"; import { SuccessStep } from "../SuccessStep"; +import { type CalculatedSignProps, type SdkSignPageProps } from "../utils"; -export const useSignWithBeacon = ( - operation: EstimatedAccountOperations, - message: OperationRequestOutput -) => { +export const useSignWithBeacon = ({ + operation, + headerProps, + requestId, +}: SdkSignPageProps): CalculatedSignProps => { const { isLoading: isSigning, handleAsyncAction } = useAsyncActionHandler(); const { openWith } = useDynamicModalContext(); - const findNetwork = useFindNetwork(); const form = useForm({ defaultValues: { executeParams: operation.estimates } }); @@ -31,7 +28,7 @@ export const useSignWithBeacon = ( const response: OperationResponseInput = { type: BeaconMessageType.OperationResponse, - id: message.id, + id: requestId.id.toString(), transactionHash: opHash, }; await WalletClient.respond(response); @@ -47,7 +44,6 @@ export const useSignWithBeacon = ( fee: totalFee(form.watch("executeParams")), isSigning, onSign, - network: findNetwork(message.network.type), - form, + network: headerProps.network, }; }; diff --git a/apps/web/src/components/SendFlow/utils.tsx b/apps/web/src/components/SendFlow/utils.tsx index 2b1c3e3a3..a2c639c54 100644 --- a/apps/web/src/components/SendFlow/utils.tsx +++ b/apps/web/src/components/SendFlow/utils.tsx @@ -18,7 +18,7 @@ import { useGetOwnedAccount, useSelectedNetwork, } from "@umami/state"; -import { type ExecuteParams, type RawPkh } from "@umami/tezos"; +import { type ExecuteParams, type Network, type RawPkh } from "@umami/tezos"; import { repeat } from "lodash"; import { useState } from "react"; import { useForm, useFormContext } from "react-hook-form"; @@ -50,6 +50,38 @@ export type SignPageProps = { data: T; }; +export type CalculatedSignProps = { + fee: number; + isSigning: boolean; + onSign: (tezosToolkit: TezosToolkit) => Promise; + network: any; +}; + +export type sdkType = "beacon" | "walletconnect"; + +export type SignRequestId = + | { + sdkType: "beacon"; + id: string; + } + | { + sdkType: "walletconnect"; + id: number; + topic: string; + }; + +export type SignHeaderProps = { + network: Network; + appName: string; + appIcon?: string; +}; + +export type SdkSignPageProps = { + requestId: SignRequestId; + operation: EstimatedAccountOperations; + headerProps: SignHeaderProps; +}; + export const FormSubmitButton = ({ title = "Preview", ...props }: ButtonProps) => { const { formState: { isValid }, diff --git a/apps/web/src/components/beacon/useHandleBeaconMessage.test.tsx b/apps/web/src/components/beacon/useHandleBeaconMessage.test.tsx index 75144ee22..b8f3f833f 100644 --- a/apps/web/src/components/beacon/useHandleBeaconMessage.test.tsx +++ b/apps/web/src/components/beacon/useHandleBeaconMessage.test.tsx @@ -8,16 +8,22 @@ import { SigningType, TezosOperationType, } from "@airgap/beacon-wallet"; -import { estimate, makeAccountOperations, mockImplicitAccount } from "@umami/core"; +import { + type EstimatedAccountOperations, + estimate, + makeAccountOperations, + mockImplicitAccount, +} from "@umami/core"; import { type UmamiStore, WalletClient, addTestAccount, makeStore, mockToast } from "@umami/state"; import { executeParams } from "@umami/test-utils"; -import { mockImplicitAddress } from "@umami/tezos"; +import { MAINNET, mockImplicitAddress } from "@umami/tezos"; import { CustomError } from "@umami/utils"; import { useHandleBeaconMessage } from "./useHandleBeaconMessage"; import { BatchSignPage } from "../../components/SendFlow/Beacon/BatchSignPage"; -import { BeaconSignPage } from "../../components/SendFlow/Beacon/BeaconSignPage"; +import { SingleSignPage } from "../../components/SendFlow/Beacon/BeaconSignPage"; import { act, dynamicModalContextMock, renderHook, screen, waitFor } from "../../testUtils"; +import { type SdkSignPageProps, type SignHeaderProps } from "../SendFlow/utils"; jest.mock("@umami/core", () => ({ ...jest.requireActual("@umami/core"), @@ -321,7 +327,7 @@ describe("", () => { }); describe("single operation", () => { - it("opens a modal with the BeaconSignPage for 1 operation", async () => { + it("opens a modal with the SingleSignPage for 1 operation", async () => { jest.mocked(estimate).mockResolvedValueOnce({ ...makeAccountOperations(account, account, [ { type: "tez", amount: "1", recipient: mockImplicitAddress(2) }, @@ -344,6 +350,22 @@ describe("", () => { appMetadata: { name: "mockDappName", senderId: "mockSenderId" }, sourceAddress: account.address.pkh, }; + const operation: EstimatedAccountOperations = { + ...makeAccountOperations(account, account, [ + { type: "tez", amount: "1", recipient: mockImplicitAddress(2) }, + ]), + estimates: [executeParams()], + }; + const headerProps: SignHeaderProps = { + network: MAINNET, + appName: message.appMetadata.name, + appIcon: message.appMetadata.icon, + }; + const signProps: SdkSignPageProps = { + headerProps: headerProps, + operation: operation, + requestId: { sdkType: "beacon", id: message.id }, + }; const { result: { current: handleMessage }, @@ -353,15 +375,7 @@ describe("", () => { await waitFor(() => expect(dynamicModalContextMock.openWith).toHaveBeenCalledWith( - , + , { onClose: expect.any(Function) } ) ); @@ -440,6 +454,23 @@ describe("", () => { appMetadata: { name: "mockDappName", senderId: "mockSenderId" }, sourceAddress: account.address.pkh, }; + const estimatedOperations: EstimatedAccountOperations = { + ...makeAccountOperations(account, account, [ + { type: "tez", amount: "1", recipient: mockImplicitAddress(2) }, + { type: "tez", amount: "1", recipient: mockImplicitAddress(2) }, + ]), + estimates: [executeParams()], + }; + const headerProps: SignHeaderProps = { + network: MAINNET, + appName: message.appMetadata.name, + appIcon: message.appMetadata.icon, + }; + const signProps: SdkSignPageProps = { + headerProps: headerProps, + operation: estimatedOperations, + requestId: { sdkType: "beacon", id: message.id }, + }; const { result: { current: handleMessage }, @@ -449,16 +480,7 @@ describe("", () => { await waitFor(() => expect(dynamicModalContextMock.openWith).toHaveBeenCalledWith( - , + , { onClose: expect.any(Function) } ) ); diff --git a/apps/web/src/components/beacon/useHandleBeaconMessage.tsx b/apps/web/src/components/beacon/useHandleBeaconMessage.tsx index 782015638..37f35ee36 100644 --- a/apps/web/src/components/beacon/useHandleBeaconMessage.tsx +++ b/apps/web/src/components/beacon/useHandleBeaconMessage.tsx @@ -19,13 +19,14 @@ import { CustomError } from "@umami/utils"; import { PermissionRequestModal } from "./PermissionRequestModal"; import { SignPayloadRequestModal } from "./SignPayloadRequestModal"; import { BatchSignPage } from "../../components/SendFlow/Beacon/BatchSignPage"; -import { BeaconSignPage } from "../../components/SendFlow/Beacon/BeaconSignPage"; +import { SingleSignPage } from "../../components/SendFlow/Beacon/BeaconSignPage"; +import { type SdkSignPageProps, type SignHeaderProps } from "../SendFlow/utils"; /** * @returns a function that handles a beacon message and opens a modal with the appropriate content * * For operation requests it will also try to convert the operation(s) to our {@link Operation} format, - * estimate the fee and open the BeaconSignPage only if it succeeds + * estimate the fee and open the SingleSignPage only if it succeeds */ export const useHandleBeaconMessage = () => { const { openWith } = useDynamicModalContext(); @@ -104,16 +105,27 @@ export const useHandleBeaconMessage = () => { }); throw new CustomError(`Unknown account: ${message.sourceAddress}`); } + const operation = toAccountOperations( message.operationDetails, signer as ImplicitAccount ); const estimatedOperations = await estimate(operation, network); + const headerProps: SignHeaderProps = { + network: network, + appName: message.appMetadata.name, + appIcon: message.appMetadata.icon, + }; + const signProps: SdkSignPageProps = { + headerProps: headerProps, + operation: estimatedOperations, + requestId: { sdkType: "beacon", id: message.id }, + }; if (operation.operations.length === 1) { - modal = ; + modal = ; } else { - modal = ; + modal = ; } onClose = () => WalletClient.respond({