From 74f85ce2f0fa10515f54193e8fc72337426edc56 Mon Sep 17 00:00:00 2001 From: apoorvlathey Date: Sat, 10 Jun 2023 17:00:21 +0530 Subject: [PATCH] refactor: separate into components --- src/components/Body/AddressInput.tsx | 67 ++ src/components/Body/BrowserExtensionTab.tsx | 43 + src/components/Body/CopyToClipboard.tsx | 15 + .../Body/IFrameConnectTab/AppUrlLabel.tsx | 29 + .../SupportedDapps/DappTile.tsx | 45 ++ .../SupportedDapps/DappsSearch.tsx | 40 + .../IFrameConnectTab/SupportedDapps/index.tsx | 146 ++++ .../Body/IFrameConnectTab/index.tsx | 96 +++ src/components/Body/NetworkInput.tsx | 62 ++ src/components/Body/TabsSelect.tsx | 41 + src/components/Body/TenderlySettings.tsx | 84 ++ src/components/Body/TransactionRequests.tsx | 123 +++ .../WalletConnectTab/ConnectionDetails.tsx | 38 + .../LegacyConnectionDetails.tsx | 33 + .../Body/WalletConnectTab/Loading.tsx | 39 + .../Body/WalletConnectTab/URIInput.tsx | 57 ++ .../Body/WalletConnectTab/index.tsx | 67 ++ src/components/Body/index.tsx | 739 ++---------------- src/types.ts | 22 + 19 files changed, 1117 insertions(+), 669 deletions(-) create mode 100644 src/components/Body/AddressInput.tsx create mode 100644 src/components/Body/BrowserExtensionTab.tsx create mode 100644 src/components/Body/CopyToClipboard.tsx create mode 100644 src/components/Body/IFrameConnectTab/AppUrlLabel.tsx create mode 100644 src/components/Body/IFrameConnectTab/SupportedDapps/DappTile.tsx create mode 100644 src/components/Body/IFrameConnectTab/SupportedDapps/DappsSearch.tsx create mode 100644 src/components/Body/IFrameConnectTab/SupportedDapps/index.tsx create mode 100644 src/components/Body/IFrameConnectTab/index.tsx create mode 100644 src/components/Body/NetworkInput.tsx create mode 100644 src/components/Body/TabsSelect.tsx create mode 100644 src/components/Body/TenderlySettings.tsx create mode 100644 src/components/Body/TransactionRequests.tsx create mode 100644 src/components/Body/WalletConnectTab/ConnectionDetails.tsx create mode 100644 src/components/Body/WalletConnectTab/LegacyConnectionDetails.tsx create mode 100644 src/components/Body/WalletConnectTab/Loading.tsx create mode 100644 src/components/Body/WalletConnectTab/URIInput.tsx create mode 100644 src/components/Body/WalletConnectTab/index.tsx diff --git a/src/components/Body/AddressInput.tsx b/src/components/Body/AddressInput.tsx new file mode 100644 index 0000000..b9201c7 --- /dev/null +++ b/src/components/Body/AddressInput.tsx @@ -0,0 +1,67 @@ +import { + FormControl, + FormLabel, + InputGroup, + Input, + InputRightElement, + Button, +} from "@chakra-ui/react"; + +interface AddressInputParams { + showAddress: string; + setShowAddress: (value: string) => void; + setAddress: (value: string) => void; + setIsAddressValid: (value: boolean) => void; + bg: string; + isAddressValid: boolean; + selectedTabIndex: number; + isConnected: boolean; + appUrl: string | undefined; + isIFrameLoading: boolean; + updateAddress: () => void; +} + +function AddressInput({ + showAddress, + setShowAddress, + setAddress, + setIsAddressValid, + bg, + isAddressValid, + selectedTabIndex, + isConnected, + appUrl, + isIFrameLoading, + updateAddress, +}: AddressInputParams) { + return ( + + Enter Address or ENS to Impersonate + + { + const _showAddress = e.target.value; + setShowAddress(_showAddress); + setAddress(_showAddress); + setIsAddressValid(true); // remove inValid warning when user types again + }} + bg={bg} + isInvalid={!isAddressValid} + /> + {((selectedTabIndex === 0 && isConnected) || + (selectedTabIndex === 1 && appUrl && !isIFrameLoading)) && ( + + + + )} + + + ); +} + +export default AddressInput; diff --git a/src/components/Body/BrowserExtensionTab.tsx b/src/components/Body/BrowserExtensionTab.tsx new file mode 100644 index 0000000..dd605be --- /dev/null +++ b/src/components/Body/BrowserExtensionTab.tsx @@ -0,0 +1,43 @@ +import { + Center, + Box, + Text, + chakra, + HStack, + Link, + Image, +} from "@chakra-ui/react"; + +function BrowserExtensionTab() { + return ( +
+ + + ⭐ Download the browser extension from:{" "} + + Chrome Web Store + + + + + Read more: + + Launch Tweet + + + +
+ ); +} + +export default BrowserExtensionTab; diff --git a/src/components/Body/CopyToClipboard.tsx b/src/components/Body/CopyToClipboard.tsx new file mode 100644 index 0000000..825931c --- /dev/null +++ b/src/components/Body/CopyToClipboard.tsx @@ -0,0 +1,15 @@ +import { Button } from "@chakra-ui/react"; +import { CopyIcon } from "@chakra-ui/icons"; + +const CopyToClipboard = ({ txt }: { txt: string }) => ( + +); + +export default CopyToClipboard; diff --git a/src/components/Body/IFrameConnectTab/AppUrlLabel.tsx b/src/components/Body/IFrameConnectTab/AppUrlLabel.tsx new file mode 100644 index 0000000..0013b1a --- /dev/null +++ b/src/components/Body/IFrameConnectTab/AppUrlLabel.tsx @@ -0,0 +1,29 @@ +import { FormLabel, Tooltip, Text, Box } from "@chakra-ui/react"; +import { InfoIcon } from "@chakra-ui/icons"; + +function AppUrlLabel() { + return ( + <> + dapp URL + + Paste the URL of dapp you want to connect to + + Note: Some dapps might not support it, so use WalletConnect in + that case + + + } + hasArrow + placement="top" + > + + + + + + ); +} + +export default AppUrlLabel; diff --git a/src/components/Body/IFrameConnectTab/SupportedDapps/DappTile.tsx b/src/components/Body/IFrameConnectTab/SupportedDapps/DappTile.tsx new file mode 100644 index 0000000..c4b8b0e --- /dev/null +++ b/src/components/Body/IFrameConnectTab/SupportedDapps/DappTile.tsx @@ -0,0 +1,45 @@ +import { GridItem, Center, Image, Text } from "@chakra-ui/react"; +import { SafeDappInfo } from "../../../../types"; + +interface DappTileParams { + initIFrame: (_inputAppUrl?: string | undefined) => Promise; + setInputAppUrl: (value: string | undefined) => void; + closeSafeApps: () => void; + dapp: SafeDappInfo; +} + +function DappTile({ + initIFrame, + setInputAppUrl, + closeSafeApps, + dapp, +}: DappTileParams) { + return ( + { + initIFrame(dapp.url); + setInputAppUrl(dapp.url); + closeSafeApps(); + }} + > +
+ + + {dapp.name} + +
+
+ ); +} + +export default DappTile; diff --git a/src/components/Body/IFrameConnectTab/SupportedDapps/DappsSearch.tsx b/src/components/Body/IFrameConnectTab/SupportedDapps/DappsSearch.tsx new file mode 100644 index 0000000..6333489 --- /dev/null +++ b/src/components/Body/IFrameConnectTab/SupportedDapps/DappsSearch.tsx @@ -0,0 +1,40 @@ +import { + Center, + InputGroup, + Input, + InputRightElement, + Button, +} from "@chakra-ui/react"; +import { CloseIcon } from "@chakra-ui/icons"; + +interface DappsSearchParams { + searchSafeDapp: string | undefined; + setSearchSafeDapp: (value: string) => void; +} + +function DappsSearch({ searchSafeDapp, setSearchSafeDapp }: DappsSearchParams) { + return ( +
+ + setSearchSafeDapp(e.target.value)} + /> + {searchSafeDapp && ( + + + + )} + +
+ ); +} + +export default DappsSearch; diff --git a/src/components/Body/IFrameConnectTab/SupportedDapps/index.tsx b/src/components/Body/IFrameConnectTab/SupportedDapps/index.tsx new file mode 100644 index 0000000..f23f555 --- /dev/null +++ b/src/components/Body/IFrameConnectTab/SupportedDapps/index.tsx @@ -0,0 +1,146 @@ +import { useState, useEffect } from "react"; +import { + Box, + Button, + Center, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalOverlay, + SimpleGrid, + Spinner, + useDisclosure, +} from "@chakra-ui/react"; +import axios from "axios"; +import DappsSearch from "./DappsSearch"; +import DappTile from "./DappTile"; +import { SafeDappInfo } from "../../../../types"; + +interface SupportedDappsParams { + networkId: number; + initIFrame: (_inputAppUrl?: string | undefined) => Promise; + setInputAppUrl: (value: string | undefined) => void; +} + +function SupportedDapps({ + networkId, + initIFrame, + setInputAppUrl, +}: SupportedDappsParams) { + const { + isOpen: isSafeAppsOpen, + onOpen: openSafeAapps, + onClose: closeSafeApps, + } = useDisclosure(); + + const [safeDapps, setSafeDapps] = useState<{ + [networkId: number]: SafeDappInfo[]; + }>({}); + const [searchSafeDapp, setSearchSafeDapp] = useState(); + const [filteredSafeDapps, setFilteredSafeDapps] = useState(); + + useEffect(() => { + const fetchSafeDapps = async (networkId: number) => { + const response = await axios.get( + `https://safe-client.gnosis.io/v1/chains/${networkId}/safe-apps` + ); + setSafeDapps((dapps) => ({ + ...dapps, + [networkId]: response.data.filter((d) => ![29, 11].includes(d.id)), // Filter out Transaction Builder and WalletConnect + })); + }; + + if (isSafeAppsOpen && !safeDapps[networkId]) { + fetchSafeDapps(networkId); + } + }, [isSafeAppsOpen, safeDapps, networkId]); + + useEffect(() => { + if (safeDapps[networkId]) { + setFilteredSafeDapps( + safeDapps[networkId].filter((dapp) => { + if (!searchSafeDapp) return true; + + return ( + dapp.name + .toLowerCase() + .indexOf(searchSafeDapp.toLocaleLowerCase()) !== -1 || + dapp.url + .toLowerCase() + .indexOf(searchSafeDapp.toLocaleLowerCase()) !== -1 + ); + }) + ); + } else { + setFilteredSafeDapps(undefined); + } + }, [safeDapps, networkId, searchSafeDapp]); + + return ( + <> + + + + + + + + Select a dapp + + + {(!safeDapps || !safeDapps[networkId]) && ( +
+ +
+ )} + + {safeDapps && safeDapps[networkId] && ( + + )} + + + {filteredSafeDapps && + filteredSafeDapps.map((dapp, i) => ( + + ))} + + + +
+
+
+ + ); +} + +export default SupportedDapps; diff --git a/src/components/Body/IFrameConnectTab/index.tsx b/src/components/Body/IFrameConnectTab/index.tsx new file mode 100644 index 0000000..af16599 --- /dev/null +++ b/src/components/Body/IFrameConnectTab/index.tsx @@ -0,0 +1,96 @@ +import { + Button, + Box, + Center, + Spacer, + HStack, + FormControl, + Input, +} from "@chakra-ui/react"; +import SupportedDapps from "./SupportedDapps"; +import AppUrlLabel from "./AppUrlLabel"; + +interface IFrameConnectTabParams { + networkId: number; + initIFrame: (_inputAppUrl?: string | undefined) => Promise; + inputAppUrl: string | undefined; + setInputAppUrl: (value: string | undefined) => void; + appUrl: string | undefined; + bg: string; + isIFrameLoading: boolean; + setIsIFrameLoading: (value: boolean) => void; + iframeKey: number; + iframeRef: React.RefObject | null; +} + +function IFrameConnectTab({ + networkId, + initIFrame, + setInputAppUrl, + inputAppUrl, + bg, + isIFrameLoading, + appUrl, + iframeKey, + iframeRef, + setIsIFrameLoading, +}: IFrameConnectTabParams) { + return ( + <> + + + + + + + setInputAppUrl(e.target.value)} + bg={bg} + /> + +
+ +
+
+ {appUrl && ( + setIsIFrameLoading(false)} + /> + )} +
+ + ); +} + +export default IFrameConnectTab; diff --git a/src/components/Body/NetworkInput.tsx b/src/components/Body/NetworkInput.tsx new file mode 100644 index 0000000..cf7d122 --- /dev/null +++ b/src/components/Body/NetworkInput.tsx @@ -0,0 +1,62 @@ +import { Box } from "@chakra-ui/react"; +import { Select as RSelect, SingleValue } from "chakra-react-select"; +import { SelectedNetworkOption } from "../../types"; + +interface NetworkOption { + name: string; + rpcs: string[]; + chainId: number; +} + +interface NetworkInputParams { + primaryNetworkOptions: NetworkOption[]; + secondaryNetworkOptions: NetworkOption[]; + selectedNetworkOption: SingleValue; + setSelectedNetworkOption: (value: SingleValue) => void; +} + +function NetworkInput({ + primaryNetworkOptions, + secondaryNetworkOptions, + selectedNetworkOption, + setSelectedNetworkOption, +}: NetworkInputParams) { + return ( + + ({ + label: network.name, + value: network.chainId, + })), + }, + { + label: "", + options: secondaryNetworkOptions.map((network) => ({ + label: network.name, + value: network.chainId, + })), + }, + ]} + value={selectedNetworkOption} + onChange={setSelectedNetworkOption} + placeholder="Select chain..." + size="md" + tagVariant="solid" + chakraStyles={{ + groupHeading: (provided, state) => ({ + ...provided, + h: "1px", + borderTop: "1px solid white", + }), + }} + closeMenuOnSelect + useBasicStyles + /> + + ); +} + +export default NetworkInput; diff --git a/src/components/Body/TabsSelect.tsx b/src/components/Body/TabsSelect.tsx new file mode 100644 index 0000000..852f33d --- /dev/null +++ b/src/components/Body/TabsSelect.tsx @@ -0,0 +1,41 @@ +import { Center, HStack } from "@chakra-ui/react"; +import Tab from "./Tab"; + +const tabs = ["WalletConnect", "iFrame", "Extension"]; + +interface TabsSelectParams { + selectedTabIndex: number; + setSelectedTabIndex: (value: number) => void; +} + +function TabsSelect({ + selectedTabIndex, + setSelectedTabIndex, +}: TabsSelectParams) { + return ( +
+ + {tabs.map((t, i) => ( + + {t} + + ))} + +
+ ); +} + +export default TabsSelect; diff --git a/src/components/Body/TenderlySettings.tsx b/src/components/Body/TenderlySettings.tsx new file mode 100644 index 0000000..683a951 --- /dev/null +++ b/src/components/Body/TenderlySettings.tsx @@ -0,0 +1,84 @@ +import { + Input, + Button, + Box, + Text, + Popover, + PopoverTrigger, + PopoverContent, + Tooltip, + HStack, + chakra, + ListItem, + List, + useDisclosure, +} from "@chakra-ui/react"; +import { SettingsIcon, InfoIcon } from "@chakra-ui/icons"; + +interface TenderlySettingsParams { + tenderlyForkId: string; + setTenderlyForkId: (value: string) => void; +} + +function TenderlySettings({ + tenderlyForkId, + setTenderlyForkId, +}: TenderlySettingsParams) { + const { onOpen, onClose, isOpen } = useDisclosure(); + + return ( + + + + + + + + + + (optional) Tenderly Fork Id: + + Simulate sending transactions on forked node. + + + + Create a fork on Tenderly and grab it's id from the URL. + + + + } + hasArrow + placement="top" + > + + + + { + setTenderlyForkId(e.target.value); + }} + /> + + + + ); +} + +export default TenderlySettings; diff --git a/src/components/Body/TransactionRequests.tsx b/src/components/Body/TransactionRequests.tsx new file mode 100644 index 0000000..cc0990d --- /dev/null +++ b/src/components/Body/TransactionRequests.tsx @@ -0,0 +1,123 @@ +import { + Box, + Flex, + HStack, + Text, + Heading, + Tooltip, + Td, + Collapse, + useDisclosure, + Button, + Table, + Thead, + Tr, + Th, + Tbody, +} from "@chakra-ui/react"; +import { + InfoIcon, + ChevronDownIcon, + ChevronUpIcon, + DeleteIcon, +} from "@chakra-ui/icons"; +import CopyToClipboard from "./CopyToClipboard"; +import { TxnDataType } from "../../types"; + +const slicedText = (txt: string) => { + return txt.length > 6 + ? `${txt.slice(0, 4)}...${txt.slice(txt.length - 2, txt.length)}` + : txt; +}; + +const TD = ({ txt }: { txt: string }) => ( + + + + {slicedText(txt)} + + + + +); + +interface TransactionRequestsParams { + sendTxnData: TxnDataType[]; + setSendTxnData: (value: TxnDataType[]) => void; +} + +function TransactionRequests({ + sendTxnData, + setSendTxnData, +}: TransactionRequestsParams) { + const { isOpen: tableIsOpen, onToggle: tableOnToggle } = useDisclosure(); + + return ( + + + + + {tableIsOpen ? : } + + eth_sendTransactions + + + "eth_sendTransaction" requests by the dApp are shown here + (latest on top) + + + } + hasArrow + placement="top" + > + + + + + + + {sendTxnData.length > 0 && ( + + )} + + + + + + + + + + + + + {sendTxnData.map((d) => ( + + + ))} + +
fromtodatavalue
+ + + +
+
+
+ ); +} + +export default TransactionRequests; diff --git a/src/components/Body/WalletConnectTab/ConnectionDetails.tsx b/src/components/Body/WalletConnectTab/ConnectionDetails.tsx new file mode 100644 index 0000000..ecc3841 --- /dev/null +++ b/src/components/Body/WalletConnectTab/ConnectionDetails.tsx @@ -0,0 +1,38 @@ +import { Box, Text, Button, VStack, Avatar, Link } from "@chakra-ui/react"; +import { SessionTypes } from "@walletconnect/types"; + +interface ConnectionDetailsParams { + web3WalletSession: SessionTypes.Struct; + killSession: () => void; +} + +function ConnectionDetails({ + web3WalletSession, + killSession, +}: ConnectionDetailsParams) { + return ( + <> + + ✅ Connected To: + + + + {web3WalletSession.peer?.metadata?.name} + + {web3WalletSession.peer?.metadata?.description} + + + {web3WalletSession.peer?.metadata?.url} + + + + + + + ); +} + +export default ConnectionDetails; diff --git a/src/components/Body/WalletConnectTab/LegacyConnectionDetails.tsx b/src/components/Body/WalletConnectTab/LegacyConnectionDetails.tsx new file mode 100644 index 0000000..6a39651 --- /dev/null +++ b/src/components/Body/WalletConnectTab/LegacyConnectionDetails.tsx @@ -0,0 +1,33 @@ +import { Box, Text, Button, VStack, Avatar, Link } from "@chakra-ui/react"; +import { IClientMeta } from "@walletconnect/legacy-types"; + +interface LegacyConnectionDetailsParams { + legacyPeerMeta: IClientMeta; + killSession: () => void; +} + +function LegacyConnectionDetails({ + legacyPeerMeta, + killSession, +}: LegacyConnectionDetailsParams) { + return ( + <> + + ✅ Connected To: + + + + {legacyPeerMeta.name} + {legacyPeerMeta.description} + + {legacyPeerMeta.url} + + + + + + + ); +} + +export default LegacyConnectionDetails; diff --git a/src/components/Body/WalletConnectTab/Loading.tsx b/src/components/Body/WalletConnectTab/Loading.tsx new file mode 100644 index 0000000..7a665f1 --- /dev/null +++ b/src/components/Body/WalletConnectTab/Loading.tsx @@ -0,0 +1,39 @@ +import { + Box, + Center, + Button, + VStack, + CircularProgress, +} from "@chakra-ui/react"; + +interface LoadingParams { + isConnected: boolean; + setLoading: (value: boolean) => void; + reset: (persistUri?: boolean) => void; +} + +function Loading({ isConnected, setLoading, reset }: LoadingParams) { + return ( +
+ + + + + {!isConnected && ( + + + + )} + +
+ ); +} + +export default Loading; diff --git a/src/components/Body/WalletConnectTab/URIInput.tsx b/src/components/Body/WalletConnectTab/URIInput.tsx new file mode 100644 index 0000000..8c9eb3a --- /dev/null +++ b/src/components/Body/WalletConnectTab/URIInput.tsx @@ -0,0 +1,57 @@ +import { + FormControl, + HStack, + FormLabel, + Tooltip, + Box, + Text, + Input, +} from "@chakra-ui/react"; +import { InfoIcon } from "@chakra-ui/icons"; + +interface URIInputParams { + uri: string; + setUri: (value: string) => void; + bg: string; + isConnected: boolean; +} + +function URIInput({ uri, setUri, bg, isConnected }: URIInputParams) { + return ( + + + WalletConnect URI + + Visit any dApp and select WalletConnect. + + Click "Copy to Clipboard" beneath the QR code, and paste it + here. + + + } + hasArrow + placement="top" + > + + + + + + + setUri(e.target.value)} + bg={bg} + isDisabled={isConnected} + /> + + + ); +} + +export default URIInput; diff --git a/src/components/Body/WalletConnectTab/index.tsx b/src/components/Body/WalletConnectTab/index.tsx new file mode 100644 index 0000000..2c34886 --- /dev/null +++ b/src/components/Body/WalletConnectTab/index.tsx @@ -0,0 +1,67 @@ +import { Center, Button } from "@chakra-ui/react"; +import { IClientMeta } from "@walletconnect/legacy-types"; +import { SessionTypes } from "@walletconnect/types"; +import ConnectionDetails from "./ConnectionDetails"; +import LegacyConnectionDetails from "./LegacyConnectionDetails"; +import Loading from "./Loading"; +import URIInput from "./URIInput"; + +interface WalletConnectTabParams { + uri: string; + setUri: (value: string) => void; + bg: string; + isConnected: boolean; + initWalletConnect: () => void; + loading: boolean; + setLoading: (value: boolean) => void; + reset: (persistUri?: boolean) => void; + killSession: () => void; + legacyPeerMeta: IClientMeta | undefined; + web3WalletSession: SessionTypes.Struct | undefined; +} + +function WalletConnectTab({ + uri, + setUri, + bg, + isConnected, + initWalletConnect, + loading, + setLoading, + reset, + legacyPeerMeta, + killSession, + web3WalletSession, +}: WalletConnectTabParams) { + return ( + <> + +
+ +
+ {loading && ( + + )} + {legacyPeerMeta && isConnected && ( + + )} + {web3WalletSession && isConnected && ( + + )} + + ); +} + +export default WalletConnectTab; diff --git a/src/components/Body/index.tsx b/src/components/Body/index.tsx index d6e34d0..1c400cf 100644 --- a/src/components/Body/index.tsx +++ b/src/components/Body/index.tsx @@ -1,61 +1,14 @@ import { useState, useEffect } from "react"; import { Container, - InputGroup, - Input, - InputRightElement, - FormControl, useColorMode, - FormLabel, - Button, - Box, - Avatar, - Text, - Link, - VStack, useToast, - CircularProgress, Center, Spacer, Flex, - useDisclosure, - Popover, - PopoverTrigger, - PopoverContent, - Tooltip, - HStack, - chakra, - ListItem, - Table, - Thead, - Tbody, - Tr, - Th, - Td, - Heading, - Collapse, - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - SimpleGrid, - GridItem, - Image, - Spinner, - List, } from "@chakra-ui/react"; -import { - SettingsIcon, - InfoIcon, - ChevronDownIcon, - ChevronUpIcon, - CopyIcon, - DeleteIcon, - CloseIcon, -} from "@chakra-ui/icons"; -import { Select as RSelect, SingleValue } from "chakra-react-select"; + +import { SingleValue } from "chakra-react-select"; // WC v1 import LegacySignClient from "@walletconnect/client"; import { IClientMeta } from "@walletconnect/legacy-types"; @@ -68,19 +21,15 @@ import { ethers } from "ethers"; import axios from "axios"; import networksList from "evm-rpcs-list"; import { useSafeInject } from "../../contexts/SafeInjectContext"; -import Tab from "./Tab"; - -interface SafeDappInfo { - id: number; - url: string; - name: string; - iconUrl: string; -} - -interface SelectedNetworkOption { - label: string; - value: number; -} +import TenderlySettings from "./TenderlySettings"; +import AddressInput from "./AddressInput"; +import { SelectedNetworkOption, TxnDataType } from "../../types"; +import NetworkInput from "./NetworkInput"; +import TabsSelect from "./TabsSelect"; +import WalletConnectTab from "./WalletConnectTab"; +import IFrameConnectTab from "./IFrameConnectTab"; +import BrowserExtensionTab from "./BrowserExtensionTab"; +import TransactionRequests from "./TransactionRequests"; const WCMetadata = { name: "Impersonator", @@ -122,34 +71,6 @@ const allNetworksOptions = [ ...secondaryNetworkOptions, ]; -const slicedText = (txt: string) => { - return txt.length > 6 - ? `${txt.slice(0, 4)}...${txt.slice(txt.length - 2, txt.length)}` - : txt; -}; - -const CopyToClipboard = ({ txt }: { txt: string }) => ( - -); - -const TD = ({ txt }: { txt: string }) => ( - - - - {slicedText(txt)} - - - - -); - function Body() { const { colorMode } = useColorMode(); const bgColor = { light: "white", dark: "gray.700" }; @@ -172,13 +93,6 @@ function Body() { } } const toast = useToast(); - const { onOpen, onClose, isOpen } = useDisclosure(); - const { isOpen: tableIsOpen, onToggle: tableOnToggle } = useDisclosure(); - const { - isOpen: isSafeAppsOpen, - onOpen: openSafeAapps, - onClose: closeSafeApps, - } = useDisclosure(); const { setAddress: setIFrameAddress, @@ -211,29 +125,16 @@ function Body() { const [isConnected, setIsConnected] = useState(false); const [loading, setLoading] = useState(false); - const tabs = ["WalletConnect", "iFrame", "Extension"]; const [selectedTabIndex, setSelectedTabIndex] = useState(urlFromURL ? 1 : 0); const [isIFrameLoading, setIsIFrameLoading] = useState(false); - const [safeDapps, setSafeDapps] = useState<{ - [networkId: number]: SafeDappInfo[]; - }>({}); - const [searchSafeDapp, setSearchSafeDapp] = useState(); - const [filteredSafeDapps, setFilteredSafeDapps] = useState(); + const [inputAppUrl, setInputAppUrl] = useState( urlFromURL ?? undefined ); const [iframeKey, setIframeKey] = useState(0); // hacky way to reload iframe when key changes const [tenderlyForkId, setTenderlyForkId] = useState(""); - const [sendTxnData, setSendTxnData] = useState< - { - id: number; - from: string; - to: string; - data: string; - value: string; - }[] - >([]); + const [sendTxnData, setSendTxnData] = useState([]); useEffect(() => { // WC V1 @@ -352,43 +253,6 @@ function Body() { // eslint-disable-next-line }, [latestTransaction, tenderlyForkId]); - useEffect(() => { - const fetchSafeDapps = async (networkId: number) => { - const response = await axios.get( - `https://safe-client.gnosis.io/v1/chains/${networkId}/safe-apps` - ); - setSafeDapps((dapps) => ({ - ...dapps, - [networkId]: response.data.filter((d) => ![29, 11].includes(d.id)), // Filter out Transaction Builder and WalletConnect - })); - }; - - if (isSafeAppsOpen && !safeDapps[networkId]) { - fetchSafeDapps(networkId); - } - }, [isSafeAppsOpen, safeDapps, networkId]); - - useEffect(() => { - if (safeDapps[networkId]) { - setFilteredSafeDapps( - safeDapps[networkId].filter((dapp) => { - if (!searchSafeDapp) return true; - - return ( - dapp.name - .toLowerCase() - .indexOf(searchSafeDapp.toLocaleLowerCase()) !== -1 || - dapp.url - .toLowerCase() - .indexOf(searchSafeDapp.toLocaleLowerCase()) !== -1 - ); - }) - ); - } else { - setFilteredSafeDapps(undefined); - } - }, [safeDapps, networkId, searchSafeDapp]); - const initWeb3Wallet = async ( onlyIfActiveSessions?: boolean, _showAddress?: string @@ -866,539 +730,76 @@ function Body() { - - - - - - - - - - (optional) Tenderly Fork Id: - - Simulate sending transactions on forked node. - - - - Create a fork on Tenderly and grab it's id from the - URL. - - - - } - hasArrow - placement="top" - > - - - - { - setTenderlyForkId(e.target.value); - }} - /> - - - - - - Enter Address or ENS to Impersonate - - { - const _showAddress = e.target.value; - setShowAddress(_showAddress); - setAddress(_showAddress); - setIsAddressValid(true); // remove inValid warning when user types again - }} - bg={bgColor[colorMode]} - isInvalid={!isAddressValid} - /> - {((selectedTabIndex === 0 && isConnected) || - (selectedTabIndex === 1 && appUrl && !isIFrameLoading)) && ( - - - - )} - - - - ({ - label: network.name, - value: network.chainId, - })), - }, - { - label: "", - options: secondaryNetworkOptions.map((network) => ({ - label: network.name, - value: network.chainId, - })), - }, - ]} - value={selectedNetworkOption} - onChange={setSelectedNetworkOption} - placeholder="Select chain..." - size="md" - tagVariant="solid" - chakraStyles={{ - groupHeading: (provided, state) => ({ - ...provided, - h: "1px", - borderTop: "1px solid white", - }), - }} - closeMenuOnSelect - useBasicStyles + - -
- - {tabs.map((t, i) => ( - - {t} - - ))} - -
+ + + + {(() => { switch (selectedTabIndex) { case 0: return ( - <> - - - WalletConnect URI - - Visit any dApp and select WalletConnect. - - Click "Copy to Clipboard" beneath the QR code, and - paste it here. - - - } - hasArrow - placement="top" - > - - - - - - - setUri(e.target.value)} - bg={bgColor[colorMode]} - isDisabled={isConnected} - /> - - -
- -
- {loading && ( -
- - - - - {!isConnected && ( - - - - )} - -
- )} - {legacyPeerMeta && isConnected && ( - <> - - ✅ Connected To: - - - - {legacyPeerMeta.name} - {legacyPeerMeta.description} - - {legacyPeerMeta.url} - - - - - - - )} - {web3WalletSession && isConnected && ( - <> - - ✅ Connected To: - - - - - {web3WalletSession.peer?.metadata?.name} - - - {web3WalletSession.peer?.metadata?.description} - - - {web3WalletSession.peer?.metadata?.url} - - - - - - - )} - + ); case 1: return ( - <> - - - dapp URL - - - Paste the URL of dapp you want to connect to - - - Note: Some dapps might not support it, so use - WalletConnect in that case - - - } - hasArrow - placement="top" - > - - - - - - - - - - - - Select a dapp - - - {(!safeDapps || !safeDapps[networkId]) && ( -
- -
- )} - - {safeDapps && safeDapps[networkId] && ( -
- - - setSearchSafeDapp(e.target.value) - } - /> - {searchSafeDapp && ( - - - - )} - -
- )} - - - {filteredSafeDapps && - filteredSafeDapps.map((dapp, i) => ( - { - initIFrame(dapp.url); - setInputAppUrl(dapp.url); - closeSafeApps(); - }} - > -
- - - {dapp.name} - -
-
- ))} -
-
-
-
-
-
-
- setInputAppUrl(e.target.value)} - bg={bgColor[colorMode]} - /> -
-
- -
-
- {appUrl && ( - setIsIFrameLoading(false)} - /> - )} -
- + ); case 2: - return ( -
- - - ⭐ Download the browser extension from:{" "} - - Chrome Web Store - - - - - Read more: - - Launch Tweet - - - -
- ); + return ; } })()}
- - - - - {tableIsOpen ? : } - - eth_sendTransactions - - - "eth_sendTransaction" requests by the dApp are shown here - (latest on top) - - - } - hasArrow - placement="top" - > - - - - - - - {sendTxnData.length > 0 && ( - - )} - - - - - - - - - - - - - {sendTxnData.map((d) => ( - - - ))} - -
fromtodatavalue
- - - -
-
-
+
); diff --git a/src/types.ts b/src/types.ts index f61ddf1..6a8e2a5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,27 @@ import { BigNumberish, BytesLike } from "ethers"; +export interface SelectedNetworkOption { + label: string; + value: number; +} + +export interface SafeDappInfo { + id: number; + url: string; + name: string; + iconUrl: string; +} + +export interface TxnDataType { + id: number; + from: string; + to: string; + data: string; + value: string; +} + +// ======= iFrame Provider ====== + export declare const INTERFACE_MESSAGES: { readonly ENV_INFO: "ENV_INFO"; readonly ON_SAFE_INFO: "ON_SAFE_INFO";