Add tests for pi message fetching
Fix pi fetching bugs
Add Unknown Message.status
pull/29/head
J M Rossy 2 years ago
parent 37c97b6ba5
commit f2cddc9cbc
  1. 3
      .eslintignore
  2. 16
      jest.config.js
  3. 3
      package.json
  4. 3
      src/features/messages/cards/ContentDetailsCard.tsx
  5. 93
      src/features/messages/queries/usePiChainMessageQuery.test.ts
  6. 143
      src/features/messages/queries/usePiChainMessageQuery.ts
  7. 1
      src/types.ts
  8. 16
      src/utils/explorers.ts
  9. 2030
      yarn.lock

@ -3,4 +3,5 @@ dist
build build
coverage coverage
next.config.js next.config.js
tailwind.config.js tailwind.config.js
jest.config.js

@ -0,0 +1,16 @@
const nextJest = require('next/jest')
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
})
// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const customJestConfig = {
// Add more setup options before each test is run
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
}
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

@ -26,6 +26,7 @@
}, },
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.0.0", "@trivago/prettier-plugin-sort-imports": "^4.0.0",
"@types/jest": "^29.4.0",
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
"@types/react": "^18.0.27", "@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10", "@types/react-dom": "^18.0.10",
@ -35,6 +36,7 @@
"eslint": "^8.34.0", "eslint": "^8.34.0",
"eslint-config-next": "^13.2.0", "eslint-config-next": "^13.2.0",
"eslint-config-prettier": "^8.6.0", "eslint-config-prettier": "^8.6.0",
"jest": "^29.4.3",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"tailwindcss": "^3.2.7", "tailwindcss": "^3.2.7",
@ -57,6 +59,7 @@
"typecheck": "tsc", "typecheck": "tsc",
"lint": "next lint", "lint": "next lint",
"start": "next start", "start": "next start",
"test": "jest",
"prettier": "prettier --write ./src" "prettier": "prettier --write ./src"
}, },
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",

