mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-25 03:20:23 +01:00
37209a7d2e
Remove the IncomingTransactionController and replace it with an internal helper class. Move incoming transactions into the central transactions object. Create a new RemoteTransactionSource interface to decouple incoming transaction support from Etherscan. Split the incoming transaction logic into multiple files for easier maintenance.
154 lines
4.5 KiB
TypeScript
154 lines
4.5 KiB
TypeScript
import { handleFetch } from '@metamask/controller-utils';
|
|
|
|
import {
|
|
CHAIN_IDS,
|
|
ETHERSCAN_SUPPORTED_NETWORKS,
|
|
} from '../../../../shared/constants/network';
|
|
import type {
|
|
EtherscanTransactionMeta,
|
|
EtherscanTransactionRequest,
|
|
EtherscanTransactionResponse,
|
|
} from './etherscan';
|
|
import * as Etherscan from './etherscan';
|
|
|
|
jest.mock('@metamask/controller-utils', () => ({
|
|
...jest.requireActual('@metamask/controller-utils'),
|
|
handleFetch: jest.fn(),
|
|
}));
|
|
|
|
const ADDERSS_MOCK = '0x2A2D72308838A6A46a0B5FDA3055FE915b5D99eD';
|
|
|
|
const REQUEST_MOCK: EtherscanTransactionRequest = {
|
|
address: ADDERSS_MOCK,
|
|
chainId: CHAIN_IDS.GOERLI,
|
|
limit: 3,
|
|
fromBlock: 2,
|
|
apiKey: 'testApiKey',
|
|
};
|
|
|
|
const RESPONSE_MOCK: EtherscanTransactionResponse<EtherscanTransactionMeta> = {
|
|
result: [
|
|
{ from: ADDERSS_MOCK, nonce: '0x1' } as EtherscanTransactionMeta,
|
|
{ from: ADDERSS_MOCK, nonce: '0x2' } as EtherscanTransactionMeta,
|
|
],
|
|
};
|
|
|
|
describe('Etherscan', () => {
|
|
const handleFetchMock = handleFetch as jest.MockedFunction<
|
|
typeof handleFetch
|
|
>;
|
|
|
|
beforeEach(() => {
|
|
jest.resetAllMocks();
|
|
});
|
|
|
|
describe.each([
|
|
['fetchEtherscanTransactions', 'txlist'],
|
|
['fetchEtherscanTokenTransactions', 'tokentx'],
|
|
])('%s', (method, action) => {
|
|
it('returns fetched response', async () => {
|
|
handleFetchMock.mockResolvedValueOnce(RESPONSE_MOCK);
|
|
|
|
const result = await (Etherscan as any)[method](REQUEST_MOCK);
|
|
|
|
expect(result).toStrictEqual(RESPONSE_MOCK);
|
|
});
|
|
|
|
it('fetches from Etherscan URL', async () => {
|
|
handleFetchMock.mockResolvedValueOnce(RESPONSE_MOCK);
|
|
|
|
await (Etherscan as any)[method](REQUEST_MOCK);
|
|
|
|
expect(handleFetchMock).toHaveBeenCalledTimes(1);
|
|
expect(handleFetchMock).toHaveBeenCalledWith(
|
|
`https://${ETHERSCAN_SUPPORTED_NETWORKS[CHAIN_IDS.GOERLI].subdomain}.${
|
|
ETHERSCAN_SUPPORTED_NETWORKS[CHAIN_IDS.GOERLI].domain
|
|
}/api?` +
|
|
`module=account` +
|
|
`&address=${REQUEST_MOCK.address}` +
|
|
`&startBlock=${REQUEST_MOCK.fromBlock}` +
|
|
`&apikey=${REQUEST_MOCK.apiKey}` +
|
|
`&offset=${REQUEST_MOCK.limit}` +
|
|
`&order=desc` +
|
|
`&action=${action}` +
|
|
`&tag=latest` +
|
|
`&page=1`,
|
|
);
|
|
});
|
|
|
|
it('supports alternate networks', async () => {
|
|
handleFetchMock.mockResolvedValueOnce(RESPONSE_MOCK);
|
|
|
|
await (Etherscan as any)[method]({
|
|
...REQUEST_MOCK,
|
|
chainId: CHAIN_IDS.MAINNET,
|
|
});
|
|
|
|
expect(handleFetchMock).toHaveBeenCalledTimes(1);
|
|
expect(handleFetchMock).toHaveBeenCalledWith(
|
|
`https://${ETHERSCAN_SUPPORTED_NETWORKS[CHAIN_IDS.MAINNET].subdomain}.${
|
|
ETHERSCAN_SUPPORTED_NETWORKS[CHAIN_IDS.MAINNET].domain
|
|
}/api?` +
|
|
`module=account` +
|
|
`&address=${REQUEST_MOCK.address}` +
|
|
`&startBlock=${REQUEST_MOCK.fromBlock}` +
|
|
`&apikey=${REQUEST_MOCK.apiKey}` +
|
|
`&offset=${REQUEST_MOCK.limit}` +
|
|
`&order=desc` +
|
|
`&action=${action}` +
|
|
`&tag=latest` +
|
|
`&page=1`,
|
|
);
|
|
});
|
|
|
|
it('throws if message is not ok', async () => {
|
|
handleFetchMock.mockResolvedValueOnce({
|
|
status: '0',
|
|
message: 'NOTOK',
|
|
result: 'test error',
|
|
});
|
|
|
|
await expect((Etherscan as any)[method](REQUEST_MOCK)).rejects.toThrow(
|
|
'Etherscan request failed - test error',
|
|
);
|
|
});
|
|
|
|
it('throws if chain is not supported', async () => {
|
|
const unsupportedChainId = '0x11111111111111111111';
|
|
|
|
await expect(
|
|
(Etherscan as any)[method]({
|
|
...REQUEST_MOCK,
|
|
chainId: unsupportedChainId,
|
|
}),
|
|
).rejects.toThrow(
|
|
`Etherscan does not support chain with ID: ${unsupportedChainId}`,
|
|
);
|
|
});
|
|
|
|
it('does not include empty values in fetched URL', async () => {
|
|
handleFetchMock.mockResolvedValueOnce(RESPONSE_MOCK);
|
|
|
|
await (Etherscan as any)[method]({
|
|
...REQUEST_MOCK,
|
|
fromBlock: undefined,
|
|
apiKey: undefined,
|
|
});
|
|
|
|
expect(handleFetchMock).toHaveBeenCalledTimes(1);
|
|
expect(handleFetchMock).toHaveBeenCalledWith(
|
|
`https://${ETHERSCAN_SUPPORTED_NETWORKS[CHAIN_IDS.GOERLI].subdomain}.${
|
|
ETHERSCAN_SUPPORTED_NETWORKS[CHAIN_IDS.GOERLI].domain
|
|
}/api?` +
|
|
`module=account` +
|
|
`&address=${REQUEST_MOCK.address}` +
|
|
`&offset=${REQUEST_MOCK.limit}` +
|
|
`&order=desc` +
|
|
`&action=${action}` +
|
|
`&tag=latest` +
|
|
`&page=1`,
|
|
);
|
|
});
|
|
});
|
|
});
|