所谓「纸上得来终觉浅」,在用 Truffle console 跑过几次合约后,我开始尝试用 Python 直接与智能合约对话。惊喜地发现:不仅语法更优雅,脚本可复用性也更高。下文把我踩过的坑和跑通的全部步骤梳理出来,供你 15 分钟就能在自己的电脑里跑出一套最小闭环。
核心关键词:Python、Web3py、智能合约、以太坊测试网、转账、签名交易、Ganache、ABI、合约交互
准备战场:安装依赖与启动测试链
开始前,确保环境就绪:
- Python 3.8+
- pip install web3==6.x
- Ganache GUI 或
ganache-cli
启动 Ganache,复制 HTTP RPC 地址备用(下文默认本地 7545 端口)。
👉 点击查看如何一键部署本地测试链,省去找节点的烦恼
率先出手:链接测试网络
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7545"))
print(w3.is_connected()) # True 即成功若返回 True,说明 Web3py 已跟本地节点握手。除了 127.0.0.1 外,你也可以把节点换成 Infura、Alchemy 等第三方 RPC。
速查账户与余额
Ganache 的第一号账户即为挖矿地址(coinbase):
print(w3.eth.coinbase)
# 0x0374AD83DfEB8cfD94889631255AE43B2Aa93bbe查询指定地址余额(Wei 转 Ether):
balance_wei = w3.eth.get_balance("0x0374AD83DfEB8cfD94889631255AE43B2Aa93bbe")
print(w3.from_wei(balance_wei, 'ether')) # 100.0快速热身:无须签名的简易转账
在本地私有链做实验,Ganache 会自动解锁账户,因此可以直接 send_transaction:
from_addr = "0x0374AD83DfEB8cfD94889631255AE43B2Aa93bbe"
to_addr = "0x13d2F0AB715924cdaF9623F155275CEdA55819EE"
tx_hash = w3.eth.send_transaction({
'from': from_addr,
'to': to_addr,
'value': w3.to_wei(1, 'ether')
})
print(w3.to_hex(tx_hash))区块一确认,print(w3.from_wei(w3.eth.get_balance(to_addr), 'ether')) 立即可见余额变动。
虽然便捷,但这只是「小白通道」。真正上主网时,每笔交易都必须经过私钥 数字签名,才能避免资产被任意转走。
进阶动作:附带签名的转账
线上环境与 Ganache 非解锁模式都要求交易附签名:
import os
from eth_account import Account
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7545"))
account1 = "0x0374AD83DfEB8cfD94889631255AE43B2Aa93bbe"
private_key = os.getenv("PRIVATE_KEY") # 也可用 Ganache 返回的私钥
account2 = "0x13d2F0AB715924cdaF9623F155275CEdA55819EE"
nonce = w3.eth.get_transaction_count(account1)
tx = {
'nonce': nonce,
'to': account2,
'value': w3.to_wei(1, 'ether'),
'gas': 21000,
'gasPrice': w3.to_wei('50', 'gwei'),
'chainId': 1337 # Ganache 默认为 1337
}
signed_tx = Account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print("tx_hash", w3.to_hex(tx_hash))错误私钥会立即抛出 ValueError: Invalid transaction v,r,s values,又是一道安全防线。
终极交互:读取并调用智能合约
1. 准备合约 ABI 与地址
假设你已用 Truffle 部署过 MetaCoin 合约到 Ganache:
- 合约地址:
0x10BD02647FE442dA2E86D4b05a4aaEC24464314E - ABI 保存在
build/contracts/MetaCoin.json
from web3 import Web3
import json, os
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7545"))
account = "0x0374AD83DfEB8cfD94889631255AE43B2Aa93bbe"
to_account = "0x13d2F0AB715924cdaF9623F155275CEdA55819EE"
with open("./build/contracts/MetaCoin.json") as f:
artifact = json.load(f)
contract = w3.eth.contract(
address="0x10BD02647FE442dA2E86D4b05a4aaEC24464314E",
abi=artifact["abi"]
)2. 读函数(不需要签名)
print("Sender Balance:", contract.functions.getBalance(account).call())
print("Receiver Balance:", contract.functions.getBalance(to_account).call())3. 写函数(需要签名)
tx = contract.functions.sendCoin(to_account, 100).build_transaction({
'from': account,
'nonce': w3.eth.get_transaction_count(account),
'gas': 70000,
'gasPrice': w3.to_wei('50', 'gwei'),
'chainId': 1337
})
signed = w3.eth.account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
print("transaction written:", w3.to_hex(tx_hash))几分钟后在 Ganache 交易标签页即可看到这笔 内部交易 触发 Transfer 事件,MetaCoin 随之完成代币流转。
用例小结:一张图看懂链上流程
- 建立 HTTP 连接 → 读取状态(块高、余额)
- 构造 & 签名交易 → 广播到网络 → 等待区块确认
- 加载合约 ABI → 本地调用,解码 ABI 数据 → 验证合约事件
常见疑问 FAQ
Q1:本地测试链和主网的 gasPrice、chainId 有哪些差异?
- 本地默认 chainId = 1337,gasPrice 随便填;主网 chainId = 1,gasPrice 需参考 Etherscan Gas Tracker。
Q2:为什么我用 w3.is_connected() 报 False?
- 检查 Ganache 监听 IP 与端口;或替换为 Infura 的
https://mainnet.infura.io/v3/<PROJECT_ID>。
Q3:如何把自己的私钥安全保存到环境变量?
- Linux/Mac: 在
.bashrc中加入export PRIVATE_KEY='0x你的私钥'后source ~/.bashrc,再用os.getenv("PRIVATE_KEY")读取即可。
Q4:MetaCoin 合约无返回值怎么办?
- 务必确认 ABI 是最新的;此外某些函数
view属性遗漏,重新编译并「生成新的 JSON 文件」。
Q5:怎样监听合约事件?
- 使用
w3.eth.filter监听事件日志,或直接用 WebSocket Provider 订阅推送,减少轮询消耗。
Q6:我可以把这段脚本改成分布式签名吗?
- 可以。把私钥托管到 MPC(多方计算)钱包或硬件钱包,通过
sign_transaction调用对应 API 即可,绝不把私钥落到磁盘。
写在最后
从舀一勺本地测试网的以太币,到用 Python 脚本签名、转账、调合约,整套流程不过几分钟,但背后囊括 区块链协议、密码学、节点通信 三大技术栈。
当你下次用 DApp 感觉「秒到」时,别忘了这是节点矿工、共识算法、以及你编写的每一行 Python 代码共同协作的结果。祝编码愉快,区块高度节节高升!