diff --git a/wallets/react-wallet-v2/package.json b/wallets/react-wallet-v2/package.json index 796627c..2ed88a1 100644 --- a/wallets/react-wallet-v2/package.json +++ b/wallets/react-wallet-v2/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@walletconnect/types": "2.0.0-beta.22", - "@types/node": "17.0.17", + "@types/node": "17.0.18", "@types/react": "17.0.39", "eslint": "8.9.0", "eslint-config-next": "12.0.10", diff --git a/wallets/react-wallet-v2/public/main.css b/wallets/react-wallet-v2/public/main.css index d862414..530451c 100644 --- a/wallets/react-wallet-v2/public/main.css +++ b/wallets/react-wallet-v2/public/main.css @@ -49,4 +49,12 @@ .codeBlock span { background-color: transparent !important; overflow: scroll; +} + +.navLink { + transition: ease-in-out .2s opacity; +} + +.navLink:hover { + opacity: 0.6; } \ No newline at end of file diff --git a/wallets/react-wallet-v2/public/pairings-icon.svg b/wallets/react-wallet-v2/public/pairings-icon.svg new file mode 100644 index 0000000..05d38a8 --- /dev/null +++ b/wallets/react-wallet-v2/public/pairings-icon.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wallets/react-wallet-v2/public/sessions-icon.svg b/wallets/react-wallet-v2/public/sessions-icon.svg new file mode 100644 index 0000000..0b74731 --- /dev/null +++ b/wallets/react-wallet-v2/public/sessions-icon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/wallets/react-wallet-v2/src/components/AccountCard.tsx b/wallets/react-wallet-v2/src/components/AccountCard.tsx index fb6ff69..0350aff 100644 --- a/wallets/react-wallet-v2/src/components/AccountCard.tsx +++ b/wallets/react-wallet-v2/src/components/AccountCard.tsx @@ -1,6 +1,5 @@ import { truncate } from '@/utils/HelperUtil' import { Avatar, Card, Text } from '@nextui-org/react' -import Link from 'next/link' interface Props { name: string @@ -11,38 +10,35 @@ interface Props { export default function AccountCard({ name, logo, rgb, address }: Props) { return ( - - + - - -
- - {name} - - - {truncate(address, 19)} - -
-
-
- + +
+ + {name} + + + {truncate(address, 19)} + +
+ + ) } diff --git a/wallets/react-wallet-v2/src/components/Modal.tsx b/wallets/react-wallet-v2/src/components/Modal.tsx index 4f66a34..cf7ee66 100644 --- a/wallets/react-wallet-v2/src/components/Modal.tsx +++ b/wallets/react-wallet-v2/src/components/Modal.tsx @@ -1,5 +1,6 @@ import ModalStore from '@/store/ModalStore' import SessionProposalModal from '@/views/SessionProposalModal' +import SessionSendTransactionModal from '@/views/SessionSendTransactionModal' import SessionRequestModal from '@/views/SessionSignModal' import SessionSignTypedDataModal from '@/views/SessionSignTypedDataModal' import { Modal as NextModal } from '@nextui-org/react' @@ -13,6 +14,7 @@ export default function Modal() { {view === 'SessionProposalModal' && } {view === 'SessionSignModal' && } {view === 'SessionSignTypedDataModal' && } + {view === 'SessionSendTransactionModal' && } ) } diff --git a/wallets/react-wallet-v2/src/components/Navigation.tsx b/wallets/react-wallet-v2/src/components/Navigation.tsx index 1802690..344db92 100644 --- a/wallets/react-wallet-v2/src/components/Navigation.tsx +++ b/wallets/react-wallet-v2/src/components/Navigation.tsx @@ -4,15 +4,21 @@ import Link from 'next/link' export default function Navigation() { return ( - + - - accounts icon + + accounts icon + + + + + + sessions icon - + + + + pairings icon + + + - - settings icon + + settings icon diff --git a/wallets/react-wallet-v2/src/data/EIP155Data.ts b/wallets/react-wallet-v2/src/data/EIP155Data.ts index 7e7a730..58f55cf 100644 --- a/wallets/react-wallet-v2/src/data/EIP155Data.ts +++ b/wallets/react-wallet-v2/src/data/EIP155Data.ts @@ -21,52 +21,32 @@ export const EIP155_MAINNET_CHAINS = { chainId: 1, name: 'Ethereum', logo: LOGO_BASE_URL + 'eip155:1.png', - rgb: '99, 125, 234' - }, - 'eip155:10': { - chainId: 10, - name: 'Optimism', - logo: LOGO_BASE_URL + 'eip155:10.png', - rgb: '233, 1, 1' + rgb: '99, 125, 234', + rpc: 'https://cloudflare-eth.com/' }, 'eip155:137': { chainId: 137, name: 'Polygon', logo: LOGO_BASE_URL + 'eip155:137.png', - rgb: '130, 71, 229' + rgb: '130, 71, 229', + rpc: 'https://polygon-rpc.com/' }, 'eip155:42161': { chainId: 42161, name: 'Arbitrum', logo: LOGO_BASE_URL + 'eip155:42161.png', - rgb: '44, 55, 75' + rgb: '44, 55, 75', + rpc: 'https://arb1.arbitrum.io/rpc/' } } export const EIP155_TEST_CHAINS = { - 'eip155:4': { - chainId: 4, - name: 'Ethereum Rinkeby', - logo: LOGO_BASE_URL + 'eip155:1.png', - rgb: '99, 125, 234' - }, - 'eip155:69': { - chainId: 69, - name: 'Optimism Kovan', - logo: LOGO_BASE_URL + 'eip155:10.png', - rgb: '233, 1, 1' - }, 'eip155:80001': { chainId: 80001, name: 'Polygon Mumbai', logo: LOGO_BASE_URL + 'eip155:137.png', - rgb: '130, 71, 229' - }, - 'eip155:421611': { - chainId: 421611, - name: 'Arbitrum Rinkeby', - logo: LOGO_BASE_URL + 'eip155:42161.png', - rgb: '44, 55, 75' + rgb: '130, 71, 229', + rpc: 'https://rpc-mumbai.maticvigil.com/' } } diff --git a/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts b/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts index e1cd614..b182de4 100644 --- a/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts +++ b/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts @@ -6,26 +6,30 @@ import { SessionTypes } from '@walletconnect/types' import { useCallback, useEffect } from 'react' export default function useWalletConnectEventsManager(initialized: boolean) { - // 1. Open session proposal modal for confirmation / rejection + /****************************************************************************** + * 1. Open session proposal modal for confirmation / rejection + *****************************************************************************/ const onSessionProposal = useCallback((proposal: SessionTypes.Proposal) => { ModalStore.open('SessionProposalModal', { proposal }) }, []) - // 2. Open session created modal to show success feedback + /****************************************************************************** + * 2. Open session created modal to show success feedback + *****************************************************************************/ const onSessionCreated = useCallback((created: SessionTypes.Created) => {}, []) - // 3. Open request handling modal based on method that was used + /****************************************************************************** + * 3. Open request handling modal based on method that was used + *****************************************************************************/ const onSessionRequest = useCallback(async (requestEvent: SessionTypes.RequestEvent) => { const { topic, request } = requestEvent const { method } = request const requestSession = await walletConnectClient.session.get(topic) - // Hanle message signing requests of various formats if ([EIP155_SIGNING_METHODS.ETH_SIGN, EIP155_SIGNING_METHODS.PERSONAL_SIGN].includes(method)) { ModalStore.open('SessionSignModal', { requestEvent, requestSession }) } - // Hanle data signing requests of various formats if ( [ EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA, @@ -35,8 +39,15 @@ export default function useWalletConnectEventsManager(initialized: boolean) { ) { ModalStore.open('SessionSignTypedDataModal', { requestEvent, requestSession }) } + + if (EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION) { + ModalStore.open('SessionSendTransactionModal', { requestEvent, requestSession }) + } }, []) + /****************************************************************************** + * Set up WalletConnect event listeners + *****************************************************************************/ useEffect(() => { if (initialized) { walletConnectClient.on(CLIENT_EVENTS.session.proposal, onSessionProposal) diff --git a/wallets/react-wallet-v2/src/pages/pairings.tsx b/wallets/react-wallet-v2/src/pages/pairings.tsx new file mode 100644 index 0000000..933c433 --- /dev/null +++ b/wallets/react-wallet-v2/src/pages/pairings.tsx @@ -0,0 +1,14 @@ +import PageHeader from '@/components/PageHeader' +import { walletConnectClient } from '@/utils/WalletConnectUtil' +import { Fragment } from 'react' + +export default function PairingsPage() { + console.log(walletConnectClient.session.values) + console.log(walletConnectClient.session.history.pending) + + return ( + + Pairings + + ) +} diff --git a/wallets/react-wallet-v2/src/pages/sessions.tsx b/wallets/react-wallet-v2/src/pages/sessions.tsx index 5b0e968..2e6b6b5 100644 --- a/wallets/react-wallet-v2/src/pages/sessions.tsx +++ b/wallets/react-wallet-v2/src/pages/sessions.tsx @@ -1,20 +1,14 @@ import PageHeader from '@/components/PageHeader' -import { truncate } from '@/utils/HelperUtil' import { walletConnectClient } from '@/utils/WalletConnectUtil' -import { useRouter } from 'next/router' -import { Fragment, useEffect } from 'react' +import { Fragment } from 'react' export default function SessionsPage() { - const { query } = useRouter() - const address = (query?.address as string) ?? 'Unknown' - - useEffect(() => { - console.log(walletConnectClient.session.values) - }, []) + console.log(walletConnectClient.session.values) + console.log(walletConnectClient.session.history.pending) return ( - {truncate(address, 15)} + Sessions ) } diff --git a/wallets/react-wallet-v2/src/store/ModalStore.ts b/wallets/react-wallet-v2/src/store/ModalStore.ts index 2fe913d..171553c 100644 --- a/wallets/react-wallet-v2/src/store/ModalStore.ts +++ b/wallets/react-wallet-v2/src/store/ModalStore.ts @@ -13,7 +13,11 @@ interface ModalData { interface State { open: boolean - view?: 'SessionProposalModal' | 'SessionSignModal' | 'SessionSignTypedDataModal' + view?: + | 'SessionProposalModal' + | 'SessionSignModal' + | 'SessionSignTypedDataModal' + | 'SessionSendTransactionModal' data?: ModalData } diff --git a/wallets/react-wallet-v2/src/utils/RequestHandlerUtil.ts b/wallets/react-wallet-v2/src/utils/RequestHandlerUtil.ts index 563b8a2..e428427 100644 --- a/wallets/react-wallet-v2/src/utils/RequestHandlerUtil.ts +++ b/wallets/react-wallet-v2/src/utils/RequestHandlerUtil.ts @@ -9,32 +9,26 @@ export async function approveEIP155Request(request: RequestEvent['request'], wal const { method, params, id } = request switch (method) { - /** - * Handle message signing requests - */ case EIP155_SIGNING_METHODS.PERSONAL_SIGN: case EIP155_SIGNING_METHODS.ETH_SIGN: const message = getSignParamsMessage(params) const signedMessage = await wallet.signMessage(message) return formatJsonRpcResult(id, signedMessage) - /** - * Handle data signing requests - */ case EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA: case EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA_V3: case EIP155_SIGNING_METHODS.ETH_SIGN_TYPED_DATA_V4: const { domain, types, message: data } = getSignTypedDataParamsData(params) - // https://github.com/ethers-io/ethers.js/issues/687#issuecomment-714069471 delete types.EIP712Domain - const signedData = await wallet._signTypedData(domain, types, data) return formatJsonRpcResult(id, signedData) - /** - * Handle unsuported methods - */ + case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: + const transaction = params[0] + const { hash } = await wallet.sendTransaction(transaction) + return formatJsonRpcError(id, hash) + default: throw new Error(ERROR.UNKNOWN_JSONRPC_METHOD.format().message) } diff --git a/wallets/react-wallet-v2/src/views/SessionSendTransactionModal.tsx b/wallets/react-wallet-v2/src/views/SessionSendTransactionModal.tsx new file mode 100644 index 0000000..c288576 --- /dev/null +++ b/wallets/react-wallet-v2/src/views/SessionSendTransactionModal.tsx @@ -0,0 +1,182 @@ +import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data' +import ModalStore from '@/store/ModalStore' +import { truncate } from '@/utils/HelperUtil' +import { approveEIP155Request, rejectEIP155Request } from '@/utils/RequestHandlerUtil' +import { walletConnectClient } from '@/utils/WalletConnectUtil' +import { wallet } from '@/utils/WalletUtil' +import { + Avatar, + Button, + Col, + Container, + Divider, + Link, + Loading, + Modal, + Row, + Text +} from '@nextui-org/react' +import { providers } from 'ethers' +import { Fragment, useState } from 'react' + +export default function SessionSendTransactionModal() { + const [loading, setLoading] = useState(false) + + // Get request and wallet data from store + const requestEvent = ModalStore.state.data?.requestEvent + const requestSession = ModalStore.state.data?.requestSession + + // Ensure request and wallet are defined + if (!requestEvent || !requestSession) { + return Missing request data + } + + // Get required proposal data + const { chainId } = requestEvent + const { method, params } = requestEvent.request + const { protocol } = requestSession.relay + const { name, icons, url } = requestSession.peer.metadata + const transaction = params[0] + + console.log(transaction) + + // Handle approve action + async function onApprove() { + if (requestEvent) { + setLoading(true) + const provider = new providers.JsonRpcProvider(EIP155_CHAINS[chainId as TEIP155Chain].rpc) + const connectedWallet = wallet.connect(provider) + const response = await approveEIP155Request(requestEvent.request, connectedWallet) + await walletConnectClient.respond({ + topic: requestEvent.topic, + response + }) + ModalStore.close() + } + } + + // Handle reject action + async function onReject() { + if (requestEvent) { + const response = rejectEIP155Request(requestEvent.request) + await walletConnectClient.respond({ + topic: requestEvent.topic, + response + }) + ModalStore.close() + } + } + + return ( + + + Send Transaction + + + + + + + + + + {name} + {url} + + + + + + + + From + {truncate(transaction.from, 30)} + + + + + + + + To + {truncate(transaction.to, 30)} + + + + + + + + Value + {transaction.value} + + + + + + + + Gas Price + {transaction.gasPrice} + + + Gas Limit + {transaction.gasLimit} + + + + + + + + Nonce + {transaction.nonce} + + + Data + {transaction.data} + + + + + + + + Blockchain + + {EIP155_CHAINS[chainId as TEIP155Chain]?.name ?? chainId} + + + + + + + + + Method + {method} + + + + + + + + Relay Protocol + {protocol} + + + + + + + + + + + ) +} diff --git a/wallets/react-wallet-v2/yarn.lock b/wallets/react-wallet-v2/yarn.lock index db648e3..3ed644d 100644 --- a/wallets/react-wallet-v2/yarn.lock +++ b/wallets/react-wallet-v2/yarn.lock @@ -794,10 +794,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/node@17.0.17": - version "17.0.17" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.17.tgz#a8ddf6e0c2341718d74ee3dc413a13a042c45a0c" - integrity sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw== +"@types/node@17.0.18": + version "17.0.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" + integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== "@types/prop-types@*": version "15.7.4"