@flashbots/ethers-provider-bundle
Advanced tools
Comparing version 0.0.1 to 0.0.2
{ | ||
"name": "@flashbots/ethers-provider-bundle", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "build/index.js", |
ethers-provider-flashbots-bundle | ||
================================ | ||
Contains the `FlashbotsBundleProvider` ethers.js provider to provide high-level access to eth_sendBundle rpc | ||
Contains the `FlashbotsBundleProvider` ethers.js provider to provide high-level access to eth_sendBundle rpc endpoint. | ||
Flashbots-enabled relays and miners will expose a single jsonrpc endpoint: `eth_sendBundle`. Since this is both a brand new endpoint and the jsonrpc offered by these services will NOT service other rpc requests (like `getTransactionCount`), you will need to combine this rpc endpoint with another full-featured endpoint that supports nonce-calculation, estimation, and transaction status. | ||
Flashbots-enabled relays and miners will expose a single jsonrpc endpoint: `eth_sendBundle`. Since this is a brand-new, non-standard endpoint, ethers.js and other libraries do not natively support these requests (like `getTransactionCount`). In order to interact with `eth_sendBundle`, you will also need access to another full-featured endpoint for nonce-calculation, gas estimation, and transaction status. | ||
This library is not a fully functional ethers.js implementation, just a simple provider class, designed to interact with your existing ethers.js v5 module. | ||
You can pass in a generic ethers.js provider to the flashbots provider in the constructor: | ||
@@ -13,5 +15,7 @@ | ||
const provider = new providers.JsonRpcProvider({url: ETHEREUM_URL}, NETWORK_INFO) | ||
// Standard json rpc provider directly from ethers.js | ||
const provider = new providers.JsonRpcProvider({url: ETHEREUM_RPC_URL}, NETWORK_INFO) | ||
const flashbotsProvider = new FlashbotsBundleProvider(provider, {url: FLASHBOTS_RELAY_URL}, NETWORK_INFO) | ||
// flashbots provider requires passing in a standard provider | ||
const flashbotsProvider = new FlashbotsBundleProvider(provider, flashbotsApiKey, flashbotsSecret) | ||
``` | ||
@@ -26,15 +30,39 @@ | ||
Example | ||
------- | ||
``` | ||
// Using the map below ships two different bundles, targeting the next two blocks | ||
const blockNumber = await provider.getBlockNumber() | ||
const minTimestamp = (await provider.getBlock(blockNumber)).timestamp | ||
const maxTimestamp = minTimestamp + 120 | ||
const bundlePromises = _.map([blockNumber + 1, blockNumber + 2], targetBlockNumber => | ||
this.flashbotsProvider.sendBundle( | ||
[ | ||
{ | ||
signedTransaction: SIGNED_ORACLE_UPDATE_FROM_PENDING_POOL // serialized signed transaction hex | ||
}, | ||
{ | ||
signer: wallet, // ethers signer | ||
transaction: transaction // ethers populated transaction object | ||
} | ||
], | ||
targetBlockNumber, // block number at which this bundle is valid | ||
{ | ||
minTimestamp, // optional minimum timestamp at which this bundle is valid (inclusive) | ||
maxTimestamp // optional maximum timestamp at which this bundle is valid (inclusive) | ||
} | ||
) | ||
) | ||
``` | ||
bundledTransactions | ||
------------------- | ||
A flashbots bundle consists of one or more transactions in strict order to be relayed to the miner directly. While the miner requires signed transaction, of course, `sendBundle()` can receive a mix of pre-signed transaction and `Wallet` + `TransactionRequest` | ||
A Flashbots bundle consists of one or more transactions in strict order to be relayed to the miner directly. While the miner requires signed transaction, `sendBundle()` can receive a mix of pre-signed transaction and `TransactionRequest` + `Signer` (wallet) objects | ||
... | ||
These bundles can pay the miner either via gas fees _OR_ via `block.coinbase.transfer(minerReward)` | ||
targetBlockNumber | ||
------------------- | ||
The only block number for which the bundle is to be considered valid. If you would like more than one block to be targetted, submit multiple rpc calls targeting each block. | ||
The only block number for which the bundle is to be considered valid. If you would like more than one block to be targeted, submit multiple rpc calls targeting each specific block. This value should be higher than the value of getBlockNumber(). Submitting a bundle with a target block number of the current block, or earlier, is a no-op. | ||
... | ||
FlashbotsTransactionResponse | ||
@@ -44,2 +72,9 @@ ---------------------------- | ||
... | ||
* receipts() - Returns promise of an array of transaction receipts corresponding to the transaction hashes that were relayed as part of the bundle. Will not wait for block to be mined; could return incomplete information | ||
* wait() - Returns a promise which will wait for target block number to be reched _OR_ one of the transactions to become invalid due to nonce-issues (including, but not limited to, one of the transactions from you bundle being included too early). Returns the wait resolution as a status enum | ||
* simulate() - Returns a promise of the transaction simulation, once the proper block height has been reached. Use this function to troubleshoot failing bundles and verify miner profitability | ||
How to run demo.ts | ||
------------------- | ||
Included is a simple demo of how to construct the FlashbotsProvider with api key authentication and submit a [non-functional] bundle. This will not yield any mev, but could serve as a sample initialization to help integrate into your own functional searcher. |
import { providers, Wallet } from "ethers" | ||
import { ConnectionInfo } from "ethers/lib/utils" | ||
import { FlashbotsBundleProvider } from "./index"; | ||
import { DEFAULT_FLASHBOTS_RELAY, FlashbotsBundleProvider } from "./index"; | ||
const ETHEREUM_URL = "http://127.0.0.1:8545" | ||
const FLASHBOTS_RELAY_URL = "http://127.0.0.1:8545" // TODO: default relay | ||
const ETHEREUM_RPC_URL = process.env.ETHEREUM_RPC_URL || "http://127.0.0.1:8545" | ||
const FLASHBOTS_KEY_ID = process.env.FLASHBOTS_KEY_ID || ''; | ||
const FLASHBOTS_SECRET = process.env.FLASHBOTS_SECRET || ''; | ||
const connection: ConnectionInfo = {url: ETHEREUM_URL} | ||
const connection: ConnectionInfo = {url: ETHEREUM_RPC_URL} | ||
const NETWORK_INFO = {chainId: 1, ensAddress: '', name: 'mainnet'} | ||
const provider = new providers.JsonRpcProvider(connection, NETWORK_INFO) | ||
const flashbotsConnection: ConnectionInfo = {url: FLASHBOTS_RELAY_URL} | ||
const flashbotsProvider = new FlashbotsBundleProvider(provider, flashbotsConnection, NETWORK_INFO) | ||
provider.getBlockNumber().then(async (blockNumber) => { | ||
const flashbotsProvider = await FlashbotsBundleProvider.create(provider, FLASHBOTS_KEY_ID, FLASHBOTS_SECRET) | ||
const wallet = Wallet.createRandom().connect(provider) | ||
const wallet = Wallet.createRandom().connect(provider) | ||
provider.getBlockNumber().then(async (blockNumber) => { | ||
const minTimestamp = (await provider.getBlock(blockNumber)).timestamp | ||
const maxTimestamp = minTimestamp + 120 | ||
const f = await flashbotsProvider.sendBundle([ | ||
@@ -37,3 +39,7 @@ { | ||
], | ||
blockNumber + 3 | ||
blockNumber + 3, | ||
{ | ||
minTimestamp, | ||
maxTimestamp | ||
} | ||
) | ||
@@ -43,3 +49,2 @@ | ||
console.log(await f.receipts()) | ||
}) |
@@ -7,2 +7,4 @@ import { BigNumber, ethers, providers, Signer } from "ethers"; | ||
export const DEFAULT_FLASHBOTS_RELAY = 'https://relay.flashbots.net' | ||
export enum FlashbotsBundleResolution { | ||
@@ -23,2 +25,7 @@ BundleIncluded, | ||
export interface FlashbotsOptions { | ||
minTimestamp?: number, | ||
maxTimestamp?: number | ||
} | ||
interface TransactionAccountNonce { | ||
@@ -38,3 +45,2 @@ hash: string; | ||
interface SimulationResponse { // eslint-disable-line @typescript-eslint/no-empty-interface | ||
@@ -49,9 +55,37 @@ // TODO | ||
constructor(genericProvider: BaseProvider, url?: ConnectionInfo | string, network?: Networkish) { | ||
super(url, network); | ||
constructor(genericProvider: BaseProvider, connectionInfoOrUrl: ConnectionInfo, network: Networkish) { | ||
super(connectionInfoOrUrl, network); | ||
this.genericProvider = genericProvider; | ||
} | ||
async sendRawBundle(signedBundledTransactions: Array<string>, targetBlockNumber: number): Promise<FlashbotsTransactionResponse> { | ||
await this.send("eth_sendBundle", [signedBundledTransactions, `0x${targetBlockNumber.toString(16)}`]); | ||
static async create(genericProvider: BaseProvider, flashbotsKeyId: string, flashbotsSecret: string, connectionInfoOrUrl?: ConnectionInfo | string, network?: Networkish): Promise<FlashbotsBundleProvider> { | ||
const connectionInfo: ConnectionInfo = typeof connectionInfoOrUrl === 'string' || typeof connectionInfoOrUrl === 'undefined' ? { | ||
url: connectionInfoOrUrl || DEFAULT_FLASHBOTS_RELAY | ||
} : { | ||
...connectionInfoOrUrl | ||
} | ||
if (connectionInfo.headers === undefined) connectionInfo.headers = {} | ||
connectionInfo.headers.Authorization = `${flashbotsKeyId}:${flashbotsSecret}` | ||
const networkish: 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, connectionInfo, networkish) | ||
} | ||
async sendRawBundle(signedBundledTransactions: Array<string>, targetBlockNumber: number, opts?: FlashbotsOptions): Promise<FlashbotsTransactionResponse> { | ||
await this.send("eth_sendBundle", [signedBundledTransactions, [`0x${targetBlockNumber.toString(16)}`, opts?.minTimestamp || 0, opts?.maxTimestamp || 0]]); | ||
const bundleTransactions = signedBundledTransactions.map(signedTransaction => { | ||
@@ -75,3 +109,3 @@ const transactionDetails = ethers.utils.parseTransaction(signedTransaction) | ||
async sendBundle(bundledTransactions: Array<FlashbotsBundleTransaction | FlashbotsBundleRawTransaction>, targetBlockNumber: number): Promise<FlashbotsTransactionResponse> { | ||
async sendBundle(bundledTransactions: Array<FlashbotsBundleTransaction | FlashbotsBundleRawTransaction>, targetBlockNumber: number, opts?: FlashbotsOptions): Promise<FlashbotsTransactionResponse> { | ||
const nonces: { [address: string]: BigNumber } = {} | ||
@@ -98,3 +132,3 @@ const signedTransactions = new Array<string>() | ||
} | ||
return this.sendRawBundle(signedTransactions, targetBlockNumber) | ||
return this.sendRawBundle(signedTransactions, targetBlockNumber, opts) | ||
} | ||
@@ -101,0 +135,0 @@ |
Sorry, the diff of this file is not supported yet
78
123635
7
295