-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple.py
145 lines (121 loc) · 4.52 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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""
Minimal viable example of flashbots usage with dynamic fee transactions.
Sends a bundle of two transactions which transfer some ETH into a specified account.
Environment Variables:
- ETH_SENDER_KEY: Private key of account which will send the ETH.
- ETH_RECEIVER_KEY: Private key of account which will receive 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
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 web3morebundlers import bundler
# change this to `False` if you want to use mainnet
USE_GOERLI = False
CHAIN_ID = 5 if USE_GOERLI else 1
BUILDER_ENPOINTS = [
"https://relay.flashbots.net",
"https://rpc.titanbuilder.xyz",
"https://builder0x69.io",
"https://rpc.beaverbuild.org",
"https://rsync-builder.xyz",
"https://eth-builder.com",
"https://builder.gmbit.co/rpc",
"https://buildai.net",
"https://rpc.payload.de",
"https://rpc.nfactorial.xyz",
]
def env(key: str) -> str:
return os.environ.get(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 = Account.from_key(env("ETH_RECEIVER_KEY")).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:
bundler(
w3=w3,
signature_account=signer,
endpoint_uris=BUILDER_ENPOINTS,
flashbots_uri="https://relay-goerli.flashbots.net",
)
else:
bundler(w3=w3, signature_account=signer, endpoint_uris=BUILDER_ENPOINTS)
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
replacement_uuid = str(uuid4())
try:
print(w3.flashbots.simulate(bundle, block))
print("Simulation successful")
except Exception as error:
print(f"Simulation failed, {error=}")
continue
send_result = w3.flashbots.send_bundle(
bundle,
target_block_number=block + 1,
opts={"replacementUuid": replacement_uuid},
)
print("bundleHash", w3.toHex(send_result.bundle_hash()))
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()