Skip to content

Commit

Permalink
Merge branch 'master' into s/babylon-staking
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan authored Jan 3, 2025
2 parents b6ce652 + 6ac227a commit d16c923
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,31 @@ class TestEthereumAbiDecoder {
assertEquals(decodingOutput.getTokens(0).name, "name")
assertEquals(decodingOutput.getTokens(0).stringValue, "deadbeef")
}

@Test
fun testEthereumAbiGetFunctionSignature() {
val abiJson = """
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
""".trimIndent()

val functionSignature = wallet.core.jni.EthereumAbi.getFunctionSignature(abiJson)
assertEquals(functionSignature, "transfer(address,uint256)")
}
}
15 changes: 15 additions & 0 deletions codegen-v2/manifest/TWEthereumAbi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,18 @@ functions:
is_constant: true
is_nullable: false
is_pointer: true
- name: TWEthereumAbiGetFunctionSignature
is_public: true
is_static: true
params:
- name: abi
type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
return_type:
variant: string
is_constant: true
is_nullable: true
is_pointer: true
7 changes: 7 additions & 0 deletions include/TrustWalletCore/TWEthereumAbi.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,11 @@ TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull data, TWString* _No
TW_EXPORT_STATIC_METHOD
TWData* _Nonnull TWEthereumAbiEncodeTyped(TWString* _Nonnull messageJson);

/// Get function signature from Ethereum ABI json
///
/// \param abi The function ABI json string, for example: {"inputs":[{"internalType":"bool","name":"arg1","type":"bool"}],"name":"fun1","outputs":[],"stateMutability":"nonpayable","type":"function"}
/// \return the function type signature, of the form "baz(int32,uint256)", null if the abi is invalid.
TW_EXPORT_STATIC_METHOD
TWString* _Nullable TWEthereumAbiGetFunctionSignature(TWString* _Nonnull abi);

TW_EXTERN_C_END
24 changes: 19 additions & 5 deletions rust/tw_evm/src/evm_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// Copyright © 2017 Trust Wallet.

