python以太坊开发web3.py智能合约类简介

智能合约

值得花时间去弄明白所有与以太坊智能合约相关的知识。开始时,看看这个例子:

智能合约部署示例

要运行这个示例,你需要安装这些依赖项:

  • 由eth-tester提供的沙盒,可以用pip install -U web3[tester]安装。
  • solc solidity编译器。请参见安装solidity编译器

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import json
import web3

from web3 import Web3
from solc import compile_source
from web3.contract import ConciseContract

# Solidity source code
contract_source_code = '''
pragma solidity ^0.4.21;

contract Greeter {
string public greeting;

function Greeter() public {
greeting = 'Hello';
}

function setGreeting(string _greeting) public {
greeting = _greeting;
}

function greet() view public returns (string) {
return greeting;
}
}
'''

compiled_sol = compile_source(contract_source_code) # Compiled source code
contract_interface = compiled_sol['<stdin>:Greeter']

# web3.py instance
w3 = Web3(Web3.EthereumTesterProvider())

# set pre-funded account as sender
w3.eth.defaultAccount = w3.eth.accounts[0]

# Instantiate and deploy contract
Greeter = w3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin'])

# Submit the transaction that deploys the contract
tx_hash = Greeter.constructor().transact()

# Wait for the transaction to be mined, and get the transaction receipt
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)

# Create the contract instance with the newly-deployed address
greeter = w3.eth.contract(
address=tx_receipt.contractAddress,
abi=contract_interface['abi'],
)

# Display the default greeting from the contract
print('Default contract greeting: {}'.format(
greeter.functions.greet().call()
))

print('Setting the greeting to Nihao...')
tx_hash = greeter.functions.setGreeting('Nihao').transact()

# Wait for transaction to be mined...
w3.eth.waitForTransactionReceipt(tx_hash)

# Display the new greeting value
print('Updated contract greeting: {}'.format(
greeter.functions.greet().call()
))

# When issuing a lot of reads, try this more concise reader:
reader = ConciseContract(greeter)
assert reader.greet() == "Nihao"

智能合约工厂

这些工厂并不直接初始化。相反,使用w3.eth.contract()方法创建智能合约对象。默认情况下,智能合约工厂是Contract。请参见ConciseContract中指定备用工厂的示例。

1.class web3.contract.Contract(address)

Contract提供了一个默认接口,用于部署和与以太坊智能合约交互。

地址参数可以是十六进制地址,也可以是ENS名称,比如mycontract.eth.

  1. class web3.contract.ConciseContract(Contract())

这种Contract的变化是为了更简洁的读访问而设计的,而避免使写访问过于冗长。这是以失去访问诸如deploy()address属性为代价的。建议使用Contract来使用这些用例。为了更清楚,ConciseContract只公开智能合约函数,所有其他Contract class类方法和属性都不能通过ConciseContract API来使用。这包括但不限于contract.addresscontract.abicontract.deploy()

通过将智能合约实例传递给ConciseContract来创建这种类型的智能合约:

1
>>> concise = ConciseContract(myContract)

这个变量调用所有方法作为一个调用,所以如果智能合约有一个类似于contract.functions.owner().call()的方法,则可以用简洁的concise.owner()调用它。

对于发送交易或估计gas的访问,可以添加关键字参数,例如:

1
2
3
4
5
>>> concise.withdraw(amount, transact={'from': eth.accounts[1], 'gas': 100000, ...})

>>> # which is equivalent to this transaction in the classic contract:

>>> contract.functions.withdraw(amount).transact({'from': eth.accounts[1], 'gas': 100000, ...})
  1. class web3.contract.ImplicitContract(Contract())

这各变量反射ConciseContract,但它将所有方法调用都作为一个交易而是一个调用,因此,如果智能合约有一个类似于contract.functions.owner.transact()的方法,则可以用implicit.owner()调用它。

通过将Contract实例传递给ImplicitContract来创建这种类型的智能合约:

1
>>> concise = ImplicitContract(myContract)

属性

每个智能合约工厂提供下列属性。

Contract.address

智能合约的十六进制编码的20字节地址,或者是一个ENS名称。如果在工厂创建过程中没有提供,则可能为None

Contract.abi

智能合约ABI集合。

Contract.bytecode

智能合约字节码字符串。如果在工厂创建过程中没有提供,则可能为None

Contract.bytecode_runtime

智能合约字节码字符串的运行时部分。如果在工厂创建过程中没有提供,则可能为None

Contract.functions

这提供了对智能合约函数的访问作为属性。例如:myContract.functions.MyMethod()。提供的智能合约函数是ContractFunction的类型。

Contract.events

这提供了对合约事件的访问作为属性。例如:myContract.events.MyEvent()。提供的智能合约事件是ContractEvent类型。

方法

每个智能合约工厂都提供了以下方法。

1.classmethod Contract.constructor(*args, **kwargs).transact(transaction=None)

通过发送新的public交易来构建和部署合约。

如果提供transaction应该是符合web3.eth.sendTransaction(transaction)方法。此值可能不包含密钥datato

如果合约采用构造函数参数,则应提供地址参数或关键字参数。

如果ABI中指定的任何参数都是address类型,则它们将接受ENS名称。

如果没有提供gas值,那么部署交易的gas值将使用web3.eth.estimateGas()方法创建。

返回部署交易的交易哈希。

