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 <ben@walletconnect.com> * 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 <ben@walletconnect.com>
This commit is contained in:
		
							parent
							
								
									74c54bf6f0
								
							
						
					
					
						commit
						d774f52135
					
				| @ -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 ( | ||||
|     <ChainCard rgb={rgb} flexDirection="row" alignItems="center"> | ||||
|       <Avatar src={logo} /> | ||||
| @ -46,6 +58,19 @@ export default function AccountCard({ name, logo, rgb, address }: Props) { | ||||
|           /> | ||||
|         </Button> | ||||
|       </Tooltip> | ||||
|     <Button | ||||
|         size="sm" | ||||
|         css={{ | ||||
|           minWidth: "auto", | ||||
|           backgroundColor: "rgba(255, 255, 255, 0.15)", | ||||
|           marginLeft: "$5", | ||||
|         }} | ||||
|         onPress={() => { | ||||
|           onChainChanged(chainId, address); | ||||
|         }} | ||||
|       > | ||||
|         {activeChainId === chainId ? `✅` : `🔄`} | ||||
|       </Button> | ||||
|     </ChainCard> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,7 @@ export const POLKADOT_TEST_CHAINS = { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const POLKADOT_CHAINS = { ...POLKADOT_MAINNET_CHAINS, ...POLKADOT_TEST_CHAINS } | ||||
| /** | ||||
|  * Methods | ||||
|  */ | ||||
|  | ||||
| @ -35,26 +35,26 @@ export default function HomePage() { | ||||
|       <Text h4 css={{ marginBottom: '$5' }}> | ||||
|         Mainnets | ||||
|       </Text> | ||||
|       {Object.values(EIP155_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} /> | ||||
|       {Object.entries(EIP155_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} chainId={caip10.toString()}/> | ||||
|       ))} | ||||
|       {Object.values(COSMOS_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={cosmosAddress} /> | ||||
|       {Object.entries(COSMOS_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={cosmosAddress} chainId={caip10}/> | ||||
|       ))} | ||||
|       {Object.values(SOLANA_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} /> | ||||
|       {Object.entries(SOLANA_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} chainId={caip10}/> | ||||
|       ))} | ||||
|       {Object.values(POLKADOT_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} /> | ||||
|       {Object.entries(POLKADOT_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} chainId={caip10}/> | ||||
|       ))} | ||||
|       {Object.values(ELROND_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} /> | ||||
|       {Object.entries(ELROND_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} chainId={caip10}/> | ||||
|       ))} | ||||
|       {Object.values(TRON_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} /> | ||||
|       {Object.entries(TRON_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} chainId={caip10}/> | ||||
|       ))} | ||||
|       {Object.values(TEZOS_MAINNET_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} /> | ||||
|       {Object.entries(TEZOS_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|         <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} chainId={caip10}/> | ||||
|       ))} | ||||
| 
 | ||||
|       {testNets ? ( | ||||
| @ -62,26 +62,26 @@ export default function HomePage() { | ||||
|           <Text h4 css={{ marginBottom: '$5' }}> | ||||
|             Testnets | ||||
|           </Text> | ||||
|           {Object.values(EIP155_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} /> | ||||
|           {Object.entries(EIP155_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} chainId={caip10.toString()}/> | ||||
|           ))} | ||||
|           {Object.values(SOLANA_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} /> | ||||
|           {Object.entries(SOLANA_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} chainId={caip10}/> | ||||
|           ))} | ||||
|           {Object.values(POLKADOT_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} /> | ||||
|           {Object.entries(POLKADOT_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} chainId={caip10}/> | ||||
|           ))} | ||||
|           {Object.values(NEAR_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={nearAddress} /> | ||||
|           {Object.entries(NEAR_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={nearAddress} chainId={caip10}/> | ||||
|           ))} | ||||
|           {Object.values(ELROND_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} /> | ||||
|           {Object.entries(ELROND_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} chainId={caip10}/> | ||||
|           ))} | ||||
|           {Object.values(TRON_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} /> | ||||
|           {Object.entries(TRON_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} chainId={caip10}/> | ||||
|           ))} | ||||
|           {Object.values(TEZOS_TEST_CHAINS).map(({ name, logo, rgb }) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} /> | ||||
|           {Object.entries(TEZOS_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => ( | ||||
|             <AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} chainId={caip10}/> | ||||
|           ))} | ||||
|         </Fragment> | ||||
|       ) : null} | ||||
|  | ||||
| @ -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) | ||||
|   } | ||||
|  | ||||
| @ -15,6 +15,7 @@ interface State { | ||||
|   tronAddress: string | ||||
|   tezosAddress: string | ||||
|   relayerRegionURL: string | ||||
|   activeChainId: string | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -23,6 +24,7 @@ interface State { | ||||
| const state = proxy<State>({ | ||||
|   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) { | ||||
|  | ||||
| @ -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 | ||||
|   ) | ||||
|  | ||||
| @ -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}`) | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user