@ -8,6 +8,7 @@ import { SelectField } from '../../../components/input/SelectField';
import { Card } from '../../../components/layout/Card'; import { Card } from '../../../components/layout/Card';
import { MAILBOX_VERSION } from '../../../consts/environments'; import { MAILBOX_VERSION } from '../../../consts/environments';
import { Message } from '../../../types'; import { Message } from '../../../types';
import { ensureLeading0x } from '../../../utils/addresses';
import { tryUtf8DecodeBytes } from '../../../utils/string'; import { tryUtf8DecodeBytes } from '../../../utils/string';
import { CodeBlock, LabelAndCodeBlock } from './CodeBlock'; import { CodeBlock, LabelAndCodeBlock } from './CodeBlock';
@ -73,7 +74,7 @@ export function ContentDetailsCard({
<KeyValueRow <KeyValueRow
label="Message Id:" label="Message Id:"
labelWidth="w-20" labelWidth="w-20"
display={msgId} display={ensureLeading0x(msgId)}
displayWidth="w-60 sm:w-80" displayWidth="w-60 sm:w-80"
showCopy={true} showCopy={true}
blurValue={shouldBlur} blurValue={shouldBlur}

@ -0,0 +1,93 @@
import { chainMetadata, hyperlaneCoreAddresses } from '@hyperlane-xyz/sdk';
import { ChainConfig } from '../../chains/chainConfig';
import { fetchMessagesFromPiChain } from './usePiChainMessageQuery';
jest.setTimeout(15000);
const goerliMailbox = hyperlaneCoreAddresses.goerli.mailbox;
const goerliConfigWithExplorer: ChainConfig = {
...chainMetadata.goerli,
contracts: { mailbox: goerliMailbox },
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { blockExplorers, ...goerliConfigNoExplorer } = goerliConfigWithExplorer;
// https://explorer.hyperlane.xyz/message/0d9dc662da32d3737835295e9c9eb2b92ac630aec2756b93a187b8fd22a82afd
const txHash = '0xb81ba87ee7ae30dea0f67f7f25b67d973cec6533e7407ea7a8c761f39d8dee1b';
const msgId = '0x0d9dc662da32d3737835295e9c9eb2b92ac630aec2756b93a187b8fd22a82afd';
const senderAddress = '0x0637a1360ea44602dae5c4ba515c2bcb6c762fbc';
const recipientAddress = '0x921d3a71386d3ab8f3ad4ec91ce1556d5fc26859';
const goerliMessage = {
body: '0x48656c6c6f21',
destinationChainId: 44787,
destinationDomainId: 44787,
destinationTimestamp: 0,
destinationTransaction: {
blockNumber: 0,
from: '0x0000000000000000000000000000000000000000',
gasUsed: 0,
timestamp: 0,
transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
},
id: '',
msgId: '0x0d9dc662da32d3737835295e9c9eb2b92ac630aec2756b93a187b8fd22a82afd',
nonce: 20763,
originChainId: 5,
originDomainId: 5,
originTimestamp: 0,
originTransaction: {
blockNumber: 8600958,
from: '0x0000000000000000000000000000000000000000',
gasUsed: 0,
timestamp: 0,
transactionHash: '0xb81ba87ee7ae30dea0f67f7f25b67d973cec6533e7407ea7a8c761f39d8dee1b',
},
recipient: '0x000000000000000000000000921d3a71386d3ab8f3ad4ec91ce1556d5fc26859',
sender: '0x0000000000000000000000000637a1360ea44602dae5c4ba515c2bcb6c762fbc',
status: 'unknown',
};
describe('fetchMessagesFromPiChain', () => {
it('Fetches messages using explorer for tx hash', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigWithExplorer, txHash);
expect(messages).toEqual([goerliMessage]);
});
it.skip('Fetches messages using explorer for msg id', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigWithExplorer, msgId);
expect(messages).toEqual([goerliMessage]);
});
it.skip('Fetches messages using explorer for sender address', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigWithExplorer, senderAddress);
expect(messages).toEqual([goerliMessage]);
});
it.skip('Fetches messages using explorer for recipient address', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigWithExplorer, recipientAddress);
expect(messages).toEqual([goerliMessage]);
});
it('Fetches messages using provider for tx hash', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigNoExplorer, txHash);
expect(messages).toEqual([goerliMessage]);
});
it('Fetches messages using provider for msg id', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigNoExplorer, msgId);
expect(messages).toEqual([goerliMessage]);
});
it('Fetches messages using provider for sender address', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigNoExplorer, senderAddress);
const testMsg = messages.find((m) => m.msgId === msgId);
expect([testMsg]).toEqual([goerliMessage]);
});
it('Fetches messages using provider for recipient address', async () => {
const messages = await fetchMessagesFromPiChain(goerliConfigNoExplorer, recipientAddress);
const testMsg = messages.find((m) => m.msgId === msgId);
expect([testMsg]).toEqual([goerliMessage]);
});
it('Throws error for invalid input', async () => {
await expect(
fetchMessagesFromPiChain(goerliConfigWithExplorer, 'invalidInput'),
).rejects.toThrow();
});
});

