Skip to content

Commit

Permalink
Merge pull request #3873 from Koniverse/koni/dev/issue-3870
Browse files Browse the repository at this point in the history
[Issue-3870] WalletConnect - Allow Polkadot namespace use EVM address
  • Loading branch information
saltict authored Dec 9, 2024
2 parents ea8d9a5 + 51152ca commit 02651f9
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,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';
Expand Down Expand Up @@ -2985,14 +2984,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -49,9 +50,9 @@ function Component ({ className, request }: Props) {
const [blockAddNetwork, setBlockAddNetwork] = useState(false);
const [networkNeedToImport, setNetworkNeedToImport] = useState<string[]>([]);

const nameSpaceNameMap = useMemo((): Record<string, string> => ({
[WALLET_CONNECT_EIP155_NAMESPACE]: t('EVM networks'),
[WALLET_CONNECT_POLKADOT_NAMESPACE]: t('Substrate networks')
const accountTypeNameMap = useMemo((): Record<string, string> => ({
[AccountChainType.ETHEREUM]: t('EVM accounts'),
[AccountChainType.SUBSTRATE]: t('Substrate accounts')
}), [t]);
const { activeModal, inactiveModal } = useContext(ModalContext);

Expand All @@ -73,8 +74,8 @@ function Component ({ className, request }: Props) {
onApplyAccounts,
onCancelSelectAccounts,
onSelectAccount,
supportOneAccountType,
supportOneChain,
supportOneNamespace,
supportedChains } = useSelectWalletConnectAccount(params);

const allowSubmit = useMemo(() => {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -234,17 +238,17 @@ function Component ({ className, request }: Props) {
<div className='namespaces-list'>
{
Object.entries(namespaceAccounts).map(([namespace, value]) => {
const { appliedAccounts, availableAccounts, networks, selectedAccounts } = value;
const { accountType, appliedAccounts, availableAccounts, networks, selectedAccounts } = value;

return (
<div
className={CN('namespace-container', { 'space-xs': !supportOneNamespace })}
className={CN('namespace-container', { 'space-xs': !supportOneAccountType })}
key={namespace}
>
{!supportOneChain && (
<>
<div className='namespace-title'>
{supportOneNamespace ? t('Networks') : nameSpaceNameMap[namespace]}
{supportOneAccountType ? t('Networks') : accountTypeNameMap[namespace]}
</div>
<WCNetworkSelected
id={`${namespace}-networks`}
Expand All @@ -253,13 +257,14 @@ function Component ({ className, request }: Props) {
</>
)}
{
supportOneNamespace && (
supportOneAccountType && (
<div className='account-list-title'>
{t('Choose the account(s) you’d like to connect')}
</div>
)
}
<WCAccountSelect
accountType={accountType}
appliedAccounts={appliedAccounts}
availableAccounts={availableAccounts}
id={`${namespace}-accounts`}
Expand All @@ -268,7 +273,7 @@ function Component ({ className, request }: Props) {
onCancel={onCancelModal(namespace)}
onSelectAccount={_onSelectAccount(namespace)}
selectedAccounts={selectedAccounts}
useModal={!supportOneNamespace}
useModal={!supportOneAccountType}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -33,7 +34,7 @@ interface Props extends ThemeProps {
const renderEmpty = () => <GeneralEmptyList />;

const Component: React.FC<Props> = (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();

Expand All @@ -44,26 +45,26 @@ const Component: React.FC<Props> = (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 || '' }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const Component: React.FC<Props> = (props: Props) => {
slug: '',
name: t('{{number}} unknown network', { replace: { number: unSupportNetworks.length } })
},
slug: ''
slug: '',
wcChain: ''
}
)
: null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -16,19 +16,21 @@ 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<Record<string, SelectAccount>>({});

const { accounts } = useSelector((state) => state.accountState);
const { chainInfoMap } = useSelector((state) => state.chainStore);

const noAllAccount = useMemo(() => accounts.filter(({ address }) => !isAccountAll(address)), [accounts]);

const namespaces = useMemo(() => {
const accountTypeMap = useMemo(() => {
const availableNamespaces: Record<string, string[]> = {};
const result: Record<string, WalletConnectChainInfo[]> = {};

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<AccountChainType>());

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 || {})
Expand All @@ -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]);
Expand Down Expand Up @@ -192,35 +218,30 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => {
setResult((oldState) => {
const result: Record<string, SelectAccount> = {};

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 => {
Expand Down Expand Up @@ -257,7 +278,7 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => {
onSelectAccount,
isExitedAnotherUnsupportedNamespace,
supportOneChain,
supportOneNamespace,
supportOneAccountType,
supportedChains
};
};
Expand Down
3 changes: 3 additions & 0 deletions packages/extension-koni-ui/src/types/walletConnect.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

1 comment on commit 02651f9

@saltict
Copy link
Author

@saltict saltict commented on 02651f9 Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.