OpenZeppelin / cairo-contracts

OpenZeppelin Contracts written in Cairo for Starknet, a decentralized ZK Rollup
https://docs.openzeppelin.com/contracts-cairo
MIT License
797 stars 320 forks source link

Add hooks usage section #979

Open andrew-fleming opened 2 months ago

andrew-fleming commented 2 months ago

It might be helpful for users not familiar with hooks to have a more formal description and example. This probably belongs in /components.adoc#customization

o-tsaruk commented 2 months ago

Hey, does someone have a simple example of ERC20HooksTrait usage?

ericnordelo commented 2 months ago

We will include a guide for them, but in the meantime we will be updating the wizard probably this week with the ERC20Votes extension, and that extension uses hooks.

andrew-fleming commented 2 months ago

Hey, @o-tsaruk. In the mean time, you can also take a look at our ERC20Votes mock for a usage example: https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/tests/mocks/erc20_votes_mocks.cairo#L63-L86

o-tsaruk commented 2 months ago

Thank you, friends! Do you know if can we use the storage variable inside ERC20HooksTrait implementation? Or it's possible only for ERC-20 storage variables?

#[storage]
struct Storage {
    #[substorage(v0)]
    erc20: ERC20Component::Storage,
    some_variable: u256,
 }
andrew-fleming commented 1 month ago

Sorry for the delay @o-tsaruk . We can definitely use a contract's storage variables inside the hooks trait. It's a different approach than with the mock example.

First, we need to remove the generic syntax of the implementation and instead pass in the contract's state (ContractState). And then we can access the state with get_contract from the HasComponent trait.

Here's an example with your struct:

impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait<ContractState> {
    fn before_update(
        ref self: ERC20Component::ComponentState<ContractState>,
        from: ContractAddress,
        recipient: ContractAddress,
        amount: u256
    ) {
        let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);
        contract_state.some_variable.write(12345);
    }

    (...)
}

S/o to @ericnordelo for the solution