Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: function call enums EthCall macro and more #517

Merged
merged 27 commits into from Oct 18, 2021

Conversation

mattsse
Copy link
Collaborator

@mattsse mattsse commented Oct 16, 2021

Motivation

Closes #507

Solution

  • Introduce EthCall trait that works similar to EthEvent
  • Add derive macro for EthCall
  • generate structs for every call with ethers trait support (EthAbitype, EthCall, EthDisplay)
  • bundle those call structs to an enum if the contract has > 1 calls
  • make EthAbiType work for enums with a single field

PR Checklist

  • Added Tests
  • Added Documentation
  • Updated the changelog

@gakonst
Copy link
Owner

gakonst commented Oct 17, 2021

For anyone following along, here's how the codegen looks like

    #[doc = "Container type for all input parameters for the `getValue`function with signature `getValue()` and selector `[32, 150, 82, 85]`"]
    #[derive(
        Clone,
        Debug,
        Default,
        Eq,
        PartialEq,
        ethers_contract :: EthCall,
        ethers_contract :: EthDisplay,
    )]
    #[ethcall(name = "getValue", abi = "getValue()")]
    pub struct GetValueCall();
    #[doc = "Container type for all input parameters for the `setValue`function with signature `setValue(string)` and selector `[147, 160, 147, 82]`"]
    #[derive(
        Clone,
        Debug,
        Default,
        Eq,
        PartialEq,
        ethers_contract :: EthCall,
        ethers_contract :: EthDisplay,
    )]
    #[ethcall(name = "setValue", abi = "setValue(string)")]
    pub struct SetValueCall {
        pub value: String,
    }
    #[derive(Debug, Clone, PartialEq, Eq, ethers_contract :: EthAbiType)]
    pub enum SimpleStorageCalls {
        GetValue(GetValueCall),
        SetValue(SetValueCall),
    }
    impl SimpleStorageCalls {
        #[doc = r" Decodes the provided ABI encoded function arguments with the selected function name."]
        pub fn decode(data: &[u8]) -> Result<Self, ethers_core::abi::Error> {
            if let Ok(decoded) = <GetValueCall as ethers_contract::EthCall>::decode(data) {
                return Ok(SimpleStorageCalls::GetValue(decoded));
            }
            if let Ok(decoded) = <SetValueCall as ethers_contract::EthCall>::decode(data) {
                return Ok(SimpleStorageCalls::SetValue(decoded));
            }
            Err(ethers_core::abi::Error::InvalidData)
        }
    }
    impl ::std::fmt::Display for SimpleStorageCalls {
        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
            match self {
                SimpleStorageCalls::GetValue(element) => element.fmt(f),
                SimpleStorageCalls::SetValue(element) => element.fmt(f),
            }
        }
    }

Copy link
Owner

@gakonst gakonst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - feel free to merge whenever. Minor comments.

Function {
name: function_call_name.clone(),
inputs,
outputs: vec![],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not have any outputs / state mutability? What about view functions for example? Maybe they can be omitted by default, but add a #[ethcall(return = "(uint256, string)"]?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rn only inputs are supported, but we should extend on that on also support response types,
will follow up with this separately.

/// }
///
/// #[derive(Debug, PartialEq, EthCall)]
/// #[ethcall(name = "foo", abi = "foo(address,(address,string),string)")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to do the overriding here, or can it be automatically detected too for nested structs?

Copy link
Collaborator Author

@mattsse mattsse Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is currently a limitation, because we don't know the ABI of the nested struct during macro expansion, and we need to know the param types for that type so we can generate the decode function body.
To mitigate this we could think of introducing a nother trait looks like 👍

 trait AbiType {
   fn type_param() -> ParamType;
}

// Example impl
impl AbiType for bool {
     fn type_param() -> ParamType {
         ParamType::Bool
   }
}

this would improve things in the EthEvent and EthAbiType macro as well

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that connecting the native` types to ParamTypes makes sense. Surprised EthABI didn't have that in the first place.

/// A helper trait for types that represent all call input parameters of a specific function
pub trait EthCall: Tokenizable + AbiDecode + Send + Sync {
/// The name of the function
fn function_name() -> Cow<'static, str>;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the advantage of returning a Cow instead of a 'static str here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently none, I think, but I wasn't sure whether this also allows others to implement calls that construct the function name dynamically, since this is a type function, &'static str should be sufficient.

@gakonst gakonst merged commit fb4d9a9 into gakonst:master Oct 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Auto impl display for Abigen Event Structs
2 participants