Reducing boilerplate
In a previous section, we saw this example of an implementation block in a contract that didn't have any corresponding trait.
#[generate_trait]
impl InternalFunctions of InternalFunctionsTrait {
fn _store_name(
ref self: ContractState,
user: ContractAddress,
name: felt252,
registration_type: RegistrationType
) {
let mut total_names = self.total_names.read();
self.names.write(user, name);
self.registration_type.write(user, registration_type);
self.total_names.write(total_names + 1);
self.emit(StoredName { user: user, name: name });
}
}
It's not the first time that we encounter this attribute, we already talked about in it Traits in Cairo. In this section, we'll be taking a deeper look at it and see how it can be used in contracts.
Recall that in order to access the ContractState in a function, this function must be defined in an implementation block whose generic parameter is ContractState
. This implies that we first need to define a generic trait that takes a TContractState
, and then implement this trait for the ContractState
type.
But by using the #[generate_trait]
attribute, this whole process can be skipped and we can simply define the implementation block directly, without any generic parameter, and use self: ContractState
in our functions.
If we had to manually define the trait for the InternalFunctions
implementation, it would look something like this:
trait InternalFunctionsTrait<TContractState> {
fn _store_name(ref self: TContractState, user: ContractAddress, name: felt252);
}
impl InternalFunctions of InternalFunctionsTrait<ContractState> {
fn _store_name(ref self: ContractState, user: ContractAddress, name: felt252) {
let mut total_names = self.total_names.read();
self.names.write(user, name);
self.total_names.write(total_names + 1);
self.emit(Event::StoredName(StoredName { user: user, name: name }));
}
}
}