UNPKG

@flashbots-sdk/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-sdk/mev-relay-

119 lines 5.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ethers_1 = require("ethers"); const index_1 = require("./index"); const uuid_1 = require("uuid"); const FLASHBOTS_AUTH_KEY = process.env.FLASHBOTS_AUTH_KEY; const GWEI = ethers_1.BigNumber.from(10).pow(9); const PRIORITY_FEE = GWEI.mul(3); const LEGACY_GAS_PRICE = GWEI.mul(12); const BLOCKS_IN_THE_FUTURE = 2; // ===== Uncomment this for mainnet ======= // const CHAIN_ID = 1 // const provider = new providers.JsonRpcProvider( // { url: process.env.ETHEREUM_RPC_URL || 'http://127.0.0.1:8545' }, // { chainId: CHAIN_ID, ensAddress: '', name: 'mainnet' } // ) // const FLASHBOTS_EP = 'https://relay.flashbots.net/' // ===== Uncomment this for mainnet ======= // ===== Uncomment this for Goerli ======= const CHAIN_ID = 5; const provider = new ethers_1.providers.InfuraProvider(CHAIN_ID, process.env.INFURA_API_KEY); const FLASHBOTS_EP = 'https://relay-goerli.flashbots.net/'; // ===== Uncomment this for Goerli ======= for (const e of ['FLASHBOTS_AUTH_KEY', 'INFURA_API_KEY', 'ETHEREUM_RPC_URL', 'PRIVATE_KEY']) { if (!process.env[e]) { // don't warn for skipping ETHEREUM_RPC_URL if using goerli if (FLASHBOTS_EP.includes('goerli') && e === 'ETHEREUM_RPC_URL') { continue; } console.warn(`${e} should be defined as an environment variable`); } } async function main() { const authSigner = FLASHBOTS_AUTH_KEY ? new ethers_1.Wallet(FLASHBOTS_AUTH_KEY) : ethers_1.Wallet.createRandom(); const wallet = new ethers_1.Wallet(process.env.PRIVATE_KEY || '', provider); const flashbotsProvider = await index_1.FlashbotsBundleProvider.create(provider, authSigner, FLASHBOTS_EP); const userStats = flashbotsProvider.getUserStats(); if (process.env.TEST_V2) { try { const userStats2 = await flashbotsProvider.getUserStatsV2(); console.log('userStatsV2', userStats2); } catch (e) { console.error('[v2 error]', e); } } const legacyTransaction = { to: wallet.address, gasPrice: LEGACY_GAS_PRICE, gasLimit: 21000, data: '0x', nonce: await provider.getTransactionCount(wallet.address), chainId: CHAIN_ID }; provider.on('block', async (blockNumber) => { const block = await provider.getBlock(blockNumber); const replacementUuid = (0, uuid_1.v4)(); let eip1559Transaction; if (block.baseFeePerGas == null) { console.warn('This chain is not EIP-1559 enabled, defaulting to two legacy transactions for demo'); eip1559Transaction = { ...legacyTransaction }; // We set a nonce in legacyTransaction above to limit validity to a single landed bundle. Delete that nonce for tx#2, and allow bundle provider to calculate it delete eip1559Transaction.nonce; } else { const maxBaseFeeInFutureBlock = index_1.FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock(block.baseFeePerGas, BLOCKS_IN_THE_FUTURE); eip1559Transaction = { to: wallet.address, type: 2, maxFeePerGas: PRIORITY_FEE.add(maxBaseFeeInFutureBlock), maxPriorityFeePerGas: PRIORITY_FEE, gasLimit: 21000, data: '0x', chainId: CHAIN_ID }; } const signedTransactions = await flashbotsProvider.signBundle([ { signer: wallet, transaction: legacyTransaction }, { signer: wallet, transaction: eip1559Transaction } ]); const targetBlock = blockNumber + BLOCKS_IN_THE_FUTURE; const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlock); // Using TypeScript discrimination if ('error' in simulation) { console.warn(`Simulation Error: ${simulation.error.message}`); process.exit(1); } else { console.log(`Simulation Success: ${JSON.stringify(simulation, null, 2)}`); } const bundleSubmission = await flashbotsProvider.sendRawBundle(signedTransactions, targetBlock, { replacementUuid }); console.log('bundle submitted, waiting'); if ('error' in bundleSubmission) { throw new Error(bundleSubmission.error.message); } const cancelResult = await flashbotsProvider.cancelBundles(replacementUuid); console.log('cancel response', cancelResult); const waitResponse = await bundleSubmission.wait(); console.log(`Wait Response: ${index_1.FlashbotsBundleResolution[waitResponse]}`); if (waitResponse === index_1.FlashbotsBundleResolution.BundleIncluded || waitResponse === index_1.FlashbotsBundleResolution.AccountNonceTooHigh) { process.exit(0); } else { console.log({ bundleStats: await flashbotsProvider.getBundleStats(simulation.bundleHash, targetBlock), bundleStatsV2: process.env.TEST_V2 && (await flashbotsProvider.getBundleStatsV2(simulation.bundleHash, targetBlock)), userStats: await userStats }); } }); } main(); //# sourceMappingURL=demo.js.map