You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不使用ABI的情况下用ethers.rs调用智能合约函数?

用ethers.rs通过Function Selector调用合约函数(无需ABI)

要实现不依赖ABI、仅通过合约地址和目标函数信息调用合约,核心是手动生成function selector并编码参数,最终组装交易的data字段。以下是具体实现步骤:

1. 生成Function Selector

Function Selector是函数签名的Keccak-256哈希前4字节,函数签名格式为函数名(参数类型1,参数类型2,...)(比如transfer(address,uint256))。用ethers.rs的工具函数即可生成:

use ethers::core::utils::keccak256;

// 定义目标函数的标准签名
let function_signature = "transfer(address,uint256)";
// 计算哈希并截取前4字节作为selector
let selector = &keccak256(function_signature.as_bytes())[0..4];

2. 编码函数参数

按照Solidity ABI规则编码参数,ethers.rs提供了encode函数直接处理,只需传入参数对应的Token数组:

use ethers::abi::{encode, Token};
use ethers::core::types::{Address, U256};

// 构造与函数参数匹配的Token集合
let params = vec![
    Token::Address("0x1234567890abcdef1234567890abcdef12345678".parse::<Address>().unwrap()),
    Token::Uint(U256::from(1000)),
];
// 编码参数
let encoded_params = encode(&params);

3. 组装交易Data字段

将selector与编码后的参数拼接,得到交易所需的data内容:

let mut data = selector.to_vec();
data.extend(encoded_params);

4. 构建并发送交易

最后用ethers.rs的Provider和Signer构建交易,发送至目标合约地址:

use ethers::{
    prelude::*,
    core::types::{TransactionRequest, U256},
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 连接以太坊节点(示例为本地节点,可替换为Infura/Alchemy等远程节点)
    let provider = Provider::<Http>::try_from("http://localhost:8545")?;
    // 加载签名者(用私钥实例化钱包)
    let wallet = "YOUR_PRIVATE_KEY".parse::<LocalWallet>()?;
    let signer = wallet.connect(provider);

    // 目标合约地址
    let contract_address = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0".parse::<Address>()?;

    // 生成selector
    let function_signature = "transfer(address,uint256)";
    let selector = &keccak256(function_signature.as_bytes())[0..4];

    // 编码参数
    let params = vec![
        Token::Address("0x1234567890abcdef1234567890abcdef12345678".parse().unwrap()),
        Token::Uint(U256::from(1000)),
    ];
    let encoded_params = encode(&params);

    // 组装data字段
    let mut data = selector.to_vec();
    data.extend(encoded_params);

    // 构建交易请求
    let tx = TransactionRequest::new()
        .to(contract_address)
        .data(data)
        .value(U256::from(0)) // 无需ETH的函数设为0
        .gas_price(U256::from(20_000_000_000)) // 可根据网络动态调整,或用provider估算
        .gas(provider.estimate_gas(&tx, None).await?); // 自动估算gas更可靠

    // 发送交易并等待确认
    let tx_response = signer.send_transaction(tx, None).await?;
    println!("交易哈希: {:?}", tx_response.tx_hash());

    let receipt = tx_response.confirmations(1).await?;
    println!("交易收据: {:?}", receipt);

    Ok(())
}

关键注意事项

  • 函数签名必须严格匹配Solidity定义:参数类型要写全称(比如uint256不能简写为uint,address不能省略)。
  • 参数编码需对应正确的Token类型:比如地址用Token::Address,大数值用Token::Uint/Token::Int。
  • 若调用view/pure只读函数,无需发送交易,改用provider.call(&tx, None).await?获取返回值,再用ethers::abi::decode解析结果。

内容的提问来源于stack exchange,提问作者NewToCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 07:20:42