use crate::abi::AbiResult;
use crate::evm_context::EvmContext;
use crate::modules::abi_encoder::AbiEncoder;
use crate::modules::rlp_encoder::RlpEncoder;
Expand Down Expand Up @@ -46,8 +47,14 @@ pub trait EvmEntry {

/// Returns the function type signature, of the form "baz(int32,uint256)".
#[inline]
fn get_abi_function_signature(input: AbiProto::FunctionGetTypeInput<'_>) -> String {
AbiEncoder::<Self::Context>::get_function_signature(input)
fn get_function_signature_from_proto(input: AbiProto::FunctionGetTypeInput<'_>) -> String {
AbiEncoder::<Self::Context>::get_function_signature_from_proto(input)
}

/// Returns the function type signature, of the form "baz(int32,uint256)".
#[inline]
fn get_function_signature_from_abi(abi: &str) -> AbiResult<String> {
AbiEncoder::<Self::Context>::get_function_signature_from_abi(abi)
}

// Encodes function inputs to Eth ABI binary.
Expand All @@ -71,7 +78,10 @@ pub trait EvmEntryExt {
fn decode_abi_params(&self, input: &[u8]) -> ProtoResult<Data>;

/// Returns the function type signature, of the form "baz(int32,uint256)".
fn get_abi_function_signature(&self, input: &[u8]) -> ProtoResult<String>;
fn get_function_signature_from_proto(&self, input: &[u8]) -> ProtoResult<String>;

/// Returns the function type signature, of the form "baz(int32,uint256)".
fn get_function_signature_from_abi(&self, abi: &str) -> AbiResult<String>;

/// Encodes function inputs to Eth ABI binary.
fn encode_abi_function(&self, input: &[u8]) -> ProtoResult<Data>;
Expand Down Expand Up @@ -102,9 +112,13 @@ where
serialize(&output)
}

fn get_abi_function_signature(&self, input: &[u8]) -> ProtoResult<String> {
fn get_function_signature_from_proto(&self, input: &[u8]) -> ProtoResult<String> {
let input = deserialize(input)?;
Ok(<Self as EvmEntry>::get_abi_function_signature(input))
Ok(<Self as EvmEntry>::get_function_signature_from_proto(input))
}

fn get_function_signature_from_abi(&self, abi: &str) -> AbiResult<String> {
<Self as EvmEntry>::get_function_signature_from_abi(abi)
}

fn encode_abi_function(&self, input: &[u8]) -> ProtoResult<Data> {
Expand Down
21 changes: 18 additions & 3 deletions rust/tw_evm/src/modules/abi_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,13 @@ impl<Context: EvmContext> AbiEncoder<Context> {
}

#[inline]
pub fn get_function_signature(input: Proto::FunctionGetTypeInput<'_>) -> String {
Self::get_function_signature_impl(input)
pub fn get_function_signature_from_proto(input: Proto::FunctionGetTypeInput<'_>) -> String {
Self::get_function_signature_from_proto_impl(input)
}

#[inline]
pub fn get_function_signature_from_abi(abi: &str) -> AbiResult<String> {
Self::get_function_signature_from_abi_impl(abi)
}

#[inline]
Expand Down Expand Up @@ -174,7 +179,7 @@ impl<Context: EvmContext> AbiEncoder<Context> {
})
}

fn get_function_signature_impl(input: Proto::FunctionGetTypeInput<'_>) -> String {
fn get_function_signature_from_proto_impl(input: Proto::FunctionGetTypeInput<'_>) -> String {
let function_inputs = input
.inputs
.into_iter()
Expand All @@ -190,6 +195,16 @@ impl<Context: EvmContext> AbiEncoder<Context> {
fun.signature()
}

fn get_function_signature_from_abi_impl(function_abi: &str) -> AbiResult<String> {
let mut fun: Function = serde_json::from_str(function_abi)
.tw_err(|_| AbiErrorKind::Error_invalid_abi)
.context("Error deserializing Function ABI as JSON")?;

// Clear the `outputs` to avoid adding them to the signature.
fun.outputs.clear();
Ok(fun.signature())
}

fn encode_contract_call_impl(
input: Proto::FunctionEncodingInput<'_>,
) -> AbiResult<Proto::FunctionEncodingOutput<'static>> {
Expand Down
107 changes: 103 additions & 4 deletions rust/tw_tests/tests/chains/ethereum/ethereum_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tw_proto::{deserialize, serialize};
use wallet_core_rs::ffi::ethereum::abi::{
tw_ethereum_abi_decode_contract_call, tw_ethereum_abi_decode_params,
tw_ethereum_abi_decode_value, tw_ethereum_abi_encode_function,
tw_ethereum_abi_function_get_signature,
tw_ethereum_abi_function_get_type, tw_ethereum_abi_get_function_signature,
};

use tw_coin_registry::coin_type::CoinType;
Expand Down Expand Up @@ -117,7 +117,7 @@ fn test_ethereum_abi_decode_params() {
}

#[test]
fn test_ethereum_abi_function_get_signature() {
fn test_ethereum_abi_function_get_type() {
let input = AbiProto::FunctionGetTypeInput {
function_name: "baz".into(),
inputs: vec![
Expand All @@ -132,10 +132,10 @@ fn test_ethereum_abi_function_get_signature() {
let input_data = TWDataHelper::create(serialize(&input).unwrap());

let actual = TWStringHelper::wrap(unsafe {
tw_ethereum_abi_function_get_signature(CoinType::Ethereum as u32, input_data.ptr())
tw_ethereum_abi_function_get_type(CoinType::Ethereum as u32, input_data.ptr())
})
.to_string()
.expect("!tw_ethereum_abi_function_get_signature returned nullptr");
.expect("!tw_ethereum_abi_function_get_type returned nullptr");

assert_eq!(actual, "baz(uint64,address)");
}
Expand Down Expand Up @@ -191,3 +191,102 @@ fn test_ethereum_abi_decode_value() {
assert!(output.error_message.is_empty());
assert_eq!(output.param_str, "42");
}

#[test]
fn test_ethereum_abi_get_function_signature() {
let abi = r#"{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}"#;

let abi_string = TWStringHelper::create(abi);

let actual = TWStringHelper::wrap(unsafe {
tw_ethereum_abi_get_function_signature(CoinType::Ethereum as u32, abi_string.ptr())
})
.to_string()
.expect("!tw_ethereum_abi_get_function_signature returned nullptr");

assert_eq!(actual, "transfer(address,uint256)");
}

#[test]
fn test_ethereum_get_function_signature_complex() {
// From: https://docs.soliditylang.org/en/latest/abi-spec.html#handling-tuple-types
let abi = r#"
{
"inputs": [
{
"components": [
{
"internalType": "uint256",
"name": "a",
"type": "uint256"
},
{
"internalType": "uint256[]",
"name": "b",
"type": "uint256[]"
},
{
"components": [
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"internalType": "struct Test.T[]",
"name": "c",
"type": "tuple[]"
}
],
"internalType": "struct Test.S",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"internalType": "struct Test.T",
"name": "",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "f",
"outputs": [],
"stateMutability": "pure",
"type": "function"
}
"#;

let abi_string = TWStringHelper::create(abi);

let actual = TWStringHelper::wrap(unsafe {
tw_ethereum_abi_get_function_signature(CoinType::Ethereum as u32, abi_string.ptr())
})
.to_string()
.expect("!tw_ethereum_abi_get_function_signature returned nullptr");

assert_eq!(
actual,
"f((uint256,uint256[],(uint256,uint256)[]),(uint256,uint256),uint256)"
);
}
25 changes: 23 additions & 2 deletions rust/wallet_core_rs/src/ffi/ethereum/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub unsafe extern "C" fn tw_ethereum_abi_decode_params(
/// \param input The serialized data of `TW.EthereumAbi.Proto.FunctionGetTypeInput`.
/// \return function type signature as a Non-null string.
#[no_mangle]
pub unsafe extern "C" fn tw_ethereum_abi_function_get_signature(
pub unsafe extern "C" fn tw_ethereum_abi_function_get_type(
coin: u32,
input: *const TWData,
) -> *mut TWString {
Expand All @@ -67,11 +67,32 @@ pub unsafe extern "C" fn tw_ethereum_abi_function_get_signature(
let evm_dispatcher = try_or_else!(evm_dispatcher(coin), || TWString::new().into_ptr());

evm_dispatcher
.get_abi_function_signature(input_data.as_slice())
.get_function_signature_from_proto(input_data.as_slice())
.map(|str| TWString::from(str).into_ptr())
.unwrap_or_else(|_| TWString::new().into_ptr())
}

/// Returns the function type signature, of the form "baz(int32,uint256)".
///
/// \param coin EVM-compatible coin type.
/// \param abi The function ABI json string, for example: {"inputs":[{"internalType":"bool","name":"arg1","type":"bool"}],"name":"fun1","outputs":[],"stateMutability":"nonpayable","type":"function"}
/// \return function type signature, null if the input is invalid.
#[no_mangle]
pub unsafe extern "C" fn tw_ethereum_abi_get_function_signature(
coin: u32,
abi: *const TWString,
) -> *mut TWString {
let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut);
let abi_string = try_or_else!(TWString::from_ptr_as_ref(abi), std::ptr::null_mut);
let abi_str = try_or_else!(abi_string.as_str(), std::ptr::null_mut);

let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut);
evm_dispatcher
.get_function_signature_from_abi(abi_str)
.map(|str| TWString::from(str).into_ptr())
.unwrap_or_else(|_| std::ptr::null_mut())
}

/// Encode function inputs to Eth ABI binary.
///
/// \param coin EVM-compatible coin type.
Expand Down
2 changes: 1 addition & 1 deletion src/Ethereum/ABI/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ std::string Function::getType() const {
*input.mutable_inputs() = inputs.params();

Rust::TWDataWrapper inputData(data(input.SerializeAsString()));
Rust::TWStringWrapper outputPtr = Rust::tw_ethereum_abi_function_get_signature(TWCoinTypeEthereum, inputData.get());
Rust::TWStringWrapper outputPtr = Rust::tw_ethereum_abi_function_get_type(TWCoinTypeEthereum, inputData.get());

return outputPtr.toStringOrDefault();
}
Expand Down
16 changes: 16 additions & 0 deletions src/interface/TWEthereumAbi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,19 @@ TWData* _Nonnull TWEthereumAbiEncodeTyped(TWString* _Nonnull messageJson) {
} catch (...) {} // return empty
return TWDataCreateWithBytes(data.data(), data.size());
}

TWString* _Nullable TWEthereumAbiGetFunctionSignature(TWString* _Nonnull abi) {
try {
const Rust::TWStringWrapper abiStr = TWStringUTF8Bytes(abi);

const Rust::TWStringWrapper outputDataPtr = Rust::tw_ethereum_abi_get_function_signature(TWCoinTypeEthereum, abiStr.get());
if (!outputDataPtr) {
return nullptr;
}

return TWStringCreateWithUTF8Bytes(outputDataPtr.c_str());
}
catch(...) {
return nullptr;
}
}
26 changes: 26 additions & 0 deletions swift/Tests/Blockchains/EthereumAbiTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,30 @@ class EthereumAbiTests: XCTestCase {
XCTAssertEqual(decodingOutput.tokens[0].name, "name")
XCTAssertEqual(decodingOutput.tokens[0].stringValue, "deadbeef")
}

func testEthereumAbiGetFunctionSignature() throws {
let abiJson = """
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
"""

let functionSignature = EthereumAbi.getFunctionSignature(abi: abiJson)
XCTAssertEqual(functionSignature, "transfer(address,uint256)")
}
}
Loading

0 comments on commit d16c923

Please sign in to comment.