Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new simple examples for Sepolia testnet #89

Merged
merged 7 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading