-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathairdrop.py
More file actions
202 lines (160 loc) Β· 6.64 KB
/
airdrop.py
File metadata and controls
202 lines (160 loc) Β· 6.64 KB
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import json
import os
import time
import logging
from web3 import Web3
from eth_account import Account
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(message)s',
)
logger = logging.getLogger(__name__)
# Constants from .env file
TOKEN_MAX = float(os.getenv('TOKEN_MAX', 0.5)) # Maximum token balance to maintain
SLEEP_BETWEEN_TXS = 2 # Seconds to wait between transactions
# Chain configuration from .env file
RPC_URL = os.getenv('RPC_URL')
if not RPC_URL:
raise ValueError("π΄ RPC_URL is not set in the environment variables")
CHAIN_ID = os.getenv('CHAIN_ID')
if not CHAIN_ID:
raise ValueError("π΄ CHAIN_ID is not set in the environment variables")
CHAIN_ID = int(CHAIN_ID)
# Get private keys from .env file
MASTER_PRIVATE_KEY = os.getenv('MASTER_PRIVATE_KEY')
if not MASTER_PRIVATE_KEY:
raise ValueError("π΄ MASTER_PRIVATE_KEY is not set in the environment variables")
# Get wallet private keys from .env file (comma-separated)
WALLET_PRIVATE_KEYS = os.getenv('WALLET_PRIVATE_KEYS', '').split(',')
if not WALLET_PRIVATE_KEYS or not WALLET_PRIVATE_KEYS[0]:
raise ValueError("π΄ WALLET_PRIVATE_KEYS is not set in the environment variables")
def derive_address_from_private_key(private_key):
"""Derive Ethereum address from a private key"""
if isinstance(private_key, str) and private_key.startswith('0x'):
private_key = private_key[2:]
account = Account.from_key(private_key)
return account.address
def create_accounts_json():
"""Create accounts.json with wallet details"""
wallets = []
for i, private_key in enumerate(WALLET_PRIVATE_KEYS):
if not private_key.strip(): # Skip empty entries
continue
address = derive_address_from_private_key(private_key)
wallets.append({
"id": i,
"address": address,
"private_key": private_key
})
accounts_data = {
"master": MASTER_PRIVATE_KEY,
"wallets": wallets
}
with open('accounts.json', 'w') as f:
json.dump(accounts_data, f, indent=2)
logger.info(f"β
Created accounts.json with {len(wallets)} wallets")
return accounts_data
def get_balance(w3, address):
"""Get balance in ETH for an address"""
wei_balance = w3.eth.get_balance(address)
eth_balance = w3.from_wei(wei_balance, 'ether')
return eth_balance
def send_transaction(w3, from_private_key, to_address, amount_in_eth):
"""Send ETH from one account to another"""
# Make sure we have the right private key format (with or without 0x prefix)
if isinstance(from_private_key, str) and from_private_key.startswith('0x'):
from_private_key_hex = from_private_key[2:]
else:
from_private_key_hex = from_private_key
# Create account from private key
from_account = Account.from_key(from_private_key_hex)
from_address = from_account.address
# Get the nonce for the sender address
nonce = w3.eth.get_transaction_count(from_address)
# Convert ETH amount to Wei
amount_in_wei = w3.to_wei(amount_in_eth, 'ether')
# Get gas price and estimate gas
gas_price = w3.eth.gas_price
# Build the transaction
tx = {
'nonce': nonce,
'to': to_address,
'value': amount_in_wei,
'gas': 21000, # Standard gas for ETH transfer
'gasPrice': gas_price,
'chainId': CHAIN_ID
}
# Sign the transaction
signed_tx = Account.sign_transaction(tx, from_private_key_hex)
# Send the transaction and wait for receipt
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
return {
'tx_hash': tx_hash.hex(),
'from': from_address,
'to': to_address,
'amount_eth': amount_in_eth,
'status': receipt['status']
}
def main():
# Connect to the Ethereum network
w3 = Web3(Web3.HTTPProvider(RPC_URL))
if not w3.is_connected():
logger.error(f"π΄ Failed to connect to {RPC_URL}")
return
logger.info(f"π Connected to network: {w3.net.version}")
# Create or load accounts.json
if os.path.exists('accounts.json'):
with open('accounts.json', 'r') as f:
accounts_data = json.load(f)
logger.info(f"π Loaded accounts.json with {len(accounts_data['wallets'])} wallets")
else:
accounts_data = create_accounts_json()
# Get master wallet info
master_private_key = accounts_data['master']
master_address = derive_address_from_private_key(master_private_key)
master_balance = get_balance(w3, master_address)
logger.info(f"πΌ Master wallet: {master_address}")
logger.info(f"π° Master balance: {master_balance} ETH")
# Check if master has enough funds
if master_balance < 1:
logger.warning(f"β οΈ Warning: Master wallet has less than 1 ETH ({master_balance})")
# Check each wallet balance and fund if needed
for wallet in accounts_data['wallets']:
wallet_address = wallet['address']
# Skip the master wallet if it's in the list
if wallet_address.lower() == master_address.lower():
logger.info(f"β© Skipping master wallet {wallet['id']} ({wallet_address}): {get_balance(w3, wallet_address)} ETH")
continue
wallet_balance = get_balance(w3, wallet_address)
logger.info(f"π Wallet {wallet['id']} ({wallet_address}): {wallet_balance} ETH")
# If balance is below TOKEN_MAX, send funds
if wallet_balance < TOKEN_MAX:
amount_to_send = float(TOKEN_MAX - wallet_balance)
# Check if master has enough funds to send
if master_balance < amount_to_send:
logger.error(f"π΄ Master wallet doesn't have enough funds to send {amount_to_send} ETH")
continue
logger.info(f"π€ Sending {amount_to_send} ETH to {wallet_address}...")
try:
result = send_transaction(
w3,
master_private_key,
wallet_address,
amount_to_send
)
if result['status'] == 1:
logger.info(f"β
Transaction successful: {result['tx_hash']}")
master_balance = float(master_balance) - amount_to_send
else:
logger.error(f"β Transaction failed: {result['tx_hash']}")
except Exception as e:
logger.error(f"β Error sending transaction: {str(e)}")
# Wait between transactions
time.sleep(SLEEP_BETWEEN_TXS)
if __name__ == "__main__":
main()