mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-25 11:28:51 +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.
206 lines
5.4 KiB
TypeScript
206 lines
5.4 KiB
TypeScript
import { handleFetch } from '@metamask/controller-utils';
|
|
import { Hex } from '@metamask/utils';
|
|
import { ETHERSCAN_SUPPORTED_NETWORKS } from '../../../../shared/constants/network';
|
|
|
|
export interface EtherscanTransactionMetaBase {
|
|
blockNumber: string;
|
|
blockHash: string;
|
|
confirmations: string;
|
|
contractAddress: string;
|
|
cumulativeGasUsed: string;
|
|
from: string;
|
|
gas: string;
|
|
gasPrice: string;
|
|
gasUsed: string;
|
|
hash: string;
|
|
nonce: string;
|
|
timeStamp: string;
|
|
to: string;
|
|
transactionIndex: string;
|
|
value: string;
|
|
}
|
|
|
|
export interface EtherscanTransactionMeta extends EtherscanTransactionMetaBase {
|
|
functionName: string;
|
|
input: string;
|
|
isError: string;
|
|
methodId: string;
|
|
txreceipt_status: string;
|
|
}
|
|
|
|
export interface EtherscanTokenTransactionMeta
|
|
extends EtherscanTransactionMetaBase {
|
|
tokenDecimal: string;
|
|
tokenName: string;
|
|
tokenSymbol: string;
|
|
}
|
|
|
|
export interface EtherscanTransactionResponse<
|
|
T extends EtherscanTransactionMetaBase,
|
|
> {
|
|
result: T[];
|
|
}
|
|
|
|
export interface EtherscanTransactionRequest {
|
|
address: string;
|
|
apiKey?: string;
|
|
chainId: Hex;
|
|
fromBlock?: number;
|
|
limit?: number;
|
|
}
|
|
|
|
interface RawEtherscanResponse<T extends EtherscanTransactionMetaBase> {
|
|
status: '0' | '1';
|
|
message: string;
|
|
result: string | T[];
|
|
}
|
|
|
|
/**
|
|
* Retrieves transaction data from Etherscan.
|
|
*
|
|
* @param request - Configuration required to fetch transactions.
|
|
* @param request.address - Address to retrieve transactions for.
|
|
* @param request.apiKey - Etherscan API key.
|
|
* @param request.chainId - Current chain ID used to determine subdomain and domain.
|
|
* @param request.fromBlock - Block number to start fetching transactions from.
|
|
* @param request.limit - Number of transactions to retrieve.
|
|
* @returns An Etherscan response object containing the request status and an array of token transaction data.
|
|
*/
|
|
export async function fetchEtherscanTransactions({
|
|
address,
|
|
apiKey,
|
|
chainId,
|
|
fromBlock,
|
|
limit,
|
|
}: EtherscanTransactionRequest): Promise<
|
|
EtherscanTransactionResponse<EtherscanTransactionMeta>
|
|
> {
|
|
return await fetchTransactions('txlist', {
|
|
address,
|
|
apiKey,
|
|
chainId,
|
|
fromBlock,
|
|
limit,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retrieves token transaction data from Etherscan.
|
|
*
|
|
* @param request - Configuration required to fetch token transactions.
|
|
* @param request.address - Address to retrieve token transactions for.
|
|
* @param request.apiKey - Etherscan API key.
|
|
* @param request.chainId - Current chain ID used to determine subdomain and domain.
|
|
* @param request.fromBlock - Block number to start fetching token transactions from.
|
|
* @param request.limit - Number of token transactions to retrieve.
|
|
* @returns An Etherscan response object containing the request status and an array of token transaction data.
|
|
*/
|
|
export async function fetchEtherscanTokenTransactions({
|
|
address,
|
|
apiKey,
|
|
chainId,
|
|
fromBlock,
|
|
limit,
|
|
}: EtherscanTransactionRequest): Promise<
|
|
EtherscanTransactionResponse<EtherscanTokenTransactionMeta>
|
|
> {
|
|
return await fetchTransactions('tokentx', {
|
|
address,
|
|
apiKey,
|
|
chainId,
|
|
fromBlock,
|
|
limit,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retrieves transaction data from Etherscan from a specific endpoint.
|
|
*
|
|
* @param action - The Etherscan endpoint to use.
|
|
* @param options - Options bag.
|
|
* @param options.address - Address to retrieve transactions for.
|
|
* @param options.apiKey - Etherscan API key.
|
|
* @param options.chainId - Current chain ID used to determine subdomain and domain.
|
|
* @param options.fromBlock - Block number to start fetching transactions from.
|
|
* @param options.limit - Number of transactions to retrieve.
|
|
* @returns An object containing the request status and an array of transaction data.
|
|
*/
|
|
async function fetchTransactions<T extends EtherscanTransactionMetaBase>(
|
|
action: string,
|
|
{
|
|
address,
|
|
apiKey,
|
|
chainId,
|
|
fromBlock,
|
|
limit,
|
|
}: {
|
|
address: string;
|
|
apiKey?: string;
|
|
chainId: Hex;
|
|
fromBlock?: number;
|
|
limit?: number;
|
|
},
|
|
): Promise<EtherscanTransactionResponse<T>> {
|
|
const urlParams = {
|
|
module: 'account',
|
|
address,
|
|
startBlock: fromBlock?.toString(),
|
|
apikey: apiKey,
|
|
offset: limit?.toString(),
|
|
order: 'desc',
|
|
};
|
|
|
|
const etherscanTxUrl = getEtherscanApiUrl(chainId, {
|
|
...urlParams,
|
|
action,
|
|
});
|
|
|
|
const response = (await handleFetch(
|
|
etherscanTxUrl,
|
|
)) as RawEtherscanResponse<T>;
|
|
|
|
if (response.status === '0' && response.message === 'NOTOK') {
|
|
throw new Error(`Etherscan request failed - ${response.result}`);
|
|
}
|
|
|
|
return { result: response.result as T[] };
|
|
}
|
|
|
|
/**
|
|
* Return a URL that can be used to fetch data from Etherscan.
|
|
*
|
|
* @param chainId - Current chain ID used to determine subdomain and domain.
|
|
* @param urlParams - The parameters used to construct the URL.
|
|
* @returns URL to access Etherscan data.
|
|
*/
|
|
function getEtherscanApiUrl(
|
|
chainId: Hex,
|
|
urlParams: Record<string, string | undefined>,
|
|
): string {
|
|
type SupportedChainId = keyof typeof ETHERSCAN_SUPPORTED_NETWORKS;
|
|
|
|
const networkInfo = ETHERSCAN_SUPPORTED_NETWORKS[chainId as SupportedChainId];
|
|
|
|
if (!networkInfo) {
|
|
throw new Error(`Etherscan does not support chain with ID: ${chainId}`);
|
|
}
|
|
|
|
const apiUrl = `https://${networkInfo.subdomain}.${networkInfo.domain}`;
|
|
let url = `${apiUrl}/api?`;
|
|
|
|
// eslint-disable-next-line guard-for-in
|
|
for (const paramKey in urlParams) {
|
|
const value = urlParams[paramKey];
|
|
|
|
if (!value) {
|
|
continue;
|
|
}
|
|
|
|
url += `${paramKey}=${value}&`;
|
|
}
|
|
|
|
url += 'tag=latest&page=1';
|
|
|
|
return url;
|
|
}
|