Errors

Errors can be used to handle validation and other conditions that may occur during the execution of a smart contract. If an error is thrown during the execution of a smart contract call, the execution is stopped and any changes made during the transaction are reverted.

Para generar un error, use las funciones assert o panic:

  • assert is used to validate conditions. If the check fails, an error is thrown along with a specified value, often a message. It's similar to the require statement in Solidity.

  • panic immediately halt the execution with the given error value. It should be used when the condition to check is complex and for internal errors. It's similar to the revert statement in Solidity. (Use panic_with_felt252 to be able to directly pass a felt252 as the error value)

Aquí hay un ejemplo simple que demuestra el uso de estas funciones:

#[starknet::interface]
trait IErrorsExample<TContractState> {
    fn test_assert(self: @TContractState, i: u256);
    fn test_panic(self: @TContractState, i: u256);
}
#[starknet::contract]
mod ErrorsExample {
    #[storage]
    struct Storage {}

    #[abi(embed_v0)]
    impl ErrorsExample of super::IErrorsExample<ContractState> {
        fn test_assert(self: @ContractState, i: u256) {
            // Assert used to validate a condition
            // and abort execution if the condition is not met
            assert(i > 0, 'i must be greater than 0');
        }

        fn test_panic(self: @ContractState, i: u256) {
            if (i == 0) {
                // Panic used to abort execution directly
                panic_with_felt252('i must not be 0');
            }
        }
    }
}

Visita el contrato en Voyager o juega con él en Remix.

Custom errors

Puede facilitar el manejo de errores definiendo sus códigos de error en un módulo específico.

mod Errors {
    const NOT_POSITIVE: felt252 = 'must be greater than 0';
    const NOT_NULL: felt252 = 'must not be null';
}

#[starknet::interface]
trait ICustomErrorsExample<TContractState> {
    fn test_assert(self: @TContractState, i: u256);
    fn test_panic(self: @TContractState, i: u256);
}

#[starknet::contract]
mod CustomErrorsExample {
    use super::Errors;

    #[storage]
    struct Storage {}

    #[abi(embed_v0)]
    impl CustomErrorsExample of super::ICustomErrorsExample<ContractState> {
        fn test_assert(self: @ContractState, i: u256) {
            assert(i > 0, Errors::NOT_POSITIVE);
        }

        fn test_panic(self: @ContractState, i: u256) {
            if (i == 0) {
                panic_with_felt252(Errors::NOT_NULL);
            }
        }
    }
}

Visita el contrato en Voyager o juega con él en Remix.

Vault example

Aquí hay otro ejemplo que demuestra el uso de errores en un contrato más complejo:

mod VaultErrors {
    const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance';
// you can define more errors here
}

#[starknet::interface]
trait IVaultErrorsExample<TContractState> {
    fn deposit(ref self: TContractState, amount: u256);
    fn withdraw(ref self: TContractState, amount: u256);
}

#[starknet::contract]
mod VaultErrorsExample {
    use super::VaultErrors;

    #[storage]
    struct Storage {
        balance: u256,
    }

    #[abi(embed_v0)]
    impl VaultErrorsExample of super::IVaultErrorsExample<ContractState> {
        fn deposit(ref self: ContractState, amount: u256) {
            let mut balance = self.balance.read();
            balance = balance + amount;
            self.balance.write(balance);
        }

        fn withdraw(ref self: ContractState, amount: u256) {
            let mut balance = self.balance.read();

            assert(balance >= amount, VaultErrors::INSUFFICIENT_BALANCE);

            // Or using panic:
            if (balance >= amount) {
                panic_with_felt252(VaultErrors::INSUFFICIENT_BALANCE);
            }

            let balance = balance - amount;

            self.balance.write(balance);
        }
    }
}

Visita el contrato en Voyager o juega con él en Remix.

Last change: 2023-10-12, commit: 90aa7c0