零基础用python开发区块链(四ABI详解)

什么是ABI

在以太坊中,ABI(Application Binary Interface) 是智能合约与外部世界(如 Web3 应用或 DApp)进行交互的接口。它定义了智能合约的函数、事件、数据结构的编码规则,并且通过 ABI,你可以从外部调用合约的函数,或监听合约事件。

一个智能合约的 ABI 通常是一个 JSON 文件,包含了合约中所有可调用的函数、可监听的事件及其参数的描述。了解 ABI 对于与智能合约交互非常重要,下面详细解释 ABI 的各个部分。

1. ABI 的结构

ABI 是一个包含对象的数组,每个对象定义了一个函数、构造函数或事件。每个对象包含的信息因类型不同而略有差异。常见类型包括 functionconstructorevent

[
  {
    "constant": false,
    "inputs": [{"name": "recipient", "type": "address"}, {"name": "amount", "type": "uint256"}],
    "name": "transfer",
    "outputs": [{"name": "", "type": "bool"}],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [{"name": "_initialSupply", "type": "uint256"}],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [{"indexed": true, "name": "from", "type": "address"}, {"indexed": true, "name": "to", "type": "address"}, {"indexed": false, "name": "value", "type": "uint256"}],
    "name": "Transfer",
    "type": "event"
  }
]

