原文作者:PaperMoon团队
与区块链交互通常需要一个应用与区块链网络之间的接口。Web3.py 正是提供了这样的接口,它通过一组 Python 库,使应用能够通过 HTTP 或 WebSocket 协议与区块链节点进行无缝交互。
本篇指南将演示如何使用 Web3.py 与 Polkadot Hub 进行交互。
项目初始化
开始使用 Web3.py 前,先初始化你的项目:
bash
mkdir web3py-project
cd web3py-project
为项目创建并激活一个虚拟环境:
bash
python -m venv venv
source venv/bin/activate
然后安装 Web3.py:
bash
pip install web3
设置 Web3 Provider
Provider(提供者)是任何 Web3.py 应用的基础,它作为你的应用与区块链之间的桥梁,使你能够查询链上数据并与智能合约交互。
要与 Polkadot Hub 交互,你需要配置一个 Web3.py Provider,用于连接区块链节点。示例如下:
python
from web3 import Web3
PROVIDER_RPC = "INSERT_RPC_URL"
web3 = Web3(Web3.HTTPProvider(PROVIDER_RPC))
注意
请将 INSERT_RPC_URL 替换为实际的 RPC 地址。例如,连接 Polkadot Hub 测试网 时可以使用:
python
PROVIDER_RPC = 'https://services.polkadothub-rpc.com/testnet'
完成 Provider 配置后,你就可以开始查询区块链数据了。例如,获取当前链上的最新区块高度。
(示例:获取最新区块)
编译合约
Polkadot Hub 暴露了 Ethereum JSON-RPC 接口,因此你可以使用 py-solc-x 编译器,将 Solidity 合约编译为标准的 EVM 字节码。生成的产物可以直接用于任何 EVM 兼容工具链,并可通过 Web3.py 部署。
首先安装 py-solc-x:
bash
pip install py-solc-x
示例:Storage 智能合约
下面是一个简单的 Solidity 示例合约 Storage.sol,用于存储一个数值,并允许用户更新它。
Storage.sol
bash
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Storage {
// 公共状态变量,用于存储数值
uint256 public storedNumber;
/**
* 更新存储的数值
*
* public 修饰符允许任何人调用该函数
*
* @param _newNumber - 要存储的新值
*/
function setNumber(uint256 _newNumber) public {
storedNumber = _newNumber;
}
}
编译智能合约
创建一个名为 compile.py 的 Python 脚本:
python
import json
import solcx
from pathlib import Path
SOLC_VERSION = '0.8.9'
try:
solcx.install_solc(SOLC_VERSION)
except Exception as e:
print(f"Solc 版本 {SOLC_VERSION} 已安装或出现错误: {e}")
solcx.set_solc_version(SOLC_VERSION)
contract_path = Path('Storage.sol')
with open(contract_path, 'r') as file:
contract_source = file.read()
compiled_sol = solcx.compile_source(
contract_source,
output_values=['abi', 'bin'],
solc_version=SOLC_VERSION
)
contract_id, contract_interface = compiled_sol.popitem()
bytecode = contract_interface['bin']
abi = contract_interface['abi']
Path('abis').mkdir(exist_ok=True)
Path('artifacts').mkdir(exist_ok=True)
with open('abis/Storage.json', 'w') as abi_file:
json.dump(abi, abi_file, indent=2)
with open('artifacts/Storage.bin', 'w') as bin_file:
bin_file.write(bytecode)
print("✅ 合约编译成功!")
print("📄 ABI 已保存至: abis/Storage.json")
print("📦 字节码已保存至: artifacts/Storage.bin")
说明
上述脚本是为 Storage.sol 合约定制的。如需编译其他合约,可修改文件名或 ABI / 字节码的输出路径。
• ABI(应用二进制接口):描述合约的函数、事件及其参数,是 Python 与合约交互的接口规范
• Bytecode(字节码):EVM 可执行的底层机器码,用于链上部署
运行编译脚本:
bash
python compile.py
执行后,你将得到:
• abis/Storage.json:合约 ABI
• artifacts/Storage.bin:合约字节码
接下来即可部署合约到 Polkadot Hub。
合约部署
使用 Web3.py 部署合约与在任何 EVM 兼容链上的流程完全一致。你需要一个账户私钥来签名交易。
请将 INSERT_RPC_URL 与 INSERT_PRIVATE_KEY 替换为实际值。
bash
from web3 import Web3
import json
import time
from pathlib import Path
ARTIFACTS_DIR = Path(__file__).parent
ABI_DIR = ARTIFACTS_DIR / "abis"
BYTECODE_DIR = ARTIFACTS_DIR / "artifacts"
def get_abi(contract_name):
try:
with open(ABI_DIR / f"{contract_name}.json", 'r') as file:
return json.load(file)
except Exception as error:
print(f"❌ Could not find ABI for contract {contract_name}: {error}")
raise error
def get_bytecode(contract_name):
try:
with open(BYTECODE_DIR / f"{contract_name}.bin", 'r') as file:
bytecode = file.read().strip()
return bytecode if bytecode.startswith('0x') else f"0x{bytecode}"
except Exception as error:
print(f"❌ Could not find bytecode for contract {contract_name}: {error}")
raise error
def deploy_with_retry(config, max_retries=3):
"""Deploy with retry logic for RPC errors"""
for attempt in range(max_retries):
try:
return deploy(config)
except Exception as error:
error_str = str(error)
if "500" in error_str or "Internal Server Error" in error_str or "Connection" in error_str:
if attempt < max_retries - 1:
wait_time = (attempt + 1) * 3
print(f"RPC error, retrying in {wait_time} seconds... (attempt {attempt + 1}/{max_retries})")
time.sleep(wait_time)
continue
raise error
def deploy(config):
try:
# Initialize Web3 with RPC URL and longer timeout
web3 = Web3(Web3.HTTPProvider(
config["rpc_url"],
request_kwargs={'timeout': 120}
))
# Prepare account
formatted_private_key = config["private_key"] if config["private_key"].startswith('0x') else f"0x{config['private_key']}"
account = web3.eth.account.from_key(formatted_private_key)
print(f"Deploying from address: {account.address}")
# Load ABI and bytecode
abi = get_abi('Storage')
bytecode = get_bytecode('Storage')
print(f"Bytecode length: {len(bytecode)}")
# Create contract instance
contract = web3.eth.contract(abi=abi, bytecode=bytecode)
# Get current nonce (this will test the connection)
print("Getting nonce...")
nonce = web3.eth.get_transaction_count(account.address)
print(f"Nonce: {nonce}")
# Estimate gas
print("Estimating gas...")
gas_estimate = web3.eth.estimate_gas({
'from': account.address,
'data': bytecode
})
print(f"Estimated gas: {gas_estimate}")
# Get gas price
print("Getting gas price...")
gas_price = web3.eth.gas_price
print(f"Gas price: {web3.from_wei(gas_price, 'gwei')} gwei")
# Build deployment transaction
print("Building transaction...")
construct_txn = contract.constructor().build_transaction({
'from': account.address,
'nonce': nonce,
'gas': gas_estimate,
'gasPrice': gas_price,
})
# Sign transaction
print("Signing transaction...")
signed_txn = web3.eth.account.sign_transaction(construct_txn, private_key=formatted_private_key)
# Send transaction
print("Sending transaction...")
tx_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
print(f"Transaction hash: {tx_hash.hex()}")
# Wait for transaction receipt
print("Waiting for transaction receipt...")
tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=300)
contract_address = tx_receipt.contractAddress
# Log results
print(f"✅ Contract deployed at: {contract_address}")
print(f"Gas used: {tx_receipt.gasUsed}")
print(f"Block number: {tx_receipt.blockNumber}")
return web3.eth.contract(address=contract_address, abi=abi)
except Exception as error:
print(f'❌ Deployment failed: {error}')
raise error
if __name__ == "__main__":
deployment_config = {
"rpc_url": "https://services.polkadothub-rpc.com/testnet",
"private_key": "INSERT_PRIVATE_KEY"
}
deploy_with_retry(deployment_config)
该脚本包括:
• RPC 连接与重试机制
• Gas 估算
• 交易签名与发送
• 等待交易回执并输出合约地址
运行部署脚本:
bash
python deploy.py
部署成功后,终端将输出合约地址,你可以用它进行后续交互。
⚠️ 安全警告
切勿提交或分享你的私钥。一旦泄露,关联资产可能被立即盗取。
与合约交互
部署完成后,你可以使用 Web3.py 调用合约方法。以下示例演示如何更新并读取存储的数值。
update_storage.py
bash
from web3 import Web3
import json
def get_abi(contract_name):
try:
with open(f"{contract_name}.json", "r") as file:
return json.load(file)
except Exception as error:
print(f"❌ Could not find ABI for contract {contract_name}: {error}")
raise error
async def update_storage(config):
try:
# Initialize Web3 with RPC URL
web3 = Web3(Web3.HTTPProvider(config["rpc_url"]))
# Prepare account
account = web3.eth.account.from_key(config["private_key"])
# Load ABI
abi = get_abi("Storage")
# Create contract instance
contract = web3.eth.contract(address=config["contract_address"], abi=abi)
# Get initial value
initial_value = contract.functions.storedNumber().call()
print("Current stored value:", initial_value)
# Get current nonce
nonce = web3.eth.get_transaction_count(account.address)
# Prepare transaction
transaction = contract.functions.setNumber(1).build_transaction(
{"from": account.address, "nonce": nonce}
)
# Sign transaction
signed_txn = web3.eth.account.sign_transaction(
transaction, private_key=config["private_key"]
)
# Send transaction
tx_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
print(f"Transaction hash: {tx_hash.hex()}")
# Wait for receipt
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
# Get updated value
new_value = contract.functions.storedNumber().call()
print("New stored value:", new_value)
return receipt
except Exception as error:
print("Update failed:", error)
raise error
if __name__ == "__main__":
# Example usage
import asyncio
config = {
"rpc_url": "INSERT_RPC_URL",
"private_key": "INSERT_PRIVATE_KEY",
"contract_address": "INSERT_CONTRACT_ADDRESS",
}
asyncio.run(update_storage(config))
运行脚本前,请替换:
• INSERT_RPC_URL
• INSERT_PRIVATE_KEY
• INSERT_CONTRACT_ADDRESS
执行:
bash
python update_storage.py
以上你就完成了基本的web3.py的探索。
原文链接:https://docs.polkadot.com/smart-contracts/libraries/web3-py/