使用 web3.py 发送以太坊交易的完整指南

·

在以太坊开发中,web3.py 库提供了多种灵活的方法来发送交易。本文将详细介绍三种主流方式,并指导你根据实际需求选择最适合的方案。

核心方法概览

web3.py 提供了 send_transaction()send_raw_transaction() 两种基础方法,适用于不同场景:

对于智能合约交互,你还可以使用:

👉 查看实时交易工具

方法一:使用 eth-tester 快速测试

许多教程使用 eth-tester(通过 EthereumTesterProvider)来快速验证想法和构建概念证明。测试账户发送的交易会自动签名,无需手动处理。

from web3 import Web3, EthereumTesterProvider

w3 = Web3(EthereumTesterProvider())
acct1 = w3.eth.accounts[0]  # eth-tester 生成的测试账户
some_address = "0x0000000000000000000000000000000000000000"

# eth-tester 会自动签名并发送交易
tx_hash = w3.eth.send_transaction({
    "from": acct1,
    "to": some_address,
    "value": 123123123123123
})

tx = w3.eth.get_transaction(tx_hash)
assert tx["from"] == acct1

这种方法适合学习和原型开发,但不适合生产环境。

方法二:使用中间件自动签名

当你从测试环境过渡到实际应用时,可以使用 web3.py 中间件为特定账户自动签名交易。

from web3.middleware import SignAndSendRawMiddlewareBuilder
import os

# 重要:切勿在代码中直接硬编码私钥!使用环境变量
pk = os.environ.get('PRIVATE_KEY')
acct2 = w3.eth.account.from_key(pk)

# 添加 acct2 作为自动签名器
w3.middleware_onion.inject(SignAndSendRawMiddlewareBuilder.build(acct2), layer=0)

# 中间件会自动为来自 acct2 的交易签名
tx_hash = w3.eth.send_transaction({
    "from": acct2.address,
    "value": 3333333333,
    "to": some_address
})

tx = w3.eth.get_transaction(tx_hash)
assert tx["from"] == acct2.address

你还可以设置默认账户,这样在省略 "from" 字段时会自动使用该账户。

方法三:手动构建和发送原始交易

如果不使用中间件,你需要手动完成交易的构建、签名和发送全过程。

# 1. 构建交易
transaction = {
    'from': acct2.address,
    'to': some_address,
    'value': 1000000000,
    'nonce': w3.eth.get_transaction_count(acct2.address),
    'gas': 200000,
    'maxFeePerGas': 2000000000,
    'maxPriorityFeePerGas': 1000000000,
}

# 2. 使用私钥签名交易
signed = w3.eth.account.sign_transaction(transaction, pk)

# 3. 发送已签名的交易
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)

tx = w3.eth.get_transaction(tx_hash)
assert tx["from"] == acct2.address

这种方法提供了最大程度的控制权,适合需要精细管理交易流程的场景。

智能合约交易处理

智能合约交互同样遵循上述原理,主要通过两种方式实现:

使用 transact() 方法

# 部署合约
tx_hash = Billboard.constructor("gm").transact({"from": acct2.address})
receipt = w3.eth.get_transaction_receipt(tx_hash)
deployed_addr = receipt["contractAddress"]

# 引用已部署的合约
billboard = w3.eth.contract(address=deployed_addr, abi=abi)

手动构建合约交易

# 构建未发送的交易
unsent_billboard_tx = billboard.functions.writeBillboard("gn").build_transaction({
    "from": acct2.address,
    "nonce": w3.eth.get_transaction_count(acct2.address),
})

# 签名并发送
signed_tx = w3.eth.account.sign_transaction(unsent_billboard_tx, private_key=acct2.key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
w3.eth.wait_for_transaction_receipt(tx_hash)

👉 获取进阶交易策略

常见问题

我应该选择哪种交易发送方式?

如果你追求开发效率且主要使用同一账户,推荐使用中间件配合 send_transaction()。如果需要更高控制权或离线签名,则应选择 send_raw_transaction()

如何安全地管理私钥?

永远不要在代码中硬编码私钥。使用环境变量或专业的密钥管理服务,并确保私钥不会提交到版本控制系统。

交易发送失败有哪些常见原因?

常见原因包括:余额不足、gas 设置过低、nonce 值不正确、或是网络拥堵。发送前务必检查这些参数。

如何估算合理的 gas 费用?

可以使用 w3.eth.estimate_gas() 方法估算交易所需的 gas 量,并参考当前网络状况设置适当的 gas 价格。

智能合约交易与普通交易有何不同?

合约交易需要包含调用数据(data),而普通交易只需指定接收地址和金额。合约交易通常需要更多 gas。

如何确认交易是否成功?

使用 w3.eth.wait_for_transaction_receipt(tx_hash) 等待交易确认,并检查返回的收据中的状态字段。

选择合适的方法取决于你的具体需求:测试开发、生产环境自动化还是精细控制。无论哪种方式,web3.py 都提供了强大的工具来满足你的以太坊交易需求。