alloy-rs / core

High-performance, well-tested & documented core libraries for Ethereum, in Rust
https://alloy.rs
Apache License 2.0
758 stars 131 forks source link

abi编码 #687

Closed mingtianquan closed 2 months ago

mingtianquan commented 2 months ago
use std::os::raw::{c_char, c_void};
use std::sync::Arc;
use std::convert::TryFrom;
use ethers::prelude::*;
use ethers::providers::{Provider, Http};
use ethers::signers::{LocalWallet, Signer};
use ethers::abi::{Abi, AbiEncode, ethabi, Function, ParamType, Token, Tokenize};
use ethers::contract::Contract;
use std::ffi::CStr;
use std::ffi::CString;
use std::ptr;
use ethers::{core::k256::ecdsa::SigningKey, types::Address, utils::secret_key_to_address};
use ethers::core::k256::FieldBytes;
use ethers::prelude::k256::SecretKey;
use ethers::prelude::transaction::eip2718::TypedTransaction;
use serde_json::json;
use tokio::runtime::Runtime;
use tokio::sync::futures;
#[no_mangle]
pub extern "stdcall" fn contract_encode_abi(function_signature_str: *const c_char, params_str: *const c_char) -> *mut libc::c_void {
    // 解析函数签名字符串为 Rust 字符串
    let function_signature_c_str = unsafe { CStr::from_ptr(function_signature_str) };
    let function_signature = function_signature_c_str.to_str().expect("Function signature string is not valid UTF-8");

    // 解析参数字符串为 Rust 字符串
    let params_c_str = unsafe { CStr::from_ptr(params_str) };
    let params_str = params_c_str.to_str().expect("Parameters string is not valid UTF-8");

    // 解析函数名和参数类型
    let (function_name, param_types_str) = function_signature.split_once('(').expect("Invalid function signature");
    let param_types_str = param_types_str.trim_end_matches(')').trim();

    let param_types: Vec<&str> = if param_types_str.is_empty() {
        vec![]
    } else {
        param_types_str.split(',').collect()
    };

    // 解析参数值
    let param_values: Vec<&str> = if params_str.is_empty() {
        vec![]
    } else {
        params_str.split(',').collect()
    };

    assert_eq!(param_types.len(), param_values.len(), "Parameter types and values length mismatch");

    // 动态地将参数值解析为相应的类型
    let tokens: Vec<Token> = param_types.iter().zip(param_values.iter()).map(|(&param_type, &param_value)| {
        match param_type {
            "uint256" => {
                let value: U256 = U256::from_dec_str(param_value).expect("Failed to parse uint256");
                Token::Uint(value)
            },
            "address" => {
                let value: Address = param_value.parse().expect("Failed to parse address");
                Token::Address(value)
            },
            "address[]" => {
                let addresses: Vec<Address> = param_value.split('|').map(|s| s.trim().parse::<Address>().expect("Failed to parse address in array")).collect();
                Token::Array(addresses.into_iter().map(Token::Address).collect())
            },
            // 添加其他类型的支持,例如 string, bytes, bool 等等
            _ => panic!("Unsupported parameter type: {}", param_type),
        }
    }).collect();

    // 动态构建函数 ABI
    let function = Function {
        name: function_name.to_string(),
        inputs: param_types.iter().map(|&param_type| {
            let kind = match param_type {
                "uint256" => ParamType::Uint(256),
                "address" => ParamType::Address,
                "address[]" => ParamType::Array(Box::new(ParamType::Address)),
                // 添加其他类型的支持,例如 string, bytes, bool 等等
                _ => panic!("Unsupported parameter type: {}", param_type),
            };
            ethabi::Param {
                name: "".to_string(),
                kind,
                internal_type: None, // 添加这个字段
            }
        }).collect(),
        outputs: vec![],
        constant: None,
        state_mutability: ethabi::StateMutability::Payable, // 根据需求修改
    };
    // 编码调用数据
    let calldata = function.encode_input(&tokens).expect("Failed to encode calldata");

    // 将合约编码后的字符串返回
    let encoded_str = format!("0x{}", hex::encode(calldata));
    let c_str = CString::new(encoded_str).expect("Failed to create CString");
    c_str.into_raw() as *mut libc::c_void
}

ethers库写的一个函数,怎么用alloy来实现, 给定的abi,例如是这样的字符串transfer(address,uint256) 传递的参数字符串是0x9Cf5D791047dC7323c7c362cd60a94f8f14ec067,1000000000000000000 返回返回的是0xa9059cbb0000000000000000000000009cf5d791047dc7323c7c362cd60a94f8f14ec0670000000000000000000000000000000000000000000000000de0b6b3a7640000

mattsse commented 2 months ago

Unable to answer this unfortunately, could you please phrase this in English?

mingtianquan commented 2 months ago

How to use allocation to implement a function written in the ethers library? Given abi, for example, the string transfer (address, uint256) passes the parameter string

DaniPopes commented 2 months ago

You can parse a signature string to alloy_json_abi::Function with Function::parse and then use alloy_dyn_abi::JsonAbiExt to encode data https://docs.rs/alloy-dyn-abi/latest/alloy_dyn_abi/trait.JsonAbiExt.html

For parsing values you can use alloy_dyn_abi::DynSolType::coerce_str

DaniPopes commented 2 months ago

Here's an example with your data

fn parse_and_encode(function: &str, values: &str) -> Vec<u8> {
    let function = Function::parse(function).unwrap();
    let values = values
        .split(',')
        .zip(&function.inputs)
        .map(|(value, param)| param.resolve().unwrap().coerce_str(value).unwrap())
        .collect::<Vec<_>>();
    function.abi_encode_input(&values).unwrap()
}

#[test]
fn test_parse_and_encode() {
    let function = "transfer(address,uint256)";
    let values = "0x9Cf5D791047dC7323c7c362cd60a94f8f14ec067,1000000000000000000";
    let output = parse_and_encode(function, values);
    assert_eq!(hex::encode_prefixed(output), "0xa9059cbb0000000000000000000000009cf5d791047dc7323c7c362cd60a94f8f14ec0670000000000000000000000000000000000000000000000000de0b6b3a7640000");
}
mingtianquan commented 2 months ago

If there is an array in the parameters, for example let function = "swapExactETHForTokens(uint256,address[],address,uint256)"; let values = "12000000000000000000,0x5b7109dd5F3cD041546e6c49A08f8d5D738A32cf|0x0A8Cd01d2ffe74c90c9f2155fb8f36CeB17fFa0C,0x61d8f439cC8dD92E81fe57F70Ef1A042D5CC5692,1663409025"; Running this way will result in an error

DaniPopes commented 2 months ago

You have to write your own parser if coerce_str doesn't work for you, since I don't know what the format of "values" is. All the functions you need otherwise are in the example above