From 687b49da6a99ad589d900ca71901adb5bc69c3ac Mon Sep 17 00:00:00 2001 From: Junchen You Date: Sun, 25 Feb 2024 00:37:20 +1000 Subject: [PATCH 1/7] Only execute "eth_callBundle" when in mainnet as the sunset of Goerli. --- examples/simple.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/simple.py b/examples/simple.py index f1b1de1..1b49558 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -93,14 +93,17 @@ 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 not supported on Goerli now + if not USE_GOERLI: + 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 # send bundle targeting next block print(f"Sending bundle targeting block {block+1}") From 8ab469d20adf00aa34861026ea23959ebafc3c92 Mon Sep 17 00:00:00 2001 From: Junchen You Date: Sun, 25 Feb 2024 00:43:00 +1000 Subject: [PATCH 2/7] Implement new simple example based on "simple.py". --- examples/simple_new.py | 149 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 examples/simple_new.py diff --git a/examples/simple_new.py b/examples/simple_new.py new file mode 100644 index 0000000..9c4e091 --- /dev/null +++ b/examples/simple_new.py @@ -0,0 +1,149 @@ +""" +Minimal viable example of flashbots usage with dynamic fee transactions. +Sends a bundle of two transactions which transfer some ETH into a random account. + +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. +""" + +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.exceptions import TransactionNotFound +from web3.types import TxParams + +# change this to `False` if you want to use mainnet +USE_SEPOLIA = True +CHAIN_ID = 11155111 if USE_SEPOLIA else 1 + + +def env(key: str) -> str: + return os.environ.get(key) + + +def random_account() -> LocalAccount: + key = "0x" + secrets.token_hex(32) + return Account.from_key(key) + + +def main() -> None: + # account to send the transfer and sign transactions + sender: LocalAccount = Account.from_key(env("ETH_SENDER_KEY")) + # account to receive the transfer + receiverAddress: str = random_account().address + # account to sign bundles & establish flashbots reputation + # NOTE: this account should not store funds + signer: LocalAccount = Account.from_key(env("ETH_SIGNER_KEY")) + + w3 = Web3(HTTPProvider(env("PROVIDER_URL"))) + if USE_SEPOLIA: + flashbot(w3, signer, "https://relay-sepolia.flashbots.net") + else: + flashbot(w3, signer) + + print(f"Sender address: {sender.address}") + print(f"Receiver address: {receiverAddress}") + print( + f"Sender account balance: {Web3.fromWei(w3.eth.get_balance(sender.address), 'ether')} ETH" + ) + print( + f"Receiver account balance: {Web3.fromWei(w3.eth.get_balance(receiverAddress), 'ether')} ETH" + ) + + # bundle two EIP-1559 (type 2) transactions, pre-sign one of them + # NOTE: chainId is necessary for all EIP-1559 txns + # NOTE: nonce is required for signed txns + + nonce = w3.eth.get_transaction_count(sender.address) + tx1: TxParams = { + "to": receiverAddress, + "value": Web3.toWei(0.001, "ether"), + "gas": 21000, + "maxFeePerGas": Web3.toWei(200, "gwei"), + "maxPriorityFeePerGas": Web3.toWei(50, "gwei"), + "nonce": nonce, + "chainId": CHAIN_ID, + "type": 2, + } + tx1_signed = sender.sign_transaction(tx1) + + tx2: TxParams = { + "to": receiverAddress, + "value": Web3.toWei(0.001, "ether"), + "gas": 21000, + "maxFeePerGas": Web3.toWei(200, "gwei"), + "maxPriorityFeePerGas": Web3.toWei(50, "gwei"), + "nonce": nonce + 1, + "chainId": CHAIN_ID, + "type": 2, + } + + bundle = [ + {"signed_transaction": tx1_signed.rawTransaction}, + {"signer": sender, "transaction": tx2}, + ] + + # keep trying to send bundle until it gets mined + while True: + block = w3.eth.block_number + + # Simulation is only supported on mainnet + if not USE_SEPOLIA: + 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 + + # send bundle targeting next block + print(f"Sending bundle targeting block {block+1}") + replacement_uuid = str(uuid4()) + print(f"replacementUuid {replacement_uuid}") + send_result = w3.flashbots.send_bundle( + bundle, + target_block_number=block + 1, + opts={"replacementUuid": replacement_uuid}, + ) + print("bundleHash", w3.toHex(send_result.bundle_hash())) + + stats_v1 = w3.flashbots.get_bundle_stats( + w3.toHex(send_result.bundle_hash()), block + ) + print("bundleStats v1", stats_v1) + + stats_v2 = w3.flashbots.get_bundle_stats_v2( + w3.toHex(send_result.bundle_hash()), block + ) + print("bundleStats v2", stats_v2) + + send_result.wait() + try: + receipts = send_result.receipts() + print(f"\nBundle was mined in block {receipts[0].blockNumber}\a") + break + except TransactionNotFound: + print(f"Bundle not found in block {block+1}") + # essentially a no-op but it shows that the function works + cancel_res = w3.flashbots.cancel_bundles(replacement_uuid) + print(f"canceled {cancel_res}") + + print( + f"Sender account balance: {Web3.fromWei(w3.eth.get_balance(sender.address), 'ether')} ETH" + ) + print( + f"Receiver account balance: {Web3.fromWei(w3.eth.get_balance(receiverAddress), 'ether')} ETH" + ) + + +if __name__ == "__main__": + main() From 229bc85786b5cdc279676556dbb9600ca87f0549 Mon Sep 17 00:00:00 2001 From: Junchen You Date: Sun, 25 Feb 2024 00:57:06 +1000 Subject: [PATCH 3/7] 1. Change testnet as sepolia; 2. Provide extra builder RPCs compatible with flashbots. --- examples/simple_new.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/simple_new.py b/examples/simple_new.py index 9c4e091..7beb020 100644 --- a/examples/simple_new.py +++ b/examples/simple_new.py @@ -43,6 +43,19 @@ def main() -> None: signer: LocalAccount = Account.from_key(env("ETH_SIGNER_KEY")) w3 = Web3(HTTPProvider(env("PROVIDER_URL"))) + + """ + "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, "https://rpc.titanbuilder.xyz/") + """ if USE_SEPOLIA: flashbot(w3, signer, "https://relay-sepolia.flashbots.net") else: @@ -97,7 +110,9 @@ def main() -> None: # Simulation is only supported on mainnet if not USE_SEPOLIA: print(f"Simulating on block {block}") - # simulate bundle on current 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.") From 00c1e6be8f4693e0cc09287adc313e69fd08d4e1 Mon Sep 17 00:00:00 2001 From: George Zhang Date: Sun, 21 Jul 2024 02:11:17 +0800 Subject: [PATCH 4/7] 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 --- examples/simple_new.py | 79 +++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/examples/simple_new.py b/examples/simple_new.py index 7beb020..ed11117 100644 --- a/examples/simple_new.py +++ b/examples/simple_new.py @@ -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_SEPOLIA = True -CHAIN_ID = 11155111 if USE_SEPOLIA 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: @@ -42,22 +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"))) - - """ - "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, "https://rpc.titanbuilder.xyz/") - """ - if USE_SEPOLIA: - flashbot(w3, signer, "https://relay-sepolia.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) @@ -82,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) @@ -94,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, } @@ -108,7 +137,7 @@ def main() -> None: block = w3.eth.block_number # Simulation is only supported on mainnet - if not USE_SEPOLIA: + 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" From eb861ab7c881e7a7bffa70909558151985ebd0e5 Mon Sep 17 00:00:00 2001 From: George Zhang Date: Sun, 21 Jul 2024 02:13:33 +0800 Subject: [PATCH 5/7] Replace simple.py with simple_new.py --- examples/simple.py | 72 ++++++++++++--- examples/simple_new.py | 193 ----------------------------------------- 2 files changed, 58 insertions(+), 207 deletions(-) delete mode 100644 examples/simple_new.py diff --git a/examples/simple.py b/examples/simple.py index 1b49558..ed11117 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -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: @@ -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) @@ -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) @@ -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, } @@ -94,10 +136,12 @@ def main() -> None: while True: block = w3.eth.block_number - # Simulation is not supported on Goerli now - if not USE_GOERLI: + # Simulation is only supported on mainnet + if NETWORK == "mainnet": print(f"Simulating on block {block}") - # simulate bundle on current 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.") diff --git a/examples/simple_new.py b/examples/simple_new.py deleted file mode 100644 index ed11117..0000000 --- a/examples/simple_new.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -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: (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 web3 import HTTPProvider, Web3 -from web3.exceptions import TransactionNotFound -from web3.types import TxParams - -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: - return os.environ.get(key) - - -def random_account() -> LocalAccount: - key = "0x" + secrets.token_hex(32) - return Account.from_key(key) - - -def main() -> None: - # account to send the transfer and sign transactions - sender: LocalAccount = Account.from_key(env("ETH_SENDER_KEY")) - # account to receive the transfer - receiverAddress: str = random_account().address - # account to sign bundles & establish flashbots reputation - # NOTE: this account should not store funds - signer: LocalAccount = Account.from_key(env("ETH_SIGNER_KEY")) - - # 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) - - print(f"Sender address: {sender.address}") - print(f"Receiver address: {receiverAddress}") - print( - f"Sender account balance: {Web3.fromWei(w3.eth.get_balance(sender.address), 'ether')} ETH" - ) - print( - f"Receiver account balance: {Web3.fromWei(w3.eth.get_balance(receiverAddress), 'ether')} ETH" - ) - - # bundle two EIP-1559 (type 2) transactions, pre-sign one of them - # NOTE: chainId is necessary for all EIP-1559 txns - # NOTE: nonce is required for signed txns - - nonce = w3.eth.get_transaction_count(sender.address) - tx1: TxParams = { - "to": receiverAddress, - "value": Web3.toWei(0.001, "ether"), - "gas": 21000, - "maxFeePerGas": Web3.toWei(200, "gwei"), - "maxPriorityFeePerGas": Web3.toWei(50, "gwei"), - "nonce": nonce, - "chainId": NETWORK_CONFIG[NETWORK]["chain_id"], - "type": 2, - } - tx1_signed = sender.sign_transaction(tx1) - - tx2: TxParams = { - "to": receiverAddress, - "value": Web3.toWei(0.001, "ether"), - "gas": 21000, - "maxFeePerGas": Web3.toWei(200, "gwei"), - "maxPriorityFeePerGas": Web3.toWei(50, "gwei"), - "nonce": nonce + 1, - "chainId": NETWORK_CONFIG[NETWORK]["chain_id"], - "type": 2, - } - - bundle = [ - {"signed_transaction": tx1_signed.rawTransaction}, - {"signer": sender, "transaction": tx2}, - ] - - # keep trying to send bundle until it gets mined - while True: - block = w3.eth.block_number - - # 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}") - replacement_uuid = str(uuid4()) - print(f"replacementUuid {replacement_uuid}") - send_result = w3.flashbots.send_bundle( - bundle, - target_block_number=block + 1, - opts={"replacementUuid": replacement_uuid}, - ) - print("bundleHash", w3.toHex(send_result.bundle_hash())) - - stats_v1 = w3.flashbots.get_bundle_stats( - w3.toHex(send_result.bundle_hash()), block - ) - print("bundleStats v1", stats_v1) - - stats_v2 = w3.flashbots.get_bundle_stats_v2( - w3.toHex(send_result.bundle_hash()), block - ) - print("bundleStats v2", stats_v2) - - send_result.wait() - try: - receipts = send_result.receipts() - print(f"\nBundle was mined in block {receipts[0].blockNumber}\a") - break - except TransactionNotFound: - print(f"Bundle not found in block {block+1}") - # essentially a no-op but it shows that the function works - cancel_res = w3.flashbots.cancel_bundles(replacement_uuid) - print(f"canceled {cancel_res}") - - print( - f"Sender account balance: {Web3.fromWei(w3.eth.get_balance(sender.address), 'ether')} ETH" - ) - print( - f"Receiver account balance: {Web3.fromWei(w3.eth.get_balance(receiverAddress), 'ether')} ETH" - ) - - -if __name__ == "__main__": - main() From 804360b320c0872148c59a5419731ad593d9bc5b Mon Sep 17 00:00:00 2001 From: George Zhang Date: Sun, 21 Jul 2024 02:28:49 +0800 Subject: [PATCH 6/7] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index efc9c0f..09b89ab 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ __pycache__ build/ dist/ flashbots.egg-info/ +.venv/ +.mise.toml \ No newline at end of file From eb517e0012fc6352cbcd213815ade6aa0cf72bbf Mon Sep 17 00:00:00 2001 From: George Zhang Date: Sun, 21 Jul 2024 02:43:08 +0800 Subject: [PATCH 7/7] Resolve black formatter issue --- flashbots/flashbots.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/flashbots/flashbots.py b/flashbots/flashbots.py index 4869b2b..76ceb91 100644 --- a/flashbots/flashbots.py +++ b/flashbots/flashbots.py @@ -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"]) @@ -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 + ), } ] @@ -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"] @@ -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, }