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 ChainCard from '@/components/ChainCard'
|
||||||
|
import SettingsStore from '@/store/SettingsStore'
|
||||||
|
import { eip155Addresses } from '@/utils/EIP155WalletUtil'
|
||||||
import { truncate } from '@/utils/HelperUtil'
|
import { truncate } from '@/utils/HelperUtil'
|
||||||
|
import { updateSignClientChainId } from '@/utils/WalletConnectUtil'
|
||||||
import { Avatar, Button, Text, Tooltip } from '@nextui-org/react'
|
import { Avatar, Button, Text, Tooltip } from '@nextui-org/react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string
|
name: string
|
||||||
logo: string
|
logo: string
|
||||||
rgb: string
|
rgb: string
|
||||||
address: 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 [copied, setCopied] = useState(false)
|
||||||
|
const { activeChainId, account } = useSnapshot(
|
||||||
|
SettingsStore.state,
|
||||||
|
);
|
||||||
function onCopy() {
|
function onCopy() {
|
||||||
navigator?.clipboard?.writeText(address)
|
navigator?.clipboard?.writeText(address)
|
||||||
setCopied(true)
|
setCopied(true)
|
||||||
setTimeout(() => setCopied(false), 1500)
|
setTimeout(() => setCopied(false), 1500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onChainChanged(chainId: string, address: string) {
|
||||||
|
SettingsStore.setActiveChainId(chainId);
|
||||||
|
await updateSignClientChainId(chainId.toString(), address);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChainCard rgb={rgb} flexDirection="row" alignItems="center">
|
<ChainCard rgb={rgb} flexDirection="row" alignItems="center">
|
||||||
<Avatar src={logo} />
|
<Avatar src={logo} />
|
||||||
@ -46,6 +58,19 @@ export default function AccountCard({ name, logo, rgb, address }: Props) {
|
|||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
css={{
|
||||||
|
minWidth: "auto",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||||
|
marginLeft: "$5",
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
onChainChanged(chainId, address);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{activeChainId === chainId ? `✅` : `🔄`}
|
||||||
|
</Button>
|
||||||
</ChainCard>
|
</ChainCard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ export const POLKADOT_TEST_CHAINS = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const POLKADOT_CHAINS = { ...POLKADOT_MAINNET_CHAINS, ...POLKADOT_TEST_CHAINS }
|
||||||
/**
|
/**
|
||||||
* Methods
|
* Methods
|
||||||
*/
|
*/
|
||||||
|
@ -35,26 +35,26 @@ export default function HomePage() {
|
|||||||
<Text h4 css={{ marginBottom: '$5' }}>
|
<Text h4 css={{ marginBottom: '$5' }}>
|
||||||
Mainnets
|
Mainnets
|
||||||
</Text>
|
</Text>
|
||||||
{Object.values(EIP155_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(EIP155_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} chainId={caip10.toString()}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(COSMOS_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(COSMOS_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={cosmosAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={cosmosAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(SOLANA_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(SOLANA_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(POLKADOT_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(POLKADOT_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(ELROND_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(ELROND_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(TRON_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(TRON_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(TEZOS_MAINNET_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(TEZOS_MAINNET_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{testNets ? (
|
{testNets ? (
|
||||||
@ -62,26 +62,26 @@ export default function HomePage() {
|
|||||||
<Text h4 css={{ marginBottom: '$5' }}>
|
<Text h4 css={{ marginBottom: '$5' }}>
|
||||||
Testnets
|
Testnets
|
||||||
</Text>
|
</Text>
|
||||||
{Object.values(EIP155_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(EIP155_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={eip155Address} chainId={caip10.toString()}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(SOLANA_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(SOLANA_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={solanaAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(POLKADOT_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(POLKADOT_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={polkadotAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(NEAR_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(NEAR_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={nearAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={nearAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(ELROND_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(ELROND_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={elrondAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(TRON_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(TRON_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tronAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
{Object.values(TEZOS_TEST_CHAINS).map(({ name, logo, rgb }) => (
|
{Object.entries(TEZOS_TEST_CHAINS).map(([caip10, {name, logo, rgb}]) => (
|
||||||
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} />
|
<AccountCard key={name} name={name} logo={logo} rgb={rgb} address={tezosAddress} chainId={caip10}/>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import PageHeader from '@/components/PageHeader'
|
import PageHeader from '@/components/PageHeader'
|
||||||
import ProjectInfoCard from '@/components/ProjectInfoCard'
|
import ProjectInfoCard from '@/components/ProjectInfoCard'
|
||||||
import SessionChainCard from '@/components/SessionChainCard'
|
import SessionChainCard from '@/components/SessionChainCard'
|
||||||
|
import SettingsStore from '@/store/SettingsStore'
|
||||||
import { signClient } from '@/utils/WalletConnectUtil'
|
import { signClient } from '@/utils/WalletConnectUtil'
|
||||||
import { Button, Divider, Loading, Row, Text } from '@nextui-org/react'
|
import { Button, Divider, Loading, Row, Text } from '@nextui-org/react'
|
||||||
import { getSdkError } from '@walletconnect/utils'
|
import { getSdkError } from '@walletconnect/utils'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Fragment, useEffect, useState } from 'react'
|
import { Fragment, useEffect, useState } from 'react'
|
||||||
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component
|
* Component
|
||||||
@ -16,6 +18,8 @@ export default function SessionPage() {
|
|||||||
const { query, replace } = useRouter()
|
const { query, replace } = useRouter()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
const { activeChainId } = useSnapshot(SettingsStore.state);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query?.topic) {
|
if (query?.topic) {
|
||||||
setTopic(query.topic as string)
|
setTopic(query.topic as string)
|
||||||
@ -48,11 +52,10 @@ export default function SessionPage() {
|
|||||||
|
|
||||||
async function onSessionEmit() {
|
async function onSessionEmit() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
console.log('baleg')
|
|
||||||
await signClient.emit({
|
await signClient.emit({
|
||||||
topic,
|
topic,
|
||||||
event: { name: 'chainChanged', data: 'Hello World' },
|
event: { name: 'chainChanged', data: 'Hello World' },
|
||||||
chainId: 'eip155:1'
|
chainId: activeChainId.toString() // chainId: 'eip155:1'
|
||||||
})
|
})
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ interface State {
|
|||||||
tronAddress: string
|
tronAddress: string
|
||||||
tezosAddress: string
|
tezosAddress: string
|
||||||
relayerRegionURL: string
|
relayerRegionURL: string
|
||||||
|
activeChainId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,6 +24,7 @@ interface State {
|
|||||||
const state = proxy<State>({
|
const state = proxy<State>({
|
||||||
testNets: typeof localStorage !== 'undefined' ? Boolean(localStorage.getItem('TEST_NETS')) : true,
|
testNets: typeof localStorage !== 'undefined' ? Boolean(localStorage.getItem('TEST_NETS')) : true,
|
||||||
account: 0,
|
account: 0,
|
||||||
|
activeChainId: '1',
|
||||||
eip155Address: '',
|
eip155Address: '',
|
||||||
cosmosAddress: '',
|
cosmosAddress: '',
|
||||||
solanaAddress: '',
|
solanaAddress: '',
|
||||||
@ -78,6 +80,10 @@ const SettingsStore = {
|
|||||||
state.tezosAddress = tezosAddress
|
state.tezosAddress = tezosAddress
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setActiveChainId(value: string) {
|
||||||
|
state.activeChainId = value
|
||||||
|
},
|
||||||
|
|
||||||
toggleTestNets() {
|
toggleTestNets() {
|
||||||
state.testNets = !state.testNets
|
state.testNets = !state.testNets
|
||||||
if (state.testNets) {
|
if (state.testNets) {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { COSMOS_MAINNET_CHAINS, TCosmosChain } from '@/data/COSMOSData'
|
import { COSMOS_MAINNET_CHAINS, TCosmosChain } from '@/data/COSMOSData'
|
||||||
import { EIP155_CHAINS, TEIP155Chain } from '@/data/EIP155Data'
|
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 { 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 { TRON_CHAINS, TTronChain } from '@/data/TronData'
|
||||||
|
|
||||||
import { utils } from 'ethers'
|
import { utils } from 'ethers'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,11 +140,13 @@ export function isTezosChain(chain: string) {
|
|||||||
*/
|
*/
|
||||||
export function formatChainName(chainId: string) {
|
export function formatChainName(chainId: string) {
|
||||||
return (
|
return (
|
||||||
EIP155_CHAINS[chainId as TEIP155Chain]?.name ??
|
|
||||||
COSMOS_MAINNET_CHAINS[chainId as TCosmosChain]?.name ??
|
COSMOS_MAINNET_CHAINS[chainId as TCosmosChain]?.name ??
|
||||||
SOLANA_CHAINS[chainId as TSolanaChain]?.name ??
|
EIP155_CHAINS[chainId as TEIP155Chain]?.name ??
|
||||||
NEAR_TEST_CHAINS[chainId as TNearChain]?.name ??
|
|
||||||
ELROND_CHAINS[chainId as TElrondChain]?.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 ??
|
TRON_CHAINS[chainId as TTronChain]?.name ??
|
||||||
chainId
|
chainId
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import SignClient from '@walletconnect/sign-client'
|
import SignClient from '@walletconnect/sign-client'
|
||||||
|
|
||||||
export let signClient: SignClient
|
export let signClient: SignClient
|
||||||
|
|
||||||
export async function createSignClient(relayerRegionURL: string) {
|
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