From d774f521354aca3942f5015810b9cf03c954784b Mon Sep 17 00:00:00 2001 From: Jonathan Conn Date: Wed, 10 May 2023 10:01:36 -0400 Subject: [PATCH] Feat/examples upgrade (#153) * Added UI infastructure to support chain switching, need to update the session to swap chains * Fixed type of chainId, string -> number * Added helper method to find full chainname ex/ cosmoshub-4 -> cosmons:cosmoshub-4. Added a method updateSignClientChainId to emit a new update with the new namespace for the respected chain switch * Added checks to ensure no dup accounts get added to namespaces during chainswitching from chain A->B->A for ex * Fixed chainId type, eip155 uses number but the other chains use strings, making string the default type for chainId * Update wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts Co-authored-by: Ben Kremer * Switched the way account card components are generated in index page, removed unnecessary helper fx * Fixed the chain changed event by updating the session if the chainId namespace does not currently exist, then emitting the correct chainChanged event on the same session --------- Co-authored-by: Ben Kremer --- .../src/components/AccountCard.tsx | 29 +++++++++- .../react-wallet-v2/src/data/PolkadotData.ts | 1 + wallets/react-wallet-v2/src/pages/index.tsx | 56 +++++++++---------- wallets/react-wallet-v2/src/pages/session.tsx | 7 ++- .../src/store/SettingsStore.ts | 6 ++ .../react-wallet-v2/src/utils/HelperUtil.ts | 15 +++-- .../src/utils/WalletConnectUtil.ts | 48 +++++++++++++++- 7 files changed, 124 insertions(+), 38 deletions(-) diff --git a/wallets/react-wallet-v2/src/components/AccountCard.tsx b/wallets/react-wallet-v2/src/components/AccountCard.tsx index 9b58ec2..51e082a 100644 --- a/wallets/react-wallet-v2/src/components/AccountCard.tsx +++ b/wallets/react-wallet-v2/src/components/AccountCard.tsx @@ -1,25 +1,37 @@ import ChainCard from '@/components/ChainCard' +import SettingsStore from '@/store/SettingsStore' +import { eip155Addresses } from '@/utils/EIP155WalletUtil' import { truncate } from '@/utils/HelperUtil' +import { updateSignClientChainId } from '@/utils/WalletConnectUtil' import { Avatar, Button, Text, Tooltip } from '@nextui-org/react' import Image from 'next/image' import { useState } from 'react' +import { useSnapshot } from 'valtio' interface Props { name: string logo: string rgb: string address: string + chainId: string } -export default function AccountCard({ name, logo, rgb, address }: Props) { +export default function AccountCard({ name, logo, rgb, address, chainId }: Props) { const [copied, setCopied] = useState(false) - + const { activeChainId, account } = useSnapshot( + SettingsStore.state, + ); function onCopy() { navigator?.clipboard?.writeText(address) setCopied(true) setTimeout(() => setCopied(false), 1500) } + async function onChainChanged(chainId: string, address: string) { + SettingsStore.setActiveChainId(chainId); + await updateSignClientChainId(chainId.toString(), address); + } + return ( @@ -46,6 +58,19 @@ export default function AccountCard({ name, logo, rgb, address }: Props) { /> + ) } diff --git a/wallets/react-wallet-v2/src/data/PolkadotData.ts b/wallets/react-wallet-v2/src/data/PolkadotData.ts index a9978a9..a5c8d23 100644 --- a/wallets/react-wallet-v2/src/data/PolkadotData.ts +++ b/wallets/react-wallet-v2/src/data/PolkadotData.ts @@ -26,6 +26,7 @@ export const POLKADOT_TEST_CHAINS = { } } +export const POLKADOT_CHAINS = { ...POLKADOT_MAINNET_CHAINS, ...POLKADOT_TEST_CHAINS } /** * Methods */ diff --git a/wallets/react-wallet-v2/src/pages/index.tsx b/wallets/react-wallet-v2/src/pages/index.tsx index 736e812..b4be7d0 100644 --- a/wallets/react-wallet-v2/src/pages/index.tsx +++ b/wallets/react-wallet-v2/src/pages/index.tsx @@ -35,26 +35,26 @@ export default function HomePage() { Mainnets - {Object.values(EIP155_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(EIP155_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(COSMOS_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(COSMOS_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(SOLANA_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(SOLANA_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(POLKADOT_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(POLKADOT_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(ELROND_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(ELROND_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(TRON_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(TRON_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(TEZOS_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(TEZOS_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} {testNets ? ( @@ -62,26 +62,26 @@ export default function HomePage() { Testnets - {Object.values(EIP155_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(EIP155_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(SOLANA_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(SOLANA_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(POLKADOT_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(POLKADOT_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(NEAR_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(NEAR_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(ELROND_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(ELROND_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(TRON_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(TRON_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} - {Object.values(TEZOS_TEST_CHAINS).map(({ name, logo, rgb }) => ( - + {Object.entries(TEZOS_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( + ))} ) : null} diff --git a/wallets/react-wallet-v2/src/pages/session.tsx b/wallets/react-wallet-v2/src/pages/session.tsx index 19c4a3c..0ce4f79 100644 --- a/wallets/react-wallet-v2/src/pages/session.tsx +++ b/wallets/react-wallet-v2/src/pages/session.tsx @@ -1,11 +1,13 @@ import PageHeader from '@/components/PageHeader' import ProjectInfoCard from '@/components/ProjectInfoCard' import SessionChainCard from '@/components/SessionChainCard' +import SettingsStore from '@/store/SettingsStore' import { signClient } from '@/utils/WalletConnectUtil' import { Button, Divider, Loading, Row, Text } from '@nextui-org/react' import { getSdkError } from '@walletconnect/utils' import { useRouter } from 'next/router' import { Fragment, useEffect, useState } from 'react' +import { useSnapshot } from 'valtio' /** * Component @@ -16,6 +18,8 @@ export default function SessionPage() { const { query, replace } = useRouter() const [loading, setLoading] = useState(false) + const { activeChainId } = useSnapshot(SettingsStore.state); + useEffect(() => { if (query?.topic) { setTopic(query.topic as string) @@ -48,11 +52,10 @@ export default function SessionPage() { async function onSessionEmit() { setLoading(true) - console.log('baleg') await signClient.emit({ topic, event: { name: 'chainChanged', data: 'Hello World' }, - chainId: 'eip155:1' + chainId: activeChainId.toString() // chainId: 'eip155:1' }) setLoading(false) } diff --git a/wallets/react-wallet-v2/src/store/SettingsStore.ts b/wallets/react-wallet-v2/src/store/SettingsStore.ts index 1bf0bee..b3b0e41 100644 --- a/wallets/react-wallet-v2/src/store/SettingsStore.ts +++ b/wallets/react-wallet-v2/src/store/SettingsStore.ts @@ -15,6 +15,7 @@ interface State { tronAddress: string tezosAddress: string relayerRegionURL: string + activeChainId: string } /** @@ -23,6 +24,7 @@ interface State { const state = proxy({ testNets: typeof localStorage !== 'undefined' ? Boolean(localStorage.getItem('TEST_NETS')) : true, account: 0, + activeChainId: '1', eip155Address: '', cosmosAddress: '', solanaAddress: '', @@ -78,6 +80,10 @@ const SettingsStore = { state.tezosAddress = tezosAddress }, + setActiveChainId(value: string) { + state.activeChainId = value + }, + 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 868858a..b97fc06 100644 --- a/wallets/react-wallet-v2/src/utils/HelperUtil.ts +++ b/wallets/react-wallet-v2/src/utils/HelperUtil.ts @@ -1,9 +1,12 @@ import { COSMOS_MAINNET_CHAINS, TCosmosChain } from '@/data/COSMOSData' import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data' -import { NEAR_TEST_CHAINS, TNearChain } from '@/data/NEARData' -import { SOLANA_CHAINS, TSolanaChain } from '@/data/SolanaData' import { ELROND_CHAINS, TElrondChain } from '@/data/ElrondData' +import { NEAR_TEST_CHAINS, TNearChain } from '@/data/NEARData' +import { POLKADOT_CHAINS, TPolkadotChain } from '@/data/PolkadotData' +import { SOLANA_CHAINS, TSolanaChain } from '@/data/SolanaData' +import { TEZOS_CHAINS, TTezosChain } from '@/data/TezosData' import { TRON_CHAINS, TTronChain } from '@/data/TronData' + import { utils } from 'ethers' /** @@ -137,11 +140,13 @@ export function isTezosChain(chain: string) { */ export function formatChainName(chainId: string) { return ( - EIP155_CHAINS[chainId as TEIP155Chain]?.name ?? COSMOS_MAINNET_CHAINS[chainId as TCosmosChain]?.name ?? - SOLANA_CHAINS[chainId as TSolanaChain]?.name ?? - NEAR_TEST_CHAINS[chainId as TNearChain]?.name ?? + EIP155_CHAINS[chainId as TEIP155Chain]?.name ?? ELROND_CHAINS[chainId as TElrondChain]?.name ?? + NEAR_TEST_CHAINS[chainId as TNearChain]?.name ?? + POLKADOT_CHAINS[chainId as TPolkadotChain]?.name ?? + SOLANA_CHAINS[chainId as TSolanaChain]?.name ?? + TEZOS_CHAINS[chainId as TTezosChain]?.name ?? TRON_CHAINS[chainId as TTronChain]?.name ?? chainId ) diff --git a/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts b/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts index c3007b7..49e13eb 100644 --- a/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts +++ b/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts @@ -1,5 +1,4 @@ import SignClient from '@walletconnect/sign-client' - export let signClient: SignClient export async function createSignClient(relayerRegionURL: string) { @@ -15,3 +14,50 @@ export async function createSignClient(relayerRegionURL: string) { } }) } + +export async function updateSignClientChainId(chainId: string, address: string) { + // get most recent session + const session = signClient.session.getAll()[0] + if (!session) return + + // if chainId does not exist in session, an update is required first + if (!session.namespaces[chainId]) { + const newNamespace = { + [chainId]: { + accounts: [`${chainId}:${address}`], + methods: [ + 'eth_sendTransaction', + 'eth_signTransaction', + 'eth_sign', + 'personal_sign', + 'eth_signTypedData' + ], + events: ['chainChanged', 'accountsChanged'] + } + } + try { + // need to wait for update to finish before emit + await signClient.update({ + topic: session.topic, + namespaces: { ...session.namespaces, ...newNamespace } + }) + } catch (err: unknown) { + console.error(`Failed to update session: ${err}`) + } + } + + const payload = { + topic: session.topic, + event: { + name: 'chainChanged', + data: [address] + }, + chainId + } + + try { + signClient.emit(payload) + } catch (err: unknown) { + console.error(`Failed to emit chainChanged event: ${err}`) + } +}