以太坊:通过Web3实现智能合约交互

1. 环境准备

1.1 安装相关依赖包

​ 安装pip3

sudo apt install python3-pip -y

​ 使用pip3安装web3 python包

pip3 install web3

​ 安装ipython3

sudo apt install ipython3 -y

1.2 安装ganache

​ 下载ganache

​ 输入以下命令,运行ganache(./后的内容视具体版本而定)

./ganache-2.5.4-linux-x86_64.AppImage &

1.3 测试

​ 进入Python交互环境

ipython3

​ 输入以下代码测试连接情况

from web3 import Web3w3 = Web3(Web3.HTTPProvider("http://localhost:7545"))w3.isConnected()

2. 新建一个student合约

2.1 配置合约信息

​ 在contracts文件夹中新建Student.sol并输入以下内容:

// SPDX-License-Identifier: MITpragma solidity >=0.4.16 <0.9.0;contract Student{string name;constructor() {// name = _name;name = "Tom";}function getName() public view returns (string memory) {return name;}}

2.2 编写web3脚本

2.2.1新建一个Student.py文件

​ 导入库

from web3 import Web3import osimport sysimport getoptimport uuid

​ 声明一些全局变量

url = "http://localhost:7545" # 以太坊测试链 rpc 连接端口contract_address_file = 'contract_student.txt'# 合约地址保存文件abi_file = "Student/Student.abi"# abi 文件bytecode_file = "Student/Student.bin"# 字节码文件account_id = 0# 默认账户

​ 连接测试链

# 连接测试链w3 = Web3(Web3.HTTPProvider(url)) eth = w3.ethprint("eth connect:", w3.isConnected())

​ 设置默认账户

def set_default_account():"""设置调用合约、发送交易的账户"""global account_ideth.defaultAccount = eth.accounts[account_id]

​ 获取abi和bytecode

def get_abi_from_file(file):""" 从文件中获取abi"""with open(file, 'r') as f:return f.read() def get_bytecode_from_file(file):"""从文件中获取字节码"""with open(file, 'r') as f:return "0x" + f.read()

​ 部署合约、获取合约地址

def deploy_contract(abi, bytecode):"""部署合约"""contract = eth.contract(abi=abi, bytecode=bytecode) # 创建合约tx_hash = contract.constructor().transact() # 部署合约(发送构造函数的交易,需相对应合约中的参数)tx_receipt = eth.waitForTransactionReceipt(tx_hash) # 等待交易回执print("contract address:", tx_receipt.contractAddress)# 合约地址# 保存合约global contract_address_filewith open(contract_address_file, "w") as f:f.write(tx_receipt.contractAddress)# 通过地址获取已部署合约deployed_contract = eth.contract(address=tx_receipt.contractAddress, abi=abi)return deployed_contractdef get_deployed_contract(abi, bytecode):"""获取部署合约,如果本地已保存合约地址,则调用该地址的合约,否则重新创建一个新的合约"""try:# 尝试获取已有合约with open(contract_address_file, "r") as f:contract_address = f.read()print("contract address:", contract_address)deployed_contract = eth.contract(address=contract_address, abi=abi)return deployed_contractexcept IOError:# 获取已有合约失败则重新部署新合约return deploy_contract(abi, bytecode)

​ main方法

if __name__ == '__main__':set_default_account()abi = get_abi_from_file(abi_file)bytecode = get_bytecode_from_file(bytecode_file)deployed_contract = get_deployed_contract(abi, bytecode)print(deployed_contract.functions.getName().call())

2.2.2 编译合约与运行

​ 编译

solc --abi --bin --overwrite -o Student Student.sol 

​ 运行

python3 Student.py