@floyddd-vm/ethers-provider-bundle
Version:
This repository contains the `FlashbotsBundleProvider` ethers.js provider, an additional `Provider` to `ethers.js` to enable high-level access to `eth_sendBundle` and `eth_callBundle` rpc endpoint on [mev-relay](https://github.com/flashbots/mev-relay-js).
602 lines • 30.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FlashbotsBundleProvider = exports.FlashbotsBundleConflictType = exports.FlashbotsBundleResolution = exports.BASE_FEE_MAX_CHANGE_DENOMINATOR = exports.DEFAULT_FLASHBOTS_RELAY = void 0;
const web_1 = require("@ethersproject/web");
const ethers_1 = require("ethers");
const utils_1 = require("ethers/lib/utils");
const transactions_1 = require("@ethersproject/transactions");
exports.DEFAULT_FLASHBOTS_RELAY = 'https://relay.flashbots.net';
exports.BASE_FEE_MAX_CHANGE_DENOMINATOR = 8;
var FlashbotsBundleResolution;
(function (FlashbotsBundleResolution) {
FlashbotsBundleResolution[FlashbotsBundleResolution["BundleIncluded"] = 0] = "BundleIncluded";
FlashbotsBundleResolution[FlashbotsBundleResolution["BlockPassedWithoutInclusion"] = 1] = "BlockPassedWithoutInclusion";
FlashbotsBundleResolution[FlashbotsBundleResolution["AccountNonceTooHigh"] = 2] = "AccountNonceTooHigh";
})(FlashbotsBundleResolution = exports.FlashbotsBundleResolution || (exports.FlashbotsBundleResolution = {}));
var FlashbotsBundleConflictType;
(function (FlashbotsBundleConflictType) {
FlashbotsBundleConflictType[FlashbotsBundleConflictType["NoConflict"] = 0] = "NoConflict";
FlashbotsBundleConflictType[FlashbotsBundleConflictType["NonceCollision"] = 1] = "NonceCollision";
FlashbotsBundleConflictType[FlashbotsBundleConflictType["Error"] = 2] = "Error";
FlashbotsBundleConflictType[FlashbotsBundleConflictType["CoinbasePayment"] = 3] = "CoinbasePayment";
FlashbotsBundleConflictType[FlashbotsBundleConflictType["GasUsed"] = 4] = "GasUsed";
FlashbotsBundleConflictType[FlashbotsBundleConflictType["NoBundlesInBlock"] = 5] = "NoBundlesInBlock";
})(FlashbotsBundleConflictType = exports.FlashbotsBundleConflictType || (exports.FlashbotsBundleConflictType = {}));
const TIMEOUT_MS = 5 * 60 * 1000;
class FlashbotsBundleProvider extends ethers_1.providers.JsonRpcProvider {
constructor(genericProvider, authSigner, connectionInfoOrUrl, network) {
super(connectionInfoOrUrl, network);
this.genericProvider = genericProvider;
this.authSigner = authSigner;
this.bloXrouteAuthKey = "";
this.connectionInfo = connectionInfoOrUrl;
}
setBloXrouteKey(bloXrouteAuthKey) {
this.bloXrouteAuthKey = bloXrouteAuthKey;
}
static async throttleCallback() {
console.warn('Rate limited');
return false;
}
static async create(genericProvider, authSigner, connectionInfoOrUrl, network) {
const connectionInfo = typeof connectionInfoOrUrl === 'string' || typeof connectionInfoOrUrl === 'undefined'
? {
url: connectionInfoOrUrl || exports.DEFAULT_FLASHBOTS_RELAY
}
: {
...connectionInfoOrUrl
};
if (connectionInfo.headers === undefined)
connectionInfo.headers = {};
connectionInfo.throttleCallback = FlashbotsBundleProvider.throttleCallback;
const networkish = {
chainId: 0,
name: ''
};
if (typeof network === 'string') {
networkish.name = network;
}
else if (typeof network === 'number') {
networkish.chainId = network;
}
else if (typeof network === 'object') {
networkish.name = network.name;
networkish.chainId = network.chainId;
}
if (networkish.chainId === 0) {
networkish.chainId = (await genericProvider.getNetwork()).chainId;
}
return new FlashbotsBundleProvider(genericProvider, authSigner, connectionInfo, networkish);
}
static getMaxBaseFeeInFutureBlock(baseFee, blocksInFuture) {
let maxBaseFee = ethers_1.BigNumber.from(baseFee);
for (let i = 0; i < blocksInFuture; i++) {
maxBaseFee = maxBaseFee.mul(1125).div(1000).add(1);
}
return maxBaseFee;
}
static getBaseFeeInNextBlock(currentBaseFeePerGas, currentGasUsed, currentGasLimit) {
const currentGasTarget = currentGasLimit.div(2);
if (currentGasUsed.eq(currentGasTarget)) {
return currentBaseFeePerGas;
}
else if (currentGasUsed.gt(currentGasTarget)) {
const gasUsedDelta = currentGasUsed.sub(currentGasTarget);
const baseFeePerGasDelta = currentBaseFeePerGas.mul(gasUsedDelta).div(currentGasTarget).div(exports.BASE_FEE_MAX_CHANGE_DENOMINATOR);
return currentBaseFeePerGas.add(baseFeePerGasDelta);
}
else {
const gasUsedDelta = currentGasTarget.sub(currentGasUsed);
const baseFeePerGasDelta = currentBaseFeePerGas.mul(gasUsedDelta).div(currentGasTarget).div(exports.BASE_FEE_MAX_CHANGE_DENOMINATOR);
return currentBaseFeePerGas.sub(baseFeePerGasDelta);
}
}
static generateBundleHash(txHashes) {
const concatenatedHashes = txHashes.map((txHash) => txHash.slice(2)).join('');
return (0, utils_1.keccak256)(`0x${concatenatedHashes}`);
}
async sendRawBundle(signedBundledTransactions, targetBlockNumber, opts) {
const params = {
txs: signedBundledTransactions,
blockNumber: `0x${targetBlockNumber.toString(16)}`,
minTimestamp: opts === null || opts === void 0 ? void 0 : opts.minTimestamp,
maxTimestamp: opts === null || opts === void 0 ? void 0 : opts.maxTimestamp,
revertingTxHashes: opts === null || opts === void 0 ? void 0 : opts.revertingTxHashes
};
const request = JSON.stringify(this.prepareBundleRequest('eth_sendBundle', [params]));
/*
const response = await this.request(request)
if (response.error !== undefined && response.error !== null) {
return {
error: {
message: response.error.message,
code: response.error.code
}
}
}
*/
const bundleTransactions = signedBundledTransactions.map((signedTransaction) => {
const transactionDetails = ethers_1.ethers.utils.parseTransaction(signedTransaction);
return {
signedTransaction,
hash: ethers_1.ethers.utils.keccak256(signedTransaction),
account: transactionDetails.from || '0x0',
nonce: transactionDetails.nonce
};
});
return {
bundleTransactions,
wait: () => this.wait(bundleTransactions, targetBlockNumber, TIMEOUT_MS),
simulate: () => this.simulate(bundleTransactions.map((tx) => tx.signedTransaction), targetBlockNumber, undefined, opts === null || opts === void 0 ? void 0 : opts.minTimestamp),
receipts: () => this.fetchReceipts(bundleTransactions),
bundleHash: "0x" //response.result.bundleHash
};
}
async sendRawBundle_blxr(signedBundledTransactions, targetBlockNumber, opts) {
const params = {
transaction: signedBundledTransactions,
block_number: `0x${targetBlockNumber.toString(16)}`,
min_timestamp: opts === null || opts === void 0 ? void 0 : opts.minTimestamp,
max_timestamp: opts === null || opts === void 0 ? void 0 : opts.maxTimestamp,
reverting_hashes: opts === null || opts === void 0 ? void 0 : opts.revertingTxHashes
};
const request = JSON.stringify(this.prepareBundleRequest('blxr_submit_bundle', [params]));
/*const response = await this.request_blxr(request)
if (response.error !== undefined && response.error !== null) {
return {
error: {
message: response.error.message,
code: response.error.code
}
}
}*/
const bundleTransactions = signedBundledTransactions.map((signedTransaction) => {
const transactionDetails = ethers_1.ethers.utils.parseTransaction(signedTransaction);
return {
signedTransaction,
hash: ethers_1.ethers.utils.keccak256(signedTransaction),
account: transactionDetails.from || '0x0',
nonce: transactionDetails.nonce
};
});
return {
bundleTransactions,
wait: () => this.wait(bundleTransactions, targetBlockNumber, TIMEOUT_MS),
simulate: () => this.simulate(bundleTransactions.map((tx) => tx.signedTransaction), targetBlockNumber, undefined, opts === null || opts === void 0 ? void 0 : opts.minTimestamp),
receipts: () => this.fetchReceipts(bundleTransactions),
bundleHash: "0x" //response.result.bundleHash
};
}
async sendBundle(bundledTransactions, targetBlockNumber, opts) {
const signedTransactions = await this.signBundle(bundledTransactions);
return this.sendRawBundle(signedTransactions, targetBlockNumber, opts);
}
async signBundle(bundledTransactions) {
const nonces = {};
const signedTransactions = new Array();
for (const tx of bundledTransactions) {
if ('signedTransaction' in tx) {
// in case someone is mixing pre-signed and signing transactions, decode to add to nonce object
const transactionDetails = ethers_1.ethers.utils.parseTransaction(tx.signedTransaction);
if (transactionDetails.from === undefined)
throw new Error('Could not decode signed transaction');
nonces[transactionDetails.from] = ethers_1.BigNumber.from(transactionDetails.nonce + 1);
signedTransactions.push(tx.signedTransaction);
continue;
}
const transaction = { ...tx.transaction };
const address = await tx.signer.getAddress();
if (typeof transaction.nonce === 'string')
throw new Error('Bad nonce');
const nonce = transaction.nonce !== undefined
? ethers_1.BigNumber.from(transaction.nonce)
: nonces[address] || ethers_1.BigNumber.from(await this.genericProvider.getTransactionCount(address, 'latest'));
nonces[address] = nonce.add(1);
if (transaction.nonce === undefined)
transaction.nonce = nonce;
if ((transaction.type == null || transaction.type == 0) && transaction.gasPrice === undefined)
transaction.gasPrice = ethers_1.BigNumber.from(0);
if (transaction.gasLimit === undefined)
transaction.gasLimit = await tx.signer.estimateGas(transaction); // TODO: Add target block number and timestamp when supported by geth
signedTransactions.push(await tx.signer.signTransaction(transaction));
}
return signedTransactions;
}
wait(transactionAccountNonces, targetBlockNumber, timeout) {
return new Promise((resolve, reject) => {
let timer = null;
let done = false;
const minimumNonceByAccount = transactionAccountNonces.reduce((acc, accountNonce) => {
if (accountNonce.nonce > 0) {
if (!acc[accountNonce.account] || accountNonce.nonce < acc[accountNonce.account]) {
acc[accountNonce.account] = accountNonce.nonce;
}
}
return acc;
}, {});
const handler = async (blockNumber) => {
console.log(`wait on_block ${blockNumber}`);
const noncesValid = await Promise.all(Object.entries(minimumNonceByAccount).map(async ([account, nonce]) => {
const transactionCount = await this.genericProvider.getTransactionCount(account, blockNumber);
return nonce >= transactionCount;
}));
const allNoncesValid = noncesValid.every(Boolean);
if (allNoncesValid) {
if (blockNumber < targetBlockNumber)
return;
const block = await this.genericProvider.getBlock(targetBlockNumber);
console.log(`node block number ${block.number}`);
for (const bt of transactionAccountNonces) {
let res = await this.genericProvider.getTransaction(bt.hash);
if (res == null) {
console.log(`tx not found in node`);
continue;
}
console.log(`tx in block - ${res.blockNumber}`);
}
// check bundle against block:
const blockTransactionsHash = {};
for (const bt of block.transactions) {
blockTransactionsHash[bt] = true;
}
const bundleIncluded = transactionAccountNonces.every((transaction) => blockTransactionsHash[transaction.hash]);
resolve(bundleIncluded ? FlashbotsBundleResolution.BundleIncluded : FlashbotsBundleResolution.BlockPassedWithoutInclusion);
}
else
// target block not yet reached, but nonce has become invalid
resolve(FlashbotsBundleResolution.AccountNonceTooHigh);
if (timer) {
clearTimeout(timer);
}
if (done) {
return;
}
done = true;
this.genericProvider.removeListener('block', handler);
};
this.genericProvider.on('block', handler);
if (typeof timeout === 'number' && timeout > 0) {
timer = setTimeout(() => {
if (done) {
return;
}
timer = null;
done = true;
this.genericProvider.removeListener('block', handler);
reject('Timed out');
}, timeout);
if (timer.unref) {
timer.unref();
}
}
});
}
bundleWait(signedBundledTransactions, timeout) {
const transactionAccountNonces = signedBundledTransactions.map((signedTransaction) => {
const transactionDetails = ethers_1.ethers.utils.parseTransaction(signedTransaction);
return {
signedTransaction,
hash: ethers_1.ethers.utils.keccak256(signedTransaction),
account: transactionDetails.from || '0x0',
nonce: transactionDetails.nonce
};
});
return new Promise((resolve, reject) => {
let timer = null;
let done = false;
let needRemoveListener = false;
const minimumNonceByAccount = transactionAccountNonces.reduce((acc, accountNonce) => {
if (accountNonce.nonce > 0) {
if (!acc[accountNonce.account] || accountNonce.nonce < acc[accountNonce.account]) {
acc[accountNonce.account] = accountNonce.nonce;
}
}
return acc;
}, {});
const handler = async (blockNumber) => {
console.log(`wait on_block ${blockNumber}`);
const noncesValid = await Promise.all(Object.entries(minimumNonceByAccount).map(async ([account, nonce]) => {
const transactionCount = await this.genericProvider.getTransactionCount(account); //, blockNumber)
return nonce >= transactionCount;
}));
const allNoncesValid = noncesValid.every(Boolean);
if (allNoncesValid) {
const block = await this.genericProvider.getBlock(blockNumber);
console.log(`node block number ${block.number} - ${block.hash}`);
// check bundle against block:
const blockTransactionsHash = {};
for (const bt of block.transactions) {
blockTransactionsHash[bt] = true;
}
const bundleIncluded = transactionAccountNonces.every((transaction) => blockTransactionsHash[transaction.hash]);
if (bundleIncluded) {
resolve([FlashbotsBundleResolution.BundleIncluded, blockNumber]);
needRemoveListener = true;
}
else {
console.log();
}
}
else {
// target block not yet reached, but nonce has become invalid
resolve([FlashbotsBundleResolution.AccountNonceTooHigh, blockNumber]);
needRemoveListener = true;
}
if (timer) {
clearTimeout(timer);
}
if (done) {
return;
}
if (needRemoveListener) {
this.genericProvider.removeListener('block', handler);
done = true;
}
};
this.genericProvider.on('block', handler);
if (typeof timeout === 'number' && timeout > 0) {
timer = setTimeout(() => {
if (done) {
return;
}
timer = null;
done = true;
this.genericProvider.removeListener('block', handler);
reject('Timed out');
}, timeout);
if (timer.unref) {
timer.unref();
}
}
});
}
async getUserStats() {
const blockDetails = await this.genericProvider.getBlock('latest');
const evmBlockNumber = `0x${blockDetails.number.toString(16)}`;
const params = [evmBlockNumber];
const request = JSON.stringify(this.prepareBundleRequest('flashbots_getUserStats', params));
const response = await this.request(request);
if (response.error !== undefined && response.error !== null) {
return {
error: {
message: response.error.message,
code: response.error.code
}
};
}
return response.result;
}
async getBundleStats(bundleHash, blockNumber) {
const evmBlockNumber = `0x${blockNumber.toString(16)}`;
const params = [{ bundleHash, blockNumber: evmBlockNumber }];
const request = JSON.stringify(this.prepareBundleRequest('flashbots_getBundleStats', params));
const response = await this.request(request);
if (response.error !== undefined && response.error !== null) {
return {
error: {
message: response.error.message,
code: response.error.code
}
};
}
return response.result;
}
async simulate(signedBundledTransactions, blockTag, stateBlockTag, blockTimestamp) {
let evmBlockNumber;
if (typeof blockTag === 'number') {
evmBlockNumber = `0x${blockTag.toString(16)}`;
}
else {
const blockTagDetails = await this.genericProvider.getBlock(blockTag);
const blockDetails = blockTagDetails !== null ? blockTagDetails : await this.genericProvider.getBlock('latest');
evmBlockNumber = `0x${blockDetails.number.toString(16)}`;
}
let evmBlockStateNumber;
if (typeof stateBlockTag === 'number') {
evmBlockStateNumber = `0x${stateBlockTag.toString(16)}`;
}
else if (!stateBlockTag) {
evmBlockStateNumber = 'latest';
}
else {
evmBlockStateNumber = stateBlockTag;
}
const params = [
{
txs: signedBundledTransactions,
blockNumber: evmBlockNumber,
stateBlockNumber: evmBlockStateNumber,
timestamp: blockTimestamp
}
];
const request = JSON.stringify(this.prepareBundleRequest('eth_callBundle', params));
const response = await this.request(request);
if (response.error !== undefined && response.error !== null) {
return {
error: {
message: response.error.message,
code: response.error.code
}
};
}
const callResult = response.result;
return {
bundleHash: callResult.bundleHash,
coinbaseDiff: ethers_1.BigNumber.from(callResult.coinbaseDiff),
results: callResult.results,
totalGasUsed: callResult.results.reduce((a, b) => a + b.gasUsed, 0),
firstRevert: callResult.results.find((txSim) => 'revert' in txSim || 'error' in txSim)
};
}
calculateBundlePricing(bundleTransactions, baseFee) {
const bundleGasPricing = bundleTransactions.reduce((acc, transactionDetail) => {
const gasUsed = 'gas_used' in transactionDetail ? transactionDetail.gas_used : transactionDetail.gasUsed;
const gasPricePaidBySearcher = ethers_1.BigNumber.from('gas_price' in transactionDetail ? transactionDetail.gas_price : transactionDetail.gasPrice);
const priorityFeeReceivedByMiner = gasPricePaidBySearcher.sub(baseFee);
const ethSentToCoinbase = 'coinbase_transfer' in transactionDetail
? transactionDetail.coinbase_transfer
: 'ethSentToCoinbase' in transactionDetail
? transactionDetail.ethSentToCoinbase
: ethers_1.BigNumber.from(0);
return {
gasUsed: acc.gasUsed + gasUsed,
gasFeesPaidBySearcher: acc.gasFeesPaidBySearcher.add(gasPricePaidBySearcher.mul(gasUsed)),
priorityFeesReceivedByMiner: acc.priorityFeesReceivedByMiner.add(priorityFeeReceivedByMiner.mul(gasUsed)),
ethSentToCoinbase: acc.ethSentToCoinbase.add(ethSentToCoinbase)
};
}, {
gasUsed: 0,
gasFeesPaidBySearcher: ethers_1.BigNumber.from(0),
priorityFeesReceivedByMiner: ethers_1.BigNumber.from(0),
ethSentToCoinbase: ethers_1.BigNumber.from(0)
});
const effectiveGasPriceToSearcher = bundleGasPricing.gasUsed > 0
? bundleGasPricing.ethSentToCoinbase.add(bundleGasPricing.gasFeesPaidBySearcher).div(bundleGasPricing.gasUsed)
: ethers_1.BigNumber.from(0);
const effectivePriorityFeeToMiner = bundleGasPricing.gasUsed > 0
? bundleGasPricing.ethSentToCoinbase.add(bundleGasPricing.priorityFeesReceivedByMiner).div(bundleGasPricing.gasUsed)
: ethers_1.BigNumber.from(0);
return {
...bundleGasPricing,
txCount: bundleTransactions.length,
effectiveGasPriceToSearcher,
effectivePriorityFeeToMiner
};
}
async getConflictingBundle(targetSignedBundledTransactions, targetBlockNumber) {
const baseFee = (await this.genericProvider.getBlock(targetBlockNumber)).baseFeePerGas || ethers_1.BigNumber.from(0);
const conflictDetails = await this.getConflictingBundleWithoutGasPricing(targetSignedBundledTransactions, targetBlockNumber);
return {
...conflictDetails,
targetBundleGasPricing: this.calculateBundlePricing(conflictDetails.initialSimulation.results, baseFee),
conflictingBundleGasPricing: conflictDetails.conflictingBundle.length > 0 ? this.calculateBundlePricing(conflictDetails.conflictingBundle, baseFee) : undefined
};
}
async getConflictingBundleWithoutGasPricing(targetSignedBundledTransactions, targetBlockNumber) {
const [initialSimulation, competingBundles] = await Promise.all([
this.simulate(targetSignedBundledTransactions, targetBlockNumber, targetBlockNumber - 1),
this.fetchBlocksApi(targetBlockNumber)
]);
if (competingBundles.latest_block_number <= targetBlockNumber) {
throw new Error('Blocks-api has not processed target block');
}
if ('error' in initialSimulation || initialSimulation.firstRevert !== undefined) {
throw new Error('Target bundle errors at top of block');
}
const blockDetails = competingBundles.blocks[0];
if (blockDetails === undefined) {
return {
initialSimulation,
conflictType: FlashbotsBundleConflictType.NoBundlesInBlock,
conflictingBundle: []
};
}
const bundleTransactions = blockDetails.transactions;
const bundleCount = bundleTransactions[bundleTransactions.length - 1].bundle_index + 1;
const signedPriorBundleTransactions = [];
for (let currentBundleId = 0; currentBundleId < bundleCount; currentBundleId++) {
const currentBundleTransactions = bundleTransactions.filter((bundleTransaction) => bundleTransaction.bundle_index === currentBundleId);
const currentBundleSignedTxs = await Promise.all(currentBundleTransactions.map(async (competitorBundleBlocksApiTx) => {
const tx = await this.genericProvider.getTransaction(competitorBundleBlocksApiTx.transaction_hash);
if (tx.raw !== undefined) {
return tx.raw;
}
if (tx.v !== undefined && tx.r !== undefined && tx.s !== undefined) {
return (0, transactions_1.serialize)(tx, {
v: tx.v,
r: tx.r,
s: tx.s
});
}
throw new Error('Could not get raw tx');
}));
signedPriorBundleTransactions.push(...currentBundleSignedTxs);
const competitorAndTargetBundleSimulation = await this.simulate([...signedPriorBundleTransactions, ...targetSignedBundledTransactions], targetBlockNumber, targetBlockNumber - 1);
if ('error' in competitorAndTargetBundleSimulation) {
if (competitorAndTargetBundleSimulation.error.message.startsWith('err: nonce too low:')) {
return {
conflictType: FlashbotsBundleConflictType.NonceCollision,
initialSimulation,
conflictingBundle: currentBundleTransactions
};
}
throw new Error('Simulation error');
}
const targetSimulation = competitorAndTargetBundleSimulation.results.slice(-targetSignedBundledTransactions.length);
for (let j = 0; j < targetSimulation.length; j++) {
const targetSimulationTx = targetSimulation[j];
const initialSimulationTx = initialSimulation.results[j];
if ('error' in targetSimulationTx || 'error' in initialSimulationTx) {
if ('error' in targetSimulationTx != 'error' in initialSimulationTx) {
return {
conflictType: FlashbotsBundleConflictType.Error,
initialSimulation,
conflictingBundle: currentBundleTransactions
};
}
continue;
}
if (targetSimulationTx.ethSentToCoinbase != initialSimulationTx.ethSentToCoinbase) {
return {
conflictType: FlashbotsBundleConflictType.CoinbasePayment,
initialSimulation,
conflictingBundle: currentBundleTransactions
};
}
if (targetSimulationTx.gasUsed != initialSimulation.results[j].gasUsed) {
return {
conflictType: FlashbotsBundleConflictType.GasUsed,
initialSimulation,
conflictingBundle: currentBundleTransactions
};
}
}
}
return {
conflictType: FlashbotsBundleConflictType.NoConflict,
initialSimulation,
conflictingBundle: []
};
}
async fetchBlocksApi(blockNumber) {
return (0, web_1.fetchJson)(`https://blocks.flashbots.net/v1/blocks?block_number=${blockNumber}`);
}
async request(request) {
const connectionInfo = { ...this.connectionInfo };
connectionInfo.headers = {
'X-Flashbots-Signature': `${await this.authSigner.getAddress()}:${await this.authSigner.signMessage((0, utils_1.id)(request))}`,
...this.connectionInfo.headers
};
return (0, web_1.fetchJson)(connectionInfo, request);
}
async request_blxr(request) {
const connectionInfo = { ...this.connectionInfo };
connectionInfo.url = "https://54.157.119.190"; // "https://mev.api.blxrbdn.com";
connectionInfo.headers = {
"Content-Type": "application/json",
"Authorization": `${this.bloXrouteAuthKey}`,
...this.connectionInfo.headers
};
let bundle = JSON.parse(request);
//qqq.params[0].transaction[0] = qqq.params[0].transaction[0].substring(2);
//qqq.params[0].transaction[1] = qqq.params[0].transaction[1].substring(2);
for (let i = 0; i < bundle.params[0].transaction.length; i++) {
bundle.params[0].transaction[i] = bundle.params[0].transaction[i].substring(2);
}
let new_request = JSON.stringify(bundle);
return (0, web_1.fetchJson)(connectionInfo, new_request);
}
async fetchReceipts(bundledTransactions) {
return Promise.all(bundledTransactions.map((bundledTransaction) => this.genericProvider.getTransactionReceipt(bundledTransaction.hash)));
}
prepareBundleRequest(method, params) {
return {
method: method,
params: params,
id: this._nextId++,
jsonrpc: '2.0'
};
}
}
exports.FlashbotsBundleProvider = FlashbotsBundleProvider;
//# sourceMappingURL=index.js.map