Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WalletConnect as a connection option #647

Merged
merged 6 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
"@polkadot-labs/hdkd-helpers": "^0.0.11",
"@polkadot/react-identicon": "^3.12.1",
"@polkadot/util-crypto": "^13.4.2",
"@reactive-dot/core": "^0.32.0",
"@reactive-dot/react": "^0.32.0",
"@reactive-dot/core": "^0.33.0",
"@reactive-dot/react": "^0.33.0",
"@reactive-dot/wallet-walletconnect": "^0.17.5",
"@tanstack/react-query": "^5.66.5",
"@walletconnect/web3wallet": "^1.16.1",
"dayjs": "^1.11.13",
Expand Down
1 change: 0 additions & 1 deletion packages/ui/src/components/TransferAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ const TransferAsset = ({
const toAddress = useMemo(() => selected?.address || '', [selected?.address])
const [selectedAsset, setSelectedAsset] = useState<Option | undefined>()

console.log('-->load')
const isNativeAssetSelected = useMemo(() => {
if (!selectedAsset || selectedAsset.id === undefined) return true

Expand Down
82 changes: 50 additions & 32 deletions packages/ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// import { chainsAmplitudeSVG } from './logos/amplitudeSVG'
// import { nodesJoystreamSVG } from './logos/joystreamSVG'
// import { chainsWatrPNG } from './logos/watrPNG'
import { nodesKhalaSVG } from './logos/khalaSVG'
// import { nodesKhalaSVG } from './logos/khalaSVG'
import { chainsKusamaSVG } from './logos/kusamaSVG '
import { localSVG } from './logos/localSVG'
import { chainsPolkadotCircleSVG } from './logos/polkadot-circleSVG'
Expand Down Expand Up @@ -35,6 +35,8 @@ export const AH_SUPPORTED_ASSETS = [
{ assetId: 1337, logo: usdc }
]

export const WALLETCONNECT_PROJECT_ID = '3cb99365b226c0f1918b24cbc2b84d49'

export interface NetworkInfo {
chainId: string
explorerNetworkName?: string
Expand All @@ -45,6 +47,7 @@ export interface NetworkInfo {
pplChainRpcUrls?: string[]
descriptor: keyof typeof DESCRIPTORS
pplChainDescriptor?: PplDescriptorKeys
genesisHash?: string
}

export const HTTP_GRAPHQL_URL = `https://chainsafe.squids.live/multix-arrow@v7/api/graphql`
Expand Down Expand Up @@ -95,7 +98,8 @@ export const networkList: Record<string, NetworkInfo> = {
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: chainsPolkadotCircleSVG,
descriptor: 'dot',
pplChainDescriptor: 'dotPpl'
pplChainDescriptor: 'dotPpl',
genesisHash: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3'
},
kusama: {
chainId: 'kusama',
Expand All @@ -116,7 +120,8 @@ export const networkList: Record<string, NetworkInfo> = {
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: chainsKusamaSVG,
descriptor: 'ksm',
pplChainDescriptor: 'ksmPpl'
pplChainDescriptor: 'ksmPpl',
genesisHash: '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe'
},
'asset-hub-polkadot': {
chainId: 'asset-hub-polkadot',
Expand All @@ -137,7 +142,8 @@ export const networkList: Record<string, NetworkInfo> = {
nativeAssetLogo: chainsPolkadotCircleSVG,
networkLogo: nodesAssetHubSVG,
descriptor: 'dotAssetHub',
pplChainDescriptor: 'dotPpl'
pplChainDescriptor: 'dotPpl',
genesisHash: '0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f'
},
'asset-hub-kusama': {
chainId: 'asset-hub-kusama',
Expand All @@ -157,7 +163,8 @@ export const networkList: Record<string, NetworkInfo> = {
nativeAssetLogo: chainsKusamaSVG,
networkLogo: nodesAssetHubSVG,
descriptor: 'ksmAssetHub',
pplChainDescriptor: 'ksmPpl'
pplChainDescriptor: 'ksmPpl',
genesisHash: '0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a'
},
'coretime-polkadot': {
chainId: 'coretime-polkadot',
Expand All @@ -171,7 +178,8 @@ export const networkList: Record<string, NetworkInfo> = {
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: nodesCoretimeSVG,
descriptor: 'coretimeDot',
pplChainDescriptor: 'dotPpl'
pplChainDescriptor: 'dotPpl',
genesisHash: '0xefb56e30d9b4a24099f88820987d0f45fb645992416535d87650d98e00f46fc4'
},
// 'coretime-kusama': {
// chainId: 'coretime-kusama',
Expand All @@ -187,7 +195,8 @@ export const networkList: Record<string, NetworkInfo> = {
// pplChainRpcUrls: kusamaPplChains,
// httpGraphqlUrl: HTTP_GRAPHQL_URL,
// logo: nodesCoretimeSVG,
// descriptor: coretimeKsm
// descriptor: coretimeKsm,
// genesisHash: '0x638cd2b9af4b3bb54b8c1f0d22711fc89924ca93300f0caf25a580432b29d050'
// },
acala: {
chainId: 'acala',
Expand All @@ -199,7 +208,8 @@ export const networkList: Record<string, NetworkInfo> = {
],
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: chainsAcalaSVG,
descriptor: 'acala'
descriptor: 'acala',
genesisHash: '0xfc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c'
},
// astar: {
// chainId: 'astar',
Expand Down Expand Up @@ -227,7 +237,8 @@ export const networkList: Record<string, NetworkInfo> = {
],
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: nodesBifrostSVG,
descriptor: 'bifrostDot'
descriptor: 'bifrostDot',
genesisHash: '0x262e1b2ad728475fd6fe88e62d34c200abe6fd693931ddad144059b1eb884e5b'
},
hydration: {
chainId: 'hydradx',
Expand All @@ -241,7 +252,8 @@ export const networkList: Record<string, NetworkInfo> = {
],
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: hydrationSVG,
descriptor: 'hydration'
descriptor: 'hydration',
genesisHash: '0xafdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d'
},
// interlay: {
// chainId: 'interlay',
Expand All @@ -250,20 +262,20 @@ export const networkList: Record<string, NetworkInfo> = {
// httpGraphqlUrl: HTTP_GRAPHQL_URL,
// logo: nodesInterlaySVG,
// },
khala: {
chainId: 'khala',
explorerNetworkName: 'khala',
rpcUrls: [
'wss://khala-rpc.dwellir.com',
'wss://rpc.helikon.io/khala',
'wss://khala.api.onfinality.io/public-ws',
'wss://khala-api.phala.network/ws',
'wss://khala.public.curie.radiumblock.co/ws'
],
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: nodesKhalaSVG,
descriptor: 'khala'
},
// khala: {
// chainId: 'khala',
// explorerNetworkName: 'khala',
// rpcUrls: [
// 'wss://khala-rpc.dwellir.com',
// 'wss://rpc.helikon.io/khala',
// 'wss://khala.api.onfinality.io/public-ws',
// 'wss://khala-api.phala.network/ws',
// 'wss://khala.public.curie.radiumblock.co/ws'
// ],
// httpGraphqlUrl: HTTP_GRAPHQL_URL,
// networkLogo: nodesKhalaSVG,
// descriptor: 'khala'
// },
// moonbeam: {
// chainId: 'moonbeam',
// explorerNetworkName: 'moonbeam',
Expand All @@ -290,7 +302,8 @@ export const networkList: Record<string, NetworkInfo> = {
],
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: phalaSVG,
descriptor: 'phala'
descriptor: 'phala',
genesisHash: '0x1bb969d85965e4bb5a651abbedf21a54b6b31a21f66b5401cc3f1e286268d736'
},
// 'rhala testnet': {
// chainId: 'rhala',
Expand Down Expand Up @@ -322,7 +335,8 @@ export const networkList: Record<string, NetworkInfo> = {
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: nodesWestendColourSVG,
descriptor: 'westend',
pplChainDescriptor: 'wesPpl'
pplChainDescriptor: 'wesPpl',
genesisHash: '0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e'
},
'asset-hub-westend': {
chainId: 'asset-hub-westend',
Expand All @@ -338,7 +352,8 @@ export const networkList: Record<string, NetworkInfo> = {
nativeAssetLogo: nodesWestendColourSVG,
networkLogo: nodesAssetHubSVG,
descriptor: 'wesAssetHub',
pplChainDescriptor: 'wesPpl'
pplChainDescriptor: 'wesPpl',
genesisHash: '0x67f9723393ef76214df0118c34bbbd3dbebc8ed46a10973a8c969d48fe7598c9'
},
paseo: {
chainId: 'paseo',
Expand All @@ -354,7 +369,8 @@ export const networkList: Record<string, NetworkInfo> = {
pplChainRpcUrls: ['wss://people-paseo.rpc.amforc.com', 'wss://people-paseo.dotters.network'],
networkLogo: paseoSVG,
descriptor: 'paseo',
pplChainDescriptor: 'pasPpl'
pplChainDescriptor: 'pasPpl',
genesisHash: '0x77afd6190f1554ad45fd0d31aee62aacc33c6db0ea801129acb813f913e0764f'
},
// amplitude: {
// chainId: 'amplitude',
Expand Down Expand Up @@ -401,7 +417,8 @@ export const networkList: Record<string, NetworkInfo> = {
],
httpGraphqlUrl: HTTP_GRAPHQL_URL,
networkLogo: polimecSVG,
descriptor: 'polimec'
descriptor: 'polimec',
genesisHash: '0x7eb9354488318e7549c722669dcbdcdc526f1fef1420e7944667212f3601fdbd'
},
local: {
chainId: import.meta.env.VITE_CHAIN_ID,
Expand All @@ -410,7 +427,8 @@ export const networkList: Record<string, NetworkInfo> = {
httpGraphqlUrl: import.meta.env.VITE_GRAPHQL_HTTP_PROVIDER,
networkLogo: localSVG,
descriptor: 'dot',
pplChainDescriptor: 'dotPpl'
pplChainDescriptor: 'dotPpl',
genesisHash: undefined
}
}

Expand All @@ -432,10 +450,10 @@ export const polkadotNetworksAndParachains: Partial<keyof typeof networkList>[]
]
export const kusamaNetworksAndParachains: Partial<keyof typeof networkList>[] = [
'kusama',
'asset-hub-kusama',
'asset-hub-kusama'
// 'coretime-kusama'
// 'amplitude',
'khala'
// 'khala'
// 'moonriver'
]
export const soloChains: Partial<keyof typeof networkList>[] = [
Expand Down
17 changes: 13 additions & 4 deletions packages/ui/src/contexts/AccountsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useAccounts as useRedotAccounts } from '@reactive-dot/react'
import { useApi } from './ApiContext'
import { encodeAccounts } from '../utils/encodeAccounts'
import { InjectedPolkadotAccount } from 'polkadot-api/pjs-signer'
import { useGetWalletConnectNamespace } from '../hooks/useWalletConnectNamespace'

const LOCALSTORAGE_SELECTED_ACCOUNT_KEY = 'multix.selectedAccount'
const LOCALSTORAGE_ALLOWED_CONNECTION_KEY = 'multix.canConnectToExtension'
Expand All @@ -35,12 +36,20 @@ export interface IAccountContext {
const AccountContext = createContext<IAccountContext | undefined>(undefined)

const AccountContextProvider = ({ children }: AccountContextProps) => {
const { currentNamespace } = useGetWalletConnectNamespace()
const redotAccountList = useRedotAccounts()
const { chainInfo } = useApi()
const ownAccountList = useMemo(
() => chainInfo && encodeAccounts(redotAccountList, chainInfo.ss58Format),
[chainInfo, redotAccountList]
)
const ownAccountList = useMemo(() => {
if (!chainInfo || !redotAccountList) return []
// redot would share 10 accounts if we connect say Nova with 1 account, and 10 networks
// for this reason, we need to filter out the accounts that are not for the current network
// this only applies to wallet-connect accounts
const filteredAccounts = redotAccountList.filter((account) => {
return account.wallet.id !== 'wallet-connect' || account.genesisHash === currentNamespace
})
return encodeAccounts(filteredAccounts, chainInfo.ss58Format)
}, [chainInfo, currentNamespace, redotAccountList])

const [selectedAccount, setSelected] = useState<InjectedPolkadotAccount | undefined>(
ownAccountList?.[0]
)
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/src/contexts/WalletConnectContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import React, { createContext, useCallback, useContext, useEffect, useMemo, useS
import { Core } from '@walletconnect/core'
import { ICore, PairingTypes } from '@walletconnect/types'
import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet'
import { DAPP_NAME } from '../constants'
import { DAPP_NAME, WALLETCONNECT_PROJECT_ID } from '../constants'

export type WalletConnect = { [address: string]: string }

const WALLETCONNECT_PROJECT_ID = '3cb99365b226c0f1918b24cbc2b84d49'
type WalletConnectContextProps = {
children: React.ReactNode | React.ReactNode[]
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/hooks/useSigningCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const useSigningCallback = ({ onSubmitting, onSuccess, onFinalized, onErr
error: (e: Error) => {
console.error(e)
const error = translateError(e)
addToast({ title: error.toString(), type: 'error' })
addToast({ title: error.message ?? error.toString(), type: 'error' })
onError && onError()
}
}
Expand Down
5 changes: 2 additions & 3 deletions packages/ui/src/hooks/useWalletConnectNamespace.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useApi } from '../contexts/ApiContext'
import { getWalletConnectId } from '../utils/getWalletConnectId'

export const useGetWalletConnectNamespace = () => {
const { client } = useApi()
Expand All @@ -16,9 +17,7 @@ export const useGetWalletConnectNamespace = () => {
.finally(() => setIsLoading(false))
}, [client])

const genesisTruncated = useMemo(() => genesisHash.substring(2, 34), [genesisHash])

const namespace = useMemo(() => `polkadot:${genesisTruncated}`, [genesisTruncated])
const namespace = useMemo(() => getWalletConnectId(genesisHash), [genesisHash])

const getAccountsWithNamespace = useCallback(
(accounts: string[]) => {
Expand Down
10 changes: 10 additions & 0 deletions packages/ui/src/utils/getAllNetworkWalletConnectIds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { networkList } from '../constants'
import { getWalletConnectId } from './getWalletConnectId'

export const getAllNetworkWalletConnectIds = (): string[] => {
return (
Object.values(networkList)
.map((network) => network.genesisHash)
.filter(Boolean) as string[]
).map((genesisHash) => `polkadot:${getWalletConnectId(genesisHash)}`)
}
1 change: 1 addition & 0 deletions packages/ui/src/utils/getWalletConnectId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const getWalletConnectId = (genesisHash: string) => genesisHash.substring(2, 34)
28 changes: 22 additions & 6 deletions packages/ui/src/walletConfigs.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import type { Config } from '@reactive-dot/core'
import { InjectedWalletProvider } from '@reactive-dot/core/wallets.js'
import { registerDotConnect } from 'dot-connect'
import { DAPP_NAME } from './constants'
import { DAPP_NAME, WALLETCONNECT_PROJECT_ID } from './constants'
import { WalletConnect } from '@reactive-dot/wallet-walletconnect'
import { defineConfig } from '@reactive-dot/core'
import { getAllNetworkWalletConnectIds } from './utils/getAllNetworkWalletConnectIds'

export const config = {
export const config = defineConfig({
chains: {},
wallets: [new InjectedWalletProvider({ originName: DAPP_NAME })]
} satisfies Config
wallets: [
new InjectedWalletProvider({ originName: DAPP_NAME }),
new WalletConnect({
projectId: WALLETCONNECT_PROJECT_ID,
providerOptions: {
metadata: {
name: 'Multix',
description: 'The best interface to create and manage multisigs on Polkadot.',
url: 'https://multix.chainsafe.io',
icons: ['https://multix.chainsafe.io/android-chrome-192x192.png?raw=true']
}
},
optionalChainIds: getAllNetworkWalletConnectIds()
})
]
})

// Register dot-connect custom elements & configure supported wallets
registerDotConnect({
wallets: config.wallets
wallets: config.wallets ?? []
})
Loading
Loading