如何使用 Web3.py 与 Polkadot Hub 进行交互

原文作者: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 脚本:

compile.py

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 替换为实际值。

deploy.py

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/

相关推荐
方见华Richard7 小时前
递归对抗引擎RAE V4.0(AGI自主进化版)
经验分享·笔记·其他·交互·学习方法
voidmort8 小时前
Web3 中的 DEX 流程详解:从原理到实现
web3·区块链
lsrsyx9 小时前
SUNX交易所好用吗?第一视角清晰完整版介绍
区块链
方见华Richard9 小时前
递归对抗引擎(RAE)核心极简实现框架
人工智能·交互·学习方法·原型模式·空间计算
傻小胖9 小时前
10.BTC-分叉-北大肖臻老师客堂笔记
区块链
方见华Richard9 小时前
递归对抗引擎RAE V2.0(多智能体分布式对抗版)
人工智能·经验分享·交互·学习方法·原型模式
晚霞的不甘12 小时前
Flutter for OpenHarmony《淘淘购物》主页点击跳转功能详解:从 UI 响应到页面导航的完整实现
前端·flutter·ui·搜索引擎·前端框架·交互
China_Yanhy12 小时前
我的区块链运维日记 · 第 7 日:影子区块的诱惑 —— 多节点下的“重复充值”危机
运维·区块链
●VON14 小时前
Flutter for OpenHarmony:基于软删除状态机与双轨数据管理的 TodoList 回收站安全体系实现
安全·flutter·交互·openharmony·跨平台开发·von