Contract interfaces and Traits generation

Las interfaces de contrato definen la estructura y el comportamiento de un contrato y sirven como la ABI pública del contrato. Enumeran todas las firmas de funciones que expone un contrato. Para obtener una explicación detallada de las interfaces, puede consultar el Cairo Book.

En Cairo, para especificar la interfaz es necesario definir un trait anotado con #[starknet::interface] y luego implementar ese trait en el contrato.

Cuando una función necesita acceder al estado del contrato, debe tener un parámetro self de tipo ContractState. Esto implica que la firma de función correspondiente en el trait de interfaz también debe tomar un tipo TContractState como parámetro. Es importante tener en cuenta que todas las funciones de la interfaz del contrato deben tener este parámetro self de tipo TContractState.

Puedes utilizar el atributo #[generate_trait] para generar implícitamente el trait para un bloque de implementación específico. Este atributo genera automáticamente un trait con las mismas funciones que las del bloque implementado, sustituyendo el parámetro self por un parámetro genérico TContractState. Sin embargo, tendrás que anotar el bloque con el atributo #[abi(per_item)], y cada función con el atributo apropiado dependiendo de si es una función externa, un constructor o un manejador l1.

En resumen, hay dos formas de manejar las interfaces:

  • Explicitly, by defining a trait annoted with #[starknet::interface]
  • Implicitly, by using #[generate_trait] combined with the #[abi(per_item)]` attributes, and annotating each function inside the implementation block with the appropriate attribute.

Explicit interface

#[starknet::interface]
trait IExplicitInterfaceContract<TContractState> {
    fn get_value(self: @TContractState) -> u32;
    fn set_value(ref self: TContractState, value: u32);
}

#[starknet::contract]
mod ExplicitInterfaceContract {
    #[storage]
    struct Storage {
        value: u32
    }

    #[abi(embed_v0)]
    impl ExplicitInterfaceContract of super::IExplicitInterfaceContract<ContractState> {
        fn get_value(self: @ContractState) -> u32 {
            self.value.read()
        }

        fn set_value(ref self: ContractState, value: u32) {
            self.value.write(value);
        }
    }
}

Juega con este contrato en Remix.

Implicit interface

#[starknet::contract]
mod ImplicitInterfaceContract {
    #[storage]
    struct Storage {
        value: u32
    }

    #[abi(per_item)]
    #[generate_trait]
    impl ImplicitInterfaceContract of IImplicitInterfaceContract {
        #[external(v0)]
        fn get_value(self: @ContractState) -> u32 {
            self.value.read()
        }

        #[external(v0)]
        fn set_value(ref self: ContractState, value: u32) {
            self.value.write(value);
        }
    }
}

Juega con este contrato en Remix.

Note: You can import an implicitly generated contract interface with use contract::{GeneratedContractInterface}. However, the Dispatcher will not be generated automatically.

Internal functions

You can also use #[generate_trait] for your internal functions. Since this trait is generated in the context of the contract, you can define pure functions as well (functions without the self parameter).

#[starknet::interface]
trait IImplicitInternalContract<TContractState> {
    fn add(ref self: TContractState, nb: u32);
    fn get_value(self: @TContractState) -> u32;
    fn get_const(self: @TContractState) -> u32;
}

#[starknet::contract]
mod ImplicitInternalContract {
    #[storage]
    struct Storage {
        value: u32
    }

    #[generate_trait]
    impl InternalFunctions of InternalFunctionsTrait {
        fn set_value(ref self: ContractState, value: u32) {
            self.value.write(value);
        }
        fn get_const() -> u32 {
            42
        }
    }

    #[constructor]
    fn constructor(ref self: ContractState) {
        self.set_value(0);
    }

    #[abi(embed_v0)]
    impl ImplicitInternalContract of super::IImplicitInternalContract<ContractState> {
        fn add(ref self: ContractState, nb: u32) {
            self.set_value(self.value.read() + nb);
        }
        fn get_value(self: @ContractState) -> u32 {
            self.value.read()
        }
        fn get_const(self: @ContractState) -> u32 {
            self.get_const()
        }
    }
}

Juega con este contrato en Remix.

Last change: 2023-11-26, commit: 35ec815