├── env.py ├── 1-serch.py ├── README.md ├── 2-transferETH.py ├── 5-accounts.py ├── 3-ArbitrumBridge.py └── 4-zkSyncBridge.py /env.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | # 保管好私钥 3 | class ENV(Enum): 4 | Private_Key = '' 5 | From_Address = '' 6 | Target_Address = '' 7 | INFURA_SECRET_KEY = '' -------------------------------------------------------------------------------- /1-serch.py: -------------------------------------------------------------------------------- 1 | # Web3撸毛脚本 🧵 演示代码 2 | 3 | from web3 import Web3 4 | from env import * 5 | 6 | # Your Infura Project ID 7 | INFURA_SECRET_KEY = ENV.INFURA_SECRET_KEY.value 8 | 9 | # get w3 endpoint by network name 10 | def get_w3_by_network(network='mainnet'): 11 | infura_url = f'https://{network}.infura.io/v3/{INFURA_SECRET_KEY}' # 接入 Infura 节点 12 | w3 = Web3(Web3.HTTPProvider(infura_url)) 13 | return w3 14 | 15 | 16 | def main(): 17 | 18 | # 🐳 Task 1: 接入并读取区块链信息 19 | 20 | # 接入 Web3 21 | w3 = get_w3_by_network(network='mainnet') 22 | 23 | # 检查接入状态 24 | print(w3.isConnected()) 25 | 26 | # 当前区块高度 27 | print(w3.eth.block_number) 28 | 29 | # V神 3号钱包地址 30 | vb = '0x220866b1a2219f40e72f5c628b65d54268ca3a9d' 31 | 32 | # 地址格式转换 33 | address = Web3.toChecksumAddress(vb) 34 | 35 | # 查询地址 ETH余额 36 | balance = w3.eth.get_balance(address) / 1e18 37 | print(f'V神地址余额: {balance = } ETH') 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web3_script_tutorial 2 | 3 | Web3脚本交互(撸毛)极简入门指南 4 | 5 | * 目标:通过4个小例子循序渐进学习并使用 Web3.py 模块实现链上数据查询、转账、合约交互等简单功能。 6 | * 合约交互四部曲: 7 | 1. 确定合约地址 8 | 2. 找到合约ABI 9 | 3. 研究函数名及参数具体含义 10 | 4. 写交互代码,广播交易信息 11 | 12 | ## 00: 前期准备工作 13 | 14 | 1. 安装 Python3 15 | 2. 安装 web3.py 库 `pip install web3` 16 | 3. 申请 Infura API Key: https://infura.io/ 17 | 4. 申请测试币 https://faucets.chain.link/ 18 | ❕注意:保管好示例代码中的私钥,注意风险。 19 | 20 | ## 01: 读取链上信息 21 | 22 | * 目标:通过 Infura 接入以太坊主网并查询v神钱包余额信息 23 | * 代码: https://github.com/JetCyC/web3_script_tutorial/blob/main/1-serch.py 24 | 25 | * 接入节点后的Web3对象为我们通向区块链世界的钥匙,不论是查询链上数据,还是进行转账、合约交互,都是通过它来实现。 26 | 27 | 28 | ## 02: Goerli 测试网转账 ETH 29 | 30 | * 目标:接入 Goerli 测试网并完成一笔转账交易 31 | * 代码: https://github.com/JetCyC/web3_script_tutorial/blob/main/2-transferETH.py 32 | 33 | * 转账是所有链上交互的灵魂,是一种改变区块链状态的行为。相比较前面的“查询地址余额”,属于“写入”的操作类型。转账、合约交互等操作,都需要用地址对应的私钥签名交易并广播。 34 | 35 | 36 | ## 03: Arbitrum 测试网跨链桥交互 37 | 38 | * 目标: 完成 Arbitrum 测试网的跨链桥存入 ETH 的交互 39 | * 代码: https://github.com/JetCyC/web3_script_tutorial/blob/main/3-ArbitrumBridge.py 40 | 41 | * 合约交互比起普通转账,又要复杂了一些。从合约交互开始,会需要额外几个参数:▪️ 合约地址 ▪️ 合约ABI ▪️ 交互的函数名称及具体参数 42 | * 获取合约abi的途径 ▪️ etherscan ▪️ 项目前端开发者模式—>Sources->查找目录下是否有abi文件 ▪️ https://twitter.com/gm365/status/1521058983838380032?s=20&t=5GJAoEw0teYA9ZPcyhTGYg 43 | 44 | 45 | 46 | ## 04: zkSync 测试网跨链桥交互 47 | 48 | * 目标: 完成 zkSync 测试网的跨链桥存入 ETH 交互 49 | 50 | * 代码: https://github.com/JetCyC/web3_script_tutorial/blob/main/4-zkSyncBridge.py 51 | * 相比较于 Arbitrum,zkSync 的难度稍微高了一点。因为后者使用了一个可升级合约,导致无法在 Etherscan 网站找到确切的 ABI 信息。不过,通过前一条 🧵 中的方法,最终在网站 chunk-vendor-xxx.js 文件中定位到了完整的 ABI 信息。 52 | 53 | 54 | 55 | ## 05: 批量生成钱包地址并调用撸毛业务逻辑 56 | 57 | * 目标:批量生成钱包地址(一套助记词对应多地址),使用每个钱包地址与合约交互 58 | 59 | * 代码: https://github.com/JetCyC/web3_script_tutorial/blob/main/5-accounts.py 60 | * 我们使用一套助记词对应多个钱包地址的方式批量生成撸毛钱包,使用时千万要备份好这套助记词,之后批量调用合约方法。 61 | 62 | 63 | 64 | ## 进阶 (更新ing...) 65 | 66 | * 交互大师 67 | 测试网简单合约 ➜ 测试网复杂合约 ➜ 主网合约 ➜ 多账号单合约 ➜ 多账号多合约 ➜ 多账号切断关联 ➜ 多账号多合约模拟真实用户行为轨迹 ➜ ( Ξ ) ➜ 躺平 68 | 69 | * 套利大师 70 | MEV套利 ➜ ( Ξ Ξ Ξ ) ➜ 躺平 71 | -------------------------------------------------------------------------------- /2-transferETH.py: -------------------------------------------------------------------------------- 1 | # Web3撸毛脚本 🧵 演示代码 2 | 3 | from web3 import Web3 4 | from env import * 5 | 6 | # Your Infura Project ID 7 | INFURA_SECRET_KEY = ENV.INFURA_SECRET_KEY.value 8 | 9 | 10 | # get w3 endpoint by network name 11 | def get_w3_by_network(network='goerli'): 12 | infura_url = f'https://{network}.infura.io/v3/{INFURA_SECRET_KEY}' # 接入 Infura 节点 13 | w3 = Web3(Web3.HTTPProvider(infura_url)) 14 | return w3 15 | 16 | 17 | def transfer_eth(w3,from_address,private_key,target_address,amount,gas_price=10,gas_limit=21000,chainId=5): 18 | from_address = Web3.toChecksumAddress(from_address) 19 | target_address = Web3.toChecksumAddress(target_address) 20 | nonce = w3.eth.getTransactionCount(from_address) # 获取 nonce 值 21 | params = { 22 | 'from': from_address, 23 | 'nonce': nonce, 24 | 'to': target_address, 25 | 'value': w3.toWei(amount, 'ether'), 26 | 'gas': gas_limit, 27 | #'gasPrice': w3.toWei(gas_price, 'gwei'), 28 | 'maxFeePerGas': w3.toWei(gas_price, 'gwei'), 29 | 'maxPriorityFeePerGas': w3.toWei(gas_price, 'gwei'), 30 | 'chainId': chainId, 31 | 32 | } 33 | try: 34 | signed_tx = w3.eth.account.signTransaction(params, private_key=private_key) 35 | txn = w3.eth.sendRawTransaction(signed_tx.rawTransaction) 36 | return {'status': 'succeed', 'txn_hash': w3.toHex(txn), 'task': 'Transfer ETH'} 37 | except Exception as e: 38 | return {'status': 'failed', 'error': e, 'task': 'Transfer ETH'} 39 | 40 | 41 | def main(): 42 | 43 | # 🐳 Task 2: ETH 转账 44 | 45 | # 接入 goerli Testnet 46 | w3 = get_w3_by_network('goerli') 47 | 48 | # 测试地址 49 | from_address = ENV.From_Address.value 50 | 51 | print(f'当前地址余额----------: {from_address} ') 52 | 53 | # 测试私钥, 千万不能泄漏你自己的私钥信息 54 | private_key = ENV.Private_Key.value 55 | 56 | # 测试转入地址 57 | target_address = ENV.Target_Address.value 58 | 59 | # 转账 ETH 金额 60 | amount = 0.0012 61 | 62 | # goerli Chain ID 63 | chainId = 5 64 | 65 | # 查询地址 ETH余额 66 | balance = w3.eth.get_balance(from_address) / 1e18 67 | print(f'当前地址余额: {balance = } ETH') 68 | 69 | result = transfer_eth(w3, from_address, private_key, target_address, amount, chainId=chainId) 70 | print(result) 71 | 72 | 73 | if __name__ == "__main__": 74 | main() 75 | -------------------------------------------------------------------------------- /5-accounts.py: -------------------------------------------------------------------------------- 1 | import json 2 | from eth_account import Account 3 | from web3 import Web3 4 | import csv 5 | 6 | #写入wallets.csv文件 7 | def saveETHWalletInCsv(jsonData): 8 | with open('wallets.csv', 'w', newline='') as csv_file: 9 | csv_writer = csv.writer(csv_file) 10 | csv_writer.writerow(["序号", "钱包地址", "私钥", "助记词"]) 11 | csv_writer.writerows(jsonData) 12 | 13 | 14 | def read_wallets_and_callback(csv_path, callback): 15 | print("---- 开始读取钱包 ----") 16 | with open(csv_path) as f: 17 | f_csv = csv.reader(f) 18 | next(f_csv) 19 | for each_wallet in f_csv: 20 | callback(each_wallet) 21 | 22 | 23 | def create_new_wallet(self): 24 | Account.enable_unaudited_hdwallet_features() 25 | account = Account.create() 26 | privateKey = str.lower(self.bytes_to_hex(account.key)) 27 | address = account.address 28 | return address, privateKey 29 | 30 | 31 | def bytes_to_hex(bs): 32 | return ''.join(['%02X' % b for b in bs]) 33 | 34 | # 批量创建私钥与地址 同一助记词对应多个钱包地址 35 | def create_new_wallet_with_mnemonic(quantity): 36 | print("---- 开始创建钱包 ----") 37 | Account.enable_unaudited_hdwallet_features() 38 | create_result = Account.create_with_mnemonic() 39 | #account = create_result[0] 40 | mnemonic = create_result[1] 41 | wallets = [] 42 | for index in range(quantity): 43 | 44 | localAccount = Account.from_mnemonic(mnemonic=mnemonic, 45 | account_path="m/44'/60'/0'/0/"+ str(index)) 46 | privateKey = str.lower(bytes_to_hex(localAccount.key)) 47 | address = localAccount.address 48 | wallet = { 49 | "id": index, 50 | "address": address, 51 | "privateKey": privateKey, 52 | "mnemonic": mnemonic 53 | } 54 | wallets.append(wallet.values()) 55 | 56 | print(wallets) 57 | saveETHWalletInCsv(wallets) 58 | print("---- 写入csv完成 ----") 59 | return wallets 60 | 61 | 62 | if __name__ == "__main__": 63 | 64 | 65 | # 创建一定数量的钱包地址 66 | create_new_wallet_with_mnemonic(11) 67 | 68 | # 读取.csv钱包地址 69 | # callback = lambda wallet: { 70 | # 拿到底wallet对象之后处理 71 | # print(wallet[1]) 72 | # print(wallet.address) 73 | # print(wallet.privateKey) 74 | # print(wallet.publicKey) 75 | # } 76 | # read_wallets_and_callback('wallets.csv',callback) 77 | -------------------------------------------------------------------------------- /3-ArbitrumBridge.py: -------------------------------------------------------------------------------- 1 | # Web3撸毛脚本 🧵 演示代码 2 | 3 | from web3 import Web3 4 | from env import * 5 | 6 | # Your Infura Project ID 7 | INFURA_SECRET_KEY = ENV.INFURA_SECRET_KEY 8 | 9 | 10 | # get w3 endpoint by network name 11 | def get_w3_by_network(network='mainnet'): 12 | # 接入 Infura 节点 13 | infura_url = f'https://{network}.infura.io/v3/{INFURA_SECRET_KEY}' 14 | w3 = Web3(Web3.HTTPProvider(infura_url)) 15 | return w3 16 | 17 | 18 | # bridge eth from rinkeby to arbitrum testnet 19 | def bridge_arbitrum_eth(w3, from_address, private_key, contract_address, amount_in_ether, chainId): 20 | from_address = Web3.toChecksumAddress(from_address) 21 | contract_address = Web3.toChecksumAddress(contract_address) 22 | 23 | ABI = '[{"inputs":[{"internalType":"uint256","name":"maxSubmissionCost","type":"uint256"}],"name":"depositEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"}]' 24 | 25 | amount_in_wei = w3.toWei(amount_in_ether, 'ether') 26 | maxSubmissionCost = int(amount_in_wei * 0.01) # 定义参数值 27 | nonce = w3.eth.getTransactionCount(from_address) 28 | 29 | params = { 30 | 'chainId': chainId, 31 | 'gas': 250000, 32 | 'nonce': nonce, 33 | 'from': from_address, 34 | 'value': amount_in_wei, 35 | # 'gasPrice': w3.toWei('5', 'gwei'), 36 | 'maxFeePerGas': w3.toWei(5, 'gwei'), 37 | 'maxPriorityFeePerGas': w3.toWei(5, 'gwei'), 38 | 'chainId': chainId, 39 | } 40 | contract = w3.eth.contract(address=contract_address, abi=ABI) 41 | 42 | # 调用合约的 depositEth 函数,参数为 maxSubmissionCost 43 | func = contract.functions.depositEth(maxSubmissionCost) 44 | try: 45 | tx = func.buildTransaction(params) 46 | signed_tx = w3.eth.account.sign_transaction(tx, private_key=private_key) 47 | txn = w3.eth.sendRawTransaction(signed_tx.rawTransaction) 48 | return {'status': 'succeed', 'txn_hash': w3.toHex(txn), 'task': 'Bridge ETH'} 49 | except Exception as e: 50 | return {'status': 'failed', 'error': e, 'task': 'Bridge ETH'} 51 | 52 | 53 | def main(): 54 | 55 | # 🐳 Task 3: Arbitrum 跨链 ETH 56 | 57 | # 接入 Arbiturm Rinkeby Testnet 58 | w3 = get_w3_by_network('rinkeby') 59 | 60 | # 测试地址 61 | from_address = ENV.From_Address 62 | 63 | # 测试私钥, 千万不能泄漏你自己的私钥信息 64 | private_key = ENV.Private_Key 65 | 66 | # Arbitrum 测试网跨链桥合约地址 67 | contract_address = '0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e' 68 | 69 | # 跨链 ETH 金额 70 | amount_in_ether = 0.088 71 | 72 | # Rinkeby Chain ID 73 | chainId = 4 74 | 75 | # 查询地址 ETH余额 76 | balance = w3.eth.get_balance(from_address) / 1e18 77 | print(f'当前地址余额: {balance = } ETH') 78 | 79 | result = bridge_arbitrum_eth(w3, from_address, private_key, contract_address, amount_in_ether, chainId) 80 | print(result) 81 | 82 | 83 | 84 | if __name__ == "__main__": 85 | main() 86 | -------------------------------------------------------------------------------- /4-zkSyncBridge.py: -------------------------------------------------------------------------------- 1 | # Web3撸毛脚本 🧵 演示代码 2 | 3 | from web3 import Web3 4 | from web3.middleware import geth_poa_middleware 5 | from env import * 6 | 7 | 8 | # Your Infura Project ID 9 | INFURA_SECRET_KEY = ENV.INFURA_SECRET_KEY.value 10 | 11 | 12 | # get w3 endpoint by network name 13 | def get_w3_by_network(network='goerli'): 14 | # 接入 Infura 节点 15 | infura_url = f'https://{network}.infura.io/v3/{INFURA_SECRET_KEY}' 16 | w3 = Web3(Web3.HTTPProvider(infura_url)) 17 | w3.middleware_onion.inject(geth_poa_middleware, layer=0) # goerli 需要添加这句 18 | return w3 19 | 20 | 21 | # bridge eth from goerli to zkSync 2.0 testnet 22 | def bridge_zkSync_eth(w3, from_address, private_key, contract_address, amount_in_ether, chainId): 23 | from_address = Web3.toChecksumAddress(from_address) 24 | contract_address = Web3.toChecksumAddress(contract_address) 25 | 26 | # Deposit ETH ABI 27 | ABI = '[{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_zkSyncAddress","type":"address"},{"internalType":"enum Operations.QueueType","name":"_queueType","type":"uint8"},{"internalType":"enum Operations.OpTree","name":"_opTree","type":"uint8"}],"name":"depositETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"emergencyFreezeDiamond","outputs":[],"stateMutability":"nonpayable","type":"function"}]' 28 | 29 | amount_in_wei = w3.toWei(amount_in_ether, 'ether') 30 | nonce = w3.eth.getTransactionCount(from_address) 31 | 32 | # goerli链:无须设置 gas, gas price , chainId, 会自动计算并配置为 EIP 1559 类型 33 | tx_params = { 34 | 'value': amount_in_wei, 35 | "nonce": nonce, 36 | # 'gas': 150000, 37 | # 'gasPrice': w3.toWei(2, 'gwei'), 38 | # 'maxFeePerGas': w3.toWei(8, 'gwei'), 39 | # 'maxPriorityFeePerGas': w3.toWei(2, 'gwei'), 40 | # 'chainId': chainId, 41 | } 42 | 43 | contract = w3.eth.contract(address=contract_address, abi=ABI) 44 | 45 | try: 46 | raw_txn = contract.functions.depositETH(amount_in_wei, from_address, 0, 0).buildTransaction(tx_params) 47 | signed_txn = w3.eth.account.sign_transaction(raw_txn, private_key=private_key) 48 | txn = w3.eth.send_raw_transaction(signed_txn.rawTransaction) 49 | return {'status': 'succeed', 'txn_hash': w3.toHex(txn), 'task': 'zkSync Bridge ETH'} 50 | except Exception as e: 51 | return {'status': 'failed', 'error': e, 'task': 'zkSync Bridge ETH'} 52 | 53 | 54 | def main(): 55 | 56 | # 🐳 Task 4: zkSync 跨链 ETH 57 | 58 | # 接入 goerli Testnet 59 | w3 = get_w3_by_network('goerli') 60 | 61 | # 测试地址 62 | from_address = ENV.From_Address.value 63 | 64 | # 测试私钥, 千万不能泄漏你自己的私钥信息 65 | private_key = ENV.Private_Key.value 66 | 67 | # zkSync 测试网跨链桥合约地址 68 | contract_address = '0x0e9B63A28d26180DBf40E8c579af3aBf98aE05C5' 69 | 70 | # 跨链 ETH 金额 71 | amount_in_ether = 0.0018 72 | 73 | # goerli Testnet ChainID 74 | chainId = 5 75 | 76 | # 查询地址 ETH余额 77 | balance = w3.eth.get_balance(from_address) / 1e18 78 | print(f'当前地址余额: {balance = } ETH') 79 | 80 | result = bridge_zkSync_eth(w3, from_address, private_key, contract_address, amount_in_ether, chainId) 81 | print(result) 82 | 83 | 84 | if __name__ == "__main__": 85 | main() 86 | --------------------------------------------------------------------------------