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

[Issue-1874] Allow Polkadot namespace use EVM address via WalletConnect #1892

Merged
merged 2 commits into from
Jan 16, 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
2 changes: 1 addition & 1 deletion html/DevModeWeb.bundle/site/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>SubWallet</title><script inline inline-asset="fallback.js$" inline-asset-delete></script><script inline inline-asset="web-runner.js$" inline-asset-delete></script><script defer="defer" src="fallback-0b81a7f9f3a8fbf3b257.js"></script><script defer="defer" src="web-runner-e3836fbe8eb063db5a88.js"></script></head><body><div id="root">SubWallet</div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>SubWallet</title><script inline inline-asset="fallback.js$" inline-asset-delete></script><script inline inline-asset="web-runner.js$" inline-asset-delete></script><script defer="defer" src="fallback-0b81a7f9f3a8fbf3b257.js"></script><script defer="defer" src="web-runner-29461d321f6b930dc736.js"></script></head><body><div id="root">SubWallet</div></body></html>

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion html/Web.bundle/site/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>SubWallet</title><script inline inline-asset="fallback.js$" inline-asset-delete></script><script inline inline-asset="web-runner.js$" inline-asset-delete></script><script defer="defer" src="fallback-0b81a7f9f3a8fbf3b257.js"></script><script defer="defer" src="web-runner-7ef576356afe78e6d44f.js"></script></head><body><div id="root">SubWallet</div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>SubWallet</title><script inline inline-asset="fallback.js$" inline-asset-delete></script><script inline inline-asset="web-runner.js$" inline-asset-delete></script><script defer="defer" src="fallback-0b81a7f9f3a8fbf3b257.js"></script><script defer="defer" src="web-runner-833394d31ed408db3bbe.js"></script></head><body><div id="root">SubWallet</div></body></html>

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions src/components/WalletConnect/Account/WCAccountSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ import AccountItemWithName from 'components/common/Account/Item/AccountItemWithN
import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
import { CheckCircle } from 'phosphor-react-native';
import { ModalRef } from 'types/modalRef';
import { AccountJson } from '@subwallet/extension-base/types';
import { AccountChainType, AccountJson } from '@subwallet/extension-base/types';
import { isSameAddress } from '@subwallet/extension-base/utils';

interface Props {
selectedAccounts: string[];
appliedAccounts: string[];
availableAccounts: AccountJson[];
accountType: AccountChainType;
onSelectAccount: (account: string, applyImmediately?: boolean) => VoidFunction;
useModal: boolean;
onApply: () => void;
onCancel: () => void;
namespace: string;
}

const renderButtonIcon = (color: string) => <Icon phosphorIcon={CheckCircle} weight={'fill'} iconColor={color} />;

export const WCAccountSelect = ({
accountType,
appliedAccounts,
availableAccounts,
onApply,
onCancel,
onSelectAccount,
selectedAccounts,
useModal,
namespace,
}: Props) => {
const modalRef = useRef<ModalRef>();

Expand Down Expand Up @@ -72,26 +72,26 @@ export const WCAccountSelect = ({
);

const noAccountTitle = useMemo(() => {
switch (namespace) {
case 'polkadot':
switch (accountType) {
case AccountChainType.SUBSTRATE:
return i18n.formatString(i18n.common.noAvailableAccount, 'Substrate') as string;
case 'eip155':
case AccountChainType.ETHEREUM:
return i18n.formatString(i18n.common.noAvailableAccount, 'EVM') as string;
default:
return i18n.formatString(i18n.common.noAvailableAccount, '') as string;
}
}, [namespace]);
}, [accountType]);

const noAccountDescription = useMemo(() => {
switch (namespace) {
case 'polkadot':
switch (accountType) {
case AccountChainType.SUBSTRATE:
return i18n.formatString(i18n.common.youDonotHaveAnyAcc, 'Substrate') as string;
case 'eip155':
case AccountChainType.ETHEREUM:
return i18n.formatString(i18n.common.youDonotHaveAnyAcc, 'EVM') as string;
default:
return i18n.formatString(i18n.common.youDonotHaveAnyAcc, '') as string;
}
}, [namespace]);
}, [accountType]);

return (
<View style={{ width: '100%' }}>
Expand Down
3 changes: 2 additions & 1 deletion src/components/WalletConnect/Network/WCNetworkSelected.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ export const WCNetworkSelected = ({ networks }: Props) => {
name:
unSupportNetworks.length <= 1
? i18n.message.unknownNetwork
: i18n.formatString(i18n.message.unknownNetworks, unSupportNetworks.length),
: (i18n.formatString(i18n.message.unknownNetworks, unSupportNetworks.length) as string),
},
slug: '',
wcChain: '',
}
: null;

