Skip to content

Commit

Permalink
Add new simple examples for Sepolia testnet (#89)
Browse files Browse the repository at this point in the history
* Only execute "eth_callBundle" when in mainnet as the sunset of Goerli.

* Implement new simple example based on "simple.py".

* 1. Change testnet as sepolia; 2. Provide extra builder RPCs compatible with flashbots.

* Update simple_new.py example for multi-network support

- Add support for Sepolia, Holesky, and Mainnet networks
- Introduce NETWORK_CONFIG for network-specific settings
- Update provider and relay URL handling
- Improve comments and documentation
- Enhance error handling and logging

* Replace simple.py with simple_new.py

* Update .gitignore

* Resolve black formatter  issue

---------

Co-authored-by: George Zhang <[email protected]>
  • Loading branch information
jasonthewhale and odysseus0 authored Jul 22, 2024
1 parent c3a1216 commit c00711a
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 28 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ __pycache__
build/
dist/
flashbots.egg-info/
.venv/
.mise.toml
85 changes: 66 additions & 19 deletions examples/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,57 @@
Minimal viable example of flashbots usage with dynamic fee transactions.
Sends a bundle of two transactions which transfer some ETH into a random account.
"eth_sendBundle" is a generic method that can be used to send a bundle to any relay.
For instance, you can use the following relay URLs:
titan: 'https://rpc.titanbuilder.xyz/'
beaver: 'https://rpc.beaverbuild.org/'
builder69: 'https://builder0x69.io/'
rsync: 'https://rsync-builder.xyz/'
flashbots: 'https://relay.flashbots.net'
You can simply replace the URL in the flashbot method to use a different relay like:
flashbot(w3, signer, YOUR_CHOSEN_RELAY_URL)
Environment Variables:
- ETH_SENDER_KEY: Private key of account which will send the ETH.
- ETH_SIGNER_KEY: Private key of account which will sign the bundle.
- This account is only used for reputation on flashbots and should be empty.
- PROVIDER_URL: HTTP JSON-RPC Ethereum provider URL.
- PROVIDER_URL: (Optional) HTTP JSON-RPC Ethereum provider URL. If not set, Flashbots Protect RPC will be used.
"""

import os
import secrets
from uuid import uuid4

from eth_account.account import Account
from eth_account.signers.local import LocalAccount
from flashbots import flashbot
from web3 import Web3, HTTPProvider
from web3 import HTTPProvider, Web3
from web3.exceptions import TransactionNotFound
from web3.types import TxParams

# change this to `False` if you want to use mainnet
USE_GOERLI = True
CHAIN_ID = 5 if USE_GOERLI else 1
from flashbots import flashbot

# Define the network to use
NETWORK = "holesky" # Options: "sepolia", "holesky", "mainnet"

# Define chain IDs and Flashbots Protect RPC URLs
NETWORK_CONFIG = {
"sepolia": {
"chain_id": 11155111,
"provider_url": "https://rpc-sepolia.flashbots.net",
"relay_url": "https://relay-sepolia.flashbots.net",
},
"holesky": {
"chain_id": 17000,
"provider_url": "https://rpc-holesky.flashbots.net",
"relay_url": "https://relay-holesky.flashbots.net",
},
"mainnet": {
"chain_id": 1,
"provider_url": "https://rpc.flashbots.net",
"relay_url": None, # Mainnet uses default Flashbots relay
},
}


def env(key: str) -> str:
Expand All @@ -42,9 +73,20 @@ def main() -> None:
# NOTE: this account should not store funds
signer: LocalAccount = Account.from_key(env("ETH_SIGNER_KEY"))

w3 = Web3(HTTPProvider(env("PROVIDER_URL")))
if USE_GOERLI:
flashbot(w3, signer, "https://relay-goerli.flashbots.net")
# Use user-provided RPC URL if available, otherwise use Flashbots Protect RPC
user_provider_url = env("PROVIDER_URL")
if user_provider_url:
provider_url = user_provider_url
print(f"Using user-provided RPC: {provider_url}")
else:
provider_url = NETWORK_CONFIG[NETWORK]["provider_url"]
print(f"Using Flashbots Protect RPC: {provider_url}")

w3 = Web3(HTTPProvider(provider_url))

relay_url = NETWORK_CONFIG[NETWORK]["relay_url"]
if relay_url:
flashbot(w3, signer, relay_url)
else:
flashbot(w3, signer)

Expand All @@ -69,7 +111,7 @@ def main() -> None:
"maxFeePerGas": Web3.toWei(200, "gwei"),
"maxPriorityFeePerGas": Web3.toWei(50, "gwei"),
"nonce": nonce,
"chainId": CHAIN_ID,
"chainId": NETWORK_CONFIG[NETWORK]["chain_id"],
"type": 2,
}
tx1_signed = sender.sign_transaction(tx1)
Expand All @@ -81,7 +123,7 @@ def main() -> None:
"maxFeePerGas": Web3.toWei(200, "gwei"),
"maxPriorityFeePerGas": Web3.toWei(50, "gwei"),
"nonce": nonce + 1,
"chainId": CHAIN_ID,
"chainId": NETWORK_CONFIG[NETWORK]["chain_id"],
"type": 2,
}

Expand All @@ -93,14 +135,19 @@ def main() -> None:
# keep trying to send bundle until it gets mined
while True:
block = w3.eth.block_number
print(f"Simulating on block {block}")
# simulate bundle on current block
try:
w3.flashbots.simulate(bundle, block)
print("Simulation successful.")
except Exception as e:
print("Simulation error", e)
return

# Simulation is only supported on mainnet
if NETWORK == "mainnet":
print(f"Simulating on block {block}")
# Simulate bundle on current block.
# If your RPC provider is not fast enough, you may get "block extrapolation negative"
# error message triggered by "extrapolate_timestamp" function in "flashbots.py".
try:
w3.flashbots.simulate(bundle, block)
print("Simulation successful.")
except Exception as e:
print("Simulation error", e)
return

# send bundle targeting next block
print(f"Sending bundle targeting block {block+1}")
Expand Down
21 changes: 12 additions & 9 deletions flashbots/flashbots.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ def __init__(self, w3: Web3, signed_tx: HexBytes, max_block_number: int):
def wait(self) -> bool:
"""Waits up to max block number, returns `True` if/when tx has been mined.
If tx has not been mined by the time the current block > max_block_number, returns `False`."""
If tx has not been mined by the time the current block > max_block_number, returns `False`.
"""
while True:
try:
self.w3.eth.get_transaction(self.tx["hash"])
Expand Down Expand Up @@ -220,12 +221,12 @@ def send_raw_bundle_munger(
"blockNumber": hex(target_block_number),
"minTimestamp": opts["minTimestamp"] if "minTimestamp" in opts else 0,
"maxTimestamp": opts["maxTimestamp"] if "maxTimestamp" in opts else 0,
"revertingTxHashes": opts["revertingTxHashes"]
if "revertingTxHashes" in opts
else [],
"replacementUuid": opts["replacementUuid"]
if "replacementUuid" in opts
else None,
"revertingTxHashes": (
opts["revertingTxHashes"] if "revertingTxHashes" in opts else []
),
"replacementUuid": (
opts["replacementUuid"] if "replacementUuid" in opts else None
),
}
]

Expand Down Expand Up @@ -400,7 +401,8 @@ def send_private_transaction_munger(
) -> Any:
"""Sends a single transaction to Flashbots.
If `max_block_number` is set, Flashbots will try to submit the transaction in every block <= that block (max 25 blocks from present)."""
If `max_block_number` is set, Flashbots will try to submit the transaction in every block <= that block (max 25 blocks from present).
"""
signed_transaction: str
if "signed_transaction" in transaction:
signed_transaction = transaction["signed_transaction"]
Expand Down Expand Up @@ -438,7 +440,8 @@ def cancel_private_transaction_munger(
) -> bool:
"""Stops a private transaction from being sent to miners by Flashbots.
Note: if a transaction has already been received by a miner, it may still be mined. This simply stops further submissions."""
Note: if a transaction has already been received by a miner, it may still be mined. This simply stops further submissions.
"""
params = {
"txHash": tx_hash,
}
Expand Down

0 comments on commit c00711a

Please sign in to comment.