mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Additional incoming transactions support (#14219)
This commit is contained in:
parent
3ec042fa68
commit
3a00b5ab54
@ -9,11 +9,7 @@ import {
|
||||
TransactionType,
|
||||
TransactionStatus,
|
||||
} from '../../../shared/constants/transaction';
|
||||
import {
|
||||
CHAIN_IDS,
|
||||
CHAIN_ID_TO_NETWORK_ID_MAP,
|
||||
CHAIN_ID_TO_TYPE_MAP,
|
||||
} from '../../../shared/constants/network';
|
||||
import { ETHERSCAN_SUPPORTED_NETWORKS } from '../../../shared/constants/network';
|
||||
import { bnToHex } from '../../../shared/modules/conversion.utils';
|
||||
|
||||
const fetchWithTimeout = getFetchWithTimeout();
|
||||
@ -46,15 +42,9 @@ const fetchWithTimeout = getFetchWithTimeout();
|
||||
* This controller is responsible for retrieving incoming transactions. Etherscan is polled once every block to check
|
||||
* for new incoming transactions for the current selected account on the current network
|
||||
*
|
||||
* Note that only the built-in Infura networks are supported (i.e. anything in `INFURA_PROVIDER_TYPES`). We will not
|
||||
* attempt to retrieve incoming transactions on any custom RPC endpoints.
|
||||
* Note that only Etherscan-compatible networks are supported. We will not attempt to retrieve incoming transactions
|
||||
* on non-compatible custom RPC endpoints.
|
||||
*/
|
||||
const etherscanSupportedNetworks = [
|
||||
CHAIN_IDS.GOERLI,
|
||||
CHAIN_IDS.MAINNET,
|
||||
CHAIN_IDS.SEPOLIA,
|
||||
];
|
||||
|
||||
export default class IncomingTransactionsController {
|
||||
constructor(opts = {}) {
|
||||
const {
|
||||
@ -75,13 +65,16 @@ export default class IncomingTransactionsController {
|
||||
await this._update(selectedAddress, newBlockNumberDec);
|
||||
};
|
||||
|
||||
const incomingTxLastFetchedBlockByChainId = Object.keys(
|
||||
ETHERSCAN_SUPPORTED_NETWORKS,
|
||||
).reduce((network, chainId) => {
|
||||
network[chainId] = null;
|
||||
return network;
|
||||
}, {});
|
||||
|
||||
const initState = {
|
||||
incomingTransactions: {},
|
||||
incomingTxLastFetchedBlockByChainId: {
|
||||
[CHAIN_IDS.GOERLI]: null,
|
||||
[CHAIN_IDS.MAINNET]: null,
|
||||
[CHAIN_IDS.SEPOLIA]: null,
|
||||
},
|
||||
incomingTxLastFetchedBlockByChainId,
|
||||
...opts.initState,
|
||||
};
|
||||
this.store = new ObservableStore(initState);
|
||||
@ -171,7 +164,7 @@ export default class IncomingTransactionsController {
|
||||
const { completedOnboarding } = this.onboardingController.store.getState();
|
||||
const chainId = this.getCurrentChainId();
|
||||
if (
|
||||
!etherscanSupportedNetworks.includes(chainId) ||
|
||||
!Object.hasOwnProperty.call(ETHERSCAN_SUPPORTED_NETWORKS, chainId) ||
|
||||
!address ||
|
||||
!completedOnboarding
|
||||
) {
|
||||
@ -235,12 +228,10 @@ export default class IncomingTransactionsController {
|
||||
* @returns {TransactionMeta[]}
|
||||
*/
|
||||
async _getNewIncomingTransactions(address, fromBlock, chainId) {
|
||||
const etherscanSubdomain =
|
||||
chainId === CHAIN_IDS.MAINNET
|
||||
? 'api'
|
||||
: `api-${CHAIN_ID_TO_TYPE_MAP[chainId]}`;
|
||||
const etherscanDomain = ETHERSCAN_SUPPORTED_NETWORKS[chainId].domain;
|
||||
const etherscanSubdomain = ETHERSCAN_SUPPORTED_NETWORKS[chainId].subdomain;
|
||||
|
||||
const apiUrl = `https://${etherscanSubdomain}.etherscan.io`;
|
||||
const apiUrl = `https://${etherscanSubdomain}.${etherscanDomain}`;
|
||||
let url = `${apiUrl}/api?module=account&action=txlist&address=${address}&tag=latest&page=1`;
|
||||
|
||||
if (fromBlock) {
|
||||
@ -303,7 +294,7 @@ export default class IncomingTransactionsController {
|
||||
blockNumber: etherscanTransaction.blockNumber,
|
||||
id: createId(),
|
||||
chainId,
|
||||
metamaskNetworkId: CHAIN_ID_TO_NETWORK_ID_MAP[chainId],
|
||||
metamaskNetworkId: ETHERSCAN_SUPPORTED_NETWORKS[chainId].networkId,
|
||||
status,
|
||||
time,
|
||||
txParams,
|
||||
|
@ -6,7 +6,7 @@ import { cloneDeep } from 'lodash';
|
||||
|
||||
import waitUntilCalled from '../../../test/lib/wait-until-called';
|
||||
import {
|
||||
CHAIN_ID_TO_TYPE_MAP,
|
||||
ETHERSCAN_SUPPORTED_NETWORKS,
|
||||
CHAIN_IDS,
|
||||
NETWORK_TYPES,
|
||||
NETWORK_IDS,
|
||||
@ -34,11 +34,12 @@ const PREPOPULATED_BLOCKS_BY_NETWORK = {
|
||||
[CHAIN_IDS.MAINNET]: 3,
|
||||
[CHAIN_IDS.SEPOLIA]: 6,
|
||||
};
|
||||
const EMPTY_BLOCKS_BY_NETWORK = {
|
||||
[CHAIN_IDS.GOERLI]: null,
|
||||
[CHAIN_IDS.MAINNET]: null,
|
||||
[CHAIN_IDS.SEPOLIA]: null,
|
||||
};
|
||||
const EMPTY_BLOCKS_BY_NETWORK = Object.keys(
|
||||
ETHERSCAN_SUPPORTED_NETWORKS,
|
||||
).reduce((network, chainId) => {
|
||||
network[chainId] = null;
|
||||
return network;
|
||||
}, {});
|
||||
|
||||
function getEmptyInitState() {
|
||||
return {
|
||||
@ -150,20 +151,13 @@ const getFakeEtherscanTransaction = ({
|
||||
};
|
||||
|
||||
function nockEtherscanApiForAllChains(mockResponse) {
|
||||
for (const chainId of [
|
||||
CHAIN_IDS.GOERLI,
|
||||
CHAIN_IDS.MAINNET,
|
||||
CHAIN_IDS.SEPOLIA,
|
||||
'undefined',
|
||||
]) {
|
||||
nock(
|
||||
`https://api${
|
||||
chainId === CHAIN_IDS.MAINNET ? '' : `-${CHAIN_ID_TO_TYPE_MAP[chainId]}`
|
||||
}.etherscan.io`,
|
||||
)
|
||||
.get(/api.+/u)
|
||||
.reply(200, JSON.stringify(mockResponse));
|
||||
}
|
||||
Object.values(ETHERSCAN_SUPPORTED_NETWORKS).forEach(
|
||||
({ domain, subdomain }) => {
|
||||
nock(`https://${domain}.${subdomain}`)
|
||||
.get(/api.+/u)
|
||||
.reply(200, JSON.stringify(mockResponse));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
describe('IncomingTransactionsController', function () {
|
||||
|
@ -201,17 +201,24 @@ export const CHAIN_IDS = {
|
||||
GOERLI: '0x5',
|
||||
LOCALHOST: '0x539',
|
||||
BSC: '0x38',
|
||||
BSC_TESTNET: '0x61',
|
||||
OPTIMISM: '0xa',
|
||||
OPTIMISM_TESTNET: '0x1a4',
|
||||
POLYGON: '0x89',
|
||||
POLYGON_TESTNET: '0x13881',
|
||||
AVALANCHE: '0xa86a',
|
||||
AVALANCHE_TESTNET: '0xa869',
|
||||
FANTOM: '0xfa',
|
||||
FANTOM_TESTNET: '0xfa2',
|
||||
CELO: '0xa4ec',
|
||||
ARBITRUM: '0xa4b1',
|
||||
HARMONY: '0x63564c40',
|
||||
PALM: '0x2a15c308d',
|
||||
SEPOLIA: '0xaa36a7',
|
||||
AURORA: '0x4e454152',
|
||||
MOONBEAM: '0x504',
|
||||
MOONBEAM_TESTNET: '0x507',
|
||||
MOONRIVER: '0x505',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -542,6 +549,98 @@ export const NATIVE_CURRENCY_TOKEN_IMAGE_MAP = {
|
||||
|
||||
export const INFURA_BLOCKED_KEY = 'countryBlocked';
|
||||
|
||||
const defaultEtherscanDomain = 'etherscan.io';
|
||||
const defaultEtherscanSubdomainPrefix = 'api';
|
||||
/**
|
||||
* Map of all Etherscan supported networks.
|
||||
*/
|
||||
export const ETHERSCAN_SUPPORTED_NETWORKS = {
|
||||
[CHAIN_IDS.GOERLI]: {
|
||||
domain: defaultEtherscanDomain,
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-${
|
||||
CHAIN_ID_TO_TYPE_MAP[CHAIN_IDS.GOERLI]
|
||||
}`,
|
||||
networkId: CHAIN_ID_TO_NETWORK_ID_MAP[CHAIN_IDS.GOERLI],
|
||||
},
|
||||
[CHAIN_IDS.MAINNET]: {
|
||||
domain: defaultEtherscanDomain,
|
||||
subdomain: defaultEtherscanSubdomainPrefix,
|
||||
networkId: CHAIN_ID_TO_NETWORK_ID_MAP[CHAIN_IDS.MAINNET],
|
||||
},
|
||||
[CHAIN_IDS.SEPOLIA]: {
|
||||
domain: defaultEtherscanDomain,
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-${
|
||||
CHAIN_ID_TO_TYPE_MAP[CHAIN_IDS.SEPOLIA]
|
||||
}`,
|
||||
networkId: CHAIN_ID_TO_NETWORK_ID_MAP[CHAIN_IDS.SEPOLIA],
|
||||
},
|
||||
[CHAIN_IDS.BSC]: {
|
||||
domain: 'bscscan.com',
|
||||
subdomain: defaultEtherscanSubdomainPrefix,
|
||||
networkId: parseInt(CHAIN_IDS.BSC, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.BSC_TESTNET]: {
|
||||
domain: 'bscscan.com',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-testnet`,
|
||||
networkId: parseInt(CHAIN_IDS.BSC_TESTNET, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.OPTIMISM]: {
|
||||
domain: defaultEtherscanDomain,
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-optimistic`,
|
||||
networkId: parseInt(CHAIN_IDS.OPTIMISM, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.OPTIMISM_TESTNET]: {
|
||||
domain: defaultEtherscanDomain,
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-goerli-optimistic`,
|
||||
networkId: parseInt(CHAIN_IDS.OPTIMISM_TESTNET, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.POLYGON]: {
|
||||
domain: 'polygonscan.com',
|
||||
subdomain: defaultEtherscanSubdomainPrefix,
|
||||
networkId: parseInt(CHAIN_IDS.POLYGON, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.POLYGON_TESTNET]: {
|
||||
domain: 'polygonscan.com',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-mumbai`,
|
||||
networkId: parseInt(CHAIN_IDS.POLYGON_TESTNET, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.AVALANCHE]: {
|
||||
domain: 'snowtrace.io',
|
||||
subdomain: defaultEtherscanSubdomainPrefix,
|
||||
networkId: parseInt(CHAIN_IDS.AVALANCHE, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.AVALANCHE_TESTNET]: {
|
||||
domain: 'snowtrace.io',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-testnet`,
|
||||
networkId: parseInt(CHAIN_IDS.AVALANCHE_TESTNET, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.FANTOM]: {
|
||||
domain: 'ftmscan.com',
|
||||
subdomain: defaultEtherscanSubdomainPrefix,
|
||||
networkId: parseInt(CHAIN_IDS.FANTOM, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.FANTOM_TESTNET]: {
|
||||
domain: 'ftmscan.com',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-testnet`,
|
||||
networkId: parseInt(CHAIN_IDS.FANTOM_TESTNET, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.MOONBEAM]: {
|
||||
domain: 'moonscan.io',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-moonbeam`,
|
||||
networkId: parseInt(CHAIN_IDS.MOONBEAM, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.MOONBEAM_TESTNET]: {
|
||||
domain: 'moonscan.io',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-moonbase`,
|
||||
networkId: parseInt(CHAIN_IDS.MOONBEAM_TESTNET, 16).toString(),
|
||||
},
|
||||
[CHAIN_IDS.MOONRIVER]: {
|
||||
domain: 'moonscan.io',
|
||||
subdomain: `${defaultEtherscanSubdomainPrefix}-moonriver`,
|
||||
networkId: parseInt(CHAIN_IDS.MOONRIVER, 16).toString(),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Hardforks are points in the chain where logic is changed significantly
|
||||
* enough where there is a fork and the new fork becomes the active chain.
|
||||
@ -591,6 +690,13 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
| typeof CHAIN_IDS.PALM
|
||||
| typeof CHAIN_IDS.HARMONY
|
||||
| typeof CHAIN_IDS.OPTIMISM_TESTNET
|
||||
| typeof CHAIN_IDS.BSC_TESTNET
|
||||
| typeof CHAIN_IDS.POLYGON_TESTNET
|
||||
| typeof CHAIN_IDS.AVALANCHE_TESTNET
|
||||
| typeof CHAIN_IDS.FANTOM_TESTNET
|
||||
| typeof CHAIN_IDS.MOONBEAM
|
||||
| typeof CHAIN_IDS.MOONBEAM_TESTNET
|
||||
| typeof CHAIN_IDS.MOONRIVER
|
||||
>]: BuyableChainSettings;
|
||||
} = {
|
||||
[CHAIN_IDS.MAINNET]: {
|
||||
|
Loading…
Reference in New Issue
Block a user