diff --git a/dapps/react-dapp-v2/src/components/OriginSimulationDropdown.tsx b/dapps/react-dapp-v2/src/components/OriginSimulationDropdown.tsx new file mode 100644 index 0000000..7fab261 --- /dev/null +++ b/dapps/react-dapp-v2/src/components/OriginSimulationDropdown.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { ORIGIN_OPTIONS } from "../constants/default"; +import styled from "styled-components"; + +interface OriginSimulationProps { + origin: string; + show: boolean; +} + +const SelectContainer = styled.select` + width: 150px; + background: transparent; + color: black; + height: 30px; + border-radius: 4px; + padding: 2px; + font-size: "1.25em"; + bottom: 40px; + left: 50px; + direction: ltr; + unicode-bidi: embed; + margin: 5px; +`; + +const SelectOption = styled.option` + font-size: "1.25em"; +`; + +const OriginSimulationDropdown = (props: OriginSimulationProps) => { + const { origin, show } = props; + const setOrigin = React.useCallback((origin: string) => { + localStorage.setItem("wallet_connect_dapp_origin", origin); + location.reload(); + }, []); + return ( +
+ {show && ( + setOrigin(e?.target?.value)} + > + + {ORIGIN_OPTIONS.map((e, i) => { + return ( + + {e.label} + + ); + })} + + )} +
+ ); +}; + +export default OriginSimulationDropdown; diff --git a/dapps/react-dapp-v2/src/components/Dropdown.tsx b/dapps/react-dapp-v2/src/components/RelayRegionDropdown.tsx similarity index 56% rename from dapps/react-dapp-v2/src/components/Dropdown.tsx rename to dapps/react-dapp-v2/src/components/RelayRegionDropdown.tsx index 3d64767..67afc6c 100644 --- a/dapps/react-dapp-v2/src/components/Dropdown.tsx +++ b/dapps/react-dapp-v2/src/components/RelayRegionDropdown.tsx @@ -4,9 +4,10 @@ import styled from "styled-components"; import Icon from "./Icon"; import { useState } from "react"; -interface DropdownProps { +interface RelayRegionDropdownProps { relayerRegion: string; setRelayerRegion?: (relayer: string) => void; + show: boolean; } const SelectContainer = styled.select` @@ -17,47 +18,27 @@ const SelectContainer = styled.select` border-radius: 4px; padding: 2px; font-size: "1.25em"; - position: absolute; bottom: 40px; left: 50px; direction: ltr; unicode-bidi: embed; + margin: 5px; `; const SelectOption = styled.option` font-size: "1.25em"; `; -const Dropdown = (props: DropdownProps) => { - const { relayerRegion, setRelayerRegion } = props; - const [openSelect, setOpenSelect] = useState(false); - const selectRef = React.createRef(); - - const openDropdown = () => { - setOpenSelect(!openSelect); - }; - +const RelayRegionDropdown = (props: RelayRegionDropdownProps) => { + const { relayerRegion, setRelayerRegion, show } = props; return ( -
- - {openSelect && ( +
+ {show && ( setRelayerRegion?.(e?.target?.value)} > - + {REGIONALIZED_RELAYER_ENDPOINTS.map((e, i) => { return ( @@ -71,4 +52,4 @@ const Dropdown = (props: DropdownProps) => { ); }; -export default Dropdown; +export default RelayRegionDropdown; diff --git a/dapps/react-dapp-v2/src/components/app/index.tsx b/dapps/react-dapp-v2/src/components/app/index.tsx index 119cfd5..eda24c0 100644 --- a/dapps/react-dapp-v2/src/components/app/index.tsx +++ b/dapps/react-dapp-v2/src/components/app/index.tsx @@ -53,6 +53,13 @@ export const SToggleContainer = styled.div` } `; +export const SDropDownContainer = styled.div` + padding-top: 20px; + display: flex; + justify-content: center; + align-items: flex-end; +`; + export const SFullWidthContainer = styled.div` width: 100%; display: flex; diff --git a/dapps/react-dapp-v2/src/constants/default.ts b/dapps/react-dapp-v2/src/constants/default.ts index 83528b6..6ac925a 100644 --- a/dapps/react-dapp-v2/src/constants/default.ts +++ b/dapps/react-dapp-v2/src/constants/default.ts @@ -1,3 +1,5 @@ +import { getAppMetadata } from "@walletconnect/utils"; + if (!process.env.NEXT_PUBLIC_PROJECT_ID) throw new Error("`NEXT_PUBLIC_PROJECT_ID` env variable is missing."); @@ -48,6 +50,7 @@ export const DEFAULT_APP_METADATA = { description: "React App for WalletConnect", url: "https://walletconnect.com/", icons: ["https://avatars.githubusercontent.com/u/37784886"], + verifyUrl: "https://verify.walletconnect.com", }; /** @@ -185,3 +188,18 @@ export const REGIONALIZED_RELAYER_ENDPOINTS: RelayerType[] = [ label: "Asia Pacific", }, ]; + +export const ORIGIN_OPTIONS = [ + { + value: getAppMetadata().url, + label: "VALID", + }, + { + value: "https://invalid.origin", + label: "INVALID", + }, + { + value: "unknown", + label: "UNKNOWN", + }, +]; diff --git a/dapps/react-dapp-v2/src/contexts/ClientContext.tsx b/dapps/react-dapp-v2/src/contexts/ClientContext.tsx index 61b8025..fc56383 100644 --- a/dapps/react-dapp-v2/src/contexts/ClientContext.tsx +++ b/dapps/react-dapp-v2/src/contexts/ClientContext.tsx @@ -46,6 +46,7 @@ interface IContext { isFetchingBalances: boolean; setChains: any; setRelayerRegion: any; + origin: string; } /** @@ -86,7 +87,7 @@ export function ClientContextProvider({ const [relayerRegion, setRelayerRegion] = useState( DEFAULT_RELAY_URL! ); - + const [origin, setOrigin] = useState(getAppMetadata().url); const reset = () => { setSession(undefined); setBalances({}); @@ -284,15 +285,24 @@ export function ClientContextProvider({ const createClient = useCallback(async () => { try { setIsInitializing(true); - + const claimedOrigin = + localStorage.getItem("wallet_connect_dapp_origin") || origin; const _client = await Client.init({ logger: DEFAULT_LOGGER, relayUrl: relayerRegion, projectId: DEFAULT_PROJECT_ID, - metadata: getAppMetadata() || DEFAULT_APP_METADATA, + metadata: { + ...(getAppMetadata() || DEFAULT_APP_METADATA), + url: claimedOrigin, + verifyUrl: + claimedOrigin === "unknown" + ? "http://non-existent-url" + : DEFAULT_APP_METADATA.verifyUrl, // simulates `UNKNOWN` verify context + }, }); setClient(_client); + setOrigin(_client.metadata.url); prevRelayerValue.current = relayerRegion; await _subscribeToEvents(_client); await _checkPersistedState(_client); @@ -302,7 +312,13 @@ export function ClientContextProvider({ } finally { setIsInitializing(false); } - }, [_checkPersistedState, _subscribeToEvents, _logClientId, relayerRegion]); + }, [ + _checkPersistedState, + _subscribeToEvents, + _logClientId, + relayerRegion, + origin, + ]); useEffect(() => { if (!client) { @@ -329,6 +345,7 @@ export function ClientContextProvider({ disconnect, setChains, setRelayerRegion, + origin, }), [ pairings, @@ -345,6 +362,7 @@ export function ClientContextProvider({ disconnect, setChains, setRelayerRegion, + origin, ] ); diff --git a/dapps/react-dapp-v2/src/pages/index.tsx b/dapps/react-dapp-v2/src/pages/index.tsx index cbfe88d..baf6f0c 100644 --- a/dapps/react-dapp-v2/src/pages/index.tsx +++ b/dapps/react-dapp-v2/src/pages/index.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useRef, useState } from "react"; import Banner from "../components/Banner"; import Blockchain from "../components/Blockchain"; import Column from "../components/Column"; -import Dropdown from "../components/Dropdown"; +import RelayRegionDropdown from "../components/RelayRegionDropdown"; import Header from "../components/Header"; import Modal from "../components/Modal"; import { @@ -32,6 +32,7 @@ import { SButtonContainer, SConnectButton, SContent, + SDropDownContainer, SLanding, SLayout, SToggleContainer, @@ -39,6 +40,8 @@ import { import { useWalletConnectClient } from "../contexts/ClientContext"; import { useJsonRpc } from "../contexts/JsonRpcContext"; import { useChainData } from "../contexts/ChainDataContext"; +import Icon from "../components/Icon"; +import OriginSimulationDropdown from "../components/OriginSimulationDropdown"; // Normal import does not work here const { version } = require("@walletconnect/sign-client/package.json"); @@ -66,6 +69,7 @@ const Home: NextPage = () => { isInitializing, setChains, setRelayerRegion, + origin, } = useWalletConnectClient(); // Use `JsonRpcContext` to provide us with relevant RPC methods and states. @@ -444,6 +448,12 @@ const Home: NextPage = () => { } }; + const [openSelect, setOpenSelect] = useState(false); + + const openDropdown = () => { + setOpenSelect(!openSelect); + }; + const renderContent = () => { const chainOptions = isTestnet ? DEFAULT_TEST_CHAINS : DEFAULT_MAIN_CHAINS; @@ -469,10 +479,17 @@ const Home: NextPage = () => { Connect - + + + + + ) : ( diff --git a/wallets/react-wallet-v2/src/components/ProjectInfoCard.tsx b/wallets/react-wallet-v2/src/components/ProjectInfoCard.tsx index c2f6b6f..47917b8 100644 --- a/wallets/react-wallet-v2/src/components/ProjectInfoCard.tsx +++ b/wallets/react-wallet-v2/src/components/ProjectInfoCard.tsx @@ -1,3 +1,8 @@ +import { useMemo } from 'react' +import { useSnapshot } from 'valtio' + +import SettingsStore from '@/store/SettingsStore' +import { getVerifyStatus } from '@/utils/HelperUtil' import { Avatar, Col, Link, Row, Text } from '@nextui-org/react' import { SignClientTypes } from '@walletconnect/types' @@ -12,17 +17,26 @@ interface IProps { * Components */ export default function ProjectInfoCard({ metadata }: IProps) { + const { currentRequestVerifyContext } = useSnapshot(SettingsStore.state) const { icons, name, url } = metadata - + const validation = useMemo( + () => getVerifyStatus(currentRequestVerifyContext), + [currentRequestVerifyContext] + ) return ( - - - - - - {name} - {url} - - + <> + + + + + + {name} + + {url} + {validation} + + + + ) } diff --git a/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts b/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts index 2d6cf0f..9b190e6 100644 --- a/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts +++ b/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts @@ -5,6 +5,8 @@ import { POLKADOT_SIGNING_METHODS } from '@/data/PolkadotData' import { MULTIVERSX_SIGNING_METHODS } from '@/data/MultiversxData' import { TRON_SIGNING_METHODS } from '@/data/TronData' import ModalStore from '@/store/ModalStore' +import SettingsStore from '@/store/SettingsStore' +import { useSnapshot } from 'valtio' import { signClient } from '@/utils/WalletConnectUtil' import { SignClientTypes } from '@walletconnect/types' import { useCallback, useEffect } from 'react' @@ -19,6 +21,8 @@ export default function useWalletConnectEventsManager(initialized: boolean) { *****************************************************************************/ const onSessionProposal = useCallback( (proposal: SignClientTypes.EventArguments['session_proposal']) => { + // set the verify context so it can be displayed in the projectInfoCard + SettingsStore.setCurrentRequestVerifyContext(proposal.verifyContext) ModalStore.open('SessionProposalModal', { proposal }) }, [] @@ -30,9 +34,11 @@ export default function useWalletConnectEventsManager(initialized: boolean) { const onSessionRequest = useCallback( async (requestEvent: SignClientTypes.EventArguments['session_request']) => { console.log('session_request', requestEvent) - const { topic, params } = requestEvent + const { topic, params, verifyContext } = requestEvent const { request } = params const requestSession = signClient.session.get(topic) + // set the verify context so it can be displayed in the projectInfoCard + SettingsStore.setCurrentRequestVerifyContext(verifyContext) switch (request.method) { case EIP155_SIGNING_METHODS.ETH_SIGN: diff --git a/wallets/react-wallet-v2/src/store/SettingsStore.ts b/wallets/react-wallet-v2/src/store/SettingsStore.ts index 628e2b5..5be3b35 100644 --- a/wallets/react-wallet-v2/src/store/SettingsStore.ts +++ b/wallets/react-wallet-v2/src/store/SettingsStore.ts @@ -1,3 +1,4 @@ +import { Verify } from '@walletconnect/types' import { proxy } from 'valtio' /** @@ -17,6 +18,7 @@ interface State { kadenaAddress: string relayerRegionURL: string activeChainId: string + currentRequestVerifyContext?: Verify.Context } /** @@ -89,6 +91,10 @@ const SettingsStore = { state.activeChainId = value }, + setCurrentRequestVerifyContext(context: Verify.Context) { + state.currentRequestVerifyContext = context + }, + toggleTestNets() { state.testNets = !state.testNets if (state.testNets) { diff --git a/wallets/react-wallet-v2/src/utils/HelperUtil.ts b/wallets/react-wallet-v2/src/utils/HelperUtil.ts index b7a007f..8498be5 100644 --- a/wallets/react-wallet-v2/src/utils/HelperUtil.ts +++ b/wallets/react-wallet-v2/src/utils/HelperUtil.ts @@ -9,6 +9,7 @@ import { TRON_CHAINS, TTronChain } from '@/data/TronData' import { KADENA_CHAINS, TKadenaChain } from '@/data/KadenaData' import { utils } from 'ethers' +import { Verify } from '@walletconnect/types' /** * Truncates string (in the middle) via given lenght value @@ -160,3 +161,15 @@ export function formatChainName(chainId: string) { chainId ) } + +export function getVerifyStatus(context?: Verify.Context) { + if (!context) return '' + switch (context.verified.validation) { + case 'VALID': + return '✅ Verified' + case 'INVALID': + return '❌ Origin does not match' + case 'UNKNOWN': + return '❓ Unknown' + } +}