Last active
April 2, 2025 20:28
-
-
Save claytantor/0c53047054478d93ba8b339f8cfb4dde to your computer and use it in GitHub Desktop.
Simulated Builder Tx on Titan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| RPC_ENDPOINT_ETHERS = "https://mainnet.infura.io/v3/<secret>" | |
| BUILDER_RPC = "https://rpc.titanbuilder.xyz" | |
| ETH_NETWORK = "mainnet" | |
| */ | |
| const ethers = require('ethers'); | |
| const { | |
| FlashbotsBundleProvider, | |
| } = require("@flashbots/ethers-provider-bundle"); | |
| const dotenv = require("dotenv"); | |
| let TX_ACTION = process.argv.length == 4 ? process.argv[3] : 'simulate'; | |
| if (process.argv.length < 3) { | |
| console.error("Please provide the environment file path as an argument."); | |
| process.exit(1); | |
| } else { | |
| dotenv.config({ path: process.argv[2] }); | |
| } | |
| // dotenv.config({ path: './config/.env.fbbundle.mainnet' }); | |
| const { | |
| ETH_NETWORK, | |
| FROM_ADDRESS_PRIVATE_KEY, | |
| TO_ADDRESS, | |
| BUILDER_RPC, | |
| SIMULATION_RPC, | |
| RPC_ENDPOINT_ETHERS, | |
| PRIORITY_FEE_GWEI | |
| } = process.env; | |
| async function main() { | |
| console.log("ethers version:", ethers.version); | |
| console.log("ETH_NETWORK:", ETH_NETWORK); | |
| console.log("RPC_ENDPOINT_ETHERS:", RPC_ENDPOINT_ETHERS); | |
| console.log("BUILDER_RPC:", BUILDER_RPC); | |
| console.log("SIMULATION_RPC:", SIMULATION_RPC); | |
| console.log("TX_ACTION:", TX_ACTION); | |
| const provider = new ethers.JsonRpcProvider(RPC_ENDPOINT_ETHERS); | |
| const latestBlockNumber = await provider.getBlockNumber(); | |
| console.log("Latest Block number:", latestBlockNumber); | |
| console.log(`getting block ${latestBlockNumber}...`); | |
| const block = await provider.getBlock(latestBlockNumber); | |
| console.log(block); | |
| if (block.baseFeePerGas == null) { | |
| console.error('This chain is not EIP-1559 enabled') | |
| return false; | |
| } | |
| let chainId = (await provider.getNetwork()).chainId; | |
| console.log("chainId:", chainId); | |
| const wallet = new ethers.Wallet( | |
| FROM_ADDRESS_PRIVATE_KEY, | |
| provider | |
| ); | |
| // Flashbots provider requires passing in a standard provider and an auth signer | |
| const builderProvider = await FlashbotsBundleProvider.create( | |
| provider, | |
| wallet, | |
| BUILDER_RPC, | |
| ETH_NETWORK | |
| ); | |
| // Flashbots provider requires passing in a standard provider and an auth signer | |
| const simlationProvider = await FlashbotsBundleProvider.create( | |
| provider, | |
| wallet, | |
| SIMULATION_RPC, | |
| ETH_NETWORK | |
| ); | |
| const maxBaseFeeInFutureBlock = FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock(block.baseFeePerGas, 1) | |
| // convert the priority fee from gwei to wei | |
| const priorityFee = ethers.parseUnits(PRIORITY_FEE_GWEI, "gwei"); | |
| const valueToSend = ethers.parseEther("0.001"); | |
| let tx = { | |
| to: TO_ADDRESS, | |
| value: valueToSend, | |
| maxFeePerGas: priorityFee + maxBaseFeeInFutureBlock, | |
| maxPriorityFeePerGas: priorityFee, | |
| gasLimit: 21000, | |
| type: 2, | |
| chainId: chainId, | |
| }; | |
| if (TX_ACTION === "simulate") { | |
| console.log("simulating...") | |
| const targetBlock = latestBlockNumber + 1; | |
| const signedBundleSimlation = await simlationProvider.signBundle([ | |
| { | |
| signer: wallet, | |
| transaction: tx, | |
| }, | |
| ]); | |
| const simulation = await simlationProvider.simulate(signedBundleSimlation, targetBlock) | |
| console.log(simulation); | |
| if ("error" in simulation) { | |
| console.error("Simulation Error:", simulation.error); | |
| process.exitCode = 3; | |
| } else { | |
| console.log("Simulation Success:", simulation); | |
| const signedBundleBundler = await builderProvider.signBundle([ | |
| { | |
| signer: wallet, | |
| transaction: tx, | |
| }, | |
| ]); | |
| // Submitting the bundle if simulation succeeds | |
| const response = await builderProvider.sendRawBundle( | |
| signedBundleBundler, | |
| block.number + 1 | |
| ); | |
| if (response.error) { | |
| console.error("Failed to send bundle:", response.error); | |
| process.exitCode = 2; | |
| } else { | |
| console.log(JSON.stringify(response, null, 2)); | |
| } | |
| } | |
| console.log("simulation completed bundle...") | |
| } else if (TX_ACTION === "send") { | |
| console.log("sending...") | |
| const targetBlock = latestBlockNumber + 1; | |
| const signedBundle = await builderProvider.signBundle([ | |
| { | |
| signer: wallet, | |
| transaction: tx, | |
| }, | |
| ]); | |
| const txResponse = await builderProvider.sendRawBundle(signedBundle, targetBlock); | |
| console.log("txResponse:", txResponse); | |
| if ('error' in txResponse) { | |
| console.warn(`Send Error: ${txResponse.error.message}`) | |
| return false; | |
| } | |
| console.log("sent bundle successfully...") | |
| } else { | |
| console.error("Invalid action. Use 'simulate' or 'send'."); | |
| process.exit(1); | |
| } | |
| } | |
| main().catch((error) => { | |
| console.error(error); | |
| process.exitCode = 1; | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment