bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
785 lines • 36.4 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseEVMStateProvider = void 0;
const crypto_rpc_1 = require("crypto-rpc");
const config_1 = __importDefault(require("../../../../config"));
const decorators_1 = require("../../../../decorators/decorators");
const logger_1 = __importDefault(require("../../../../logger"));
const cache_1 = require("../../../../models/cache");
const walletAddress_1 = require("../../../../models/walletAddress");
const internal_1 = require("../../../../providers/chain-state/internal/internal");
const storage_1 = require("../../../../services/storage");
const utils_1 = require("../../../../utils");
const stats_1 = require("../../../../utils/stats");
const streamWithEventPipe_1 = require("../../../../utils/streamWithEventPipe");
const apiStream_1 = require("../../external/streams/apiStream");
const erc20_1 = require("../abi/erc20");
const multisend_1 = require("../abi/multisend");
const block_1 = require("../models/block");
const transaction_1 = require("../models/transaction");
const erc20Transform_1 = require("./erc20Transform");
const internalTxTransform_1 = require("./internalTxTransform");
const populateEffectsTransform_1 = require("./populateEffectsTransform");
const populateReceiptTransform_1 = require("./populateReceiptTransform");
const provider_1 = require("./provider");
const transform_1 = require("./transform");
;
class BaseEVMStateProvider extends internal_1.InternalStateProvider {
constructor(chain = 'ETH') {
super(chain);
this.chain = chain;
this.config = config_1.default.chains[this.chain];
}
async getWeb3(network, params) {
for (const rpc of BaseEVMStateProvider.rpcs[this.chain]?.[network] || []) {
if (!(0, provider_1.isValidProviderType)(params?.type, rpc.dataType)) {
continue;
}
try {
if (Date.now() - (rpc.lastPingTime || 0) < 10000) { // Keep the rpc from being blasted with ping calls
return rpc;
}
await Promise.race([
rpc.web3.eth.getBlockNumber(),
new Promise((_, reject) => setTimeout(reject, 5000))
]);
rpc.lastPingTime = Date.now();
return rpc; // return the first applicable rpc that's responsive
}
catch (e) {
// try reconnecting
logger_1.default.info(`Reconnecting to ${this.chain}:${network}`);
if (typeof rpc.web3.currentProvider?.disconnect === 'function') {
rpc.web3.currentProvider?.disconnect?.();
rpc.web3.currentProvider?.connect?.();
if (rpc.web3.currentProvider?.connected) {
return rpc;
}
}
const idx = BaseEVMStateProvider.rpcs[this.chain][network].indexOf(rpc);
BaseEVMStateProvider.rpcs[this.chain][network].splice(idx, 1);
}
}
logger_1.default.info(`Making a new connection for ${this.chain}:${network}`);
const dataType = params?.type;
const providerConfig = (0, provider_1.getProvider)({ network, dataType, config: this.config });
// Default to using ETH CryptoRpc with all EVM chain configs
const rpcConfig = { ...providerConfig, chain: 'ETH', currencyConfig: {} };
const rpc = new crypto_rpc_1.CryptoRpc(rpcConfig, {}).get('ETH');
const rpcObj = { rpc, web3: rpc.web3, dataType: rpcConfig.dataType || 'combined' };
if (!BaseEVMStateProvider.rpcs[this.chain]) {
BaseEVMStateProvider.rpcs[this.chain] = {};
}
if (!BaseEVMStateProvider.rpcs[this.chain][network]) {
BaseEVMStateProvider.rpcs[this.chain][network] = [];
}
BaseEVMStateProvider.rpcs[this.chain][network].push(rpcObj);
return rpcObj;
}
async erc20For(network, address) {
const { web3 } = await this.getWeb3(network);
const contract = new web3.eth.Contract(erc20_1.ERC20Abi, address);
return contract;
}
async getMultisendContract(network, address) {
const { web3 } = await this.getWeb3(network);
const contract = new web3.eth.Contract(multisend_1.MultisendAbi, address);
return contract;
}
async getERC20TokenInfo(network, tokenAddress) {
const token = await this.erc20For(network, tokenAddress);
const [name, decimals, symbol] = await Promise.all([
token.methods.name().call(),
token.methods.decimals().call(),
token.methods.symbol().call()
]);
return {
name,
decimals,
symbol
};
}
async getERC20TokenAllowance(network, tokenAddress, ownerAddress, spenderAddress) {
const token = await this.erc20For(network, tokenAddress);
return await token.methods.allowance(ownerAddress, spenderAddress).call();
}
async getFee(params) {
let { network, target = 4, txType } = params;
const chain = this.chain;
if (network === 'livenet') {
network = 'mainnet';
}
let cacheKey = `getFee-${chain}-${network}-${target}`;
if (txType) {
cacheKey += `-type${txType}`;
}
return cache_1.CacheStorage.getGlobalOrRefresh(cacheKey, async () => {
let feerate;
if (txType?.toString() === '2') {
const { rpc } = await this.getWeb3(network, { type: 'historical' });
feerate = await rpc.estimateFee({ nBlocks: target, txType });
}
else {
const txs = await transaction_1.EVMTransactionStorage.collection
.find({ chain, network, blockHeight: { $gt: 0 } })
.project({ gasPrice: 1, blockHeight: 1 })
.sort({ blockHeight: -1 })
.limit(20 * 200)
.toArray();
const blockGasPrices = txs
.map(tx => Number(tx.gasPrice))
.filter(gasPrice => gasPrice)
.sort((a, b) => b - a);
const whichQuartile = Math.min(target, 4) || 1;
const quartileMedian = stats_1.StatsUtil.getNthQuartileMedian(blockGasPrices, whichQuartile);
const roundedGwei = (quartileMedian / 1e9).toFixed(2);
const gwei = Number(roundedGwei) || 0;
feerate = gwei * 1e9;
}
return { feerate, blocks: target };
}, cache_1.CacheStorage.Times.Minute);
}
async getPriorityFee(params) {
let { network, percentile } = params;
const chain = this.chain;
const priorityFeePercentile = percentile || 15;
if (network === 'livenet') {
network = 'mainnet';
}
let cacheKey = `getFee-${chain}-${network}-priorityFee-${priorityFeePercentile}`;
return cache_1.CacheStorage.getGlobalOrRefresh(cacheKey, async () => {
const { rpc } = await this.getWeb3(network);
let feerate = await rpc.estimateMaxPriorityFee({ percentile: priorityFeePercentile });
return { feerate };
}, cache_1.CacheStorage.Times.Minute);
}
async getBalanceForAddress(params) {
const { chain, network, address, args } = params;
const { web3 } = await this.getWeb3(network, { type: 'realtime' });
const tokenAddress = args?.tokenAddress;
const addressLower = address.toLowerCase();
const hex = args?.hex === 'true' || args?.hex === '1';
const cacheKey = tokenAddress
? `getBalanceForAddress-${chain}-${network}-${addressLower}-${tokenAddress.toLowerCase()}`
: `getBalanceForAddress-${chain}-${network}-${addressLower}`;
const balance = await cache_1.CacheStorage.getGlobalOrRefresh(cacheKey, async () => {
if (tokenAddress) {
const token = await this.erc20For(network, tokenAddress);
const balance = await token.methods.balanceOf(address).call();
const numberBalance = '0x' + BigInt(balance).toString(16);
return { confirmed: numberBalance, unconfirmed: '0x0', balance: numberBalance };
}
else {
const balance = await web3.eth.getBalance(address);
const numberBalance = '0x' + BigInt(balance).toString(16);
return { confirmed: numberBalance, unconfirmed: '0x0', balance: numberBalance };
}
}, cache_1.CacheStorage.Times.Minute);
return {
confirmed: hex ? balance.confirmed : Number(balance.confirmed),
unconfirmed: hex ? balance.unconfirmed : Number(balance.unconfirmed),
balance: hex ? balance.balance : Number(balance.balance)
};
}
async getLocalTip({ chain, network }) {
return block_1.EVMBlockStorage.getLocalTip({ chain, network });
}
async getReceipt(network, txid) {
const { web3 } = await this.getWeb3(network, { type: 'historical' });
return web3.eth.getTransactionReceipt(txid);
}
async populateReceipt(tx) {
if (!tx.receipt) {
const receipt = await this.getReceipt(tx.network, tx.txid);
if (receipt) {
const fee = receipt.gasUsed * tx.gasPrice;
await transaction_1.EVMTransactionStorage.collection.updateOne({ _id: tx._id }, { $set: { receipt, fee } });
tx.receipt = receipt;
tx.fee = fee;
}
}
return tx;
}
populateEffects(tx) {
if (!tx.effects || (tx.effects && tx.effects.length == 0)) {
tx.effects = transaction_1.EVMTransactionStorage.getEffects(tx);
}
return tx;
}
async getTransaction(params) {
try {
params.network = params.network.toLowerCase();
let { chain, network, txId } = params;
if (typeof txId !== 'string') {
throw new Error('Missing required param: txId');
}
if (!chain) {
throw new Error('Missing required param: chain');
}
if (!network) {
throw new Error('Missing required param: network');
}
let { tipHeight, found } = await this._getTransaction(params);
if (found) {
let confirmations = 0;
if (found.blockHeight && found.blockHeight >= 0) {
confirmations = tipHeight - found.blockHeight + 1;
}
found = await this.populateReceipt(found);
// Add effects to old db entries
found = this.populateEffects(found);
const convertedTx = transaction_1.EVMTransactionStorage._apiTransform(found, { object: true });
return { ...convertedTx, confirmations };
}
}
catch (err) {
console.error(err);
}
return undefined;
}
async _getTransaction(params) {
let { chain, network, txId } = params;
let query = { chain, network, txid: txId };
const tip = await this.getLocalTip(params);
const tipHeight = tip ? tip.height : 0;
const found = await transaction_1.EVMTransactionStorage.collection.findOne(query);
return { tipHeight, found };
}
async broadcastTransaction(params) {
const { network, rawTx } = params;
const { web3 } = await this.getWeb3(network, { type: 'realtime' });
const rawTxs = typeof rawTx === 'string' ? [rawTx] : rawTx;
const txids = new Array();
for (const tx of rawTxs) {
const txid = await new Promise((resolve, reject) => {
web3.eth
.sendSignedTransaction(tx)
.on('transactionHash', resolve)
.on('error', reject)
.catch(e => {
logger_1.default.error('%o', e);
reject(e);
});
});
txids.push(txid);
}
return txids.length === 1 ? txids[0] : txids;
}
async streamAddressTransactions(params) {
return new Promise(async (resolve, reject) => {
try {
await this._buildAddressTransactionsStream(params);
return resolve();
}
catch (err) {
return reject(err);
}
});
}
async _buildAddressTransactionsStream(params) {
const { req, res, args, chain, network, address } = params;
const { limit, /*since,*/ tokenAddress } = args;
if (!args.tokenAddress) {
const query = {
$or: [
{ chain, network, from: address },
{ chain, network, to: address },
{ chain, network, 'internal.action.to': address }, // Retained for old db entries
{ chain, network, 'effects.to': address }
]
};
// NOTE: commented out since and paging for now b/c they were causing extra long query times on insight.
// The case where an address has >1000 txns is an edge case ATM and can be addressed later
storage_1.Storage.apiStreamingFind(transaction_1.EVMTransactionStorage, query, { limit /*since, paging: '_id'*/ }, req, res);
}
else {
try {
const tokenTransfers = await this.getErc20Transfers(network, address, tokenAddress, args);
res.json(tokenTransfers);
}
catch (err) {
logger_1.default.error('Error streaming address transactions: %o', err.stack || err.message || err);
throw err;
}
}
}
async streamTransactions(params) {
const { chain, network, req, res, args } = params;
let { blockHash, blockHeight } = args;
if (!chain || !network) {
throw new Error('Missing chain or network');
}
let query = {
chain,
network: network.toLowerCase()
};
if (blockHeight !== undefined) {
query.blockHeight = Number(blockHeight);
}
if (blockHash !== undefined) {
query.blockHash = blockHash;
}
const tip = await this.getLocalTip(params);
const tipHeight = tip ? tip.height : 0;
return storage_1.Storage.apiStreamingFind(transaction_1.EVMTransactionStorage, query, args, req, res, t => {
let confirmations = 0;
if (t.blockHeight !== undefined && t.blockHeight >= 0) {
confirmations = tipHeight - t.blockHeight + 1;
}
// Add effects to old db entries
if (!t.effects || (t.effects && t.effects.length == 0)) {
t.effects = transaction_1.EVMTransactionStorage.getEffects(t);
}
const convertedTx = transaction_1.EVMTransactionStorage._apiTransform(t, { object: true });
return JSON.stringify({ ...convertedTx, confirmations });
});
}
async getWalletBalance(params) {
const { network, args, wallet } = params;
const hex = args.hex === 'true' || args.hex === '1';
if (wallet._id === undefined) {
throw new Error('Wallet balance can only be retrieved for wallets with the _id property');
}
let addresses = await this.getWalletAddresses(wallet._id);
addresses = !args.address ? addresses : addresses.filter(({ address }) => address.toLowerCase() === args.address.toLowerCase());
let addressBalances = await Promise.all(addresses.map(({ address }) => this.getBalanceForAddress({ chain: this.chain, network, address, args })));
let balance = addressBalances.reduce((prev, cur) => ({
unconfirmed: BigInt(prev.unconfirmed) + BigInt(cur.unconfirmed),
confirmed: BigInt(prev.confirmed) + BigInt(cur.confirmed),
balance: BigInt(prev.balance) + BigInt(cur.balance)
}), { unconfirmed: 0n, confirmed: 0n, balance: 0n });
return {
unconfirmed: hex ? '0x' + balance.unconfirmed.toString(16) : Number(balance.unconfirmed),
confirmed: hex ? '0x' + balance.confirmed.toString(16) : Number(balance.confirmed),
balance: hex ? '0x' + balance.balance.toString(16) : Number(balance.balance)
};
}
getWalletTransactionQuery(params) {
const { chain, network, wallet, args } = params;
let query = {
chain,
network,
wallets: wallet._id,
'wallets.0': { $exists: true },
blockHeight: { $gt: -3 } // Exclude invalid transactions
};
if (args) {
if (args.startBlock || args.endBlock) {
query.$or = [];
if (args.includeMempool) {
query.$or.push({ blockHeight: -1 /* SpentHeightIndicators.pending */ });
}
let blockRangeQuery = {};
if (args.startBlock) {
blockRangeQuery.$gte = Number(args.startBlock);
}
if (args.endBlock) {
blockRangeQuery.$lte = Number(args.endBlock);
}
query.$or.push({ blockHeight: blockRangeQuery });
}
else {
if (args.startDate) {
const startDate = new Date(args.startDate);
if (startDate.getTime()) {
query.blockTimeNormalized = { $gte: new Date(args.startDate) };
}
}
if (args.endDate) {
const endDate = new Date(args.endDate);
if (endDate.getTime()) {
query.blockTimeNormalized = query.blockTimeNormalized || {};
query.blockTimeNormalized.$lt = new Date(args.endDate);
}
}
}
if (args.includeInvalidTxs) {
delete query.blockHeight;
}
}
return query;
}
async streamWalletTransactions(params) {
return new Promise(async (resolve, reject) => {
const { network, wallet, req, res, args } = params;
const { web3 } = await this.getWeb3(network);
let transactionStream = new streamWithEventPipe_1.TransformWithEventPipe({ objectMode: true, passThrough: true });
const walletAddresses = (await this.getWalletAddresses(wallet._id)).map(waddres => waddres.address);
const ethTransactionTransform = new transform_1.EVMListTransactionsStream(walletAddresses);
const populateReceipt = new populateReceiptTransform_1.PopulateReceiptTransform();
const populateEffects = new populateEffectsTransform_1.PopulateEffectsTransform();
const streamParams = {
transactionStream,
populateEffects,
walletAddresses
};
transactionStream = await this._buildWalletTransactionsStream(params, streamParams);
if (!args.tokenAddress && wallet._id) {
const internalTxTransform = new internalTxTransform_1.InternalTxRelatedFilterTransform(web3, wallet._id);
transactionStream = transactionStream.eventPipe(internalTxTransform);
}
transactionStream = transactionStream
.eventPipe(populateReceipt)
.eventPipe(ethTransactionTransform);
try {
const result = await apiStream_1.ExternalApiStream.onStream(transactionStream, req, res, { jsonl: true });
if (!result?.success) {
logger_1.default.error('Error mid-stream (streamWalletTransactions): %o', result.error?.log || result.error);
}
return resolve();
}
catch (err) {
return reject(err);
}
});
}
async _buildWalletTransactionsStream(params, streamParams) {
const query = this.getWalletTransactionQuery(params);
let { transactionStream, populateEffects } = streamParams;
transactionStream = transaction_1.EVMTransactionStorage.collection
.find(query)
.sort({ blockTimeNormalized: 1 })
.addCursorFlag('noCursorTimeout', true)
.pipe(new streamWithEventPipe_1.TransformWithEventPipe({ objectMode: true, passThrough: true }));
transactionStream = transactionStream.eventPipe(populateEffects); // For old db entires
if (params.args.tokenAddress) {
const erc20Transform = new erc20Transform_1.Erc20RelatedFilterTransform(params.args.tokenAddress);
transactionStream = transactionStream.eventPipe(erc20Transform);
}
return transactionStream;
}
async getErc20Transfers(network, address, tokenAddress, args = {}) {
const token = await this.erc20For(network, tokenAddress);
let windowSize = 100;
const { web3 } = await this.getWeb3(network);
const tip = await web3.eth.getBlockNumber();
// If endBlock or startBlock is negative, it is a block offset from the tip
if (args.endBlock < 0) {
args.endBlock = tip + Number(args.endBlock);
}
if (args.startBlock < 0) {
args.startBlock = tip + Number(args.startBlock);
}
args.endBlock = Math.min(args.endBlock ?? tip, tip);
args.startBlock = Math.max(args.startBlock != null ? Number(args.startBlock) : args.endBlock - 10000, 0);
if (isNaN(args.startBlock) || isNaN(args.endBlock)) {
throw new Error('startBlock and endBlock must be numbers');
}
else if (args.endBlock < args.startBlock) {
throw new Error('startBlock cannot be greater than endBlock');
}
else if (args.endBlock - args.startBlock > 10000) {
throw new Error('Cannot scan more than 10000 blocks at a time. Please limit your search with startBlock and endBlock');
}
windowSize = Math.min(windowSize, args.endBlock - args.startBlock);
let endBlock = args.endBlock;
const tokenTransfers = [];
while (windowSize > 0) {
const [sent, received] = await Promise.all([
token.getPastEvents('Transfer', {
filter: { _from: address },
fromBlock: endBlock - windowSize,
toBlock: endBlock
}),
token.getPastEvents('Transfer', {
filter: { _to: address },
fromBlock: endBlock - windowSize,
toBlock: endBlock
})
]);
tokenTransfers.push(...this.convertTokenTransfers([...sent, ...received]));
endBlock -= windowSize + 1;
windowSize = Math.min(windowSize, endBlock - args.startBlock);
}
return tokenTransfers;
}
convertTokenTransfers(tokenTransfers) {
return tokenTransfers.map(this.convertTokenTransfer);
}
convertTokenTransfer(transfer) {
const { blockHash, blockNumber, transactionHash, returnValues, transactionIndex } = transfer;
return {
blockHash,
blockNumber,
transactionHash,
transactionIndex,
hash: transactionHash,
from: returnValues['_from'],
to: returnValues['_to'],
value: returnValues['_value']
};
}
async getAccountNonce(network, address) {
const { web3 } = await this.getWeb3(network, { type: 'realtime' });
const count = await web3.eth.getTransactionCount(address);
return count;
/*
*return EthTransactionStorage.collection.countDocuments({
* chain: 'ETH',
* network,
* from: address,
* blockHeight: { $gt: -1 }
*});
*/
}
async getWalletTokenTransactions(network, walletId, tokenAddress, args) {
const addresses = await this.getWalletAddresses(walletId);
const allTokenQueries = Array();
for (const walletAddress of addresses) {
const transfers = this.getErc20Transfers(network, walletAddress.address, tokenAddress, args);
allTokenQueries.push(transfers);
}
let batches = await Promise.all(allTokenQueries);
let txs = batches.reduce((agg, batch) => agg.concat(batch));
return txs.sort((tx1, tx2) => tx1.blockNumber - tx2.blockNumber);
}
async estimateGas(params) {
return new Promise(async (resolve, reject) => {
try {
let { network, value, from, data, /*gasPrice,*/ to } = params;
const { web3 } = await this.getWeb3(network, { type: 'realtime' });
const dataDecoded = transaction_1.EVMTransactionStorage.abiDecode(data);
if (dataDecoded && dataDecoded.type === 'INVOICE' && dataDecoded.name === 'pay') {
value = dataDecoded.params[0].value;
// gasPrice = dataDecoded.params[1].value;
}
else if (data && data.type === 'MULTISEND') {
try {
let method, gasLimit;
const contract = await this.getMultisendContract(network, to);
const addresses = web3.eth.abi.decodeParameter('address[]', data.addresses);
const amounts = web3.eth.abi.decodeParameter('uint256[]', data.amounts);
switch (data.method) {
case 'sendErc20':
method = contract.methods.sendErc20(data.tokenAddress, addresses, amounts);
gasLimit = method ? await method.estimateGas({ from }) : undefined;
break;
case 'sendEth':
method = contract.methods.sendEth(addresses, amounts);
gasLimit = method ? await method.estimateGas({ from, value }) : undefined;
break;
default:
break;
}
return resolve(Number(gasLimit));
}
catch (err) {
return reject(err);
}
}
let _value;
if (data) {
// Gas estimation might fail with `insufficient funds` if value is higher than balance for a normal send.
// We want this method to give a blind fee estimation, though, so we should not include the value
// unless it's needed for estimating smart contract execution.
_value = web3.utils.toHex(value);
}
const opts = {
method: 'eth_estimateGas',
params: [
{
data,
to: to && to.toLowerCase(),
from: from && from.toLowerCase(),
// gasPrice: web3.utils.toHex(gasPrice), // Setting this lower than the baseFee of the last block will cause an error. Better to just leave it out.
value: _value
}
],
jsonrpc: '2.0',
id: 'bitcore-' + Date.now()
};
let provider = web3.currentProvider;
provider.send(opts, (err, data) => {
if (err)
return reject(err);
if (!data.result)
return reject(data.error || data);
return resolve(Number(data.result));
});
}
catch (err) {
return reject(err);
}
});
}
async getBlocks(params) {
const { tipHeight, blocks } = await this._getBlocks(params);
const blockTransform = (b) => {
let confirmations = 0;
if (b.height && b.height >= 0) {
confirmations = tipHeight - b.height + 1;
}
const convertedBlock = block_1.EVMBlockStorage._apiTransform(b, { object: true });
return { ...convertedBlock, confirmations };
};
return blocks.map(blockTransform);
}
async _getBlocks(params) {
const { query, options } = this.getBlocksQuery(params);
let cursor = block_1.EVMBlockStorage.collection.find(query, options).addCursorFlag('noCursorTimeout', true);
if (options.sort) {
cursor = cursor.sort(options.sort);
}
const blocks = await cursor.toArray();
const tip = await this.getLocalTip(params);
const tipHeight = tip ? tip.height : 0;
return { tipHeight, blocks };
}
async updateWallet(params) {
const { chain, network } = params;
const addressBatches = (0, utils_1.partition)(params.addresses, 500);
for (let addressBatch of addressBatches) {
const walletAddressInserts = addressBatch.map(address => {
return {
insertOne: {
document: { chain, network, wallet: params.wallet._id, address, processed: false }
}
};
});
try {
await walletAddress_1.WalletAddressStorage.collection.bulkWrite(walletAddressInserts);
}
catch (err) {
if (err.code !== 11000) {
throw err;
}
}
const addressBatchLC = addressBatch.map(address => address.toLowerCase());
await transaction_1.EVMTransactionStorage.collection.updateMany({
$or: [
{ chain, network, from: { $in: addressBatch } },
{ chain, network, to: { $in: addressBatch } },
{ chain, network, 'internal.action.to': { $in: addressBatchLC } }, // Support old db entries
{ chain, network, 'calls.to': { $in: addressBatchLC } }, // Support old db entries
{
chain,
network,
'calls.abiType.type': 'ERC20',
'calls.abiType.name': { $in: ['transfer', 'transferFrom'] },
'calls.abiType.params.type': 'address',
'calls.abiType.params.value': { $in: addressBatchLC }
},
{ chain, network, 'effects.to': { $in: addressBatch } },
{ chain, network, 'effects.from': { $in: addressBatch } },
]
}, { $addToSet: { wallets: params.wallet._id } });
await walletAddress_1.WalletAddressStorage.collection.updateMany({ chain, network, address: { $in: addressBatch }, wallet: params.wallet._id }, { $set: { processed: true } });
}
}
async getBlocksRange(params) {
const { chain, network, chainId, sinceBlock, args = {} } = params;
let { blockId } = params;
let { startDate, endDate, date, limit = 10, sort = { height: -1 } } = args;
const query = {};
if (!chain || !network) {
throw new Error('Missing required chain and/or network param');
}
// limit - 1 because startBlock is inclusive; ensure limit is >= 0
limit = Math.max(limit - 1, 0);
let height = null;
if (blockId && blockId.length < 64) {
height = parseInt(blockId, 10);
if (isNaN(height) || height.toString(10) != blockId) {
throw new Error('invalid block id provided');
}
blockId = undefined;
}
if (date) {
startDate = new Date(date);
endDate = new Date(date);
endDate.setDate(endDate.getDate() + 1);
}
if (startDate || endDate) {
if (startDate) {
query.startBlock = await this._getBlockNumberByDate({ date: startDate, chainId }) || 0;
}
if (endDate) {
query.endBlock = await this._getBlockNumberByDate({ date: endDate, chainId }) || 0;
}
}
// Get range
if (sinceBlock) {
let height = Number(sinceBlock);
if (isNaN(height) || height.toString(10) != sinceBlock) {
throw new Error('invalid block id provided');
}
const { web3 } = await this.getWeb3(network);
const tipHeight = await web3.eth.getBlockNumber();
if (tipHeight < height) {
return [];
}
query.endBlock = query.endBlock ?? tipHeight;
query.startBlock = query.startBlock ?? query.endBlock - limit;
}
else if (blockId) {
const { web3 } = await this.getWeb3(network);
const blk = await web3.eth.getBlock(blockId);
if (!blk || blk.number == null) {
throw new Error(`Could not get block ${blockId}`);
}
height = blk.number;
}
if (height != null) {
query.startBlock = height;
query.endBlock = height + limit;
}
if (query.startBlock == null || query.endBlock == null) {
// Calaculate range with options
const { web3 } = await this.getWeb3(network);
const tipHeight = await web3.eth.getBlockNumber();
query.endBlock = query.endBlock ?? tipHeight;
query.startBlock = query.startBlock ?? query.endBlock - limit;
}
if (query.endBlock - query.startBlock > limit) {
query.endBlock = query.startBlock + limit;
}
const r = (0, utils_1.range)(query.startBlock, query.endBlock + 1); // +1 since range is [start, end)
if (sort?.height === -1 && query.startBlock < query.endBlock) {
return r.reverse();
}
return r;
}
async _getBlockNumberByDate(params) {
const { date, network } = params;
const block = await block_1.EVMBlockStorage.collection.findOne({ chain: this.chain, network, timeNormalized: { $gte: date } }, { sort: { timeNormalized: 1 } });
return block?.height;
}
async getChainId({ network }) {
const { web3 } = await this.getWeb3(network);
return web3.eth.getChainId();
}
async getCoinsForTx() {
return {
inputs: [],
outputs: []
};
}
}
exports.BaseEVMStateProvider = BaseEVMStateProvider;
BaseEVMStateProvider.rpcs = {};
__decorate([
decorators_1.historical
], BaseEVMStateProvider.prototype, "getFee", null);
__decorate([
decorators_1.historical,
decorators_1.internal
], BaseEVMStateProvider.prototype, "streamTransactions", null);
__decorate([
decorators_1.realtime
], BaseEVMStateProvider.prototype, "getWalletBalance", null);
__decorate([
decorators_1.realtime
], BaseEVMStateProvider.prototype, "getAccountNonce", null);
__decorate([
decorators_1.realtime
], BaseEVMStateProvider.prototype, "estimateGas", null);
//# sourceMappingURL=csp.js.map