forked from flashbots/web3-flashbots
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple.py
124 lines (104 loc) · 3.97 KB
/
simple.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""
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 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_GOERLI = True
CHAIN_ID = 5 if USE_GOERLI 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_GOERLI:
flashbot(w3, signer, "https://relay-goerli.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
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}")
send_result = w3.flashbots.send_bundle(bundle, target_block_number=block + 1)
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}")
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()