@flashbots/ethers-provider-bundle
Advanced tools
Comparing version 0.6.1 to 0.6.2
@@ -17,3 +17,3 @@ "use strict"; | ||
// ) | ||
// const FLASHBOTS_EP = undefined; | ||
// const FLASHBOTS_EP = 'https://relay.flashbots.net/' | ||
// ===== Uncomment this for mainnet ======= | ||
@@ -28,3 +28,3 @@ // ===== Uncomment this for Goerli ======= | ||
// don't warn for skipping ETHEREUM_RPC_URL if using goerli | ||
if (FLASHBOTS_EP && FLASHBOTS_EP.includes('goerli') && e === 'ETHEREUM_RPC_URL') { | ||
if (FLASHBOTS_EP.includes('goerli') && e === 'ETHEREUM_RPC_URL') { | ||
continue; | ||
@@ -54,3 +54,4 @@ } | ||
data: '0x', | ||
nonce: await provider.getTransactionCount(wallet.address) | ||
nonce: await provider.getTransactionCount(wallet.address), | ||
chainId: CHAIN_ID | ||
}; | ||
@@ -57,0 +58,0 @@ provider.on('block', async (blockNumber) => { |
@@ -69,2 +69,3 @@ import { BlockTag, TransactionReceipt, TransactionRequest } from '@ethersproject/abstract-provider'; | ||
ethSentToCoinbase: string; | ||
coinbaseDiff: string; | ||
} | ||
@@ -199,9 +200,85 @@ export interface TransactionSimulationRevert extends TransactionSimulationBase { | ||
static throttleCallback(): Promise<boolean>; | ||
/** | ||
* Creates a new Flashbots provider. | ||
* @param genericProvider ethers.js mainnet provider | ||
* @param authSigner account to sign bundles | ||
* @param connectionInfoOrUrl (optional) connection settings | ||
* @param network (optional) network settings | ||
* | ||
* @example | ||
* ```typescript | ||
* const {providers, Wallet} = require("ethers") | ||
* const {FlashbotsBundleProvider} = require("@flashbots/ethers-provider-bundle") | ||
* const authSigner = Wallet.createRandom() | ||
* const provider = new providers.JsonRpcProvider("http://localhost:8545") | ||
* const fbProvider = await FlashbotsBundleProvider.create(provider, authSigner) | ||
* ``` | ||
*/ | ||
static create(genericProvider: BaseProvider, authSigner: Signer, connectionInfoOrUrl?: ConnectionInfo | string, network?: Networkish): Promise<FlashbotsBundleProvider>; | ||
/** | ||
* Calculates maximum base fee in a future block. | ||
* @param baseFee current base fee | ||
* @param blocksInFuture number of blocks in the future | ||
*/ | ||
static getMaxBaseFeeInFutureBlock(baseFee: BigNumber, blocksInFuture: number): BigNumber; | ||
/** | ||
* Calculates base fee for the next block. | ||
* @param currentBaseFeePerGas base fee of current block (wei) | ||
* @param currentGasUsed gas used by tx in simulation | ||
* @param currentGasLimit gas limit of transaction | ||
*/ | ||
static getBaseFeeInNextBlock(currentBaseFeePerGas: BigNumber, currentGasUsed: BigNumber, currentGasLimit: BigNumber): BigNumber; | ||
/** | ||
* Calculates a bundle hash locally. | ||
* @param txHashes hashes of transactions in the bundle | ||
*/ | ||
static generateBundleHash(txHashes: Array<string>): string; | ||
/** | ||
* Sends a signed flashbots bundle to Flashbots Relay. | ||
* @param signedBundledTransactions array of raw signed transactions | ||
* @param targetBlockNumber block to target for bundle inclusion | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and the bundle hash | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x02..."}, | ||
* {signedTransaction: "0x02..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const bundleRes = await fbProvider.sendRawBundle(signedBundle, blockNum + 1) | ||
* const success = (await bundleRes.wait()) === FlashbotsBundleResolution.BundleIncluded | ||
* ``` | ||
*/ | ||
sendRawBundle(signedBundledTransactions: Array<string>, targetBlockNumber: number, opts?: FlashbotsOptions): Promise<FlashbotsTransaction>; | ||
/** | ||
* Sends a bundle to Flashbots, supports multiple transaction interfaces. | ||
* @param bundledTransactions array of transactions, either signed or provided with a signer. | ||
* @param targetBlockNumber block to target for bundle inclusion | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and the bundle hash | ||
*/ | ||
sendBundle(bundledTransactions: Array<FlashbotsBundleTransaction | FlashbotsBundleRawTransaction>, targetBlockNumber: number, opts?: FlashbotsOptions): Promise<FlashbotsTransaction>; | ||
cancelBundles(bidId: string): Promise<FlashbotsCancelBidResponse>; | ||
/** Cancel any bundles submitted with the given `replacementUuid` | ||
* @param replacementUuid specified in `sendBundle` | ||
* @returns bundle hashes of the cancelled bundles | ||
*/ | ||
cancelBundles(replacementUuid: string): Promise<FlashbotsCancelBidResponse>; | ||
/** | ||
* Sends a single private transaction to Flashbots. | ||
* @param transaction transaction, either signed or provided with a signer | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and transaction data | ||
* | ||
* @example | ||
* ```typescript | ||
* const tx: FlashbotsBundleRawTransaction = {signedTransaction: "0x02..."} | ||
* const blockNum = await provider.getBlockNumber() | ||
* // try sending for 5 blocks | ||
* const response = await fbProvider.sendPrivateTransaction(tx, {maxBlockNumber: blockNum + 5}) | ||
* const success = (await response.wait()) === FlashbotsTransactionResolution.TransactionIncluded | ||
* ``` | ||
*/ | ||
sendPrivateTransaction(transaction: FlashbotsBundleTransaction | FlashbotsBundleRawTransaction, opts?: { | ||
@@ -211,14 +288,107 @@ maxBlockNumber?: number; | ||
}): Promise<FlashbotsPrivateTransaction>; | ||
/** | ||
* Attempts to cancel a pending private transaction. | ||
* | ||
* **_Note_**: This function removes the transaction from the Flashbots | ||
* bundler, but miners may still include it if they have received it already. | ||
* @param txHash transaction hash corresponding to pending tx | ||
* @returns true if transaction was cancelled successfully | ||
* | ||
* @example | ||
* ```typescript | ||
* const pendingTxHash = (await fbProvider.sendPrivateTransaction(tx)).transaction.hash | ||
* const isTxCanceled = await fbProvider.cancelPrivateTransaction(pendingTxHash) | ||
* ``` | ||
*/ | ||
cancelPrivateTransaction(txHash: string): Promise<boolean | RelayResponseError>; | ||
/** | ||
* Signs a Flashbots bundle with this provider's `authSigner` key. | ||
* @param bundledTransactions | ||
* @returns signed bundle | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x02..."}, | ||
* {signedTransaction: "0x02..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const simResult = await fbProvider.simulate(signedBundle, blockNum + 1) | ||
* ``` | ||
*/ | ||
signBundle(bundledTransactions: Array<FlashbotsBundleTransaction | FlashbotsBundleRawTransaction>): Promise<Array<string>>; | ||
private waitForBlock; | ||
/** | ||
* Watches for a specific block to see if a bundle was included in it. | ||
* @param transactionAccountNonces bundle transactions | ||
* @param targetBlockNumber block number to check for bundle inclusion | ||
* @param timeout ms | ||
*/ | ||
private waitForBundleInclusion; | ||
/** | ||
* Waits for a transaction to be included on-chain. | ||
* @param transactionHash | ||
* @param maxBlockNumber highest block number to check before stopping | ||
* @param timeout ms | ||
*/ | ||
private waitForTxInclusion; | ||
/** | ||
* Gets stats for provider instance's `authSigner` address. | ||
* @deprecated use {@link getUserStatsV2} instead. | ||
*/ | ||
getUserStats(): Promise<GetUserStatsResponse>; | ||
/** | ||
* Gets stats for provider instance's `authSigner` address. | ||
*/ | ||
getUserStatsV2(): Promise<GetUserStatsResponseV2>; | ||
/** | ||
* Gets information about a specific bundle. | ||
* @param bundleHash hash of bundle to investigate | ||
* @param blockNumber block in which the bundle should be included | ||
* @deprecated use {@link getBundleStatsV2} instead. | ||
*/ | ||
getBundleStats(bundleHash: string, blockNumber: number): Promise<GetBundleStatsResponse>; | ||
/** | ||
* Gets information about a specific bundle. | ||
* @param bundleHash hash of bundle to investigate | ||
* @param blockNumber block in which the bundle should be included | ||
*/ | ||
getBundleStatsV2(bundleHash: string, blockNumber: number): Promise<GetBundleStatsResponseV2>; | ||
simulate(signedBundledTransactions: Array<string>, blockTag: BlockTag, stateBlockTag?: BlockTag, blockTimestamp?: number): Promise<SimulationResponse>; | ||
/** | ||
* Simluates a bundle on a given block. | ||
* @param signedBundledTransactions signed Flashbots bundle | ||
* @param blockTag block tag to simulate against, can use "latest" | ||
* @param stateBlockTag (optional) simulated block state tag | ||
* @param blockTimestamp (optional) simulated timestamp | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x1..."}, | ||
* {signedTransaction: "0x2..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const simResult = await fbProvider.simulate(signedBundle, blockNum + 1) | ||
* ``` | ||
*/ | ||
simulate(signedBundledTransactions: Array<string>, blockTag: BlockTag, stateBlockTag?: BlockTag, blockTimestamp?: number, coinbase?: string): Promise<SimulationResponse>; | ||
private calculateBundlePricing; | ||
/** | ||
* Gets information about a conflicting bundle. Useful if you're competing | ||
* for well-known MEV and want to know why your bundle didn't land. | ||
* @param targetSignedBundledTransactions signed bundle | ||
* @param targetBlockNumber block in which bundle should be included | ||
* @returns conflict and gas price details | ||
*/ | ||
getConflictingBundle(targetSignedBundledTransactions: Array<string>, targetBlockNumber: number): Promise<FlashbotsBundleConflictWithGasPricing>; | ||
/** | ||
* Gets information about a conflicting bundle. Useful if you're competing | ||
* for well-known MEV and want to know why your bundle didn't land. | ||
* @param targetSignedBundledTransactions signed bundle | ||
* @param targetBlockNumber block in which bundle should be included | ||
* @returns conflict details | ||
*/ | ||
getConflictingBundleWithoutGasPricing(targetSignedBundledTransactions: Array<string>, targetBlockNumber: number): Promise<FlashbotsBundleConflict>; | ||
/** Gets information about a block from Flashbots blocks API. */ | ||
fetchBlocksApi(blockNumber: number): Promise<BlocksApiResponse>; | ||
@@ -225,0 +395,0 @@ private request; |
@@ -42,2 +42,18 @@ "use strict"; | ||
} | ||
/** | ||
* Creates a new Flashbots provider. | ||
* @param genericProvider ethers.js mainnet provider | ||
* @param authSigner account to sign bundles | ||
* @param connectionInfoOrUrl (optional) connection settings | ||
* @param network (optional) network settings | ||
* | ||
* @example | ||
* ```typescript | ||
* const {providers, Wallet} = require("ethers") | ||
* const {FlashbotsBundleProvider} = require("@flashbots/ethers-provider-bundle") | ||
* const authSigner = Wallet.createRandom() | ||
* const provider = new providers.JsonRpcProvider("http://localhost:8545") | ||
* const fbProvider = await FlashbotsBundleProvider.create(provider, authSigner) | ||
* ``` | ||
*/ | ||
static async create(genericProvider, authSigner, connectionInfoOrUrl, network) { | ||
@@ -73,2 +89,7 @@ const connectionInfo = typeof connectionInfoOrUrl === 'string' || typeof connectionInfoOrUrl === 'undefined' | ||
} | ||
/** | ||
* Calculates maximum base fee in a future block. | ||
* @param baseFee current base fee | ||
* @param blocksInFuture number of blocks in the future | ||
*/ | ||
static getMaxBaseFeeInFutureBlock(baseFee, blocksInFuture) { | ||
@@ -81,2 +102,8 @@ let maxBaseFee = ethers_1.BigNumber.from(baseFee); | ||
} | ||
/** | ||
* Calculates base fee for the next block. | ||
* @param currentBaseFeePerGas base fee of current block (wei) | ||
* @param currentGasUsed gas used by tx in simulation | ||
* @param currentGasLimit gas limit of transaction | ||
*/ | ||
static getBaseFeeInNextBlock(currentBaseFeePerGas, currentGasUsed, currentGasLimit) { | ||
@@ -98,2 +125,6 @@ const currentGasTarget = currentGasLimit.div(2); | ||
} | ||
/** | ||
* Calculates a bundle hash locally. | ||
* @param txHashes hashes of transactions in the bundle | ||
*/ | ||
static generateBundleHash(txHashes) { | ||
@@ -103,2 +134,21 @@ const concatenatedHashes = txHashes.map((txHash) => txHash.slice(2)).join(''); | ||
} | ||
/** | ||
* Sends a signed flashbots bundle to Flashbots Relay. | ||
* @param signedBundledTransactions array of raw signed transactions | ||
* @param targetBlockNumber block to target for bundle inclusion | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and the bundle hash | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x02..."}, | ||
* {signedTransaction: "0x02..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const bundleRes = await fbProvider.sendRawBundle(signedBundle, blockNum + 1) | ||
* const success = (await bundleRes.wait()) === FlashbotsBundleResolution.BundleIncluded | ||
* ``` | ||
*/ | ||
async sendRawBundle(signedBundledTransactions, targetBlockNumber, opts) { | ||
@@ -134,3 +184,3 @@ const params = { | ||
bundleTransactions, | ||
wait: () => this.waitForBlock(bundleTransactions, targetBlockNumber, TIMEOUT_MS), | ||
wait: () => this.waitForBundleInclusion(bundleTransactions, targetBlockNumber, TIMEOUT_MS), | ||
simulate: () => this.simulate(bundleTransactions.map((tx) => tx.signedTransaction), targetBlockNumber, undefined, opts === null || opts === void 0 ? void 0 : opts.minTimestamp), | ||
@@ -141,2 +191,9 @@ receipts: () => this.fetchReceipts(bundleTransactions), | ||
} | ||
/** | ||
* Sends a bundle to Flashbots, supports multiple transaction interfaces. | ||
* @param bundledTransactions array of transactions, either signed or provided with a signer. | ||
* @param targetBlockNumber block to target for bundle inclusion | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and the bundle hash | ||
*/ | ||
async sendBundle(bundledTransactions, targetBlockNumber, opts) { | ||
@@ -146,5 +203,9 @@ const signedTransactions = await this.signBundle(bundledTransactions); | ||
} | ||
async cancelBundles(bidId) { | ||
/** Cancel any bundles submitted with the given `replacementUuid` | ||
* @param replacementUuid specified in `sendBundle` | ||
* @returns bundle hashes of the cancelled bundles | ||
*/ | ||
async cancelBundles(replacementUuid) { | ||
const params = { | ||
replacementUuid: bidId | ||
replacementUuid: replacementUuid | ||
}; | ||
@@ -165,3 +226,19 @@ const request = JSON.stringify(this.prepareRelayRequest('eth_cancelBundle', [params])); | ||
} | ||
/** | ||
* Sends a single private transaction to Flashbots. | ||
* @param transaction transaction, either signed or provided with a signer | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and transaction data | ||
* | ||
* @example | ||
* ```typescript | ||
* const tx: FlashbotsBundleRawTransaction = {signedTransaction: "0x02..."} | ||
* const blockNum = await provider.getBlockNumber() | ||
* // try sending for 5 blocks | ||
* const response = await fbProvider.sendPrivateTransaction(tx, {maxBlockNumber: blockNum + 5}) | ||
* const success = (await response.wait()) === FlashbotsTransactionResolution.TransactionIncluded | ||
* ``` | ||
*/ | ||
async sendPrivateTransaction(transaction, opts) { | ||
const startBlockNumberPromise = this.genericProvider.getBlockNumber(); | ||
let signedTransaction; | ||
@@ -174,3 +251,2 @@ if ('signedTransaction' in transaction) { | ||
} | ||
const startBlockNumberPromise = this.genericProvider.getBlockNumber(); | ||
const params = { | ||
@@ -205,2 +281,16 @@ tx: signedTransaction, | ||
} | ||
/** | ||
* Attempts to cancel a pending private transaction. | ||
* | ||
* **_Note_**: This function removes the transaction from the Flashbots | ||
* bundler, but miners may still include it if they have received it already. | ||
* @param txHash transaction hash corresponding to pending tx | ||
* @returns true if transaction was cancelled successfully | ||
* | ||
* @example | ||
* ```typescript | ||
* const pendingTxHash = (await fbProvider.sendPrivateTransaction(tx)).transaction.hash | ||
* const isTxCanceled = await fbProvider.cancelPrivateTransaction(pendingTxHash) | ||
* ``` | ||
*/ | ||
async cancelPrivateTransaction(txHash) { | ||
@@ -222,2 +312,18 @@ const params = { | ||
} | ||
/** | ||
* Signs a Flashbots bundle with this provider's `authSigner` key. | ||
* @param bundledTransactions | ||
* @returns signed bundle | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x02..."}, | ||
* {signedTransaction: "0x02..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const simResult = await fbProvider.simulate(signedBundle, blockNum + 1) | ||
* ``` | ||
*/ | ||
async signBundle(bundledTransactions) { | ||
@@ -254,3 +360,9 @@ const nonces = {}; | ||
} | ||
waitForBlock(transactionAccountNonces, targetBlockNumber, timeout) { | ||
/** | ||
* Watches for a specific block to see if a bundle was included in it. | ||
* @param transactionAccountNonces bundle transactions | ||
* @param targetBlockNumber block number to check for bundle inclusion | ||
* @param timeout ms | ||
*/ | ||
waitForBundleInclusion(transactionAccountNonces, targetBlockNumber, timeout) { | ||
return new Promise((resolve, reject) => { | ||
@@ -315,2 +427,8 @@ let timer = null; | ||
} | ||
/** | ||
* Waits for a transaction to be included on-chain. | ||
* @param transactionHash | ||
* @param maxBlockNumber highest block number to check before stopping | ||
* @param timeout ms | ||
*/ | ||
waitForTxInclusion(transactionHash, maxBlockNumber, timeout) { | ||
@@ -364,2 +482,6 @@ return new Promise((resolve, reject) => { | ||
} | ||
/** | ||
* Gets stats for provider instance's `authSigner` address. | ||
* @deprecated use {@link getUserStatsV2} instead. | ||
*/ | ||
async getUserStats() { | ||
@@ -381,2 +503,5 @@ const blockDetails = await this.genericProvider.getBlock('latest'); | ||
} | ||
/** | ||
* Gets stats for provider instance's `authSigner` address. | ||
*/ | ||
async getUserStatsV2() { | ||
@@ -398,2 +523,8 @@ const blockDetails = await this.genericProvider.getBlock('latest'); | ||
} | ||
/** | ||
* Gets information about a specific bundle. | ||
* @param bundleHash hash of bundle to investigate | ||
* @param blockNumber block in which the bundle should be included | ||
* @deprecated use {@link getBundleStatsV2} instead. | ||
*/ | ||
async getBundleStats(bundleHash, blockNumber) { | ||
@@ -414,2 +545,7 @@ const evmBlockNumber = `0x${blockNumber.toString(16)}`; | ||
} | ||
/** | ||
* Gets information about a specific bundle. | ||
* @param bundleHash hash of bundle to investigate | ||
* @param blockNumber block in which the bundle should be included | ||
*/ | ||
async getBundleStatsV2(bundleHash, blockNumber) { | ||
@@ -430,3 +566,21 @@ const evmBlockNumber = `0x${blockNumber.toString(16)}`; | ||
} | ||
async simulate(signedBundledTransactions, blockTag, stateBlockTag, blockTimestamp) { | ||
/** | ||
* Simluates a bundle on a given block. | ||
* @param signedBundledTransactions signed Flashbots bundle | ||
* @param blockTag block tag to simulate against, can use "latest" | ||
* @param stateBlockTag (optional) simulated block state tag | ||
* @param blockTimestamp (optional) simulated timestamp | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x1..."}, | ||
* {signedTransaction: "0x2..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const simResult = await fbProvider.simulate(signedBundle, blockNum + 1) | ||
* ``` | ||
*/ | ||
async simulate(signedBundledTransactions, blockTag, stateBlockTag, blockTimestamp, coinbase) { | ||
let evmBlockNumber; | ||
@@ -456,3 +610,4 @@ if (typeof blockTag === 'number') { | ||
stateBlockNumber: evmBlockStateNumber, | ||
timestamp: blockTimestamp | ||
timestamp: blockTimestamp, | ||
coinbase | ||
} | ||
@@ -485,5 +640,4 @@ ]; | ||
const bundleGasPricing = bundleTransactions.reduce((acc, transactionDetail) => { | ||
// see: https://blocks.flashbots.net/ and https://github.com/flashbots/ethers-provider-flashbots-bundle/issues/62 | ||
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 | ||
@@ -494,6 +648,12 @@ ? transactionDetail.coinbase_transfer | ||
: ethers_1.BigNumber.from(0); | ||
const totalMinerReward = 'total_miner_reward' in transactionDetail | ||
? ethers_1.BigNumber.from(transactionDetail.total_miner_reward) | ||
: 'coinbaseDiff' in transactionDetail | ||
? ethers_1.BigNumber.from(transactionDetail.coinbaseDiff) | ||
: ethers_1.BigNumber.from(0); | ||
const priorityFeeReceivedByMiner = totalMinerReward.sub(ethSentToCoinbase); | ||
return { | ||
gasUsed: acc.gasUsed + gasUsed, | ||
gasFeesPaidBySearcher: acc.gasFeesPaidBySearcher.add(gasPricePaidBySearcher.mul(gasUsed)), | ||
priorityFeesReceivedByMiner: acc.priorityFeesReceivedByMiner.add(priorityFeeReceivedByMiner.mul(gasUsed)), | ||
gasFeesPaidBySearcher: acc.gasFeesPaidBySearcher.add(baseFee.mul(gasUsed).add(priorityFeeReceivedByMiner)), | ||
priorityFeesReceivedByMiner: acc.priorityFeesReceivedByMiner.add(priorityFeeReceivedByMiner), | ||
ethSentToCoinbase: acc.ethSentToCoinbase.add(ethSentToCoinbase) | ||
@@ -520,2 +680,9 @@ }; | ||
} | ||
/** | ||
* Gets information about a conflicting bundle. Useful if you're competing | ||
* for well-known MEV and want to know why your bundle didn't land. | ||
* @param targetSignedBundledTransactions signed bundle | ||
* @param targetBlockNumber block in which bundle should be included | ||
* @returns conflict and gas price details | ||
*/ | ||
async getConflictingBundle(targetSignedBundledTransactions, targetBlockNumber) { | ||
@@ -530,2 +697,9 @@ const baseFee = (await this.genericProvider.getBlock(targetBlockNumber)).baseFeePerGas || ethers_1.BigNumber.from(0); | ||
} | ||
/** | ||
* Gets information about a conflicting bundle. Useful if you're competing | ||
* for well-known MEV and want to know why your bundle didn't land. | ||
* @param targetSignedBundledTransactions signed bundle | ||
* @param targetBlockNumber block in which bundle should be included | ||
* @returns conflict details | ||
*/ | ||
async getConflictingBundleWithoutGasPricing(targetSignedBundledTransactions, targetBlockNumber) { | ||
@@ -620,2 +794,3 @@ const [initialSimulation, competingBundles] = await Promise.all([ | ||
} | ||
/** Gets information about a block from Flashbots blocks API. */ | ||
async fetchBlocksApi(blockNumber) { | ||
@@ -622,0 +797,0 @@ return web_1.fetchJson(`https://blocks.flashbots.net/v1/blocks?block_number=${blockNumber}`); |
{ | ||
"name": "@flashbots/ethers-provider-bundle", | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"description": "", | ||
@@ -37,4 +37,3 @@ "main": "build/index.js", | ||
}, | ||
"dependencies": { | ||
} | ||
"dependencies": {} | ||
} |
@@ -19,3 +19,3 @@ import { BigNumber, providers, Wallet } from 'ethers' | ||
// ) | ||
// const FLASHBOTS_EP = undefined; | ||
// const FLASHBOTS_EP = 'https://relay.flashbots.net/' | ||
// ===== Uncomment this for mainnet ======= | ||
@@ -32,3 +32,3 @@ | ||
// don't warn for skipping ETHEREUM_RPC_URL if using goerli | ||
if (FLASHBOTS_EP && FLASHBOTS_EP.includes('goerli') && e === 'ETHEREUM_RPC_URL') { | ||
if (FLASHBOTS_EP.includes('goerli') && e === 'ETHEREUM_RPC_URL') { | ||
continue | ||
@@ -60,3 +60,4 @@ } | ||
data: '0x', | ||
nonce: await provider.getTransactionCount(wallet.address) | ||
nonce: await provider.getTransactionCount(wallet.address), | ||
chainId: CHAIN_ID | ||
} | ||
@@ -63,0 +64,0 @@ |
204
src/index.ts
@@ -83,2 +83,3 @@ import { BlockTag, TransactionReceipt, TransactionRequest } from '@ethersproject/abstract-provider' | ||
ethSentToCoinbase: string | ||
coinbaseDiff: string | ||
} | ||
@@ -251,2 +252,18 @@ | ||
/** | ||
* Creates a new Flashbots provider. | ||
* @param genericProvider ethers.js mainnet provider | ||
* @param authSigner account to sign bundles | ||
* @param connectionInfoOrUrl (optional) connection settings | ||
* @param network (optional) network settings | ||
* | ||
* @example | ||
* ```typescript | ||
* const {providers, Wallet} = require("ethers") | ||
* const {FlashbotsBundleProvider} = require("@flashbots/ethers-provider-bundle") | ||
* const authSigner = Wallet.createRandom() | ||
* const provider = new providers.JsonRpcProvider("http://localhost:8545") | ||
* const fbProvider = await FlashbotsBundleProvider.create(provider, authSigner) | ||
* ``` | ||
*/ | ||
static async create( | ||
@@ -288,2 +305,7 @@ genericProvider: BaseProvider, | ||
/** | ||
* Calculates maximum base fee in a future block. | ||
* @param baseFee current base fee | ||
* @param blocksInFuture number of blocks in the future | ||
*/ | ||
static getMaxBaseFeeInFutureBlock(baseFee: BigNumber, blocksInFuture: number): BigNumber { | ||
@@ -297,2 +319,8 @@ let maxBaseFee = BigNumber.from(baseFee) | ||
/** | ||
* Calculates base fee for the next block. | ||
* @param currentBaseFeePerGas base fee of current block (wei) | ||
* @param currentGasUsed gas used by tx in simulation | ||
* @param currentGasLimit gas limit of transaction | ||
*/ | ||
static getBaseFeeInNextBlock(currentBaseFeePerGas: BigNumber, currentGasUsed: BigNumber, currentGasLimit: BigNumber): BigNumber { | ||
@@ -316,2 +344,6 @@ const currentGasTarget = currentGasLimit.div(2) | ||
/** | ||
* Calculates a bundle hash locally. | ||
* @param txHashes hashes of transactions in the bundle | ||
*/ | ||
static generateBundleHash(txHashes: Array<string>): string { | ||
@@ -322,2 +354,21 @@ const concatenatedHashes = txHashes.map((txHash) => txHash.slice(2)).join('') | ||
/** | ||
* Sends a signed flashbots bundle to Flashbots Relay. | ||
* @param signedBundledTransactions array of raw signed transactions | ||
* @param targetBlockNumber block to target for bundle inclusion | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and the bundle hash | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x02..."}, | ||
* {signedTransaction: "0x02..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const bundleRes = await fbProvider.sendRawBundle(signedBundle, blockNum + 1) | ||
* const success = (await bundleRes.wait()) === FlashbotsBundleResolution.BundleIncluded | ||
* ``` | ||
*/ | ||
public async sendRawBundle( | ||
@@ -360,3 +411,3 @@ signedBundledTransactions: Array<string>, | ||
bundleTransactions, | ||
wait: () => this.waitForBlock(bundleTransactions, targetBlockNumber, TIMEOUT_MS), | ||
wait: () => this.waitForBundleInclusion(bundleTransactions, targetBlockNumber, TIMEOUT_MS), | ||
simulate: () => | ||
@@ -374,2 +425,9 @@ this.simulate( | ||
/** | ||
* Sends a bundle to Flashbots, supports multiple transaction interfaces. | ||
* @param bundledTransactions array of transactions, either signed or provided with a signer. | ||
* @param targetBlockNumber block to target for bundle inclusion | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and the bundle hash | ||
*/ | ||
public async sendBundle( | ||
@@ -384,5 +442,9 @@ bundledTransactions: Array<FlashbotsBundleTransaction | FlashbotsBundleRawTransaction>, | ||
public async cancelBundles(bidId: string): Promise<FlashbotsCancelBidResponse> { | ||
/** Cancel any bundles submitted with the given `replacementUuid` | ||
* @param replacementUuid specified in `sendBundle` | ||
* @returns bundle hashes of the cancelled bundles | ||
*/ | ||
public async cancelBundles(replacementUuid: string): Promise<FlashbotsCancelBidResponse> { | ||
const params = { | ||
replacementUuid: bidId | ||
replacementUuid: replacementUuid | ||
} | ||
@@ -406,2 +468,17 @@ | ||
/** | ||
* Sends a single private transaction to Flashbots. | ||
* @param transaction transaction, either signed or provided with a signer | ||
* @param opts (optional) settings | ||
* @returns callbacks for handling results, and transaction data | ||
* | ||
* @example | ||
* ```typescript | ||
* const tx: FlashbotsBundleRawTransaction = {signedTransaction: "0x02..."} | ||
* const blockNum = await provider.getBlockNumber() | ||
* // try sending for 5 blocks | ||
* const response = await fbProvider.sendPrivateTransaction(tx, {maxBlockNumber: blockNum + 5}) | ||
* const success = (await response.wait()) === FlashbotsTransactionResolution.TransactionIncluded | ||
* ``` | ||
*/ | ||
public async sendPrivateTransaction( | ||
@@ -414,2 +491,4 @@ transaction: FlashbotsBundleTransaction | FlashbotsBundleRawTransaction, | ||
): Promise<FlashbotsPrivateTransaction> { | ||
const startBlockNumberPromise = this.genericProvider.getBlockNumber() | ||
let signedTransaction: string | ||
@@ -421,3 +500,3 @@ if ('signedTransaction' in transaction) { | ||
} | ||
const startBlockNumberPromise = this.genericProvider.getBlockNumber() | ||
const params = { | ||
@@ -455,2 +534,16 @@ tx: signedTransaction, | ||
/** | ||
* Attempts to cancel a pending private transaction. | ||
* | ||
* **_Note_**: This function removes the transaction from the Flashbots | ||
* bundler, but miners may still include it if they have received it already. | ||
* @param txHash transaction hash corresponding to pending tx | ||
* @returns true if transaction was cancelled successfully | ||
* | ||
* @example | ||
* ```typescript | ||
* const pendingTxHash = (await fbProvider.sendPrivateTransaction(tx)).transaction.hash | ||
* const isTxCanceled = await fbProvider.cancelPrivateTransaction(pendingTxHash) | ||
* ``` | ||
*/ | ||
public async cancelPrivateTransaction(txHash: string): Promise<boolean | RelayResponseError> { | ||
@@ -474,2 +567,18 @@ const params = { | ||
/** | ||
* Signs a Flashbots bundle with this provider's `authSigner` key. | ||
* @param bundledTransactions | ||
* @returns signed bundle | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x02..."}, | ||
* {signedTransaction: "0x02..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const simResult = await fbProvider.simulate(signedBundle, blockNum + 1) | ||
* ``` | ||
*/ | ||
public async signBundle(bundledTransactions: Array<FlashbotsBundleTransaction | FlashbotsBundleRawTransaction>): Promise<Array<string>> { | ||
@@ -504,3 +613,9 @@ const nonces: { [address: string]: BigNumber } = {} | ||
private waitForBlock(transactionAccountNonces: Array<TransactionAccountNonce>, targetBlockNumber: number, timeout: number) { | ||
/** | ||
* Watches for a specific block to see if a bundle was included in it. | ||
* @param transactionAccountNonces bundle transactions | ||
* @param targetBlockNumber block number to check for bundle inclusion | ||
* @param timeout ms | ||
*/ | ||
private waitForBundleInclusion(transactionAccountNonces: Array<TransactionAccountNonce>, targetBlockNumber: number, timeout: number) { | ||
return new Promise<FlashbotsBundleResolution>((resolve, reject) => { | ||
@@ -571,2 +686,8 @@ let timer: NodeJS.Timer | null = null | ||
/** | ||
* Waits for a transaction to be included on-chain. | ||
* @param transactionHash | ||
* @param maxBlockNumber highest block number to check before stopping | ||
* @param timeout ms | ||
*/ | ||
private waitForTxInclusion(transactionHash: string, maxBlockNumber: number, timeout: number) { | ||
@@ -624,2 +745,6 @@ return new Promise<FlashbotsTransactionResolution>((resolve, reject) => { | ||
/** | ||
* Gets stats for provider instance's `authSigner` address. | ||
* @deprecated use {@link getUserStatsV2} instead. | ||
*/ | ||
public async getUserStats(): Promise<GetUserStatsResponse> { | ||
@@ -643,2 +768,5 @@ const blockDetails = await this.genericProvider.getBlock('latest') | ||
/** | ||
* Gets stats for provider instance's `authSigner` address. | ||
*/ | ||
public async getUserStatsV2(): Promise<GetUserStatsResponseV2> { | ||
@@ -662,2 +790,8 @@ const blockDetails = await this.genericProvider.getBlock('latest') | ||
/** | ||
* Gets information about a specific bundle. | ||
* @param bundleHash hash of bundle to investigate | ||
* @param blockNumber block in which the bundle should be included | ||
* @deprecated use {@link getBundleStatsV2} instead. | ||
*/ | ||
public async getBundleStats(bundleHash: string, blockNumber: number): Promise<GetBundleStatsResponse> { | ||
@@ -681,2 +815,7 @@ const evmBlockNumber = `0x${blockNumber.toString(16)}` | ||
/** | ||
* Gets information about a specific bundle. | ||
* @param bundleHash hash of bundle to investigate | ||
* @param blockNumber block in which the bundle should be included | ||
*/ | ||
public async getBundleStatsV2(bundleHash: string, blockNumber: number): Promise<GetBundleStatsResponseV2> { | ||
@@ -700,2 +839,20 @@ const evmBlockNumber = `0x${blockNumber.toString(16)}` | ||
/** | ||
* Simluates a bundle on a given block. | ||
* @param signedBundledTransactions signed Flashbots bundle | ||
* @param blockTag block tag to simulate against, can use "latest" | ||
* @param stateBlockTag (optional) simulated block state tag | ||
* @param blockTimestamp (optional) simulated timestamp | ||
* | ||
* @example | ||
* ```typescript | ||
* const bundle: Array<FlashbotsBundleRawTransaction> = [ | ||
* {signedTransaction: "0x1..."}, | ||
* {signedTransaction: "0x2..."}, | ||
* ] | ||
* const signedBundle = await fbProvider.signBundle(bundle) | ||
* const blockNum = await provider.getBlockNumber() | ||
* const simResult = await fbProvider.simulate(signedBundle, blockNum + 1) | ||
* ``` | ||
*/ | ||
public async simulate( | ||
@@ -705,3 +862,4 @@ signedBundledTransactions: Array<string>, | ||
stateBlockTag?: BlockTag, | ||
blockTimestamp?: number | ||
blockTimestamp?: number, | ||
coinbase?: string | ||
): Promise<SimulationResponse> { | ||
@@ -731,3 +889,4 @@ let evmBlockNumber: string | ||
stateBlockNumber: evmBlockStateNumber, | ||
timestamp: blockTimestamp | ||
timestamp: blockTimestamp, | ||
coinbase | ||
} | ||
@@ -766,7 +925,4 @@ ] | ||
(acc, transactionDetail) => { | ||
// see: https://blocks.flashbots.net/ and https://github.com/flashbots/ethers-provider-flashbots-bundle/issues/62 | ||
const gasUsed = 'gas_used' in transactionDetail ? transactionDetail.gas_used : transactionDetail.gasUsed | ||
const gasPricePaidBySearcher = BigNumber.from( | ||
'gas_price' in transactionDetail ? transactionDetail.gas_price : transactionDetail.gasPrice | ||
) | ||
const priorityFeeReceivedByMiner = gasPricePaidBySearcher.sub(baseFee) | ||
const ethSentToCoinbase = | ||
@@ -778,6 +934,13 @@ 'coinbase_transfer' in transactionDetail | ||
: BigNumber.from(0) | ||
const totalMinerReward = | ||
'total_miner_reward' in transactionDetail | ||
? BigNumber.from(transactionDetail.total_miner_reward) | ||
: 'coinbaseDiff' in transactionDetail | ||
? BigNumber.from(transactionDetail.coinbaseDiff) | ||
: BigNumber.from(0) | ||
const priorityFeeReceivedByMiner = totalMinerReward.sub(ethSentToCoinbase) | ||
return { | ||
gasUsed: acc.gasUsed + gasUsed, | ||
gasFeesPaidBySearcher: acc.gasFeesPaidBySearcher.add(gasPricePaidBySearcher.mul(gasUsed)), | ||
priorityFeesReceivedByMiner: acc.priorityFeesReceivedByMiner.add(priorityFeeReceivedByMiner.mul(gasUsed)), | ||
gasFeesPaidBySearcher: acc.gasFeesPaidBySearcher.add(baseFee.mul(gasUsed).add(priorityFeeReceivedByMiner)), | ||
priorityFeesReceivedByMiner: acc.priorityFeesReceivedByMiner.add(priorityFeeReceivedByMiner), | ||
ethSentToCoinbase: acc.ethSentToCoinbase.add(ethSentToCoinbase) | ||
@@ -809,2 +972,9 @@ } | ||
/** | ||
* Gets information about a conflicting bundle. Useful if you're competing | ||
* for well-known MEV and want to know why your bundle didn't land. | ||
* @param targetSignedBundledTransactions signed bundle | ||
* @param targetBlockNumber block in which bundle should be included | ||
* @returns conflict and gas price details | ||
*/ | ||
public async getConflictingBundle( | ||
@@ -824,2 +994,9 @@ targetSignedBundledTransactions: Array<string>, | ||
/** | ||
* Gets information about a conflicting bundle. Useful if you're competing | ||
* for well-known MEV and want to know why your bundle didn't land. | ||
* @param targetSignedBundledTransactions signed bundle | ||
* @param targetBlockNumber block in which bundle should be included | ||
* @returns conflict details | ||
*/ | ||
public async getConflictingBundleWithoutGasPricing( | ||
@@ -925,2 +1102,3 @@ targetSignedBundledTransactions: Array<string>, | ||
/** Gets information about a block from Flashbots blocks API. */ | ||
public async fetchBlocksApi(blockNumber: number): Promise<BlocksApiResponse> { | ||
@@ -927,0 +1105,0 @@ return fetchJson(`https://blocks.flashbots.net/v1/blocks?block_number=${blockNumber}`) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
274295
2630