From 51152ca5cc756c8408b77c678614705313b9c35d Mon Sep 17 00:00:00 2001 From: S2kael Date: Thu, 28 Nov 2024 16:04:47 +0700 Subject: [PATCH] [Issue-3870] [WC] Allow substrate network use evm address to connect --- .../src/koni/background/handlers/Extension.ts | 11 +- .../ConnectWalletConnectConfirmation.tsx | 25 ++-- .../WalletConnect/Account/WCAccountSelect.tsx | 21 ++-- .../Network/WCNetworkSelected.tsx | 3 +- .../useSelectWalletConnectAccount.ts | 113 +++++++++++------- .../src/types/walletConnect.ts | 3 + .../src/utils/walletConnect/index.ts | 13 +- 7 files changed, 112 insertions(+), 77 deletions(-) diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 5f986c235f..a242bbf84a 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -42,7 +42,6 @@ import { EXTENSION_REQUEST_URL } from '@subwallet/extension-base/services/reques import { AuthUrls } from '@subwallet/extension-base/services/request-service/types'; import { DEFAULT_AUTO_LOCK_TIME } from '@subwallet/extension-base/services/setting-service/constants'; import { SWTransaction, SWTransactionResponse, SWTransactionResult, TransactionEmitter, ValidateTransactionResponseInput } from '@subwallet/extension-base/services/transaction-service/types'; -import { WALLET_CONNECT_EIP155_NAMESPACE } from '@subwallet/extension-base/services/wallet-connect-service/constants'; import { isProposalExpired, isSupportWalletConnectChain, isSupportWalletConnectNamespace } from '@subwallet/extension-base/services/wallet-connect-service/helpers'; import { ResultApproveWalletConnectSession, WalletConnectNotSupportRequest, WalletConnectSessionRequest } from '@subwallet/extension-base/services/wallet-connect-service/types'; import { SWStorage } from '@subwallet/extension-base/storage'; @@ -2945,14 +2944,14 @@ export default class KoniExtension { Object.entries(availableNamespaces) .forEach(([key, namespace]) => { if (namespace.chains) { - const accounts: string[] = []; + const accounts: string[] = selectedAccounts.filter((address) => { + const [_namespace] = address.split(':'); - const chains = uniqueStringArray(namespace.chains); - - chains.forEach((chain) => { - accounts.push(...(selectedAccounts.filter((address) => isEthereumAddress(address) === (key === WALLET_CONNECT_EIP155_NAMESPACE)).map((address) => `${chain}:${address}`))); + return _namespace === key; }); + const chains = uniqueStringArray(namespace.chains); + namespaces[key] = { accounts, methods: namespace.methods, diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/ConnectWalletConnectConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/ConnectWalletConnectConfirmation.tsx index 19b622bfce..74224a3233 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/ConnectWalletConnectConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/ConnectWalletConnectConfirmation.tsx @@ -3,6 +3,7 @@ import { WALLET_CONNECT_EIP155_NAMESPACE, WALLET_CONNECT_POLKADOT_NAMESPACE } from '@subwallet/extension-base/services/wallet-connect-service/constants'; import { WalletConnectSessionRequest } from '@subwallet/extension-base/services/wallet-connect-service/types'; +import { AccountChainType } from '@subwallet/extension-base/types'; import { AddNetworkWCModal, AlertBox, ConfirmationGeneralInfo, WCAccountSelect, WCNetworkSelected, WCNetworkSupported } from '@subwallet/extension-koni-ui/components'; import { ADD_NETWORK_WALLET_CONNECT_MODAL, TIME_OUT_RECORD } from '@subwallet/extension-koni-ui/constants'; import { useNotification, useSelectWalletConnectAccount, useSetSelectedAccountTypes } from '@subwallet/extension-koni-ui/hooks'; @@ -49,9 +50,9 @@ function Component ({ className, request }: Props) { const [blockAddNetwork, setBlockAddNetwork] = useState(false); const [networkNeedToImport, setNetworkNeedToImport] = useState([]); - const nameSpaceNameMap = useMemo((): Record => ({ - [WALLET_CONNECT_EIP155_NAMESPACE]: t('EVM networks'), - [WALLET_CONNECT_POLKADOT_NAMESPACE]: t('Substrate networks') + const accountTypeNameMap = useMemo((): Record => ({ + [AccountChainType.ETHEREUM]: t('EVM accounts'), + [AccountChainType.SUBSTRATE]: t('Substrate accounts') }), [t]); const { activeModal, inactiveModal } = useContext(ModalContext); @@ -73,8 +74,8 @@ function Component ({ className, request }: Props) { onApplyAccounts, onCancelSelectAccounts, onSelectAccount, + supportOneAccountType, supportOneChain, - supportOneNamespace, supportedChains } = useSelectWalletConnectAccount(params); const allowSubmit = useMemo(() => { @@ -137,7 +138,10 @@ function Component ({ className, request }: Props) { const onConfirm = useCallback(() => { setLoading(true); - const selectedAccounts = Object.values(namespaceAccounts).map(({ appliedAccounts }) => appliedAccounts).flat(); + const selectedAccounts = Object.values(namespaceAccounts) + .flatMap(({ appliedAccounts, networks }) => { + return networks.flatMap(({ wcChain }) => appliedAccounts.map((address) => `${wcChain}:${address}`)); + }); handleConfirm(request, selectedAccounts) .catch((e) => { @@ -234,17 +238,17 @@ function Component ({ className, request }: Props) {
{ Object.entries(namespaceAccounts).map(([namespace, value]) => { - const { appliedAccounts, availableAccounts, networks, selectedAccounts } = value; + const { accountType, appliedAccounts, availableAccounts, networks, selectedAccounts } = value; return (
{!supportOneChain && ( <>
- {supportOneNamespace ? t('Networks') : nameSpaceNameMap[namespace]} + {supportOneAccountType ? t('Networks') : accountTypeNameMap[namespace]}
)} { - supportOneNamespace && ( + supportOneAccountType && (
{t('Choose the account(s) you’d like to connect')}
) }
); diff --git a/packages/extension-koni-ui/src/components/WalletConnect/Account/WCAccountSelect.tsx b/packages/extension-koni-ui/src/components/WalletConnect/Account/WCAccountSelect.tsx index 23480b582a..1f628399f7 100644 --- a/packages/extension-koni-ui/src/components/WalletConnect/Account/WCAccountSelect.tsx +++ b/packages/extension-koni-ui/src/components/WalletConnect/Account/WCAccountSelect.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants'; -import { AccountJson } from '@subwallet/extension-base/types'; +import { AccountChainType, AccountJson } from '@subwallet/extension-base/types'; import { isSameAddress } from '@subwallet/extension-base/utils'; import { AccountItemWithProxyAvatar, AccountProxySelectorAllItem, AlertBox } from '@subwallet/extension-koni-ui/components'; import { useTranslation } from '@subwallet/extension-koni-ui/hooks'; @@ -24,6 +24,7 @@ interface Props extends ThemeProps { selectedAccounts: string[]; appliedAccounts: string[]; availableAccounts: AccountJson[]; + accountType: AccountChainType; onSelectAccount: (account: string, applyImmediately?: boolean) => VoidFunction; useModal: boolean; onApply: () => void; @@ -33,7 +34,7 @@ interface Props extends ThemeProps { const renderEmpty = () => ; const Component: React.FC = (props: Props) => { - const { appliedAccounts, availableAccounts, className, id, namespace, onApply, onCancel, onSelectAccount, selectedAccounts, useModal } = props; + const { accountType, appliedAccounts, availableAccounts, className, id, onApply, onCancel, onSelectAccount, selectedAccounts, useModal } = props; const { t } = useTranslation(); @@ -44,26 +45,26 @@ const Component: React.FC = (props: Props) => { const isActive = checkActive(id); const noAccountTitle = useMemo(() => { - switch (namespace) { - case 'polkadot': + switch (accountType) { + case AccountChainType.SUBSTRATE: return t('No available Substrate account'); - case 'eip155': + case AccountChainType.ETHEREUM: return t('No available EVM account'); default: return t('No available account'); } - }, [namespace, t]); + }, [accountType, t]); const noAccountDescription = useMemo(() => { - switch (namespace) { - case 'polkadot': + switch (accountType) { + case AccountChainType.SUBSTRATE: return t("You don't have any Substrate account to connect. Please create one or skip this step by hitting Cancel."); - case 'eip155': + case AccountChainType.ETHEREUM: return t("You don't have any EVM account to connect. Please create one or skip this step by hitting Cancel."); default: return t("You don't have any account to connect. Please create one or skip this step by hitting Cancel."); } - }, [namespace, t]); + }, [accountType, t]); const basicProxyAccounts = useMemo(() => { return availableAccounts.map(({ name, proxyId }) => ({ name, id: proxyId || '' })); diff --git a/packages/extension-koni-ui/src/components/WalletConnect/Network/WCNetworkSelected.tsx b/packages/extension-koni-ui/src/components/WalletConnect/Network/WCNetworkSelected.tsx index 4ab6466565..51fe660929 100644 --- a/packages/extension-koni-ui/src/components/WalletConnect/Network/WCNetworkSelected.tsx +++ b/packages/extension-koni-ui/src/components/WalletConnect/Network/WCNetworkSelected.tsx @@ -32,7 +32,8 @@ const Component: React.FC = (props: Props) => { slug: '', name: t('{{number}} unknown network', { replace: { number: unSupportNetworks.length } }) }, - slug: '' + slug: '', + wcChain: '' } ) : null; diff --git a/packages/extension-koni-ui/src/hooks/wallet-connect/useSelectWalletConnectAccount.ts b/packages/extension-koni-ui/src/hooks/wallet-connect/useSelectWalletConnectAccount.ts index a45aa832ab..9ac9f327f8 100644 --- a/packages/extension-koni-ui/src/hooks/wallet-connect/useSelectWalletConnectAccount.ts +++ b/packages/extension-koni-ui/src/hooks/wallet-connect/useSelectWalletConnectAccount.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { AccountAuthType } from '@subwallet/extension-base/background/types'; -import { WALLET_CONNECT_EIP155_NAMESPACE, WALLET_CONNECT_POLKADOT_NAMESPACE, WALLET_CONNECT_SUPPORT_NAMESPACES } from '@subwallet/extension-base/services/wallet-connect-service/constants'; +import { WALLET_CONNECT_SUPPORT_NAMESPACES } from '@subwallet/extension-base/services/wallet-connect-service/constants'; import { isProposalExpired, isSupportWalletConnectChain, isSupportWalletConnectNamespace } from '@subwallet/extension-base/services/wallet-connect-service/helpers'; -import { AccountJson } from '@subwallet/extension-base/types'; +import { AccountChainType, AccountJson } from '@subwallet/extension-base/types'; import { isSameAddress, uniqueStringArray } from '@subwallet/extension-base/utils'; import { WalletConnectChainInfo } from '@subwallet/extension-koni-ui/types'; import { chainsToWalletConnectChainInfos, isAccountAll, reformatAddress } from '@subwallet/extension-koni-ui/utils'; @@ -16,11 +16,13 @@ import { useSelector } from '../common'; interface SelectAccount { availableAccounts: AccountJson[]; networks: WalletConnectChainInfo[]; + accountType: AccountChainType; selectedAccounts: string[]; appliedAccounts: string[]; } const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { + // Result by acc const [result, setResult] = useState>({}); const { accounts } = useSelector((state) => state.accountState); @@ -28,7 +30,7 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { const noAllAccount = useMemo(() => accounts.filter(({ address }) => !isAccountAll(address)), [accounts]); - const namespaces = useMemo(() => { + const accountTypeMap = useMemo(() => { const availableNamespaces: Record = {}; const result: Record = {}; @@ -59,8 +61,18 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { } }); - for (const [namespace, chains] of Object.entries(availableNamespaces)) { - result[namespace] = chainsToWalletConnectChainInfos(chainInfoMap, uniqueStringArray(chains)); + for (const chains of Object.values(availableNamespaces)) { + const wcChains = chainsToWalletConnectChainInfos(chainInfoMap, uniqueStringArray(chains)); + + for (const chain of wcChains) { + if (chain.accountType) { + if (!result[chain.accountType]) { + result[chain.accountType] = []; + } + + result[chain.accountType].push(chain); + } + } } return result; @@ -90,28 +102,42 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { }, [chainInfoMap, params.optionalNamespaces, params.requiredNamespaces]); const missingType = useMemo((): AccountAuthType[] => { - const result: AccountAuthType[] = []; - - Object.keys(params.requiredNamespaces || {}).forEach((namespace) => { - if (WALLET_CONNECT_SUPPORT_NAMESPACES.includes(namespace)) { - const available = noAllAccount.some((acc) => { - if (namespace === WALLET_CONNECT_EIP155_NAMESPACE && acc.chainType === 'ethereum') { - return true; - } else if (namespace === WALLET_CONNECT_POLKADOT_NAMESPACE && acc.chainType === 'substrate') { - return true; + const setAccountTypes = Object.entries(params.requiredNamespaces || {}) + .reduce((rs, [namespace, data]) => { + if (WALLET_CONNECT_SUPPORT_NAMESPACES.includes(namespace)) { + const chains = chainsToWalletConnectChainInfos(chainInfoMap, uniqueStringArray(data.chains || [])); + + for (const chain of chains) { + if (chain.chainInfo && chain.accountType) { + rs.add(chain.accountType); + } } + } - return false; - }); + return rs; + }, new Set()); - if (!available) { - result.push(WALLET_CONNECT_EIP155_NAMESPACE === namespace ? 'evm' : 'substrate'); - } + const missingAccountTypes = Array.from(setAccountTypes); + + for (const account of noAllAccount) { + if (missingAccountTypes.includes(account.chainType)) { + missingAccountTypes.splice(missingAccountTypes.indexOf(account.chainType), 1); } - }); + } - return result; - }, [noAllAccount, params.requiredNamespaces]); + return missingAccountTypes.map((value): AccountAuthType => { + switch (value) { + case AccountChainType.ETHEREUM: + return 'evm'; + case AccountChainType.SUBSTRATE: + return 'substrate'; + case AccountChainType.TON: + return 'ton'; + default: + throw new Error(`Unsupported account type: ${value}`); + } + }); + }, [noAllAccount, params.requiredNamespaces, chainInfoMap]); const isUnSupportCase = useMemo(() => Object.values(params.requiredNamespaces || {}) @@ -123,7 +149,7 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { const isExitedAnotherUnsupportedNamespace = useMemo(() => params.requiredNamespaces && Object.keys(params.requiredNamespaces).some((namespace) => !isSupportWalletConnectNamespace(namespace)), [params.requiredNamespaces]); const supportOneChain = useMemo(() => supportedChains.length === 1, [supportedChains]); - const supportOneNamespace = useMemo(() => Object.keys(namespaces).length === 1, [namespaces]); + const supportOneAccountType = useMemo(() => Object.keys(accountTypeMap).length === 1, [accountTypeMap]); const noNetwork = useMemo((): boolean => { return (!params.requiredNamespaces || !Object.keys(params.requiredNamespaces).length) && (!params.optionalNamespaces || !Object.keys(params.optionalNamespaces).length); }, [params.optionalNamespaces, params.requiredNamespaces]); @@ -192,35 +218,30 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { setResult((oldState) => { const result: Record = {}; - const selectReplace = JSON.stringify(namespaces) !== JSON.stringify(namespaceRef.current); - - for (const [namespace, networks] of Object.entries(namespaces)) { - if (WALLET_CONNECT_SUPPORT_NAMESPACES.includes(namespace)) { - result[namespace] = { - networks, - selectedAccounts: selectReplace ? [] : oldState[namespace]?.selectedAccounts || [], - appliedAccounts: selectReplace ? [] : oldState[namespace]?.appliedAccounts || [], - availableAccounts: noAllAccount - .filter((acc) => { - if (namespace === WALLET_CONNECT_EIP155_NAMESPACE && acc.chainType === 'ethereum') { - return true; - } else if (namespace === WALLET_CONNECT_POLKADOT_NAMESPACE && acc.chainType === 'substrate') { - return true; - } - - return false; - }) - }; - } + const selectReplace = JSON.stringify(accountTypeMap) !== JSON.stringify(namespaceRef.current); + + for (const [_accountType, networks] of Object.entries(accountTypeMap)) { + const accountType = _accountType as AccountChainType; + + result[accountType] = { + networks, + selectedAccounts: selectReplace ? [] : oldState[accountType]?.selectedAccounts || [], + appliedAccounts: selectReplace ? [] : oldState[accountType]?.appliedAccounts || [], + availableAccounts: noAllAccount + .filter((acc) => { + return acc.chainType === accountType; + }), + accountType + }; } return result; }); return () => { - namespaceRef.current = namespaces; + namespaceRef.current = accountTypeMap; }; - }, [noAllAccount, namespaces]); + }, [noAllAccount, accountTypeMap]); useEffect(() => { const callback = (): boolean => { @@ -257,7 +278,7 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => { onSelectAccount, isExitedAnotherUnsupportedNamespace, supportOneChain, - supportOneNamespace, + supportOneAccountType, supportedChains }; }; diff --git a/packages/extension-koni-ui/src/types/walletConnect.ts b/packages/extension-koni-ui/src/types/walletConnect.ts index 069f4ad21f..13cdc314ff 100644 --- a/packages/extension-koni-ui/src/types/walletConnect.ts +++ b/packages/extension-koni-ui/src/types/walletConnect.ts @@ -1,10 +1,13 @@ // Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { AccountChainType } from '@subwallet/extension-base/types'; import { ChainInfo } from '@subwallet/extension-koni-ui/types/chain'; export interface WalletConnectChainInfo { chainInfo: ChainInfo | null; slug: string; supported: boolean; + accountType?: AccountChainType; + wcChain: string; } diff --git a/packages/extension-koni-ui/src/utils/walletConnect/index.ts b/packages/extension-koni-ui/src/utils/walletConnect/index.ts index 122765d0f8..17aaa463ea 100644 --- a/packages/extension-koni-ui/src/utils/walletConnect/index.ts +++ b/packages/extension-koni-ui/src/utils/walletConnect/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { _ChainInfo } from '@subwallet/chain-list/types'; -import { findChainInfoByChainId, findChainInfoByHalfGenesisHash } from '@subwallet/extension-base/services/chain-service/utils'; +import { _chainInfoToChainType, findChainInfoByChainId, findChainInfoByHalfGenesisHash } from '@subwallet/extension-base/services/chain-service/utils'; import { WALLET_CONNECT_EIP155_NAMESPACE, WALLET_CONNECT_POLKADOT_NAMESPACE } from '@subwallet/extension-base/services/wallet-connect-service/constants'; import { AccountProxy } from '@subwallet/extension-base/types'; import { WalletConnectChainInfo } from '@subwallet/extension-koni-ui/types'; @@ -18,7 +18,9 @@ export const chainsToWalletConnectChainInfos = (chainMap: Record