diff --git a/src/config.ts b/src/config.ts index 8991c3cc..2d622ac4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -43,6 +43,8 @@ import { DEFAULT_PROMETHEUS_PORT, DEFAULT_STAKING_POOLS_JSON_URL, DEFAULT_STAKING_POOLS_METADATA_JSON_URL, + DEFAULT_BLOCK_RECEIPTS_MODE, + BLOCK_RECEIPTS_MODE_ENDPOINT, } from './constants'; import { logger } from './utils'; @@ -460,6 +462,11 @@ validateAddress( FEAT_SOCKET_BRIDGE_EVENT, ); +export const BLOCK_RECEIPTS_MODE = process.env.BLOCK_RECEIPTS_MODE || DEFAULT_BLOCK_RECEIPTS_MODE; +if (!BLOCK_RECEIPTS_MODE_ENDPOINT.has(BLOCK_RECEIPTS_MODE)) { + throwError(`Got invalid BLOCK_RECEIPTS_MODE, options are: ${BLOCK_RECEIPTS_MODE_ENDPOINT.keys()}`); +} + export const SOCKET_BRIDGE_EVENT_START_BLOCK = getIntConfig('SOCKET_BRIDGE_EVENT_START_BLOCK', -1); validateStartBlock( 'SOCKET_BRIDGE_EVENT_START_BLOCK', diff --git a/src/constants.ts b/src/constants.ts index fee2ba8f..7192781d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,6 +3,7 @@ export type ScraperMode = 'BLOCKS' | 'EVENTS'; export * from './abis'; export const DEFAULT_SCRAPER_MODE: ScraperMode = 'EVENTS'; +export const DEFAULT_BLOCK_RECEIPTS_MODE = 'standard'; export const DEFAULT_LOCAL_POSTGRES_URI = 'postgresql://user:password@localhost/events'; export const DEFAULT_MAX_BLOCKS_REORG = 35; export const DEFAULT_BLOCKS_REORG_CHECK_INCREMENT = 35; @@ -150,3 +151,7 @@ export const STAKING_EPOCH_ENDED_TOPIC = ['0xbb4a26fa0ace13ee4da343896c20eaa44a6 export const STAKING_EPOCH_FINALIZED_TOPIC = ['0xb463d19ecf455be65365092cf8e1db6934a0334cf8cd532ddf9964d01f36b5b2']; export const STAKING_REWARDS_PAID_TOPIC = ['0xf1116b309178aa62dcb6bf8c3b8bc2321724907c7ebf52192d14c8ce3aa9194c']; + +export const BLOCK_RECEIPTS_MODE_ENDPOINT = new Map(); +BLOCK_RECEIPTS_MODE_ENDPOINT.set('standard', 'eth_getBlockReceipts'); +BLOCK_RECEIPTS_MODE_ENDPOINT.set('alchemy', 'alchemy_getTransactionReceipts'); diff --git a/src/data_sources/events/web3.ts b/src/data_sources/events/web3.ts index e707f1c8..6faf3256 100644 --- a/src/data_sources/events/web3.ts +++ b/src/data_sources/events/web3.ts @@ -1,4 +1,5 @@ -import { MAX_TX_TO_PULL } from '../../config'; +import { MAX_TX_TO_PULL, BLOCK_RECEIPTS_MODE } from '../../config'; +import { BLOCK_RECEIPTS_MODE_ENDPOINT } from '../../constants'; import { chunk, logger } from '../../utils'; import { Web3ProviderEngine } from '@0x/subproviders'; import { Web3Wrapper } from '@0x/web3-wrapper'; @@ -12,6 +13,7 @@ import { const Web3 = require('web3'); +const utils = require('web3-utils'); const helpers = require('web3-core-helpers'); const formatter = helpers.formatters; @@ -42,6 +44,31 @@ export interface TransactionReceipt1559 extends TransactionReceipt { effectiveGasPrice: number; } +const alchemyBlockTransactionReceiptsFormatter = function (response: any): TransactionReceipt1559[] { + if (typeof response !== 'object') { + throw new Error('Received receipt is invalid: ' + response); + } + + return response.receipts.map((receipt: any) => { + if (receipt.blockNumber !== null) receipt.blockNumber = utils.hexToNumber(receipt.blockNumber); + if (receipt.transactionIndex !== null) receipt.transactionIndex = utils.hexToNumber(receipt.transactionIndex); + receipt.cumulativeGasUsed = utils.hexToNumber(receipt.cumulativeGasUsed); + receipt.gasUsed = utils.hexToNumber(receipt.gasUsed); + if (receipt.effectiveGasPrice) { + receipt.effectiveGasPrice = utils.hexToNumber(receipt.effectiveGasPrice); + } + if (Array.isArray(receipt.logs)) { + receipt.logs = receipt.logs.map(formatter.outputLogFormatter); + } + + if (typeof receipt.status !== 'undefined' && receipt.status !== null) { + receipt.status = Boolean(parseInt(receipt.status)); + } + + return receipt; + }); +}; + export class Web3Source { private readonly _web3Wrapper: Web3Wrapper; private readonly _web3: any; @@ -49,17 +76,33 @@ export class Web3Source { this._web3Wrapper = new Web3Wrapper(provider); this._web3 = new Web3(wsProvider); - this._web3.eth.extend({ - methods: [ - { - name: 'getBlockReceipts', - call: 'eth_getBlockReceipts', - params: 1, - inputFormatter: [this._web3.utils.numberToHex], - outputFormatter: (block: any) => formatter.outputTransactionReceiptFormatter(block), - }, - ], - }); + if (BLOCK_RECEIPTS_MODE === 'standard') { + this._web3.eth.extend({ + methods: [ + { + name: 'getBlockReceipts', + call: 'eth_getBlockReceipts', + params: 1, + inputFormatter: [this._web3.utils.numberToHex], + outputFormatter: (block: any) => formatter.outputTransactionReceiptFormatter(block), + }, + ], + }); + } else if (BLOCK_RECEIPTS_MODE === 'alchemy') { + this._web3.eth.extend({ + methods: [ + { + name: 'getBlockReceipts', + call: 'alchemy_getTransactionReceipts', + params: 1, + inputFormatter: [ + (blockNumber: number) => ({ blockNumber: this._web3.utils.numberToHex(blockNumber) }), + ], + outputFormatter: alchemyBlockTransactionReceiptsFormatter, + }, + ], + }); + } } public async getBatchBlockInfoForRangeAsync(