-
Notifications
You must be signed in to change notification settings - Fork 432
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
New metadata_name
attribute
#859
New metadata_name
attribute
#859
Conversation
…onstructors and impl sections. This attribute allows to specify the which name is use during generation of metadata(ABI) for messages, and constructors.
Note that with the redesigned trait system it will no longer be possible to change selectors of trait implementations but instead trait definitions will allow to customize selectors of trait methods. Also selectors between trait definitions with equally named methods only conflict if the traits are named the same and have the same syntactical structure. |
Where I can read about the redesigned trait system?=)
Yes. ink! doesn't allow injecting code of one contract to another. And the solution is to implement some logic on the rust level and reuse this implementation in the public trait. In this case methods in internal implementation will have the same signature in public trait. |
Fixed! I added support for external contributions to the Waterfall CI. |
@xgreenx Thanks for the PR, the main issue I have with it is that I think this functionality:
is already covered by the So for
and then modify the generated ABI. For example, you could modify the So those What do you think? |
I agree with you, that it can be changed manually and it is only UI sugar. But we can add this feature to change it automatically=) For example, we are using this feature in our library. Our macro will use this feature to create two traits, the first on rust level, the second on ink! level. For example, user defines this trait: #[brush::trait_definition]
pub trait Erc20 {
/// Returns the total supply.
#[ink(message)]
fn total_supply(&self) -> Balance;
/// Transfers `amount` from caller to `to`.
#[ink(message)]
fn transfer(&mut self, to: AccountId, amount: Balance) {
// Some logic here
}
} Macro will generate two traits: // This trait on rust level
pub trait Erc20 {
fn total_supply(&self) -> Balance;
fn transfer(&mut self, to: AccountId, amount: Balance) {
// Some logic here
}
}
// This trait on ink! level
#[ink_lang::trait_definition]
pub trait Erc20External {
/// Returns the total supply.
#[ink(message)]
fn total_supply_external(&self) -> Balance;
/// Transfers `amount` from caller to `to`.
#[ink(message)]
fn transfer_external(&mut self, to: AccountId, amount: Balance);
} After that, another // This implementation on rust level
impl Erc20 for Erc20Struct {
fn total_supply(&self) -> Balance { 0 }
}
// This implementation on ink! level
#[ink(metadata_name = "Erc20")]
impl Erc20External for Erc20Struct {
#[ink(message)]
// 0x3ef71755 - is identifier for "Erc20::total_supply"
#[ink(selector = "0x3ef71755", metadata_name = "total_supply")]
fn total_supply_external(&self) -> Balance {
Erc20::total_supply(self)
}
#[ink(message)]
// 0x46607e68 - is identifier for "Erc20::transfer"
#[ink(selector = "0x46607e68", metadata_name = "transfer")]
fn transfer_external(&mut self, to: AccountId, amount: Balance) {
Erc20:: transfer(self, to, amount)
}
} And the user doesn't know anything about how it works inside and it allows us to generate the same ABI, but adds additional functionality. |
@xgreenx What I don't understand in the example which you used is why the Your code looks fine, the reason why we're so cautious about merging this issue is because it ties the metadata much closer into the language. Right now, ink! doesn't have any attributes that expose control about mutating the metadata inside the language. We are currently more on the cautious side regarding this and would rather like to help you solve your issue in a different manner. |
I will try to describe why we need two traits and At the moment // This trait on rust level
pub trait Erc20Internal/Erc20 {
fn total_supply(&self) -> Balance { 0 }
fn transfer(&mut self, to: AccountId, amount: Balance) {
// Some logic here
}
}
// This trait on ink! level
#[ink_lang::trait_definition]
pub trait Erc20/Erc20External {
/// Returns the total supply.
#[ink(message)]
fn total_supply(&self) -> Balance;
/// Transfers `amount` from caller to `to`.
#[ink(message)]
fn transfer(&mut self, to: AccountId, amount: Balance);
} And later it will require us to write a boilerplate code to call methods from rust trait in ink! trait(But we need ink! trait here ONLY to generate metadata and create external dispatchers which will call implementation from internal trait). // This implementation on rust level
impl Erc20Internal for Erc20Struct {
// we override this method
fn total_supply(&self) -> Balance { 1 }
}
// This implementation on ink! level
impl Erc20 for Erc20Struct {
#[ink(message)]
fn total_supply(&self) -> Balance {
Erc20Internal::total_supply(self)
}
#[ink(message)]
fn transfer(&mut self, to: AccountId, amount: Balance) {
Erc20Internal::transfer(self, to, amount)
}
}
...
impl Erc20Struct {
#[ink(message)]
fn temp(&mut self) -> Balance {
// And here the user must specify which method he want to use
// But Erc20 trait only is used for metadata and dispatches so it is a helper trait.
Erc20Internal::total_supply(self)
}
} The usage of the same naming for methods in both traits will require the user to explicitly specify the trait during calls of methods. We want to simplify the usage of traits with our library. The user will be able to operate only with one trait You can check how it looks like here. The developer only must specify during trait declaration which methods must be public(or another ink! stuff like The combination of |
@xgreenx Thanks for the explanation. So we decided that we currently don't want to include a new attribute which can influence the metadata directly. The reasoning is:
So overall we think these implications outweigh the benefits. Sorry for the work you've put into the PR! Until the trait support is improved: are there other solutions to your problem which you are aware of? I still think that it should be possible to achieve this naming through namespacing. |
@cmichi Thanks for your response!=)
Pretty sad=( We don't want to use/support our custom version of ink! =D
It turns out pretty unfair that we don't have possibilities to affect the metadata. The metadata generation process affects the ink! very strong. It forces to create a lot of restrictions like:
I'm sure, that answering the question "How to aggregate metadata from parent and child contracts?" will resolve the restrictions above.
I agree with you, that it can take a part of the resources in the future. But the idea of this attribute is simple than other attributes, so maybe it has a chance to be alive=)
Yes, I'm afraid of that too. Because it is necessary to acquaint the user with the concept of metadata. It will be cool to move this attribute under "For experts" sections or something like this=)
Yes, at the moment it is used as a workaround. This attribute can be removed after redesigned trait implementation. @Robbepop mentioned above that you also want to remove the
No, we tried to find another solution, but seems it is the best. The original trait defined by the user is on rust level, and the user can use it everywhere across the project like simple rust trait and it will fit well. Additional trait generated by macro only is used for metadata generation(and dispatchers). The We can't use the same naming for the original trait and the same naming for the trait to generate metadata. To generate the right metadata we MUST use the original naming. It means that we need to change the naming in trait on rust level, but it will break an illusion that the user is working with a simple rust trait because he must use another trait and another naming of methods(or explicitly use the right trait). BTW, Where I can read about redesigned traits? We want to help with the implementation of it, but we need to know what you want to do and your thought on how it can be implemented. |
I'm reluctantly closing this PR after some additional private discussion. The reasons are outlined in my comment above. tl;dr is that we intend to solve the root issue (proper trait support) which will make this PR obsolete. In general we could think about renaming Just as a sidenote: The |
Added support of a new attribute
metadata_name
for ink! messages, constructors, and impl sections.This attribute allows specifying which name to use during the generation of metadata(ABI) for messages, and constructors.
This attribute can be used for cases if you want to use another naming of methods or traits on rust level, but you still want to implement some trait(generate the same ABI).
Example:
You have a trait of some standard(let's call it
Erc20
).But you already have methods
total_supply
andtransfer
for another implementation. During compilation, it will cause an error where you need to specify which implementation you want to use(for example you need to writeErc20::total_supply(self)
).In this case combination of
selector
andmetadata_name
allow using another trait and another naming of function but still generate the same ABI.Generated ABI for first trait and for second will be the same