-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
refactor: fn_selector to be resolved during runtime via ParamTypes #587
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful! I like it a lot! Left some minor nits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Co-authored-by: Halil Beglerović <hal3e.git@gmail.com>
I've implemented Halil's request. Just don't know how to communciate that with github's request changes :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so good. Excellent job. This is a massive reduction in complexity.
let encoded_fn_selector = resolve_fn_selector( | ||
"some_abi_funct", | ||
&[<MyStruct1> :: param_type(), <MyStruct2> :: param_type()] | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya love to see it! 🤘
I really disliked having to maintain that hardcoded selector byte array there.
error_message | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔥
Huge weight off of our shoulders.
While implementing vector support I needed to introduce a ParamType::Vector and a Token::Vector in order to handle the encoding and decoding of a vector. They are defined as:
and
And there is an implementation of ParamType for
Vec<T>
which looks something like this:A Vector, as far as the ABI json is concerned, is defined as follows:
which means that, if you called
ParamType::from_type_declaration
on them you would have gotten aStruct([Struct(U64, u64), U64])
. This is not equal to what callingVec<T>::param_type()
would have gotten you.The param type gotten from the
from_type_declaration
is difficult to use in order to detect you're dealing with a vector when decoding/encoding. It might very well be just a structure in a structure with some U64s thown in. Our encoding/decoding code needs to differentiate vectors and that is why they are a ParamType of their own.This got me thinking about other problems caused by having two ways of constructing ParamTypes.
Ultimately I've deduced that you would call
from_type_declaration
in a context where you couldn't use the other one. And the only such place is during the expansion of theabigen!
macro where you don't actually have the code to call::param_type
on it -- you're in the process of creating it.Furthermore, ParamTypes are needed in the
abigen
because they are used to generate thefn_selector
which is then hardcoded for each of the contract methods as a bunch of bytes.If we don't resolve the function selector during the macro expansion but rather leave the methods to do that for themselves upon being called, we would then have no need for ParamTypes during the macro expansion. Then
from_type_declaration
could be removed.If ParamTypes are no longer used during macro expansion, this would mean that they could never be given an unresolved generic, since if you're generating a ParamType via
::param_type
then the Rust compiler would have already resolved all the generics for you.This has the nice consequence of not having those pesky
panic!("you cannot encode/decode/fn_sel_resolve a generic parameter"
.Furthermore, since the rust compiler resolves the generics, we can significantly simplify the process of creating the function selector. The only thing you need is the name of the function and a slice of the ParamTypes which come from the function arguments.
So the generated code for a method would look something like this:
This way all the recursive generics resolving hassle can be removed and delegated to the rust compiler.
I didn't notice any significant performance impacts of having the methods constantly resolve the fn selector on every call, but if those should ever arise we can always cache the resolved fn selector.
There were also parts of the generics resolving code in
resolve_type
which used ParamTypes only to figure out whether the current type is a struct/enum -- i.e. the rough details -- the internal fields were ignored sincefrom_type_declaration
was not capable of resolving generics. With this change the logic fromfrom_type_declaration
was merged withresolved_type
and there is no more confusion as to who resolves the generic parameters.The downside is that
json_abi.rs
had to go. It was used in the CLI fuels-rs tool which could have given you encoded parameters when provided with a json abi. Its usage is mainly to be a debugging tool for the sdk devs. I had a talk with @digorithm and he is ok with it, especially since it isn't very difficult to use the SDK to get whatever you're interested in.The CLI tools have been commented out since the new abigen format, don't hear anybody missing it too much :D
If we ever find that we need the CLI desperately, I've got a few ideas on how to have your cake and eat it too.
So now the flow is as follows
abigen -> structs + functions
structs -> param_types