2. 函数对象(function

ABI 中最常见的是 function 类型的对象。它表示合约中的某个函数,并描述如何调用该函数以及如何解析其返回值。

属性说明:
  • name:函数的名称,如 transfer
  • inputs:函数的输入参数列表,参数包含 nametype 属性。name 是参数名称,type 是参数类型(如 address, uint256 等)。
  • outputs:函数的返回值描述,通常是一个数组,包含每个返回值的类型和名称(名称可以为空)。
  • constant(过时):指明该函数是否为只读函数,不改变区块链状态。该字段已被 stateMutability 取代。
  • stateMutability:定义函数的状态可变性。可能的值有:
    • pure:函数不读取也不改变链上状态。
    • view:函数可以读取链上状态但不能修改。
    • nonpayable:函数可以修改链上状态,但不能接受以太币。
    • payable:函数可以接受以太币,并且可能会修改链上状态。
  • payable(过时):该字段表示函数是否可以接收以太币。
示例
 

json

复制代码

{ "constant": false, "inputs": [{"name": "recipient", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], "payable": false, "stateMutability": "nonpayable", "type": "function" }

  • nametransfer 是函数名。
  • inputs:表示这个函数接受两个输入参数:recipient 是一个 address 类型,amount 是一个 uint256 类型。
  • outputs:函数返回一个 bool 类型的值,表示转账是否成功。
  • stateMutabilitynonpayable,表示这个函数不能接收以太币。

3. 构造函数对象(constructor

构造函数是合约部署时调用的函数。它用于初始化合约,通常设置初始状态。constructor 对象在 ABI 中没有 name 属性,因为构造函数没有名称。

属性说明:
  • inputs:输入参数列表。
  • payable:指明构造函数是否接受以太币。已被 stateMutability 取代。
  • stateMutability:描述该构造函数是否允许接收以太币(同函数的 stateMutability)。
  • type:构造函数的类型,固定为 constructor
示例
{
  "inputs": [{"name": "_initialSupply", "type": "uint256"}],
  "payable": false,
  "stateMutability": "nonpayable",
  "type": "constructor"
}
  • 该合约的构造函数接受一个 uint256 类型的参数 _initialSupply,表示初始代币供应量。
  • stateMutabilitynonpayable,表示构造函数不能接收以太币。

4. 事件对象(event

事件是合约中的一种日志记录机制,可以在合约外部通过 web3.jsweb3.py 等库来监听事件的触发。事件允许从合约中发送信号,通知外部某些状态的变化。

属性说明:
  • name:事件名称。
  • inputs:事件的输入参数列表。与函数参数类似,但事件的输入参数有一个额外的属性 indexed,它允许对某些参数进行索引,以便更容易地通过区块链日志查询。
  • anonymous:表示该事件是否是匿名的,匿名事件不记录事件的签名。默认为 false
  • type:事件的类型,固定为 event
示例
{
  "anonymous": false,
  "inputs": [
    {"indexed": true, "name": "from", "type": "address"},
    {"indexed": true, "name": "to", "type": "address"},
    {"indexed": false, "name": "value", "type": "uint256"}
  ],
  "name": "Transfer",
  "type": "event"
}
  • nameTransfer 是事件的名称,通常表示 ERC-20 代币转移。
  • inputs:有三个输入参数:
    • fromtoaddress 类型,且为 indexed,表示这些参数可以用于事件过滤。
    • valueuint256 类型,表示转移的代币数量。
  • anonymousfalse,表示该事件不是匿名事件。

5. 状态变量对象(state

有时 ABI 也会包含合约的状态变量,这些变量可用于查询合约的当前状态。状态变量可以是合约的公开属性。

示例
{
  "constant": true,
  "inputs": [],
  "name": "totalSupply",
  "outputs": [{"name": "", "type": "uint256"}],
  "payable": false,
  "stateMutability": "view",
  "type": "function"
}
  • 该状态变量 totalSupply 是公开的,返回一个 uint256 类型的值,表示当前代币的总供应量。
  • 由于这是一个只读操作,因此 stateMutability 设置为 view

6. 调用智能合约的 ABI 示例(Python web3.py

你可以使用 web3.py 库来加载 ABI 并调用合约方法。以下是如何使用 ABI 调用合约函数的示例:

from web3 import Web3

# 连接到以太坊网络
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'))

# 合约 ABI 和地址
contract_abi = [
    {
        "constant": False,
        "inputs": [{"name": "recipient", "type": "address"}, {"name": "amount", "type": "uint256"}],
        "name": "transfer",
        "outputs": [{"name": "", "type": "bool"}],
        "payable": False,
        "stateMutability": "nonpayable",
        "type": "function"
    }
]
contract_address = '0xContractAddress'

# 加载合约
contract = w3.eth.contract(address=contract_address, abi=contract_abi)

# 调用合约方法
tx_hash = contract.functions.transfer('0xRecipientAddress', 1000).transact({'from': '0xYourAddress'})
print(f"Transaction hash: {w3.toHex(tx_hash)}")

7. ABI 类型说明

ABI 中最常见的参数类型包括:

  • address:以太坊地址(20 字节)。
  • uint256:256 位无符号整数。
  • int256:256 位有符号整数。
  • bool:布尔类型(truefalse)。
  • bytes:字节数组,固定长度或可变长度。
  • string:字符串。
  • array:数组类型,形如 uint256[]

ABI 是智能合约与外部交互的桥梁,它详细描述了合约的函数、事件和状态变量的编码方式。通过 ABI,外部应用可以准确地调用合约的函数或监听事件。理解 ABI 的结构及其组成部分,是成功与智能合约进行交互的关键。

通过 Etherscan API 获取智能合约的 ABI

Etherscan 提供了一个 API 接口,可以根据合约地址获取已部署合约的 ABI。你需要申请一个 Etherscan API Key。

步骤:
  1. 注册 Etherscan 并获取 API Key:Etherscan API
  2. 使用 API 获取合约的 ABI
示例代码:
import requests
from web3 import Web3

# Etherscan API key 和智能合约地址
etherscan_api_key = 'YourEtherscanAPIKey'
contract_address = '0xContractAddress'

# 通过 Etherscan API 查询合约 ABI
etherscan_api_url = f'https://api.etherscan.io/api?module=contract&action=getabi&address={contract_address}&apikey={etherscan_api_key}'

response = requests.get(etherscan_api_url)
contract_abi = response.json()['result']

# 将 ABI 从 JSON 格式解析成 Python 可读格式
contract_abi = eval(contract_abi)

# 连接到以太坊网络
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'))

# 加载智能合约
contract = w3.eth.contract(address=contract_address, abi=contract_abi)

# 查询合约中的数据(以 ERC-20 代币的 `balanceOf` 方法为例)
balance = contract.functions.balanceOf('0xSomeAddress').call()
print(f"Balance: {balance}")

# 调用合约的方法,例如进行代币转账
tx_hash = contract.functions.transfer('0xRecipientAddress', 1000).transact({'from': '0xYourAddress'})
print(f"Transaction hash: {w3.toHex(tx_hash)}")

3. 解析并调用智能合约方法

无论是通过手动加载 ABI 还是通过 Etherscan 获取 ABI,你都可以调用合约的任何方法。这里详细说明如何解析 ABI 并调用合约的方法:

读取 ABI

智能合约的 ABI 是一个 JSON 格式的数组,包含合约中可调用的所有方法和事件。你可以使用 Python 的 eval() 方法将 ABI 字符串解析成 Python 字典格式。

contract_abi = eval(abi_from_etherscan)  # 将字符串解析为 Python 数据类型
加载智能合约

加载合约时,使用 web3.eth.contract() 方法,并传入合约地址和 ABI:

contract = w3.eth.contract(address=contract_address, abi=contract_abi)
调用合约的只读方法

合约中的函数可以分为读方法(不修改链上状态)和写方法(会修改链上状态)。读取合约数据时,使用 .call() 方法。

balance = contract.functions.balanceOf('0xSomeAddress').call()
print(f"Balance: {balance}")
调用合约的写方法

如果调用的是会修改链上状态的方法,例如转账代币,需要用 .transact() 方法,并传递发送者地址及其他必要的交易信息。

tx_hash = contract.functions.transfer('0xRecipientAddress', 1000).transact({'from': '0xYourAddress'})
print(f"Transaction hash: {w3.toHex(tx_hash)}")
监听合约事件

你还可以监听合约的事件,例如 ERC-20 代币的 Transfer 事件:

# 创建事件过滤器
transfer_filter = contract.events.Transfer.createFilter(fromBlock='latest')

# 监听事件
while True:
    for event in transfer_filter.get_new_entries():
        print(f"New Transfer event: {event}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值