1
2
3
4
>>> deploy_txn = token_contract.constructor(web3.eth.coinbase, 12345).transact()
>>> txn_receipt = web3.eth.getTransactionReceipt(deploy_txn)
>>> txn_receipt['contractAddress']
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'

2.classmethod Contract.constructor(*args, **kwargs).estimateGas(transaction=None)

估计创建和部署合约的gas

此方法的行为与Contract.constructor(*args, **kwargs).transact()方法相同,交易细节被传递到函数调用的最后一个参数,并且函数参数被传递到第一参数。

返回所消耗的gas量,可用于执行此项交易的gas估算。

返回合同所需的gas

1
2
>>> token_contract.constructor(web3.eth.coinbase, 12345).estimateGas()
12563

3.classmethod Contract.constructor(*args, **kwargs).buildTransaction(transaction=None)

构建合约部署交易的字节码数据。

如果合约采用构造函数参数,则应提供地址参数或关键字参数。

如果ABI中指定的任何args都是address类型,它们将接受ENS名称。

返回的交易信息你可以发送给sendTransaction方法。

1
2
3
4
5
6
>>> transaction = {
'gasPrice': w3.eth.gasPrice,
'chainId': None
}
>>> contract_data = token_contract.constructor(web3.eth.coinbase, 12345).buildTransaction(transaction)
>>> web3.eth.sendTransaction(contract_data)

4.Contract.events.<event name>.createFilter(fromBlock=block, [toBlock=block, argument_filters={"arg1": "value"}, topics=[]])

创建一个新的事件过滤器,一个web3.utils.filters.LogFilter实例。

fromBlock是一个强制字段。定义起始块(独占)筛选器块范围。它可以是起始块编号,也可以是latest一个最新被挖掘块,也可以是未开采pending交易。在fromBlock的情况下,将latestpending之一设置为开始筛选器块的静态值,toBlock是可选的参数,默认为latest。定义筛选器块范围中的结束块(包含)。特殊值latestpending设置一个动态范围,它总是包含过滤器的块范围的latestpending块,address参数可选。默认值为合约地址。筛选器匹配从address. argument_filters发出的事件日志,这个参数可选。期望一个参数名称和值的字典。提供事件日志时,对事件参数值进行筛选。事件参数既可以是索引的也可以是未索引的。将索引值转换为相应的参数,非索引参数将使用正则表达式进行筛选,topics参数是可选,接受标准JSON-RPC参数。有关 eth_newFilter的JSON-RPC文档,请参阅有关topics参数的更多信息。

5.classmethod Contract.all_functions()

返回一个合约中所有函数的列表,其中每个函数都是一个ContractFunction的实例。

1
2
>>> contract.all_functions()
[<Function identity(uint256,bool)>, <Function identity(int256,bool)>]

6.classmethod Contract.get_function_by_signature(signature)

搜索具有匹配签名的不同函数。查找匹配项后返回ContractFunction函数的实例。如果找不到匹配的,则引发ValueError

1
2
>>> contract.get_function_by_signature('identity(uint256,bool)')
<Function identity(uint256,bool)>

7.classmethod Contract.find_functions_by_name(name)

搜索具有匹配名称的所有函数。返回一个匹配函数列表,其中每个函数都是一个ContractFunction的实例。如果未找到匹配项,则返回空列表。

1
2
>>> contract.find_functions_by_name('identity')
[<Function identity(uint256,bool)>, <Function identity(int256,bool)>]

8.classmethod Contract.get_function_by_name(name)

搜索具有匹配名称的不同函数。查找匹配项后返回ContractFunction函数的实例。如果找不到匹配或找到多个匹配,则引发ValueError

1
2
>>> contract.get_function_by_name('unique_name')
<Function unique_name(uint256)>

9.classmethod Contract.get_function_by_selector(selector)

用匹配选择器搜索一个不同的函数。选择器可以是十六进制字符串、字节或int。在找到匹配后返回一个ContractFunction函数的实例。如果找不到匹配,则引发ValueError

1
2
3
4
5
6
>>> contract.get_function_by_selector('0xac37eebb')
<Function identity(uint256)'>
>>> contract.get_function_by_selector(b'\xac7\xee\xbb')
<Function identity(uint256)'>
>>> contract.get_function_by_selector(0xac37eebb)
<Function identity(uint256)'>

10.classmethod Contract.find_functions_by_args(*args)

用匹配的args搜索所有函数。返回一个匹配函数列表,其中每个函数都是一个ContractFunction函数的实例。如果未找到匹配项,则返回空列表。

1
2
>>> contract.find_functions_by_args(1, True)
[<Function identity(uint256,bool)>, <Function identity(int256,bool)>]

11.classmethod Contract.get_function_by_args(*args)

用匹配的args搜索一个不同的函数。查找匹配项后返回ContractFunction函数的实例。如果找不到匹配或找到多个匹配,则引发ValueError

1
2
>>> contract.get_function_by_args(1)
<Function unique_func_with_args(uint256)>

需要注意:合约方法,all_functions, get_function_by_signature, find_functions_by_name, get_function_by_name, get_function_by_selector, find_functions_by_args and get_function_by_args只能在ABI被提供给合约时使用。

Web3.Py拒绝使用具有相同选择器或签名的具有多个函数的合约的初始化。例如:blockHashAddendsInexpansible(uint256) 和 blockHashAskewLimitary(uint256) 具有相同的选择器值等于0x00000000。包含这两个函数的合约将被拒绝。

  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • web3j教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • 以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。

汇智网原创翻译,转载请标明出处。这里是原文