Expand Down
182 changes: 105 additions & 77 deletions src/hooks/wallet-connect/useSelectWalletConnectAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,64 +10,73 @@ import {
isSupportWalletConnectNamespace,
} from '@subwallet/extension-base/services/wallet-connect-service/helpers';
import { isSameAddress, reformatAddress, uniqueStringArray } from '@subwallet/extension-base/utils';
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 { ProposalTypes } from '@walletconnect/types';
import { chainsToWalletConnectChainInfos } from 'utils/walletConnect';
import { AccountJson } from '@subwallet/extension-base/types';
import { AccountChainType, AccountJson } from '@subwallet/extension-base/types';

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: RootState) => state.accountState);
const { chainInfoMap } = useSelector((state: RootState) => state.chainStore);

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

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

Object.entries(params.requiredNamespaces).forEach(([key, namespace]) => {
if (isSupportWalletConnectNamespace(key)) {
if (namespace.chains) {
availableNamespaces[key] = namespace.chains;
params.requiredNamespaces &&
Object.entries(params.requiredNamespaces).forEach(([key, namespace]) => {
if (isSupportWalletConnectNamespace(key)) {
if (namespace.chains) {
availableNamespaces[key] = namespace.chains;
}
}
}
});
});

Object.entries(params.optionalNamespaces).forEach(([key, namespace]) => {
if (isSupportWalletConnectNamespace(key)) {
if (namespace.chains) {
const requiredNameSpace = availableNamespaces[key];
const defaultChains: string[] = [];

if (requiredNameSpace) {
availableNamespaces[key] = [
...(requiredNameSpace || defaultChains),
...(namespace.chains || defaultChains),
];
} else {
if (namespace.chains.length) {
availableNamespaces[key] = namespace.chains;
params.optionalNamespaces &&
Object.entries(params.optionalNamespaces).forEach(([key, namespace]) => {
if (isSupportWalletConnectNamespace(key)) {
if (namespace.chains) {
const requiredNameSpace = availableNamespaces[key];
const defaultChains: string[] = [];

if (requiredNameSpace) {
availableNamespaces[key] = [
...(requiredNameSpace || defaultChains),
...(namespace.chains || defaultChains),
];
} else {
if (namespace.chains.length) {
availableNamespaces[key] = namespace.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] = [];
}

for (const [namespace, chains] of Object.entries(availableNamespaces)) {
_result[namespace] = chainsToWalletConnectChainInfos(chainInfoMap, uniqueStringArray(chains));
_result[chain.accountType].push(chain);
}
}
}

return _result;
Expand All @@ -76,15 +85,19 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => {
const supportedChains = useMemo(() => {
const chains: string[] = [];

for (const [key, namespace] of Object.entries(params.requiredNamespaces)) {
if (isSupportWalletConnectNamespace(key)) {
chains.push(...(namespace.chains || []));
if (params.requiredNamespaces) {
for (const [key, namespace] of Object.entries(params.requiredNamespaces)) {
if (isSupportWalletConnectNamespace(key)) {
chains.push(...(namespace.chains || []));
}
}
}

for (const [key, namespace] of Object.entries(params.optionalNamespaces)) {
if (isSupportWalletConnectNamespace(key)) {
chains.push(...(namespace.chains || []));
if (params.optionalNamespaces) {
for (const [key, namespace] of Object.entries(params.optionalNamespaces)) {
if (isSupportWalletConnectNamespace(key)) {
chains.push(...(namespace.chains || []));
}
}
}

Expand All @@ -94,45 +107,59 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => {
}, [chainInfoMap, params.optionalNamespaces, params.requiredNamespaces]);

const missingType = useMemo((): AccountAuthType[] => {
const _result: AccountAuthType[] = [];

Object.keys(params.requiredNamespaces).forEach(namespace => {
const setAccountTypes = Object.entries(params.requiredNamespaces || {}).reduce((rs, [namespace, data]) => {
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 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)
Object.values(params.requiredNamespaces || {})
.map(namespace => namespace.chains || [])
.flat()
.some(chain => !isSupportWalletConnectChain(chain, chainInfoMap)),
[chainInfoMap, params.requiredNamespaces],
);

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) &&
Expand Down Expand Up @@ -201,38 +228,39 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => {
});
}, []);

// const abiCoder = noAllAccount.filter(acc => {
// return acc.chainType === accountType;
// })

// console.log('abiCoder', abiCoder);

useEffect(() => {
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;
}
const selectReplace = JSON.stringify(accountTypeMap) !== JSON.stringify(namespaceRef.current);

return false;
}),
};
}
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 @@ -263,13 +291,13 @@ const useSelectWalletConnectAccount = (params: ProposalTypes.Struct) => {
isUnSupportCase,
missingType,
namespaceAccounts: result,
noNetwork,
onApplyAccounts,
onCancelSelectAccounts,
onSelectAccount,
isExitedAnotherUnsupportedNamespace,
supportOneChain,
noNetwork,
supportOneNamespace,
supportOneAccountType,
supportedChains,
};
};
Expand Down
1 change: 1 addition & 0 deletions src/screens/Confirmations/parts/Sign/Substrate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ export const SubstrateSignArea = (props: Props) => {
showQuoteExpired ||
loadingChain ||
hashLoading ||
loading ||
(isMessage ? !modeCanSignMessage.includes(signMode) : alertData?.type === 'error')
}
icon={getButtonIcon(approveIcon)}
Expand Down
Loading
Loading