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

Make DecodedParams serializable #56

Open
blessedbythestorm opened this issue Mar 12, 2022 · 7 comments
Open

Make DecodedParams serializable #56

blessedbythestorm opened this issue Mar 12, 2022 · 7 comments

Comments

@blessedbythestorm
Copy link

I would like to be able to serde_json serialize/deserialize DecodedParams struct.

This would allow me to store user transaction inputs in my database.
I understand this also implicates serializing the Value variants which could potentially be a tough task.

@FelipeRosa
Copy link
Owner

Hey @limpit666,

I guess serializing and deserializing DecodedParams could be implemented only in that struct. I did some experiments here serializing/deserializing Value to its hex encoding.

So serde_json would output something like:

[
  {
    "param": {
      "name": "deadline",
      "type": "uint256"
    },
    "value": "0000000000000000000000000000000000000000000000000000000062296604"
  },
  {
    "param": {
      "name": "data",
      "type": "bytes[]"
    },
    "value": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000124b858183f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000ace4b34cbfa95d2a7850eb74e45437d64035d5800000000000000000000000000000000000000000000000000e16bb15ad46c73000000000000000000000000000000000000000000000000008672fe3d30cea30000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480027103819f64f282bf135d62168c1e513280daf905e0600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
  }
]

I'm still working on it. I think this would be a good addition to the library.

@blessedbythestorm
Copy link
Author

blessedbythestorm commented Mar 13, 2022

I'm currently serializing the values like this in my server:

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ContractInputValue {
    #[serde(skip_serializing_if = "Option::is_none")]
    uint: Option<U256>,
    #[serde(skip_serializing_if = "Option::is_none")]
    int: Option<U256>,
    #[serde(skip_serializing_if = "Option::is_none")]
    address: Option<H160>,
    #[serde(skip_serializing_if = "Option::is_none")]
    bool: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    string: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    array: Option<Vec<ContractInputValue>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    tuple: Option<HashMap<String, ContractInputValue>>,
}

...

fn retrieve_input_value(&self, value: ethereum_abi::Value) -> Option<ContractInputValue> {
        let mut out_value = ContractInputValue {
            uint: None,
            int: None,
            address: None,
            bool: None,
            string: None,
            array: None,
            tuple: None,
        };

        match value {
            Value::Uint(val, _) => { out_value.uint = Some(val) }
            Value::Int(val, _) => { out_value.int = Some(val) }
            Value::Address(val) => { out_value.address = Some(val) }
            Value::Bool(val) => { out_value.bool = Some(val) }
            Value::String(val) => { out_value.string = Some(val) }
            Value::Array(val, _type) => {
                out_value.array = Some(Vec::new());
                for array_value in val {
                    if let Some(val) = self.retrieve_input_value(array_value) {
                        if let Some(vec) = out_value.array.as_mut() {
                            vec.push(val)
                        }
                    }
                }
            }
            Value::FixedArray(val, _type) => {
                out_value.array = Some(Vec::new());
                for array_value in val {
                    if let Some(val) = self.retrieve_input_value(array_value) {
                        if let Some(vec) = out_value.array.as_mut() {
                            vec.push(val)
                        }
                    }
                }
            }
            Value::Tuple(val) => {
                out_value.tuple = Some(HashMap::new());
                for tuple in val {
                    if let Some(val) = self.retrieve_input_value(tuple.1) {
                        if let Some(map) = out_value.tuple.as_mut() {
                            map.insert(tuple.0, val);
                        }
                    }
                }
            }
            _ => { return None; }
        }

        Some(out_value)
    }

I think it's quite similar to what you did to serialize the Abi but requires getting the serialize feature for ethereum-types. Effectively the hex values for numbers show up like this:

   {
      "contract":"SwapRouter02",
      "function_signature":"multicall(uint256,bytes[])",
      "user_input":[
         {
            "name":"deadline",
            "type_":"uint256",
            "value":{
               "uint":"0x622b9578"
            }
         }
      ]
   }

Your solution would probably be better though since my values are objects in the form"value" : { "type" : value } rather than just "value": value. I would be totally fine with having the numbers as hex strings as well.

@FelipeRosa
Copy link
Owner

hey @limpit666,

just letting you know that I ran into some problems while thinking about this format and I'm still working on it (kinda slowly but still).

@blessedbythestorm
Copy link
Author

@FelipeRosa no worries at all, I've made a workaround by serializing the types on my own but this implies I am redoing what you have already done initially to decode the params. So it's just a matter of a small performance improvement for me at the moment that can be easily solved once you allow this functionality.

I've just started implementing transaction receipt handling on my server so I might come back at you if I find any new desirable features when using decode_log_from_slice().

Cheers man, thanks for all the help.

@FelipeRosa
Copy link
Owner

I've just started implementing transaction receipt handling on my server so I might come back at you if I find any new desirable features when using decode_log_from_slice().

Cool 🎉

Thanks for all the feedback :)

@fabiojundev
Copy link

Hi @FelipeRosa,

I'm trying to serialize the decode output to json and pass it back to nodejs, using Neon.
But I couldn't find a way to do it.

let ret_json = serde_json::to_string(&decoded_input).unwrap(); | --------------------- ^^^^^^^^^^^^^^ the trait serde::ser::Serializeis not implemented forDecodedParams| | | required by a bound introduced by this call

Any tips on how to do it properly?
Thanks!

@FelipeRosa
Copy link
Owner

Hey @fabiojundev,

Currently, you would have to implement something like what @limpit666 commented above.

I'm a bit short of time to work on the lib right now. Sorry for the inconvinience there.

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

No branches or pull requests

3 participants