Recentemente, tenho revisado o Solidity para reforçar alguns detalhes e estou escrevendo um "Guia Simples do Solidity" para iniciantes (os experts em programação podem procurar outros tutoriais). Atualizo de 1 a 3 lições por semana.
Twitter: @0xAA_Science
Comunidade: Discord | Grupo do WeChat | Site da WTF Academy
Todo código e tutorial estão disponíveis no GitHub: github.com/AmazingAng/WTF-Solidity
Nesta lição, vamos falar sobre os três métodos para lançar exceções no Solidity: error
, require
e assert
, e comparar o consumo de gas
entre eles.
Ao escrever contratos inteligentes, é comum encontrar bugs. Os comandos de exceção do Solidity nos ajudam a fazer depuração.
error
é um recurso adicionado na versão 0.8.21 do Solidity, que permite explicar de forma eficiente (e econômica em termos de gas
) o motivo de uma operação falhar. É possível definir exceções fora dos contratos. Abaixo, definimos a exceção TransferNotOwner
, que é lançada quando alguém que não é o dono da moeda tenta fazer uma transferência:
error TransferNotOwner(); // Erro personalizado
Também podemos definir uma exceção com parâmetros, para mostrar o endereço que tentou fazer a transferência:
error TransferNotOwner(address sender); // Erro personalizado com parâmetros
Durante a execução, o error
deve ser usado em conjunto com o comando revert
:
function transferOwner1(uint256 tokenId, address newOwner) public {
if(_owners[tokenId] != msg.sender){
revert TransferNotOwner();
// revert TransferNotOwner(msg.sender);
}
_owners[tokenId] = newOwner;
}
Definimos a função transferOwner1()
, que verifica se o remetente é o dono da moeda. Se não for, uma exceção de TransferNotOwner
é lançada; se for, a transferência é feita.
O comando require
é uma maneira comum de lançar exceções antes da versão 0.8 do Solidity e ainda é amplamente utilizado. A desvantagem do require
é que o consumo de gas
aumenta com o tamanho da descrição da exceção, em comparação com o comando error
. A sintaxe é: require(condição de verificação, "descrição da exceção")
. Quando a condição não é atendida, uma exceção é lançada.
Podemos reescrever a função transferOwner1
usando o comando require
:
function transferOwner2(uint256 tokenId, address newOwner) public {
require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
_owners[tokenId] = newOwner;
}
O comando assert
geralmente é usado pelos programadores durante a depuração do código, pois não fornece uma explicação do motivo da exceção (ao contrário do require
). A sintaxe é simples: assert(condição)
. Se a condição não for atendida, uma exceção é lançada.
Podemos reescrever a função transferOwner1
usando o comando assert
:
function transferOwner3(uint256 tokenId, address newOwner) public {
assert(_owners[tokenId] == msg.sender);
_owners[tokenId] = newOwner;
}
-
Insira um número
uint256
qualquer e um endereço não nulo, chame a funçãotransferOwner1
(comerror
), e no console do Remix será exibida a nossa exceção customizadaTransferNotOwner
. -
Insira um número
uint256
qualquer e um endereço não nulo, chame a funçãotransferOwner2
(comrequire
), e no console do Remix será exibida a descrição da exceção do comandorequire
. -
Insira um número
uint256
qualquer e um endereço não nulo, chame a funçãotransferOwner3
(comassert
), e apenas a exceção será lançada no console do Remix.
Comparamos o consumo de gas
dos três métodos de exceção no Remix, usando o botão de Debug do console, encontramos os seguintes valores:
(compilado com a versão 0.8.17)
- Consumo de
gas
do métodoerror
: 24457 (Com parâmetro, consumo degas
: 24660) - Consumo de
gas
do métodorequire
: 24755 - Consumo de
gas
do métodoassert
: 24473
Podemos observar que o método error
é o que consome menos gas
, seguido pelo assert
, e o require
consome mais gas
. Portanto, o error
é uma boa opção, pois informa o motivo da exceção e consome menos gas
.
Observação: Antes da versão 0.8.0 do Solidity, o assert
lançava uma panic exception
que consumia todo o gas
restante, sem reembolso. Mais detalhes podem ser encontrados na documentação oficial.
Nesta lição, aprendemos sobre os três métodos para lançar exceções no Solidity: error
, require
e assert
, e comparamos o consumo de gas
entre eles. Concluímos que o error
é uma opção eficiente, pois fornece explicação da exceção e consome menos gas
.