2021-05-10 21:14:21 +02:00
|
|
|
import { ethErrors } from 'eth-rpc-errors';
|
|
|
|
import { omit } from 'lodash';
|
2023-05-10 07:36:01 +02:00
|
|
|
import { ApprovalType } from '@metamask/controller-utils';
|
2021-05-10 21:14:21 +02:00
|
|
|
import { MESSAGE_TYPE } from '../../../../../shared/constants/app';
|
2021-05-26 18:17:18 +02:00
|
|
|
import {
|
|
|
|
CHAIN_ID_TO_TYPE_MAP,
|
|
|
|
NETWORK_TO_NAME_MAP,
|
2021-06-10 00:18:38 +02:00
|
|
|
CHAIN_ID_TO_RPC_URL_MAP,
|
2022-09-14 16:55:31 +02:00
|
|
|
CURRENCY_SYMBOLS,
|
2023-04-28 14:34:10 +02:00
|
|
|
BUILT_IN_INFURA_NETWORKS,
|
2021-05-26 18:17:18 +02:00
|
|
|
} from '../../../../../shared/constants/network';
|
2021-05-10 21:14:21 +02:00
|
|
|
import {
|
|
|
|
isPrefixedFormattedHexString,
|
|
|
|
isSafeChainId,
|
|
|
|
} from '../../../../../shared/modules/network.utils';
|
|
|
|
|
|
|
|
const switchEthereumChain = {
|
|
|
|
methodNames: [MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN],
|
|
|
|
implementation: switchEthereumChainHandler,
|
2021-11-16 00:11:51 +01:00
|
|
|
hookNames: {
|
|
|
|
getCurrentChainId: true,
|
2023-03-09 22:00:28 +01:00
|
|
|
findNetworkConfigurationBy: true,
|
2021-11-16 00:11:51 +01:00
|
|
|
setProviderType: true,
|
2023-03-09 22:00:28 +01:00
|
|
|
setActiveNetwork: true,
|
2021-11-16 00:11:51 +01:00
|
|
|
requestUserApproval: true,
|
|
|
|
},
|
2021-05-10 21:14:21 +02:00
|
|
|
};
|
|
|
|
export default switchEthereumChain;
|
|
|
|
|
2023-03-09 22:00:28 +01:00
|
|
|
function findExistingNetwork(chainId, findNetworkConfigurationBy) {
|
2023-04-28 14:34:10 +02:00
|
|
|
if (
|
|
|
|
Object.values(BUILT_IN_INFURA_NETWORKS)
|
|
|
|
.map(({ chainId: id }) => id)
|
|
|
|
.includes(chainId)
|
|
|
|
) {
|
2021-05-26 18:17:18 +02:00
|
|
|
return {
|
|
|
|
chainId,
|
2022-09-14 16:55:31 +02:00
|
|
|
ticker: CURRENCY_SYMBOLS.ETH,
|
2021-06-10 00:18:38 +02:00
|
|
|
nickname: NETWORK_TO_NAME_MAP[chainId],
|
|
|
|
rpcUrl: CHAIN_ID_TO_RPC_URL_MAP[chainId],
|
|
|
|
type: CHAIN_ID_TO_TYPE_MAP[chainId],
|
2021-05-26 18:17:18 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-03-09 22:00:28 +01:00
|
|
|
return findNetworkConfigurationBy({ chainId });
|
2021-05-26 18:17:18 +02:00
|
|
|
}
|
|
|
|
|
2021-05-10 21:14:21 +02:00
|
|
|
async function switchEthereumChainHandler(
|
|
|
|
req,
|
|
|
|
res,
|
|
|
|
_next,
|
|
|
|
end,
|
2021-06-10 00:18:38 +02:00
|
|
|
{
|
|
|
|
getCurrentChainId,
|
2023-03-09 22:00:28 +01:00
|
|
|
findNetworkConfigurationBy,
|
2021-06-10 00:18:38 +02:00
|
|
|
setProviderType,
|
2023-03-09 22:00:28 +01:00
|
|
|
setActiveNetwork,
|
2021-06-10 00:18:38 +02:00
|
|
|
requestUserApproval,
|
|
|
|
},
|
2021-05-10 21:14:21 +02:00
|
|
|
) {
|
|
|
|
if (!req.params?.[0] || typeof req.params[0] !== 'object') {
|
|
|
|
return end(
|
|
|
|
ethErrors.rpc.invalidParams({
|
|
|
|
message: `Expected single, object parameter. Received:\n${JSON.stringify(
|
|
|
|
req.params,
|
|
|
|
)}`,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const { origin } = req;
|
|
|
|
|
|
|
|
const { chainId } = req.params[0];
|
|
|
|
|
|
|
|
const otherKeys = Object.keys(omit(req.params[0], ['chainId']));
|
|
|
|
|
|
|
|
if (otherKeys.length > 0) {
|
|
|
|
return end(
|
|
|
|
ethErrors.rpc.invalidParams({
|
|
|
|
message: `Received unexpected keys on object parameter. Unsupported keys:\n${otherKeys}`,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const _chainId = typeof chainId === 'string' && chainId.toLowerCase();
|
|
|
|
|
|
|
|
if (!isPrefixedFormattedHexString(_chainId)) {
|
|
|
|
return end(
|
|
|
|
ethErrors.rpc.invalidParams({
|
|
|
|
message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\n${chainId}`,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isSafeChainId(parseInt(_chainId, 16))) {
|
|
|
|
return end(
|
|
|
|
ethErrors.rpc.invalidParams({
|
|
|
|
message: `Invalid chain ID "${_chainId}": numerical value greater than max safe value. Received:\n${chainId}`,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-03-09 22:00:28 +01:00
|
|
|
const requestData = findExistingNetwork(_chainId, findNetworkConfigurationBy);
|
2021-06-10 00:18:38 +02:00
|
|
|
if (requestData) {
|
2021-05-10 21:14:21 +02:00
|
|
|
const currentChainId = getCurrentChainId();
|
|
|
|
if (currentChainId === _chainId) {
|
|
|
|
res.result = null;
|
|
|
|
return end();
|
|
|
|
}
|
|
|
|
try {
|
2021-06-10 00:18:38 +02:00
|
|
|
const approvedRequestData = await requestUserApproval({
|
|
|
|
origin,
|
2023-05-10 07:36:01 +02:00
|
|
|
type: ApprovalType.SwitchEthereumChain,
|
2021-06-10 00:18:38 +02:00
|
|
|
requestData,
|
|
|
|
});
|
2022-11-29 17:01:23 +01:00
|
|
|
if (
|
2023-04-28 14:34:10 +02:00
|
|
|
Object.values(BUILT_IN_INFURA_NETWORKS)
|
|
|
|
.map(({ chainId: id }) => id)
|
|
|
|
.includes(chainId)
|
2022-11-29 17:01:23 +01:00
|
|
|
) {
|
2023-04-17 20:15:01 +02:00
|
|
|
await setProviderType(approvedRequestData.type);
|
2021-06-10 00:18:38 +02:00
|
|
|
} else {
|
2023-04-06 17:07:34 +02:00
|
|
|
await setActiveNetwork(approvedRequestData.id);
|
2021-06-10 00:18:38 +02:00
|
|
|
}
|
2021-05-10 21:14:21 +02:00
|
|
|
res.result = null;
|
|
|
|
} catch (error) {
|
|
|
|
return end(error);
|
|
|
|
}
|
|
|
|
return end();
|
|
|
|
}
|
2021-05-11 00:29:07 +02:00
|
|
|
return end(
|
|
|
|
ethErrors.provider.custom({
|
|
|
|
code: 4902, // To-be-standardized "unrecognized chain ID" error
|
|
|
|
message: `Unrecognized chain ID "${chainId}". Try adding the chain using ${MESSAGE_TYPE.ADD_ETHEREUM_CHAIN} first.`,
|
|
|
|
}),
|
|
|
|
);
|
2021-05-10 21:14:21 +02:00
|
|
|
}
|