合约
智能合约是部署到以太网的程序。参见ethereum.org 文件以获得适当的介绍。
## 合约部署示例
要运行这个示例,您需要安装一些额外的功能:
- eth-tester 提供的沙盒节点。您可以通过以下方式安装它:
$ pip install -U "web3[tester]"
py-solc-x
。这是安装 solidity 编译器solc
的支持路径。您可以通过以下方式安装它:
$ pip install py-solc-x
安装完py-solc-x
后,你需要安装一个版本的solc
。您可以通过新的 REPL 安装最新版本,包括:
>>> from solcx import install_solc
>>> install_solc(version='latest')
现在,您应该可以运行下面的合约部署示例了:
>>> from web3 import Web3
>>> from solcx import compile_source
# Solidity source code
>>> compiled_sol = compile_source(
... '''
... pragma solidity >0.5.0;
... ... contract Greeter {
... string public greeting;
... ... constructor() public {
... greeting = 'Hello';
... }
... ... function setGreeting(string memory _greeting) public {
... greeting = _greeting;
... }
... ... function greet() view public returns (string memory) {
... return greeting;
... }
... }
... ''',
... output_values=['abi', 'bin']
... )
# retrieve the contract interface
>>> contract_id, contract_interface = compiled_sol.popitem()
# get bytecode / bin
>>> bytecode = contract_interface['bin']
# get abi
>>> abi = contract_interface['abi']
# web3.py instance
>>> w3 = Web3(Web3.EthereumTesterProvider())
# set pre-funded account as sender
>>> w3.eth.default_account = w3.eth.accounts[0]
>>> Greeter = w3.eth.contract(abi=abi, bytecode=bytecode)
# 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.wait_for_transaction_receipt(tx_hash)
>>> greeter = w3.eth.contract(
... address=tx_receipt.contractAddress,
... abi=abi
... )
>>> greeter.functions.greet().call()
'Hello'
>>> tx_hash = greeter.functions.setGreeting('Nihao').transact()
>>> tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
>>> greeter.functions.greet().call()
'Nihao'
承包工厂
这些工厂不打算直接初始化。相反,使用 w3.eth.contract()
方法创建合约对象。默认情况下,合约工厂为 Contract
。参见 ConciseContract
中指定替代工厂的示例。
*class* web3.contract.Contract(*address*)
Contract 为部署以太坊智能合约并与之交互提供了一个默认接口。
地址参数可以是十六进制地址或 ENS 名称,如mycontract.eth
。
*class* web3.contract.ConciseContract(*Contract()*)
警告
已弃用:此方法已弃用,取而代之的是 ContractCaller
API 或详细语法
这个 Contract
的变体是为更简洁的读访问而设计的,不会使写访问更冗长。这是以失去对像deploy()
这样的功能和像address
这样的属性的访问为代价的。对于那些用例,建议使用经典的Contract
。需要明确的是,construct Contract只公开合约函数,所有其他合约类方法和属性在construct ContractAPI 中不可用。这包括但不限于contract.address
、contract.abi
和contract.deploy()
。
通过将一个 Contract
实例传递给 ConciseContract
来创建这种类型的合约:
>>> concise = ConciseContract(myContract)
这个变体以调用的方式调用所有方法,所以如果经典合约有类似于contract.functions.owner().call()
的方法,您可以用concise.owner()
来调用它。
对于 access 发送的事务或估算气体,可以添加一个关键字参数,如下所示:
>>> 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, ...})
*class* web3.contract.ImplicitContract(*Contract()*)
警告
已弃用:此方法已弃用,取而代之的是详细语法
这个变体镜像了 ConciseContract
,但是它调用所有方法作为一个事务而不是一个调用,所以如果经典合约有一个类似于contract.functions.owner.transact()
的方法,你可以用implicit.owner()
来代替。
Create this type of contract by passing an instance of
Contract
toImplicitContract
:```py
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
的类。
方法
每个合约工厂公开以下方法。
*classmethod* Contract.constructor(**args*, ***kwargs).transact(transaction=None*)
通过发送新的公共事务来构造和部署协定。
如果提供的话,transaction
应该是一个符合web3.eth.send_transaction(transaction)
方法的字典。该值可能不包含键data
或to
。
如果协定采用构造函数参数,它们应该作为位置参数或关键字参数提供。
如果 ABI 中指定的任何参数是一个address
类型,它们将接受 ENS 名称。
如果没有提供gas
值,那么将使用web3.eth.estimate_gas()
方法为部署事务创建gas
值。
返回部署事务的事务哈希。
>>> deploy_txn = token_contract.constructor(web3.eth.coinbase, 12345).transact()
>>> txn_receipt = web3.eth.get_transaction_receipt(deploy_txn)
>>> txn_receipt['contractAddress']
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'
classmethod Contract.``constructor
(args, kwargs).estimateGas(transaction=None, block_identifier=None*)
警告
已弃用:此方法已弃用,取而代之的是Contract.constructor(*args, **kwargs).estimate_gas()
classmethod Contract.``constructor
(args, kwargs).estimate_gas(transaction=None, block_identifier=None*)
估算用于构建和部署合约的燃气。
该方法的行为与Contract.constructor(*args, **kwargs).transact()
方法相同,事务细节被传递到函数调用的结尾部分,函数参数被传递到第一部分。
在函数调用的最后部分,参数block_identifier
被直接传递给调用。
返回消耗的燃气量,该量可用作公开执行此交易的燃气估计量。
返回部署协定所需的气体。
>>> token_contract.constructor(web3.eth.coinbase, 12345).estimate_gas()
12563
classmethod Contract.``constructor
(args, kwargs).buildTransaction(transaction=None*)
警告
已弃用:此方法已弃用,取而代之的是Contract.constructor(*args, **kwargs).build_transaction()
classmethod Contract.``constructor
(args, kwargs).build_transaction(transaction=None*)
构造合约部署事务字节码数据。
如果协定采用构造函数参数,它们应该作为位置参数或关键字参数提供。
如果 ABI 中指定的任何args
是一个address
类型,它们将接受 ENS 名称。
返回可以传递给 send_transaction 方法的事务字典。
>>> transaction = {
'gasPrice': w3.eth.gas_price,
'chainId': None
}
>>> contract_data = token_contract.constructor(web3.eth.coinbase, 12345).build_transaction(transaction)
>>> web3.eth.send_transaction(contract_data)
```py classmethod Contract.events.your_event_name.createFilter(fromBlock=block, toBlock=block, argument_filters={"arg1": "value"}, topics=[])
创建新的事件过滤器,一个 [`web3.utils.filters.LogFilter`](filters.html#web3.utils.filters.LogFilter "web3.utils.filters.LogFilter") 的实例。
* `fromBlock`是必填字段。定义起始块(不含)过滤块范围。它可以是起始区块号,也可以是最后开采区块的“最新”号,或者是未开采交易的“待定”号。在`fromBlock`的情况下,“最新”和“待定”将“最新”或“待定”块设置为起始滤波器块的静态值。
* `toBlock`可选。默认为“最新”。定义过滤块范围内的结束块(包括结束块)。特殊值“最新”和“待定”设置一个动态范围,该动态范围始终包括过滤器上块范围的“最新”或“待定”块。
* `address`可选。默认为合约地址。过滤器匹配从`address`发出的事件日志。
* `argument_filters`,可选。需要参数名称和值的字典。为事件参数值过滤提供的事件日志时。事件参数可以有索引,也可以没有索引。索引值将被转换为相应的主题参数。将使用正则表达式过滤未编制索引的参数。
* 可选,接受标准的 JSON-RPC 主题参数。关于`topics`参数的更多信息,参见 JSON-RPC 文档中的 [eth_newFilter](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter) 。
```py
*classmethod* Contract.events.your_event_name.build_filter()
使用事件 abi 和协定地址(如果从已部署的协定实例调用)创建 EventFilterBuilder 实例。EventFilterBuilder 提供了一种便捷的方法来构造过滤器参数,并根据事件 abi 进行值检查。它允许通过 match_any 和 match_single 方法定义多个匹配值或单个值。
filter_builder = myContract.events.myEvent.build_filter()
filter_builder.fromBlock = "latest"
filter_builder.args.clientID.match_any(1, 2, 3, 4)
filter_builder.args.region.match_single("UK")
filter_instance = filter_builder.deploy()
deploy
方法从过滤器构建器生成的过滤器参数中返回一个 web3.utils.filters.LogFilter
实例。使用过滤器构建器可以轻松地为数组参数定义多个匹配值:
filter_builder = myContract.events.myEvent.build_filter()
filter_builder.args.clientGroups.match_any((1, 3, 5,), (2, 3, 5), (1, 2, 3))
过滤器构建器阻止已经定义的过滤器参数被改变。
filter_builder = myContract.events.myEvent.build_filter()
filter_builder.fromBlock = "latest"
filter_builder.fromBlock = 0 # raises a ValueError
*classmethod* Contract.deploy(*transaction=None*, *args=None*)
警告
已弃用:此方法已弃用,取而代之的是 constructor()
,它提供了更多的灵活性。
构造并发送一个事务来部署合约。
如果提供的话,transaction
应该是一个符合web3.eth.send_transaction(transaction)
方法的字典。该值可能不包含键data
或to
。
如果合约接受构造函数参数,它们应该通过args
参数以列表的形式提供。
如果 ABI 中指定的任何args
是一个address
类型,它们将接受 ENS 名称。
如果没有提供gas
值,那么将使用web3.eth.estimate_gas()
方法为部署事务创建gas
值。
返回部署事务的事务哈希。
*classmethod* Contract.encodeABI(*fn_name*, *args=None*, *kwargs=None*, *data=None*)
使用以太坊 ABI 对与给定的fn_name
和参数args
相匹配的合约函数的参数进行编码。data
参数默认为功能选择器。
>>> contract.encodeABI(fn_name="register", args=["rainbows", 10])
"0xea87152b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000087261696e626f7773000000000000000000000000000000000000000000000000"
*classmethod* Contract.all_functions()
返回合约中所有函数的列表,其中每个函数都是 ContractFunction
的实例。
>>> contract.all_functions()
[<Function identity(uint256,bool)>, <Function identity(int256,bool)>]
*classmethod* Contract.get_function_by_signature(*signature*)
搜索具有匹配签名的独特函数。找到匹配项后返回 ContractFunction
的实例。如果没有找到匹配,则引发ValueError
。
>>> contract.get_function_by_signature('identity(uint256,bool)')
<Function identity(uint256,bool)>
*classmethod* Contract.find_functions_by_name(*name*)
搜索具有匹配名称的所有函数。返回匹配函数的列表,其中每个函数都是 ContractFunction
的实例。当找不到匹配时,返回一个空列表。
>>> contract.find_functions_by_name('identity')
[<Function identity(uint256,bool)>, <Function identity(int256,bool)>]
*classmethod* Contract.get_function_by_name(*name*)
搜索具有匹配名称的不同函数。找到匹配项后返回 ContractFunction
的实例。如果没有找到匹配或找到多个匹配,则引发ValueError
。
>>> contract.get_function_by_name('unique_name')
<Function unique_name(uint256)>
*classmethod* Contract.get_function_by_selector(*selector*)
使用匹配选择器搜索不同的函数。选择器可以是十六进制字符串、字节或整数。找到匹配项后返回 ContractFunction
的实例。如果没有找到匹配,则引发ValueError
。
>>> 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)'>
*classmethod* Contract.find_functions_by_args(**args*)
搜索所有具有匹配参数的函数。返回匹配函数的列表,其中每个函数都是 ContractFunction
的实例。当找不到匹配时,返回一个空列表。
>>> contract.find_functions_by_args(1, True)
[<Function identity(uint256,bool)>, <Function identity(int256,bool)>]
*classmethod* Contract.get_function_by_args(**args*)
搜索具有匹配参数的独特函数。找到匹配项后返回 ContractFunction
的实例。如果没有找到匹配或找到多个匹配,则引发ValueError
。
>>> contract.get_function_by_args(1)
<Function unique_func_with_args(uint256)>
注意
Contract
方法all_functions
、get_function_by_signature
、find_functions_by_name
、get_function_by_name
、get_function_by_selector
、find_functions_by_args
、get_function_by_args
只有在 abi 提供给合约时才能使用。
注意
Web3.py 拒绝初始化具有相同选择器或签名的多个函数的合约。例如,blockHashAddendsInexpansible(uint256)
和blockHashAskewLimitary(uint256)
具有与0x00000000
相同的选择器值。包含这两种功能的合约将被拒绝。 ## 调用不明确的合约函数示例
下面是一个合约的例子,它有多个同名的功能,并且参数不明确。
>>> contract_source_code = """
pragma solidity ^0.4.21;
contract AmbiguousDuo {
function identity(uint256 input, bool uselessFlag) returns (uint256) {
return input;
}
function identity(int256 input, bool uselessFlag) returns (int256) {
return input;
}
}
"""
# fast forward all the steps of compiling and deploying the contract.
>>> ambiguous_contract.functions.identity(1, True) # raises ValidationError
>>> identity_func = ambiguous_contract.get_function_by_signature('identity(uint256,bool)')
>>> identity_func(1, True)
<Function identity(uint256,bool) bound to (1, True)>
>>> identity_func(1, True).call()
1
``` ## 启用字节类型的严格检查
默认情况下,web3 对十六进制和字节值的要求不是很严格。bytes 类型将接受一个十六进制字符串、一个字节字符串或一个可以解码为十六进制的常规 python 字符串。此外,如果 abi 指定了字节大小,但是传入的值小于指定的大小,web3 将自动填充该值。例如,如果 abi 指定了类型`bytes4`,web3 将处理以下所有值:
<caption>Valid byte and hex strings for a bytes4 type</caption> <colgroup><col width="25%"> <col width="75%"></colgroup>
| 投入 | 正常化为 |
| --- | --- |
| `''` | `b'\x00\x00\x00\x00'` |
| `'0x'` | `b'\x00\x00\x00\x00'` |
| `b''` | `b'\x00\x00\x00\x00'` |
| `b'ab'` | `b'ab\x00\x00'` |
| `'0xab'` | `b'\xab\x00\x00\x00'` |
| `'1234'` | `b'\x124\x00\x00'` |
| `'0x61626364'` | `b'abcd'` |
| `'1234'` | `b'1234'` |
默认情况下,下列值将引发错误:
<caption>Invalid byte and hex strings for a bytes4 type</caption> <colgroup><col width="25%"> <col width="75%"></colgroup>
| 投入 | 理由 |
| --- | --- |
| `b'abcde'` | 超过 4 个字节的字节字符串 |
| `'0x6162636423'` | 超过 4 个字节的十六进制字符串 |
| `2` | 错误的类型 |
| `'ah'` | 字符串不是有效的十六进制 |
但是,您可能希望对字节类型的可接受值更加严格。为此,您可以使用 web3 实例上提供的`w3.enable_strict_bytes_type_checking()`方法。调用了此方法的 web3 实例将执行一组更严格的规则来接受值。
> * Python strings without the prefix `0x` will throw an error.
> * A byte string whose length does not exactly match the specified byte size will generate an error.
<caption>Valid byte and hex strings for a strict bytes4 type</caption> <colgroup><col width="25%"> <col width="75%"></colgroup>
| 投入 | 正常化为 |
| --- | --- |
| `'0x'` | `b'\x00\x00\x00\x00'` |
| `'0x61626364'` | `b'abcd'` |
| `'1234'` | `b'1234'` |
<caption>Invalid byte and hex strings with strict bytes4 type checking</caption> <colgroup><col width="25%"> <col width="75%"></colgroup>
| 投入 | 理由 |
| --- | --- |
| `''` | 需要以“0x”为前缀,以解释为空的十六进制字符串 |
| `'1234'` | 需要是字节字符串(b'1234 ')或者是大小正确的十六进制值,前缀为 0x(在本例中为:“0x31323334”) |
| `b''` | 需要正好有 4 个字节 |
| `b'ab'` | 需要正好有 4 个字节 |
| `'0xab'` | 需要正好有 4 个字节 |
| `'0x6162636464'` | 需要正好有 4 个字节 |
以下面的合约代码为例:
```py
>>> # pragma solidity >=0.4.22 <0.6.0;
...
... # contract ArraysContract {
... # bytes2[] public bytes2Value;
... # constructor(bytes2[] memory _bytes2Value) public {
... # bytes2Value = _bytes2Value;
... # }
... # function setBytes2Value(bytes2[] memory _bytes2Value) public {
... # bytes2Value = _bytes2Value;
... # }
... # function getBytes2Value() public view returns (bytes2[] memory) {
... # return bytes2Value;
... # }
... # }
>>> # abi = "..."
>>> # bytecode = "6080..."
>>> ArraysContract = w3.eth.contract(abi=abi, bytecode=bytecode)
>>> tx_hash = ArraysContract.constructor([b'b']).transact()
>>> tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
>>> array_contract = w3.eth.contract(
... address=tx_receipt.contractAddress,
... abi=abi
... )
>>> array_contract.functions.getBytes2Value().call()
[b'b\x00']
>>> array_contract.functions.setBytes2Value([b'a']).transact({'gas': 420000, 'gasPrice': Web3.toWei(1, 'gwei')})
HexBytes('0xc5377ba25224bd763ceedc0ee455cc14fc57b23dbc6b6409f40a557a009ff5f4')
>>> array_contract.functions.getBytes2Value().call()
[b'a\x00']
>>> w3.enable_strict_bytes_type_checking()
>>> array_contract.functions.setBytes2Value([b'a']).transact()
Traceback (most recent call last):
...
ValidationError:
Could not identify the intended function with name `setBytes2Value`
``` ## 合约功能
```py
*class* web3.contract.ContractFunction
通过 Contract.functions
属性公开的命名函数属于 ContractFunction 类型。这个类不是直接使用,而是通过 Contract.functions
来实现。
例如:
py myContract = web3.eth.contract(address=contract_address, abi=contract_abi) twentyone = myContract.functions.multiply7(3).call()
T6】
如果变量中有函数名,您可能更喜欢以下替代方法:
py func_to_call = 'multiply7' contract_func = myContract.functions[func_to_call] twentyone = contract_func(3).call()
T6】
ContractFunction
提供了与合约函数进行交互的方法。提供给约定函数子类的位置和关键字参数将用于通过签名查找约定函数,并在适用时转发给约定函数。
方法
ContractFunction.transact(*transaction*)
通过发送新的公共事务来执行指定的函数。
请参考以下调用:
myContract.functions.myMethod(*args, **kwargs).transact(transaction)
函数调用的第一部分myMethod(*args, **kwargs)
根据名称和提供的参数选择合适的合约函数。参数可以以位置参数、关键字参数或两者混合的形式提供。
这个函数调用transact(transaction)
的结尾部分接受一个参数,这个参数应该是一个 python 字典,符合与web3.eth.send_transaction(transaction)
方法相同的格式。这本字典可能不包含关键字data
。
如果 ABI 中指定的任何args
或kwargs
是address
类型,它们将接受 ENS 名称。
如果没有提供gas
值,那么将使用web3.eth.estimate_gas()
方法创建方法事务的gas
值。
返回事务哈希。
>>> token_contract.functions.transfer(web3.eth.accounts[1], 12345).transact()
"0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd"
ContractFunction.call(*transaction*, *block_identifier='latest'*)
调用一个合约函数,使用eth_call
API 在本地执行事务。这不会创建新的公共事务。
请参考以下调用:
myContract.functions.myMethod(*args, **kwargs).call(transaction)
该方法的行为与 ContractFunction.transact()
方法相同,事务细节被传递到函数调用的结尾部分,函数参数被传递到第一部分。
返回所执行函数的返回值。
>>> my_contract.functions.multiply7(3).call()
21
>>> token_contract.functions.myBalance().call({'from': web3.eth.coinbase})
12345 # the token balance for `web3.eth.coinbase`
>>> token_contract.functions.myBalance().call({'from': web3.eth.accounts[1]})
54321 # the token balance for the account `web3.eth.accounts[1]`
您可以使用block_identifier
在历史块上调用该方法。一些例子:
# You can call your contract method at a block number:
>>> token_contract.functions.myBalance().call(block_identifier=10)
# or a number of blocks back from pending,
# in this case, the block just before the latest block:
>>> token_contract.functions.myBalance().call(block_identifier=-2)
# or a block hash:
>>> token_contract.functions.myBalance().call(block_identifier='0x4ff4a38b278ab49f7739d3a4ed4e12714386a9fdf72192f2e8f7da7822f10b4d')
>>> token_contract.functions.myBalance().call(block_identifier=b'O\xf4\xa3\x8b\'\x8a\xb4\x9fw9\xd3\xa4\xedN\x12qC\x86\xa9\xfd\xf7!\x92\xf2\xe8\xf7\xdax"\xf1\x0bM')
# Latest is the default, so this is redundant:
>>> token_contract.functions.myBalance().call(block_identifier='latest')
# You can check the state after your pending transactions (if supported by your node):
>>> token_contract.functions.myBalance().call(block_identifier='pending')
为过去的块号传递block_identifier
参数要求您的以太坊 API 节点运行在更昂贵的归档节点模式下。正常情况下,同步的以太坊节点将失败,并出现“丢失 trie 节点”错误,因为以太坊节点可能已经从其数据库中清除了过去的状态。点击了解更多关于存档节点的信息。
ContractFunction.estimateGas(*transaction*, *block_identifier=None*)
警告
已弃用:此方法已弃用,取而代之的是 estimate_gas
ContractFunction.estimate_gas(*transaction*, *block_identifier=None*)
调用一个合约函数,使用eth_call
API 在本地执行事务。这不会创建新的公共事务。
请参考以下调用:
myContract.functions.myMethod(*args, **kwargs).estimate_gas(transaction)
该方法的行为与 ContractFunction.transact()
方法相同,事务细节被传递到函数调用的结尾部分,函数参数被传递到第一部分。
返回消耗的燃气量,该量可用作公开执行此交易的燃气估计量。
>>> my_contract.functions.multiply7(3).estimate_gas()
42650
注意
geth 节点中没有启用参数block_identifier
,因此在连接到 geth 节点时传递值block_identifier
会导致类似于ValueError: {'code': -32602, 'message': 'too many arguments, want at most 1'}
的错误
ContractFunction.buildTransaction(*transaction*)
警告
已弃用:此方法已弃用,取而代之的是 build_transaction
ContractFunction.build_transaction(*transaction*)
基于指定的协定函数调用构建事务字典。
请参考以下调用:
myContract.functions.myMethod(*args, **kwargs).build_transaction(transaction)
该方法的行为与Contract.transact()
方法相同,事务细节被传递到函数调用的结尾部分,函数参数被传递到第一部分。
注意
nonce 不作为事务字典的一部分返回,除非在函数调用的第一部分中指定:
>>> math_contract.functions.increment(5).build_transaction({'nonce': 10})
您可以使用 getTransactionCount()
来获取帐户的当前随机数。因此,生成包含 nonce 的事务字典的快捷方式如下所示:
>>> math_contract.functions.increment(5).build_transaction({'nonce': web3.eth.get_transaction_count('0xF5...')})
返回事务字典。然后可以使用 send_transaction()
发送该交易字典。
此外,字典可用于使用sign_transaction()
的离线交易签名。
>>> math_contract.functions.increment(5).build_transaction({'maxFeePerGas': 2000000000, 'maxPriorityFeePerGas': 1000000000})
{
'to': '0x6Bc272FCFcf89C14cebFC57B8f1543F5137F97dE',
'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005',
'value': 0,
'gas': 43242,
'maxFeePerGas': 2000000000,
'maxPriorityFeePerGas': 1000000000,
'chainId': 1
}
### 后退功能
合约工厂还提供了与 fallback 函数交互的 API,它支持普通函数等四种方法:
Contract.fallback.call(*transaction*)
调用回退函数,使用eth_call
API 在本地执行事务。这不会创建新的公共事务。
Contract.fallback.estimateGas(*transaction*)
调用回退函数并返回气体估计值。
Contract.fallback.transact(*transaction*)
通过发送新的公共事务执行回退功能。
Contract.fallback.buildTransaction(*transaction*)
基于协定回退函数调用构建事务字典。
事件
*class* web3.contract.ContractEvents
通过 Contract.events
属性公开的命名事件属于 ContractEvents 类型。这个类不是直接使用,而是通过 Contract.events
来实现。
例如:
py myContract = web3.eth.contract(address=contract_address, abi=contract_abi) tx_hash = myContract.functions.myFunction().transact() receipt = web3.eth.get_transaction_receipt(tx_hash) myContract.events.myEvent().processReceipt(receipt)
T6】
ContractEvent
提供与合约事件交互的方法。提供给约定事件子类的位置和关键字参数将用于通过签名查找约定事件。
ContractEvents.``myEvent
(args, kwargs).processReceipt(transaction_receipt, errors=WARN*)
从交易收据中提取相关日志。
如果没有错误,processReceipt
返回事件(如myEvent
)发出的 事件日志对象 的元组,并解码输出。
>>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address})
>>> tx_receipt = w3.eth.get_transaction_receipt(tx_hash)
>>> rich_logs = contract.events.myEvent().processReceipt(tx_receipt)
>>> rich_logs[0]['args']
{'myArg': 12345}
如果有错误,将根据传入的标志以不同方式处理日志:
WARN
(default)-Record the warning of error log to the console and discard the log. Returns any log that can be processed.STRICT
-Stop all processing and raise the encountered error.IGNORE
-Return any original log that produced an error, and add the "Error" field, as well as any other logs that can be processed.DISCARD
-silently discard any logs with errors and return the processed logs without errors.
需要从web3/logs.py
导入事件日志错误标志。
>>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address})
>>> tx_receipt = w3.eth.get_transaction_receipt(tx_hash)
>>> processed_logs = contract.events.myEvent().processReceipt(tx_receipt)
>>> processed_logs
(
AttributeDict({
'args': AttributeDict({}),
'event': 'myEvent',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0xfb95ccb6ab39e19821fb339dee33e7afe2545527725b61c64490a5613f8d11fa'),
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'blockHash': HexBytes('0xd74c3e8bdb19337987b987aee0fa48ed43f8f2318edfc84e3a8643e009592a68'),
'blockNumber': 3
})
)
# Or, if there were errors encountered during processing:
>>> from web3.logs import STRICT, IGNORE, DISCARD, WARN
>>> processed_logs = contract.events.myEvent().processReceipt(tx_receipt, errors=IGNORE)
>>> processed_logs
(
AttributeDict({
'type': 'mined',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0x01682095d5abb0270d11a31139b9a1f410b363c84add467004e728ec831bd529'),
'blockHash': HexBytes('0x92abf9325a3959a911a2581e9ea36cba3060d8b293b50e5738ff959feb95258a'),
'blockNumber': 5,
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'data': '0x0000000000000000000000000000000000000000000000000000000000003039',
'topics': [
HexBytes('0xf70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb15')
],
'errors': LogTopicError('Expected 1 log topics. Got 0')})
})
)
>>> processed_logs = contract.events.myEvent().processReceipt(tx_receipt, errors=DISCARD)
>>> assert processed_logs == ()
True
ContractEvents.myEvent(**args*, ***kwargs).processLog(log*)
类似于 processReceipt ,但是一次只处理一个日志,而不是整个交易收据。如果在处理过程中没有遇到错误,将返回单个 事件日志对象 。如果在处理过程中遇到错误,将会引发该错误。
>>> tx_hash = contract.functions.myFunction(12345).transact({'to':contract_address})
>>> tx_receipt = w3.eth.get_transaction_receipt(tx_hash)
>>> log_to_process = tx_receipt['logs'][0]
>>> processed_log = contract.events.myEvent().processLog(log_to_process)
>>> processed_log
AttributeDict({
'args': AttributeDict({}),
'event': 'myEvent',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0xfb95ccb6ab39e19821fb339dee33e7afe2545527725b61c64490a5613f8d11fa'),
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'blockHash': HexBytes('0xd74c3e8bdb19337987b987aee0fa48ed43f8f2318edfc84e3a8643e009592a68'),
'blockNumber': 3
})
### 事件日志对象
T9】事件日志对象是一个大蟒字典,具有以下关键字:
args
: Dictionary-arguments from events.event
: string-the name of the event.logIndex
: number-the integer of the log index position in the block.transactionIndex
: the integer digits of the trading indicator position log are created.transactionHash
: String, 32 bytes-hash of the transaction that created this log.address
: string, 32 bytes-the address of the log source.blockHash
: string, 32 bytes-hash of the block where this log is located. Empty when suspended.blockNumber
: number-the block number where the log is located. Empty when suspended.
>>> transfer_filter = my_token_contract.events.Transfer.createFilter(fromBlock="0x0", argument_filters={'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf'})
>>> transfer_filter.get_new_entries()
[AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'value': 10}),
'event': 'Transfer',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0x9da859237e7259832b913d51cb128c8d73d1866056f7a41b52003c953e749678'),
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'blockHash': HexBytes('...'),
'blockNumber': 2})]
>>> transfer_filter.get_new_entries()
[]
>>> tx_hash = contract.functions.transfer(alice, 10).transact({'gas': 899000, 'gasPrice': 674302241})
>>> tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
>>> transfer_filter.get_new_entries()
[AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'value': 10}),
'event': 'Transfer',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0xa23e7ef4d2692c5cf34ee99123c9c73099e9c3b68c7850f91c1cbcb91ac327e0'),
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'blockHash': HexBytes('...'),
'blockNumber': 3})]
>>> transfer_filter.get_all_entries()
[AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'value': 10}),
'event': 'Transfer',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0x9da859237e7259832b913d51cb128c8d73d1866056f7a41b52003c953e749678'),
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'blockHash': HexBytes('...'),
'blockNumber': 2}),
AttributeDict({'args': AttributeDict({'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'to': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'value': 10}),
'event': 'Transfer',
'logIndex': 0,
'transactionIndex': 0,
'transactionHash': HexBytes('0xa23e7ef4d2692c5cf34ee99123c9c73099e9c3b68c7850f91c1cbcb91ac327e0'),
'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'blockHash': HexBytes('...'),
'blockNumber': 3})]
Utils
*classmethod* Contract.decode_function_input(*data*)
对用于调用智能合约函数的交易数据进行解码,返回 ContractFunction
,解码后的参数为 dict
。
>>> transaction = w3.eth.get_transaction('0x5798fbc45e3b63832abc4984b0f3574a13545f415dd672cd8540cd71f735db56')
>>> transaction.input
'0x612e45a3000000000000000000000000b656b2a9c3b2416437a811e07466ca712f5a5b5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000116c6f6e656c792c20736f206c6f6e656c7900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
>>> contract.decode_function_input(transaction.input)
(<Function newProposal(address,uint256,string,bytes,uint256,bool)>,
{'_recipient': '0xB656b2a9c3b2416437A811e07466cA712F5a5b5a',
'_amount': 0,
'_description': b'lonely, so lonely',
'_transactionData': b'',
'_debatingPeriod': 604800,
'_newCurator': True})
合约呼叫者
*class* web3.contract.ContractCaller
ContractCaller
类提供了一个 API 来调用合约中的函数。这个类不是直接使用,而是通过Contract.caller
使用。
有许多不同的方法来调用ContractCaller
。
例如:
>>> myContract = w3.eth.contract(address=address, abi=ABI)
>>> twentyone = myContract.caller.multiply7(3)
>>> twentyone
21
也可以使用括号调用它:
>>> twentyone = myContract.caller().multiply7(3)
>>> twentyone
21
和事务字典,带有或不带有transaction
关键字。也可以选择包含块标识符。例如:
>>> from_address = w3.eth.accounts[1]
>>> twentyone = myContract.caller({'from': from_address}).multiply7(3)
>>> twentyone
21
>>> twentyone = myContract.caller(transaction={'from': from_address}).multiply7(3)
>>> twentyone
21
>>> twentyone = myContract.caller(block_identifier='latest').multiply7(3)
>>> twentyone
21
与 ContractFunction
, ContractCaller
一样,提供了与合约函数进行交互的方法。提供给协定调用方子类的位置和关键字参数将用于通过签名查找协定函数,并在适用时转发给协定函数。
合约常见问题
如何将结构作为函数参数传入?
Web3.py 接受结构参数作为字典。这种格式也支持嵌套结构。让我们看一个简单的例子。给定以下可靠性合约:
contract Example {
address addr;
struct S1 {
address a1;
address a2;
}
struct S2 {
bytes32 b1;
bytes32 b2;
}
struct X {
S1 s1;
S2 s2;
address[] users;
}
function update(X memory x) public {
addr = x.s1.a2;
}
function retrieve() public view returns (address) {
return addr;
}
}
您可以与 Web3.py 合约 API 进行如下交互:
# deploy or lookup the deployed contract, then:
>>> deployed_contract.functions.retrieve().call()
'0x0000000000000000000000000000000000000000'
>>> deployed_contract.functions.update({'s1': ['0x0000000000000000000000000000000000000001', '0x0000000000000000000000000000000000000002'], 's2': [b'0'*32, b'1'*32], 'users': []}).transact()
>>> deployed_contract.functions.retrieve().call()
'0x0000000000000000000000000000000000000002'