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 && (
+
+ )}
+
+
+
+
+
+ | from |
+ to |
+ data |
+ value |
+
+
+
+ {sendTxnData.map((d) => (
+
+ |
+ |
+ |
+ |
+
+ ))}
+
+
+
+
+ );
+}
+
+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 && (
-
- )}
-
-
-
-
-
- | from |
- to |
- data |
- value |
-
-
-
- {sendTxnData.map((d) => (
-
- |
- |
- |
- |
-
- ))}
-
-
-
-
+
);
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";