@ -1,12 +1,12 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { providers } from 'ethers'; import { BigNumber, constants, ethers, providers } from 'ethers';
import { Mailbox__factory } from '@hyperlane-xyz/core'; import { Mailbox__factory } from '@hyperlane-xyz/core';
import { utils } from '@hyperlane-xyz/utils'; import { utils } from '@hyperlane-xyz/utils';
import { getMultiProvider, getProvider } from '../../../multiProvider'; import { getMultiProvider, getProvider } from '../../../multiProvider';
import { useStore } from '../../../store'; import { useStore } from '../../../store';
import { Message, MessageStatus } from '../../../types'; import { Message, MessageStatus, PartialTransactionReceipt } from '../../../types';
import { import {
ensureLeading0x, ensureLeading0x,
isValidAddressFast, isValidAddressFast,
@ -22,11 +22,13 @@ import { ChainConfig } from '../../chains/chainConfig';
import { isValidSearchQuery } from './useMessageQuery'; import { isValidSearchQuery } from './useMessageQuery';
const PROVIDER_LOGS_BLOCK_WINDOW = 150_000;
const mailbox = Mailbox__factory.createInterface(); const mailbox = Mailbox__factory.createInterface();
const dispatchTopic0 = mailbox.getEventTopic('Dispatch'); const dispatchTopic0 = mailbox.getEventTopic('Dispatch');
const dispatchIdTopic0 = mailbox.getEventTopic('DispatchId'); const dispatchIdTopic0 = mailbox.getEventTopic('DispatchId');
const processTopic0 = mailbox.getEventTopic('Process'); // const processTopic0 = mailbox.getEventTopic('Process');
const processIdTopic0 = mailbox.getEventTopic('ProcessId'); // const processIdTopic0 = mailbox.getEventTopic('ProcessId');
// Query 'Permissionless Interoperability (PI)' chains using custom // Query 'Permissionless Interoperability (PI)' chains using custom
// chain configs in store state // chain configs in store state
@ -85,32 +87,33 @@ searchForMessages(input):
GOTO hash search above GOTO hash search above
*/ */
async function fetchMessagesFromPiChain( export async function fetchMessagesFromPiChain(
chainConfig: ChainConfig, chainConfig: ChainConfig,
input: string, input: string,
): Promise<Message[]> { ): Promise<Message[]> {
const { chainId, blockExplorers } = chainConfig; const { chainId, blockExplorers } = chainConfig;
const useExplorer = !!blockExplorers?.[0]?.apiUrl; const useExplorer = !!blockExplorers?.[0]?.apiUrl;
const formattedInput = ensureLeading0x(input);
let logs: providers.Log[] | null = null; let logs: providers.Log[];
if (isValidAddressFast(input)) { if (isValidAddressFast(formattedInput)) {
logs = await fetchLogsForAddress(chainConfig, input, useExplorer); logs = await fetchLogsForAddress(chainConfig, formattedInput, useExplorer);
} else if (isValidTransactionHash(input)) { } else if (isValidTransactionHash(input)) {
logs = await fetchLogsForTxHash(chainConfig, input, useExplorer); logs = await fetchLogsForTxHash(chainConfig, formattedInput, useExplorer);
if (!logs) { if (!logs.length) {
// Input may be a msg id // Input may be a msg id
logs = await fetchLogsForMsgId(chainConfig, input, useExplorer); logs = await fetchLogsForMsgId(chainConfig, formattedInput, useExplorer);
} }
} else { } else {
throw new Error('Invalid PI search input'); throw new Error('Invalid PI search input');
} }
if (!logs?.length) { if (!logs.length) {
// Throw so Promise.any caller doesn't trigger // Throw so Promise.any caller doesn't trigger
throw new Error(`No messages found for chain ${chainId}`); throw new Error(`No messages found for chain ${chainId}`);
} }
return logs.map(logToMessage); return logs.map(logToMessage).filter((m): m is Message => !!m);
} }
async function fetchLogsForAddress( async function fetchLogsForAddress(
@ -118,17 +121,16 @@ async function fetchLogsForAddress(
address: Address, address: Address,
useExplorer?: boolean, useExplorer?: boolean,
) { ) {
logger.debug(`Fetching logs for address ${address} on chain ${chainId}`);
const mailboxAddr = contracts.mailbox; const mailboxAddr = contracts.mailbox;
const dispatchTopic1 = ensureLeading0x(address); const dispatchTopic1 = utils.addressToBytes32(address);
const dispatchTopic3 = utils.addressToBytes32(dispatchTopic1); const dispatchTopic3 = dispatchTopic1;
const processTopic1 = dispatchTopic3;
const processTopic3 = dispatchTopic1;
if (useExplorer) { if (useExplorer) {
return fetchLogsFromExplorer( return fetchLogsFromExplorer(
[ [
`&topic0=${dispatchTopic0}&topic0_1_opr=and&topic1=${dispatchTopic1}&topic1_3_opr=or&topic3=${dispatchTopic3}`, `&topic0=${dispatchTopic0}&topic0_1_opr=and&topic1=${dispatchTopic1}&topic1_3_opr=or&topic3=${dispatchTopic3}`,
`&topic0=${processTopic0}&topic0_1_opr=and&topic1=${processTopic1}&topic1_3_opr=or&topic3=${processTopic3}`, // `&topic0=${processTopic0}&topic0_1_opr=and&topic1=${dispatchTopic3}&topic1_3_opr=or&topic3=${dispatchTopic1}`,
], ],
mailboxAddr, mailboxAddr,
chainId, chainId,
@ -138,8 +140,8 @@ async function fetchLogsForAddress(
[ [
[dispatchTopic0, dispatchTopic1], [dispatchTopic0, dispatchTopic1],
[dispatchTopic0, null, null, dispatchTopic3], [dispatchTopic0, null, null, dispatchTopic3],
[processTopic0, processTopic1], // [processTopic0, dispatchTopic3],
[processTopic0, null, null, processTopic3], // [processTopic0, null, null, dispatchTopic1],
], ],
mailboxAddr, mailboxAddr,
chainId, chainId,
@ -148,40 +150,39 @@ async function fetchLogsForAddress(
} }
async function fetchLogsForTxHash({ chainId }: ChainConfig, txHash: string, useExplorer: boolean) { async function fetchLogsForTxHash({ chainId }: ChainConfig, txHash: string, useExplorer: boolean) {
logger.debug(`Fetching logs for txHash ${txHash} on chain ${chainId}`);
if (useExplorer) { if (useExplorer) {
try { try {
const txReceipt = await queryExplorerForTxReceipt(chainId, txHash); const txReceipt = await queryExplorerForTxReceipt(chainId, txHash, false);
logger.debug(`Tx receipt found from explorer for chain ${chainId}`); logger.debug(`Tx receipt found from explorer for chain ${chainId}`);
console.log(txReceipt);
return txReceipt.logs; return txReceipt.logs;
} catch (error) { } catch (error) {
logger.debug(`Tx hash not found in explorer for chain ${chainId}`); logger.debug(`Tx hash not found in explorer for chain ${chainId}`);
return null;
} }
} else { } else {
const provider = getProvider(chainId); const provider = getProvider(chainId);
const txReceipt = await provider.getTransactionReceipt(txHash); const txReceipt = await provider.getTransactionReceipt(txHash);
console.log(txReceipt);
if (txReceipt) { if (txReceipt) {
logger.debug(`Tx receipt found from provider for chain ${chainId}`); logger.debug(`Tx receipt found from provider for chain ${chainId}`);
return txReceipt.logs; return txReceipt.logs;
} else { } else {
logger.debug(`Tx hash not found from provider for chain ${chainId}`); logger.debug(`Tx hash not found from provider for chain ${chainId}`);
return null;
} }
} }
return [];
} }
async function fetchLogsForMsgId(chainConfig: ChainConfig, msgId: string, useExplorer: boolean) { async function fetchLogsForMsgId(chainConfig: ChainConfig, msgId: string, useExplorer: boolean) {
const { contracts, chainId } = chainConfig; const { contracts, chainId } = chainConfig;
logger.debug(`Fetching logs for msgId ${msgId} on chain ${chainId}`);
const mailboxAddr = contracts.mailbox; const mailboxAddr = contracts.mailbox;
const topic1 = ensureLeading0x(msgId); const topic1 = msgId;
let logs: providers.Log[]; let logs: providers.Log[];
if (useExplorer) { if (useExplorer) {
logs = await fetchLogsFromExplorer( logs = await fetchLogsFromExplorer(
[ [
`&topic0=${dispatchIdTopic0}&topic0_1_opr=and&topic1=${topic1}`, `&topic0=${dispatchIdTopic0}&topic0_1_opr=and&topic1=${topic1}`,
`&topic0=${processIdTopic0}&topic0_1_opr=and&topic1=${topic1}`, // `&topic0=${processIdTopic0}&topic0_1_opr=and&topic1=${topic1}`,
], ],
mailboxAddr, mailboxAddr,
chainId, chainId,
@ -189,14 +190,16 @@ async function fetchLogsForMsgId(chainConfig: ChainConfig, msgId: string, useExp
} else { } else {
logs = await fetchLogsFromProvider( logs = await fetchLogsFromProvider(
[ [
[dispatchTopic0, topic1], [dispatchIdTopic0, topic1],
[processTopic0, topic1], // [processIdTopic0, topic1],
], ],
mailboxAddr, mailboxAddr,
chainId, chainId,
); );
} }
// Grab first tx hash found in any log and get all logs for that tx
// Necessary because DispatchId/ProcessId logs don't contain useful info
if (logs.length) { if (logs.length) {
const txHash = logs[0].transactionHash; const txHash = logs[0].transactionHash;
logger.debug('Found tx hash with log of msg id', txHash); logger.debug('Found tx hash with log of msg id', txHash);
@ -207,13 +210,13 @@ async function fetchLogsForMsgId(chainConfig: ChainConfig, msgId: string, useExp
} }
async function fetchLogsFromExplorer(paths: Array<string>, contractAddr: Address, chainId: number) { async function fetchLogsFromExplorer(paths: Array<string>, contractAddr: Address, chainId: number) {
const pathBase = `api?module=logs&action=getLogs&fromBlock=0&toBlock=999999999&address=${contractAddr}`; const base = `?module=logs&action=getLogs&fromBlock=0&toBlock=999999999&address=${contractAddr}`;
const logs = ( let logs: providers.Log[] = [];
await Promise.all(paths.map((p) => queryExplorerForLogs(chainId, `${pathBase}${p}`))) for (const path of paths) {
) // Originally use parallel requests here with Promise.all but immediately hit rate limit errors
.flat() const result = await queryExplorerForLogs(chainId, `${base}${path}`, undefined, false);
.map(toProviderLog); logs = [...logs, ...result.map(toProviderLog)];
console.log(logs); }
return logs; return logs;
} }
@ -223,46 +226,68 @@ async function fetchLogsFromProvider(
chainId: number, chainId: number,
) { ) {
const provider = getProvider(chainId); const provider = getProvider(chainId);
const latestBlock = await provider.getBlockNumber();
// TODO may need chunking here to avoid RPC errors // TODO may need chunking here to avoid RPC errors
const logs = ( const logs = (
await Promise.all( await Promise.all(
topics.map((t) => topics.map((t) =>
provider.getLogs({ provider.getLogs({
fromBlock: latestBlock - PROVIDER_LOGS_BLOCK_WINDOW,
toBlock: 'latest',
address: contractAddr, address: contractAddr,
topics: t, topics: t,
}), }),
), ),
) )
).flat(); ).flat();
console.log(logs);
return logs; return logs;
} }
function logToMessage(log: providers.Log): Message { function logToMessage(log: providers.Log): Message | null {
let logDesc: ethers.utils.LogDescription;
try {
logDesc = mailbox.parseLog(log);
if (logDesc.name.toLowerCase() !== 'dispatch') return null;
} catch (error) {
// Probably not a message log, ignore
return null;
}
const bytes = logDesc.args['message'];
const message = utils.parseMessage(bytes);
const tx: PartialTransactionReceipt = {
from: constants.AddressZero, //TODO
transactionHash: log.transactionHash,
blockNumber: BigNumber.from(log.blockNumber).toNumber(),
gasUsed: 0, //TODO
timestamp: 0, // TODO
};
const emptyTx = {
from: constants.AddressZero, //TODO
transactionHash: constants.HashZero,
blockNumber: 0,
gasUsed: 0,
timestamp: 0,
};
const multiProvider = getMultiProvider(); const multiProvider = getMultiProvider();
const bytes = mailbox.parseLog(log).args['message'];
const parsed = utils.parseMessage(bytes);
return { return {
id: '', // No db id exists id: '', // No db id exists
msgId: utils.messageId(bytes), msgId: utils.messageId(bytes),
status: MessageStatus.Pending, // TODO status: MessageStatus.Unknown, // TODO
sender: parsed.sender, sender: message.sender,
recipient: parsed.recipient, recipient: message.recipient,
originDomainId: parsed.origin, originDomainId: message.origin,
destinationDomainId: parsed.destination, destinationDomainId: message.destination,
originChainId: multiProvider.getChainId(parsed.origin), originChainId: multiProvider.getChainId(message.origin),
destinationChainId: multiProvider.getChainId(parsed.destination), destinationChainId: multiProvider.getChainId(message.destination),
originTimestamp: 0, // TODO originTimestamp: tx.timestamp, // TODO
destinationTimestamp: undefined, // TODO destinationTimestamp: 0, // TODO
nonce: parsed.nonce, nonce: message.nonce,
body: parsed.body, body: message.body,
originTransaction: { originTransaction: tx,
from: '0x', //TODO destinationTransaction: emptyTx,
transactionHash: log.transactionHash,
blockNumber: log.blockNumber,
gasUsed: 0,
timestamp: 0, //TODO
},
destinationTransaction: undefined, // TODO
}; };
} }

@ -9,6 +9,7 @@ export interface PartialTransactionReceipt {
// TODO consider reconciling with SDK's MessageStatus // TODO consider reconciling with SDK's MessageStatus
export enum MessageStatus { export enum MessageStatus {
Unknown = 'unknown',
Pending = 'pending', Pending = 'pending',
Delivered = 'delivered', Delivered = 'delivered',
Failing = 'failing', Failing = 'failing',

@ -4,7 +4,6 @@ import { config } from '../consts/config';
import { getMultiProvider } from '../multiProvider'; import { getMultiProvider } from '../multiProvider';
import { logger } from './logger'; import { logger } from './logger';
import { retryAsync } from './retry';
import { fetchWithTimeout } from './timeout'; import { fetchWithTimeout } from './timeout';
import { isValidHttpUrl } from './url'; import { isValidHttpUrl } from './url';
@ -27,9 +26,10 @@ export function getExplorerApiUrl(chainId: number) {
} }
export function getTxExplorerUrl(chainId: number, hash?: string) { export function getTxExplorerUrl(chainId: number, hash?: string) {
const baseUrl = getExplorerUrl(chainId); if (!hash) return null;
if (!hash || !baseUrl) return null; const url = getMultiProvider().getExplorerTxUrl(chainId, { hash });
return `${baseUrl}/tx/${hash}`; if (isValidHttpUrl(url)) return url;
else return null;
} }
export async function queryExplorer<P>(chainId: number, path: string, useKey = true) { export async function queryExplorer<P>(chainId: number, path: string, useKey = true) {
@ -45,7 +45,7 @@ export async function queryExplorer<P>(chainId: number, path: string, useKey = t
url += `&apikey=${apiKey}`; url += `&apikey=${apiKey}`;
} }
const result = await retryAsync(() => executeQuery<P>(url), 2, 1000); const result = await executeQuery<P>(url);
return result; return result;
} }
@ -114,7 +114,7 @@ export function toProviderLog(log: ExplorerLogEntry): providers.Log {
} }
export async function queryExplorerForTx(chainId: number, txHash: string, useKey = true) { export async function queryExplorerForTx(chainId: number, txHash: string, useKey = true) {
const path = `api?module=proxy&action=eth_getTransactionByHash&txhash=${txHash}`; const path = `?module=proxy&action=eth_getTransactionByHash&txhash=${txHash}`;
const tx = await queryExplorer<providers.TransactionResponse>(chainId, path, useKey); const tx = await queryExplorer<providers.TransactionResponse>(chainId, path, useKey);
if (!tx || tx.hash.toLowerCase() !== txHash.toLowerCase()) { if (!tx || tx.hash.toLowerCase() !== txHash.toLowerCase()) {
const msg = 'Invalid tx result'; const msg = 'Invalid tx result';
@ -125,7 +125,7 @@ export async function queryExplorerForTx(chainId: number, txHash: string, useKey
} }
export async function queryExplorerForTxReceipt(chainId: number, txHash: string, useKey = true) { export async function queryExplorerForTxReceipt(chainId: number, txHash: string, useKey = true) {
const path = `api?module=proxy&action=eth_getTransactionReceipt&txhash=${txHash}`; const path = `?module=proxy&action=eth_getTransactionReceipt&txhash=${txHash}`;
const tx = await queryExplorer<providers.TransactionReceipt>(chainId, path, useKey); const tx = await queryExplorer<providers.TransactionReceipt>(chainId, path, useKey);
if (!tx || tx.transactionHash.toLowerCase() !== txHash.toLowerCase()) { if (!tx || tx.transactionHash.toLowerCase() !== txHash.toLowerCase()) {
const msg = 'Invalid tx result'; const msg = 'Invalid tx result';
@ -140,7 +140,7 @@ export async function queryExplorerForBlock(
blockNumber?: number | string, blockNumber?: number | string,
useKey = true, useKey = true,
) { ) {
const path = `api?module=proxy&action=eth_getBlockByNumber&tag=${ const path = `?module=proxy&action=eth_getBlockByNumber&tag=${
blockNumber || 'latest' blockNumber || 'latest'
}&boolean=false`; }&boolean=false`;
const block = await queryExplorer<providers.Block>(chainId, path, useKey); const block = await queryExplorer<providers.Block>(chainId, path, useKey);

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save