From 193558a49c922b8d1e4daf5b0e3064679fe9cd59 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 22 May 2024 14:47:44 -0400 Subject: [PATCH 1/6] add note about prod emails --- packages/bitcore-wallet-service/bws.example.config.js | 1 + packages/bitcore-wallet-service/src/config.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/bitcore-wallet-service/bws.example.config.js b/packages/bitcore-wallet-service/bws.example.config.js index 81626ae38d5..dc79b4a1bd3 100644 --- a/packages/bitcore-wallet-service/bws.example.config.js +++ b/packages/bitcore-wallet-service/bws.example.config.js @@ -344,6 +344,7 @@ module.exports = { // ignoreTLS: true, // subjectPrefix: '[Wallet Service]', // from: 'wallet-service@bitcore.io', + // // Note: Prod templates are in a the copay-emails repo (https://github.com/bitpay/copay-emails) // templatePath: 'templates', // defaultLanguage: 'en', // defaultUnit: 'btc', diff --git a/packages/bitcore-wallet-service/src/config.ts b/packages/bitcore-wallet-service/src/config.ts index 005c15e0d32..510104e3a00 100644 --- a/packages/bitcore-wallet-service/src/config.ts +++ b/packages/bitcore-wallet-service/src/config.ts @@ -376,6 +376,7 @@ const Config = (): any => { // ignoreTLS: true, // subjectPrefix: '[Wallet Service]', // from: 'wallet-service@bitcore.io', + // // Note: Prod templates are in a the copay-emails repo (https://github.com/bitpay/copay-emails) // templatePath: 'templates', // defaultLanguage: 'en', // defaultUnit: 'btc', From 6daa50f2d173571003f05f3f7d25d59c1f6ab7c8 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 22 May 2024 14:53:04 -0400 Subject: [PATCH 2/6] use chain --- .../src/lib/chain/eth/index.ts | 2 ++ .../src/lib/common/defaults.ts | 30 +++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index 0815e24043a..f81dea3a42a 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -186,6 +186,7 @@ export class EthChain implements IChain { const value = opts.tokenAddress || opts.multisigContractAddress ? 0 : output.amount; inGasLimit = await server.estimateGas({ coin, + chain: this.chain, network, from, to, @@ -218,6 +219,7 @@ export class EthChain implements IChain { gasLimit = await server.estimateGas({ coin, + chain: this.chain, network, from, to: opts.multiSendContractAddress, diff --git a/packages/bitcore-wallet-service/src/lib/common/defaults.ts b/packages/bitcore-wallet-service/src/lib/common/defaults.ts index f1838bae865..f24e7b1f86e 100644 --- a/packages/bitcore-wallet-service/src/lib/common/defaults.ts +++ b/packages/bitcore-wallet-service/src/lib/common/defaults.ts @@ -114,81 +114,81 @@ export const Defaults = { { name: 'urgent', nbBlocks: 1, - defaultValue: 300000000000 + defaultValue: 3000000000 }, { name: 'priority', nbBlocks: 2, - defaultValue: 250000000000 + defaultValue: 2500000000 }, { name: 'normal', nbBlocks: 3, - defaultValue: 200000000000 + defaultValue: 2000000000 }, { name: 'economy', nbBlocks: 4, - defaultValue: 200000000000 + defaultValue: 2000000000 }, { name: 'superEconomy', nbBlocks: 4, - defaultValue: 200000000000 + defaultValue: 2000000000 } ], base: [ { name: 'urgent', nbBlocks: 1, - defaultValue: 300000000000 + defaultValue: 3000000000 }, { name: 'priority', nbBlocks: 2, - defaultValue: 250000000000 + defaultValue: 2500000000 }, { name: 'normal', nbBlocks: 3, - defaultValue: 200000000000 + defaultValue: 2000000000 }, { name: 'economy', nbBlocks: 4, - defaultValue: 200000000000 + defaultValue: 2000000000 }, { name: 'superEconomy', nbBlocks: 4, - defaultValue: 200000000000 + defaultValue: 2000000000 } ], op: [ { name: 'urgent', nbBlocks: 1, - defaultValue: 300000000000 + defaultValue: 3000000000 }, { name: 'priority', nbBlocks: 2, - defaultValue: 250000000000 + defaultValue: 2500000000 }, { name: 'normal', nbBlocks: 3, - defaultValue: 200000000000 + defaultValue: 2000000000 }, { name: 'economy', nbBlocks: 4, - defaultValue: 200000000000 + defaultValue: 2000000000 }, { name: 'superEconomy', nbBlocks: 4, - defaultValue: 200000000000 + defaultValue: 2000000000 } ], xrp: [ From 404bd23fef7e7489737f3cb827d145104ef5b7f0 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 22 May 2024 17:19:11 -0400 Subject: [PATCH 3/6] replace moralisChains with getChainId --- .../chain-state/external/defaults.ts | 31 ---------- .../chain-state/external/providers/moralis.ts | 62 ++++++++----------- 2 files changed, 27 insertions(+), 66 deletions(-) delete mode 100644 packages/bitcore-node/src/providers/chain-state/external/defaults.ts diff --git a/packages/bitcore-node/src/providers/chain-state/external/defaults.ts b/packages/bitcore-node/src/providers/chain-state/external/defaults.ts deleted file mode 100644 index c9fb34babab..00000000000 --- a/packages/bitcore-node/src/providers/chain-state/external/defaults.ts +++ /dev/null @@ -1,31 +0,0 @@ -// list of supported chains: https://docs.moralis.io/supported-chains?list -const moralisChains = { - ETH: { - mainnet: '0x1', - testnet: 'sepolia', - sepolia: '0xaa36a7', - goerli: '0x5', - holesky: '0x4268' - }, - MATIC: { - mainnet: '0x89', - testnet: 'mumbai', - mumbai: '0x13881', - }, - ARB: { - mainnet: '0xa4b1', - testnet: 'sepolia', - sepolia: '0x66eee' - }, - OP: { - mainnet: '0x1', - }, - BASE: { - mainnet: '0x2105', - testnet: 'sepolia', - sepolia: '0x14a34', - goerli: '0x14a33' - }, -} - -export default moralisChains; \ No newline at end of file diff --git a/packages/bitcore-node/src/providers/chain-state/external/providers/moralis.ts b/packages/bitcore-node/src/providers/chain-state/external/providers/moralis.ts index 12e862c9f7e..f6257efc884 100644 --- a/packages/bitcore-node/src/providers/chain-state/external/providers/moralis.ts +++ b/packages/bitcore-node/src/providers/chain-state/external/providers/moralis.ts @@ -4,7 +4,6 @@ import { isDateValid } from '../../../../utils/check'; import { EVMTransactionStorage } from '../../evm/models/transaction'; import { ErigonTraceResponse } from '../../evm/p2p/rpcs/erigonRpc'; import { EVMTransactionJSON, Transaction } from '../../evm/types'; -import moralisChains from '../defaults'; import { ExternalApiStream as apiStream } from '../streams/apiStream'; const baseUrl = 'https://deep-index.moralis.io/api/v2.2'; @@ -13,12 +12,15 @@ const headers = { 'X-API-Key': config.externalProviders?.moralis?.apiKey, }; -const getBlockByDate = async ({ chain, network, date }) => { +const getBlockByDate = async ({ chainId, date }) => { if (!date || !isDateValid(date)) { throw new Error('Invalid date'); } + if (!chainId) { + throw new Error('Invalid chainId'); + } - const query = transformQueryParams({ chain, network, args: { date } }); + const query = transformQueryParams({ chainId: formatChainId(chainId), args: { date } }); const queryStr = buildQueryString(query); return new Promise((resolve, reject) => { @@ -35,12 +37,13 @@ const getBlockByDate = async ({ chain, network, date }) => { }); } -const getBlockByHash = async ({ chain, network, blockId }) => { +const getBlockByHash = async ({ chainId, blockId }) => { if (!blockId) { throw new Error('Invalid block number or hash string'); } - - const chainId = getMoralisChainId(chain, network); + if (!chainId) { + throw new Error('Invalid chainId'); + } return new Promise((resolve, reject) => { request({ @@ -56,13 +59,13 @@ const getBlockByHash = async ({ chain, network, blockId }) => { }); } -const getNativeBalanceByBlock = async ({ chain, network, block, addresses }) => { +const getNativeBalanceByBlock = async ({ chainId, block, addresses }) => { if (!block) { throw new Error('Invalid block number or hash string'); } // 25 wallet addresses max per Moralis API docs const queryStr = buildQueryString({ - chain: getMoralisChainId(chain, network), + chain: chainId, wallet_address: addresses }) @@ -80,12 +83,15 @@ const getNativeBalanceByBlock = async ({ chain, network, block, addresses }) => }); } -const streamTransactionsByAddress = ({ chain, network, address, args }): any => { +const streamTransactionsByAddress = ({ chainId, chain, network, address, args }): any => { if (!address) { throw new Error('Missing address'); } + if (!chainId) { + throw new Error('Invalid chainId'); + } - const query = transformQueryParams({ chain, network, args }); // throws if no chain or network + const query = transformQueryParams({ chainId, args }); // throws if no chain or network const queryStr = buildQueryString({ ...query, order: args.order || 'DESC', // default to descending order @@ -107,15 +113,18 @@ const streamTransactionsByAddress = ({ chain, network, address, args }): any => ) } -const streamERC20TransactionsByAddress = ({ chain, network, address, tokenAddress, args }): any => { +const streamERC20TransactionsByAddress = ({ chainId, chain, network, address, tokenAddress, args }): any => { if (!address) { throw new Error('Missing address'); } if (!tokenAddress) { throw new Error('Missing token address'); } + if (!chainId) { + throw new Error('Invalid chainId'); + } - const queryTransform = transformQueryParams({ chain, network, args }); // throws if no chain or network + const queryTransform = transformQueryParams({ chainId, args }); // throws if no chain or network const queryStr = buildQueryString({ ...queryTransform, order: args.order || 'DESC', // default to descending order @@ -194,9 +203,9 @@ const transformTokenTransfer = (transfer) => { } const transformQueryParams = (params) => { - const { chain, network, args } = params; + const { chainId, args } = params; let query = { - chain: getMoralisChainId(chain, network), + chain: formatChainId(chainId), } as any; if (args) { if (args.startBlock || args.endBlock) { @@ -224,27 +233,6 @@ const transformQueryParams = (params) => { return query; } -const getMoralisChainId = (chain, network): string | Error => { - if (!chain) { - throw new Error('Missing chain'); - } - if (!network) { - throw new Error('Missing network'); - } - - chain = chain.toUpperCase(); - network = network.toLowerCase(); - - if (network === 'testnet') { - network = moralisChains[chain]?.testnet; - } - if (!moralisChains[chain][network]) { - throw new Error(`${chain}:${network} is not supported`); - } - - return moralisChains[chain][network]; -} - const calculateConfirmations = (tx, tip) => { let confirmations = 0; if (tx.blockHeight && tx.blockHeight >= 0) { @@ -270,6 +258,10 @@ const buildQueryString = (params: Record): string => { return query.length ? `?${query.join('&')}` : ''; } +const formatChainId = (chainId) => { + return '0x' + parseInt(chainId).toString(16) +} + const MoralisAPI = { getBlockByDate, getBlockByHash, From 898d96412e5874ad19c3aed8bd25a7b6f76a6cff Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 22 May 2024 17:22:10 -0400 Subject: [PATCH 4/6] use baseFee and reward to calculate gas price --- .../src/providers/chain-state/evm/api/ecsp.ts | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts b/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts index 403f7e88c65..a60b0125073 100644 --- a/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts +++ b/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts @@ -1,6 +1,6 @@ import { CryptoRpc } from 'crypto-rpc'; import _ from 'lodash'; -import { Readable, Transform } from 'stream'; +import { Readable } from 'stream'; import Web3 from 'web3'; import Config from '../../../../config'; import logger from '../../../../logger'; @@ -20,7 +20,7 @@ import { import { unixToDate } from '../../../../utils/convert'; import { StatsUtil } from '../../../../utils/stats'; import MoralisAPI from '../../external/providers/moralis'; -import { ExternalApiStream } from '../../external/streams/apiStream'; +import { ExternalApiStream, MergedStream } from '../../external/streams/apiStream'; import { NodeQueryStream } from '../../external/streams/nodeStream'; import { InternalStateProvider } from '../../internal/internal'; import { EVMTransactionStorage } from '../models/transaction'; @@ -85,6 +85,11 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen return rpcObj; } + async getChainId({ network }) { + const { web3 } = await this.getWeb3(network); + return await web3.eth.getChainId(); + } + async getLocalTip({ chain, network, includeTxs = false }): Promise { const { web3 } = await this.getWeb3(network); const block = await web3.eth.getBlock('latest'); @@ -98,12 +103,19 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen const latestBlock = await web3.eth.getBlockNumber(); // Getting the 25th percentile gas prices from the last 4k blocks const feeHistory = await web3.eth.getFeeHistory(20 * 200, latestBlock, [25]); - const gasPrices = feeHistory.reward.map(reward => parseInt(reward[0])).sort((a, b) => b - a); - const whichQuartile = Math.min(target, 4) || 1; - const quartileMedian = StatsUtil.getNthQuartileMedian(gasPrices, whichQuartile); - const roundedGwei = (quartileMedian / 1e9).toFixed(6); // increased precision to handle chains with lower fees - const gwei = Number(roundedGwei) || 0; - const feerate = gwei * 1e9; + const gasPrices: Array = []; + const length = Math.min(feeHistory.baseFeePerGas.length, feeHistory.reward.length); + for (let i = 0; i < length; i++) { + // Adds base fee with reward / priority fee to get total gas price in wei + // For pre-type2 blocks the gas prices are returned as rewards and zeroes are returned for the base fee per gas + gasPrices.push(BigInt(feeHistory.baseFeePerGas[i]) + BigInt(feeHistory.reward[i][0])); + } + // Sort descending with an appropriate BigInt comparator + gasPrices.sort((a, b) => (a < b ? 1 : a > b ? -1 : 0)); + const whichQuartile: number = Math.min(target, 4) || 1; + const quartileMedian: BigInt = StatsUtil.getNthQuartileMedian(gasPrices, whichQuartile); + const feerate = quartileMedian.toString(); + // fee returned in wei as a string return { feerate, blocks: target }; } @@ -224,13 +236,14 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen try { // Calculate confirmations with tip height let result; + const chainId = await this.getChainId({ network }); const tip = await this.getLocalTip(params); args.tipHeight = tip ? tip.height : 0; if (!args.tokenAddress) { - const txStream = MoralisAPI.streamTransactionsByAddress({ chain, network, address, args }); + const txStream = MoralisAPI.streamTransactionsByAddress({ chainId, chain, network, address, args }); result = await ExternalApiStream.onStream(txStream, req!, res!); } else { - const tokenTransfers = MoralisAPI.streamERC20TransactionsByAddress({ chain, network, address, tokenAddress, args }); + const tokenTransfers = MoralisAPI.streamERC20TransactionsByAddress({ chainId, chain, network, address, tokenAddress, args }); result = await ExternalApiStream.onStream(tokenTransfers, req!, res!); } if (result?.success === false) { @@ -248,11 +261,12 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen if (!wallet?._id) { throw new Error('Missing wallet'); } + const chainId = await this.getChainId({ network }); // Calculate confirmations with tip height const tip = await this.getLocalTip(params); args.tipHeight = tip ? tip.height : 0; const walletAddresses = (await this.getWalletAddresses(wallet._id!)).map(addy => addy.address); - const mergedStream = new Transform(); + const mergedStream = new MergedStream(); const txStreams: Readable[] = []; // Only mergedStream writes to res object const _mergedStream = ExternalApiStream.onStream(mergedStream, req!, res!); @@ -260,7 +274,7 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen // Default to pulling only the first 10 transactions per address for (let i = 0; i < walletAddresses.length; i++) { // args / query params are processed at the api provider level - txStreams.push(MoralisAPI.streamTransactionsByAddress({ chain, network, address: walletAddresses[i], args })); + txStreams.push(MoralisAPI.streamTransactionsByAddress({ chainId, chain, network, address: walletAddresses[i], args })); } // Pipe all txStreams to the mergedStream ExternalApiStream.mergeStreams(txStreams, mergedStream); @@ -276,7 +290,7 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen } async getWalletBalanceAtTime(params: GetWalletBalanceAtTimeParams): Promise<{ confirmed: number; unconfirmed: number; balance: number }> { - const { chain, network, time, wallet } = params; + const { network, time, wallet } = params; try { if (!wallet?._id) { throw new Error('Missing wallet'); @@ -284,7 +298,7 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen const addresses = (await this.getWalletAddresses(wallet._id!)).map(addy => addy.address); // get block number based on time const block = await this.getBlockNumberByDate({ network, date: time }); - const result: any = await this.getNativeBalanceByBlock({ chain, network, block, addresses }); + const result: any = await this.getNativeBalanceByBlock({ network, block, addresses }); return { unconfirmed: 0, confirmed: result?.total_balance, balance: result?.total_balance }; } catch (err) { logger.error('Error getting wallet balance at time from external provider %o', err); @@ -382,19 +396,22 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen } async getBlockNumberByDate({ date, network }) { - const res = await MoralisAPI.getBlockByDate({ chain: this.chain, network, date }); + const chainId = await this.getChainId({ network }); + const res = await MoralisAPI.getBlockByDate({ chainId, date }); const block = JSON.parse((res as any).body); return block.number ? Number((block as any).number) : undefined; } async getBlockNumberByBlockId({ blockId, network }) { - const res = await MoralisAPI.getBlockByHash({ chain: this.chain, network, blockId }); + const chainId = await this.getChainId({ network }) + const res = await MoralisAPI.getBlockByHash({ chainId, blockId }); const block = JSON.parse((res as any).body); return block.number ? Number((block as any).number) : undefined; } - async getNativeBalanceByBlock({ chain, network, block, addresses }) { - const res = await MoralisAPI.getNativeBalanceByBlock({ chain, network, block, addresses }); + async getNativeBalanceByBlock({ network, block, addresses }) { + const chainId = await this.getChainId({ network }); + const res = await MoralisAPI.getNativeBalanceByBlock({ chainId, block, addresses }); return JSON.parse((res as any).body); } From e0577ec9441d3fc0190c1a0bea874845237fad44 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 22 May 2024 17:23:39 -0400 Subject: [PATCH 5/6] transform merged stream --- .../chain-state/external/streams/apiStream.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts b/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts index 9b80646ebd5..e64e341489b 100644 --- a/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts +++ b/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts @@ -1,6 +1,6 @@ import axios from 'axios'; import { Request, Response } from 'express'; -import { Readable, Stream, Writable } from 'stream'; +import { Readable, Stream, Transform, Writable } from 'stream'; export class ExternalApiStream extends Readable { url: string; @@ -145,4 +145,14 @@ export class ExternalApiStream extends Readable { }); }; } +} + +export class MergedStream extends Transform { + constructor() { + super({ objectMode: true }); + } + + async _transform(data: any, _, done) { + done(null, data); + } } \ No newline at end of file From ddda3b18d2285155e7db48d3133b43894427a9b9 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 22 May 2024 18:12:18 -0400 Subject: [PATCH 6/6] log concise axios errors --- .../src/providers/chain-state/evm/api/ecsp.ts | 35 +++++++++---------- .../chain-state/external/streams/apiStream.ts | 10 +++++- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts b/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts index a60b0125073..f7f4970bc40 100644 --- a/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts +++ b/packages/bitcore-node/src/providers/chain-state/evm/api/ecsp.ts @@ -93,7 +93,6 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen async getLocalTip({ chain, network, includeTxs = false }): Promise { const { web3 } = await this.getWeb3(network); const block = await web3.eth.getBlock('latest'); - // timestamp is incorrect. do we want to spend an api call just to get the date? return ECSP.transformBlockData({ chain, network, block, includeTxs }) as IBlock; } @@ -115,7 +114,7 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen const whichQuartile: number = Math.min(target, 4) || 1; const quartileMedian: BigInt = StatsUtil.getNthQuartileMedian(gasPrices, whichQuartile); const feerate = quartileMedian.toString(); - // fee returned in wei as a string + // Fee returned in wei as a string return { feerate, blocks: target }; } @@ -158,8 +157,8 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen return Promise.all(blockPromises) .then(blockPromises.map(blockTransform) as any) .catch(error => error); - } catch (err) { - logger.error('Error getting blocks from historical node: %o', err); + } catch (err: any) { + logger.error('Error getting blocks from historical node: %o', err.log || err); } return undefined; } @@ -174,8 +173,8 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen const tip = await this.getLocalTip(params); const tipHeight = tip ? tip.height : 0; return await this._getTransaction({ chain, network, txId, tipHeight, web3 }); - } catch (err) { - logger.error('Error getting transactions from historical node %o', err); + } catch (err: any) { + logger.error('Error getting transactions from historical node %o', err.log || err); } return undefined; } @@ -222,10 +221,10 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen // stream results into response const result = await NodeQueryStream.onStream(txStream, req!, res!); if (result?.success === false) { - logger.error('Error mid-stream (streamTransactions): %o', result.error); + logger.error('Error mid-stream (streamTransactions): %o', result.error?.log || result.error); } - } catch (err) { - logger.error('Error streaming transactions from historical node %o', err); + } catch (err: any) { + logger.error('Error streaming transactions from historical node %o', err.log || err); throw err; } } @@ -234,9 +233,9 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen const { req, res, args, chain, network, address } = params; const { tokenAddress } = args; try { - // Calculate confirmations with tip height let result; const chainId = await this.getChainId({ network }); + // Calculate confirmations with tip height const tip = await this.getLocalTip(params); args.tipHeight = tip ? tip.height : 0; if (!args.tokenAddress) { @@ -247,10 +246,10 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen result = await ExternalApiStream.onStream(tokenTransfers, req!, res!); } if (result?.success === false) { - logger.error('Error mid-stream (streamAddressTransactions): %o', result.error); + logger.error('Error mid-stream (streamAddressTransactions): %o', result.error?.log || result.error); } - } catch (err) { - logger.error('Error streaming address transactions from external provider: %o', err); + } catch (err: any) { + logger.error('Error streaming address transactions from external provider: %o', err.log || err); throw err; } } @@ -281,10 +280,10 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen // Ensure mergeStream resolves const result = await _mergedStream; if (result?.success === false) { - logger.error('Error mid-stream (streamWalletTransactions): %o', result.error); + logger.error('Error mid-stream (streamWalletTransactions): %o', result.error?.log || result.error); } - } catch (err) { - logger.error('Error streaming wallet transactions from external provider: %o', err); + } catch (err: any) { + logger.error('Error streaming wallet transactions from external provider: %o', err.log || err); throw err; } } @@ -300,8 +299,8 @@ export class BaseEVMExternalStateProvider extends InternalStateProvider implemen const block = await this.getBlockNumberByDate({ network, date: time }); const result: any = await this.getNativeBalanceByBlock({ network, block, addresses }); return { unconfirmed: 0, confirmed: result?.total_balance, balance: result?.total_balance }; - } catch (err) { - logger.error('Error getting wallet balance at time from external provider %o', err); + } catch (err: any) { + logger.error('Error getting wallet balance at time from external provider %o', err.log || err); throw err; } } diff --git a/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts b/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts index e64e341489b..47884ef7a75 100644 --- a/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts +++ b/packages/bitcore-node/src/providers/chain-state/external/streams/apiStream.ts @@ -86,9 +86,17 @@ export class ExternalApiStream extends Readable { closed = true; }); - stream.on('error', function (err) { + stream.on('error', function (err: any) { if (!closed) { closed = true; + if (err.isAxiosError) { + err.log = { + url: err?.config?.url, + statusCode: err?.response?.status, + statusMsg: err?.response?.statusText, + data: err?.response?.data, + } + } if (!isFirst) { res.write(',\n{"error": "An error occurred during data stream"}\n]'); res.end();