Errores
- Dan información específica sobre el fallo
- Revierte los cambios aplicados para dar conciencia a la ejecución (El valor del gas cobrado por la ejecución no se devuelve a quien llama el contrato)
En la lógica de un contrato inteligente pueden ocurrir errores que debemos controlar y actuar en consecuencia ante estos escenarios.
Manejo de errores en Solidity
Solidity permite la declaración de Errores cuya función es similar a la de los eventos, con la diferencia de que estos pueden revertir los cambios de estado hechos. Sin embargo, el gas usado no es devuelto a quien generó la transacción.
La declaración de errores permite dar información más detallada sobre el error que acaba de ocurrir y generar una notificación, similar que los eventos. Los cambios previos en la transacción se revierten, por lo que no debes preocuparte si cambiaste el valor de alguna variable previamente.
// SPDX-License-Identifier: MIT pragma solidity >=0.7.0 <0.9.0; contract Event { error SendError(string message); function doSomething() public { revert SendError(""Mensaje del error""); } }
La declaración de los errores personalizables se realiza con la palabra clave error y recibe todos los parámetros que el mismo pueda necesitar. Su posterior lanzamiento se realiza con revert e invocando el error a través de su nombre.
Si lanzas un error desde Remix, verás la X roja y la información respectiva al error que acaba de ocurrir.
Combina esa característica de Solidity para desarrollar flujos en el código que te permitan controlar todos los escenarios posibles y actuar en consecuencia. Informarle a los usuarios del proyecto qué está ocurriendo en el contrato te ayudará a mejorar su experiencia de usuario interactuando con una aplicación.
Contribución creada por: Luis Enrique Herrera y Kevin Fiorentino (Platzi Contributors).
En la lógica de un contrato inteligente pueden ocurrir errores que debemos controlar y actuar en consecuencia ante estos escenarios.
Solidity permite la declaración de Errores cuya función es similar a la de los eventos, con la diferencia de que estos pueden revertir los cambios de estado hechos. Sin embargo, el gas usado no es devuelto a quien generó la transacción.
La declaración de errores permite dar información más detallada sobre el error que acaba de ocurrir y generar una notificación, similar que los eventos. Los cambios previos en la transacción se revierten, por lo que no debes preocuparte si cambiaste el valor de alguna variable previamente.
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract Event {
error SendError(string message);
function doSomething() public {
revert SendError(""Mensaje del error"");
}
}
La declaración de los errores personalizables se realiza con la palabra clave error y recibe todos los parámetros que el mismo pueda necesitar. Su posterior lanzamiento se realiza con revert e invocando el error a través de su nombre.

Si lanzas un error desde Remix, verás la X roja y la información respectiva al error que acaba de ocurrir.
Combina esa característica de Solidity para desarrollar flujos en el código que te permitan controlar todos los escenarios posibles y actuar en consecuencia. Informarle a los usuarios del proyecto qué está ocurriendo en el contrato te ayudará a mejorar su experiencia de usuario interactuando con una aplicación.
Contribución creada por: Luis Enrique Herrera y Kevin Fiorentino (Platzi Contributors).
Errores
Espero les sirva los comentarios que dejo, se los dejo con amor <3
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract platziProject{
//Aqui creamos nuestras variables
bool isFundable;
uint256 Goal;
uint256 totalFunded;
address owner;
uint256 requiredFunds;
//Inicializamoslos valores, hay que recordar que el constructor se ejecuta solo una vez cuando se crea el contrato
constructor(){
Goal = 0;
owner = msg.sender;
totalFunded = 0;
isFundable = true;
}
//No te preocupes por esto,luego loaprenderemos. El modifier permite cambiar el comppoprtamiento de funciones, ene ste caso solo queria asegurarme que solo el creador del contrato pudiera mover el Goal
modifier onlyOwner{
require(msg.sender == owner, "You need to be thhe owner from this contract to change the goal");
_;
}
//Creamos el evento el cual va a necesitar quien lo hizo y en este caso preferia que cambie la meta de fndeo
event changeGoal(
address editor,
uint256 Goal
);
//Aqui ponemos la meta a recaudar,solamente el que iniciaiza el contrato puede cambiar este valor
function setGoal(uint256 goal) public onlyOwner {
Goal = goal;
emit changeGoal(msg.sender, goal);
}
function viewGoal() public view returns(uint256) {
return Goal;
}
//Creamos el evento para notificar a los demas que el autor decidio cerrar el fondeo temporalmente
event changeState(
address editor,
bool change);
function changeProjectState(bool change)public onlyOwner{
require(isFundable != change, "Can not change the state with the actual state");
isFundable = change;
emit changeState(msg.sender, change);
}
//Aqui inicia la funcion para fondear el proyecto
function fundproject() public payable {
//Primero evaluamos si el owner del contrato mantiene abiertas las donaciones (tal vez necesita reevaluar algo)
require(isFundable, "Owner has decided to stop this fundraising for a while. Stay tuned");
//Comprobamos que el total que se ha fondeado sea menor a la meta
require(totalFunded < Goal, "Goal already achieved so you are not able to fund this anymore");
//Despues nos aeguramos que la persona mande un minimo,en este caso arriba de 0
require(msg.value != uint(0), "Please add some funds to contribuite to Platzi project");
//Comprobamos que el valor que quiere fondear no exceda con a meta que tenemos
require(totalFunded + msg.value <= Goal,"unable to add more funds, check amount remaining for our goal");
//Actualizamos el total que se ha fondeado al contrato
totalFunded += msg.value;
}
//Esta funcion nos sirve para que lla persona pueda ver cuanto se necesita para alcanzar la meta, asi no tendra que estar adivinando cuanto depositar maximo
function viewRemaining() public view returns(uint256){
uint256 remainingFunds = Goal - totalFunded;
return remainingFunds;
}
}
JESUS ! en lo que voy de cursos de blockchain me doy cuenta que el gas en ethereum es un incordio de proporciones mayores O.O ! ahora ya se de donde salieron tantísimas otras "ethereum killers"
Si yo soy una persona que quiere hacer una donación haciendo una llamada al contrato, se me cobraria gas al hacer la llamada al contrato?
Felipe es un muy buen profesor y ha logrado transmitir muy bien su conocimiento. Las clases están siendo muy productivas.
error InvalidFundOrState(
uint fundValue,
string stateProject
);
function fundProject() public payable notFundFronAuthor{
if (
msg.value > 0 &&
keccak256(abi.encodePacked(string(state))) == keccak256(abi.encodePacked('Opened'))
){
author.transfer(msg.value); // to passe value fund to the author project
funds += msg.value;
emit SendFunding(msg.sender, msg.value);
if (funds >= fundraisingGoal) {
emit RaisingFundGoal(
id,
funds,
"fundRaisingGoal reached");
}
} else {
revert InvalidFundOrState(msg.value, state);
}
}
function changeProjectState(string calldata newState) public onlyAuthorOrRaisingGoal {
require(
keccak256(abi.encodePacked(string(newState))) == keccak256(abi.encodePacked("Opened")) ||
keccak256(abi.encodePacked(string(newState))) == keccak256(abi.encodePacked("Closed")),
"Invalid new state"
);
state = newState;
emit ChangeState(id, newState);
}```
Hay alguna clase que hable de la optimizacion del gas, buenas practicas en cuanto a eso?
Hola! Tal cual muestra el video, me posicione en la primera address y cree el contrato. Luego, me posicione en la segunda address para aportar 1gw. Funciono perfecto! Lo que no me funciono es al reves, es decir: Me posiciono en la segunda address para crear un nuevo proyecto y lo hizo bien. Pero cuando me posiciono en la primer address y quiero aportar al segundo proyecto, me sale el error de que no puedo porque soy el autor del mismo, cuando el autor era la segunda address. ¿Por favor, me podrías ayudar? Gracias!!
Es una buena práctica mantener los errores fuera de la función, o está bien dejar cosas como "require" dentro? (asumiendo que no voy a reutilizarlos)
Cade vez que se hace una llamada al contrato, se cobra gas?
Daniel Buitrago
Jean Nuñez
Adolfo Sebastián Jara Gavilanes
Andres Omar Carreon Diaz
Bramucci Candela
Maximiliano Santin
Carlos Andrés Pastén Bravo
Luis Alejandro Vera Hernandez
Ulises Ávila Camargo
Fredy Daniel Flores Lemus
Andres Felipe Pardo Mesa
Jimmy Buriticá Londoño
Eber Laurente Lliuyacc
Dafne Sol Villarroel
Javier Laster
Andres Felipe Pardo Mesa
Bramucci Candela
Andres Felipe Pardo Mesa
Bramucci Candela
Fredy Daniel Flores Lemus
Andres Felipe Pardo Mesa
Pablo Campiño
Metaskins Studios
Edwin Miguel Cosio Camacho
Jean Nuñez
Jean Nuñez
Cristopher Adasme
Jesus David Nazareno Cardona
Ladislao Alex Stocki
Andres Felipe Pardo Mesa
Samir Jimenez Vivas
Andrés Muñoz
Fernando Campos
Errores
Gracias de nuevo pana
Muchas gracias
Espero les sirva los comentarios que dejo, se los dejo con amor <3
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract platziProject{ //Aqui creamos nuestras variables bool isFundable; uint256 Goal; uint256 totalFunded; address owner; uint256 requiredFunds; //Inicializamoslos valores, hay que recordar que el constructor se ejecuta solo una vez cuando se crea el contrato constructor(){ Goal = 0; owner = msg.sender; totalFunded = 0; isFundable = true; } //No te preocupes por esto,luego loaprenderemos. El modifier permite cambiar el comppoprtamiento de funciones, ene ste caso solo queria asegurarme que solo el creador del contrato pudiera mover el Goal modifier onlyOwner{ require(msg.sender == owner, "You need to be thhe owner from this contract to change the goal"); _; } //Creamos el evento el cual va a necesitar quien lo hizo y en este caso preferia que cambie la meta de fndeo event changeGoal( address editor, uint256 Goal ); //Aqui ponemos la meta a recaudar,solamente el que iniciaiza el contrato puede cambiar este valor function setGoal(uint256 goal) public onlyOwner { Goal = goal; emit changeGoal(msg.sender, goal); } function viewGoal() public view returns(uint256) { return Goal; } //Creamos el evento para notificar a los demas que el autor decidio cerrar el fondeo temporalmente event changeState( address editor, bool change); function changeProjectState(bool change)public onlyOwner{ require(isFundable != change, "Can not change the state with the actual state"); isFundable = change; emit changeState(msg.sender, change); } //Aqui inicia la funcion para fondear el proyecto function fundproject() public payable { //Primero evaluamos si el owner del contrato mantiene abiertas las donaciones (tal vez necesita reevaluar algo) require(isFundable, "Owner has decided to stop this fundraising for a while. Stay tuned"); //Comprobamos que el total que se ha fondeado sea menor a la meta require(totalFunded < Goal, "Goal already achieved so you are not able to fund this anymore"); //Despues nos aeguramos que la persona mande un minimo,en este caso arriba de 0 require(msg.value != uint(0), "Please add some funds to contribuite to Platzi project"); //Comprobamos que el valor que quiere fondear no exceda con a meta que tenemos require(totalFunded + msg.value <= Goal,"unable to add more funds, check amount remaining for our goal"); //Actualizamos el total que se ha fondeado al contrato totalFunded += msg.value; } //Esta funcion nos sirve para que lla persona pueda ver cuanto se necesita para alcanzar la meta, asi no tendra que estar adivinando cuanto depositar maximo function viewRemaining() public view returns(uint256){ uint256 remainingFunds = Goal - totalFunded; return remainingFunds; } }
Para qué sirve "uint(0)" ?
“uint(0)” se refiere a un número entero sin signo de 256 bits que tiene un valor de cero. Esta expresión se utiliza a menudo como un valor por defecto o de referencia en los contratos de Solidity cuando se necesita un valor inicial para una variable de tipo uint. También se utiliza para verificar si una variable uint tiene un valor de cero, ya que la expresión msg.value != uint(0) es verdadera si y sólo si msg.value no es cero. En resumen, uint(0) se utiliza como una forma de inicializar o verificar valores de tipo uint.
JESUS ! en lo que voy de cursos de blockchain me doy cuenta que el gas en ethereum es un incordio de proporciones mayores O.O ! ahora ya se de donde salieron tantísimas otras "ethereum killers"
Jajaj la plena xD
No, solo es aprender a manejar complejidad que en sí, es un tema avanzado para los programadores.
Si yo soy una persona que quiere hacer una donación haciendo una llamada al contrato, se me cobraria gas al hacer la llamada al contrato?
Si, la persona que quiere hacer la donación tendría que tener esto en cuenta, pues cada llamada a la blockchain va a tener un costo en gas
Felipe es un muy buen profesor y ha logrado transmitir muy bien su conocimiento. Las clases están siendo muy productivas.
Cumpliendo el reto
Validación de aporte "0" y tratar de dar aporte con estado "Closed"
error InvalidFundOrState( uint fundValue, string stateProject ); function fundProject() public payable notFundFronAuthor{ if ( msg.value > 0 && keccak256(abi.encodePacked(string(state))) == keccak256(abi.encodePacked('Opened')) ){ author.transfer(msg.value); // to passe value fund to the author project funds += msg.value; emit SendFunding(msg.sender, msg.value); if (funds >= fundraisingGoal) { emit RaisingFundGoal( id, funds, "fundRaisingGoal reached"); } } else { revert InvalidFundOrState(msg.value, state); } }
Validación de cambio de estado solo permitido a "Opened" y "Closed"
function changeProjectState(string calldata newState) public onlyAuthorOrRaisingGoal { require( keccak256(abi.encodePacked(string(newState))) == keccak256(abi.encodePacked("Opened")) || keccak256(abi.encodePacked(string(newState))) == keccak256(abi.encodePacked("Closed")), "Invalid new state" ); state = newState; emit ChangeState(id, newState); }```
Hay alguna clase que hable de la optimizacion del gas, buenas practicas en cuanto a eso?
Hola! Tal cual muestra el video, me posicione en la primera address y cree el contrato. Luego, me posicione en la segunda address para aportar 1gw. Funciono perfecto! Lo que no me funciono es al reves, es decir: Me posiciono en la segunda address para crear un nuevo proyecto y lo hizo bien. Pero cuando me posiciono en la primer address y quiero aportar al segundo proyecto, me sale el error de que no puedo porque soy el autor del mismo, cuando el autor era la segunda address. ¿Por favor, me podrías ayudar? Gracias!!
Hola Javier asegúrate de estar interactuando con el contrato que es, si quieres volver a desplegar el contrato desde otra wallet, elimina el que ya esta desplegado para evitar confusiones
Es una buena práctica mantener los errores fuera de la función, o está bien dejar cosas como "require" dentro? (asumiendo que no voy a reutilizarlos)
El require generalmente se usa para hacer validaciones al inicio de una función, si dentro de la ejecución de las funciones hay un error recurrente puedes definirlo fuera de la función y utilizarlo donde se requiera
Ok, gracias!!
Cade vez que se hace una llamada al contrato, se cobra gas?
Así es Fredy
RETO 4: Observación: Para comparar strings encontré que se genera un hash con: keccak256(abi.encodePacked(str))
// SPDX-License-Identifier: GPL-3.0 pragma solidity >= 0.7.0 <0.9.0; contract projectFundPlatzi{ string public name; bool public isFundable; uint public deposits; uint public depositsGoal; uint private depositTest; address private autor; string public state = "Open"; string private tokenLog = "Log: project status changed"; string private tokenFeedback = "Feedback: Gracias"; constructor( string memory _name ){ name = _name; isFundable = true; deposits = 0; depositsGoal=1000; autor = msg.sender; } error stateNotDefined (string newState); event newFundFeedback( address autor, uint256 deposit, string Feedback ); event newFundLog( uint256 depositsGoal, uint256 deposits, string Log ); modifier notOnlyOwnerPay{ require (autor != msg.sender, "El propietario no puede abonar fondos al proyecto" ); //la funcion es insertada donde aparece este simbolo _ _; } modifier ownerRequired { require (msg.sender == autor, "Esta accion es restringida al propietario."); _; } function viewDeposits() public view returns (uint){ return deposits; } function viewRemaining() public view returns(uint256){ return depositsGoal - deposits; } function fundProject(address payable benefactor) public payable notOnlyOwnerPay { require (msg.value > 0, "No se acepta abonos sin valor."); depositTest = deposits + msg.value; if (isFundable == true && depositTest <= depositsGoal) { benefactor.transfer(msg.value); deposits += msg.value; emit newFundFeedback (autor, msg.value,tokenFeedback); } changeProjectState("Closed"); } function changeProjectState(string memory _newState) public { require (keccak256(abi.encodePacked(_newState)) != keccak256(abi.encodePacked(state)), "El nombre de estado es igual al actual y no es actualizable"); if (keccak256(abi.encodePacked(_newState)) == keccak256(abi.encodePacked("Open"))){ isFundable = true; state = "Open"; }else if (keccak256(abi.encodePacked(_newState)) == keccak256(abi.encodePacked("Closed"))){ isFundable = false; state = "Closed"; } else { revert stateNotDefined (_newState); } isFundable = !isFundable; emit newFundLog (depositsGoal, deposits, tokenLog); } }
Este es mi avance con los errores programados con el reto #4:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Proyecto_Platzi{ string public projectName; string public projectOwner; uint public projectState = 0; address payable ownerWallet; uint public goal; uint public currentFund; error SameStateChange(uint state); error StateClosed(string state); error FundZero(uint fundGiven); constructor(string memory _projectName, string memory _projectOwner, uint _goal){ projectName = _projectName; projectOwner = _projectOwner; ownerWallet = payable(msg.sender); goal = _goal; } modifier onlyOwner() { require( msg.sender == ownerWallet, "Only owner can change the project state" ); //la función es insertada en donde aparece este simbolo _; } modifier differentToTheOwner() { require( msg.sender != ownerWallet, "The owner can't fund the project" ); //la función es insertada en donde aparece este simbolo _; } event ChangeState( uint previousState, uint newState ); event FundValueGiven( uint fundGiven, uint valueToGoal, string greetingMessage ); function fundProject() public payable differentToTheOwner{ if(projectState == 0){ if(msg.value != 0){ ownerWallet.transfer(msg.value); currentFund += msg.value; emit FundValueGiven(msg.value, (goal - msg.value), "Thanks for your contribution"); } else if(msg.value == 0){ revert FundZero(msg.value); } } else if(projectState == 1){ revert StateClosed("Sorry, this project is closed"); } } function changeProjectState(uint newState) public onlyOwner{ if(msg.sender == ownerWallet){ if(newState != projectState){ emit ChangeState(projectState, newState); projectState = newState; } else{ revert SameStateChange(newState); } } } }
Reto #4
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.0; contract CrowdFunding { string id; string projectName; uint targetAmount; uint amountFunded; bool isFundable; address payable authorAddress; // Events event projectFunded( address sender, uint amount ); event projectStateChanged ( string projectName, string message ); // ./Events constructor(string memory _id, string memory _projectName, uint _targetAmount){ id = _id; projectName = _projectName; targetAmount = _targetAmount; authorAddress = payable(msg.sender); amountFunded = 0; isFundable = true; } function fundProject() public payable isNotAuthor canFund { require(msg.value > 0, 'The funded amount can not be 0'); authorAddress.transfer(msg.value); amountFunded += msg.value; emit projectFunded(msg.sender, msg.value); } function changeProjectState(string calldata newState) public isAuthor { string memory currentState = isFundable ? string('opened') : string('closed'); require(keccak256(abi.encode(newState)) == keccak256(abi.encode('opened')) || keccak256(abi.encode(newState)) == keccak256(abi.encode('closed')), 'This state is not defined'); require(keccak256(abi.encode(newState)) != keccak256(abi.encode(currentState)), string(abi.encodePacked('This project is already ', currentState ))); if(keccak256(abi.encode(newState)) == keccak256(abi.encode('opened'))){ isFundable = true; } else if(keccak256(abi.encode(newState)) == keccak256(abi.encode('closed'))) { isFundable = false; } emit projectStateChanged(projectName, isFundable ? 'Project opened' : 'Project closed'); } // Function modifiers modifier isAuthor(){ require(authorAddress == msg.sender, "You must be the project author!"); _; } modifier isNotAuthor() { require(authorAddress != msg.sender, "As author you can not fund your own project!"); _; } modifier canFund(){ require(isFundable == true, "This project is not available for funding!"); _; } // ./Function modifiers function getGoal() public view returns(uint){ return targetAmount; } function getFunds() public view returns(uint){ return amountFunded; } function getStatus() public view returns(string memory){ return isFundable ? 'Opened': 'Closed'; } }
cuando nos muestra un error emite un JSON con el field StateNotDefined
para lanzar el error usamos revert
Aplicando errores en el reto #4
// SPDX-License-Identifier: MIT pragma solidity >=0.7.0 <0.9.0; contract CrowdFunding { string projectName; string projectDescription; uint256 goal; uint projectStatus; uint currentFunds; address payable ownerWallet; address owner; error statusIsCosed(uint projectStatus); error errorZeroFunds(uint amount); constructor(string memory _projectName, string memory _projectDescription, uint256 _goal) { projectName = _projectName; projectDescription = _projectDescription; goal = _goal; currentFunds = 0; // Owner Information ownerWallet = payable(msg.sender); owner = msg.sender; // Project Status Initialization // Status 0 = "Opened" // Status 1 = "Closed" projectStatus = 0; } function fundProject() public payable notOwner onlyActiveProjects { if (msg.value == 0) { revert errorZeroFunds(msg.value); } ownerWallet.transfer(msg.value); currentFunds += msg.value; emit NewFundNotification(msg.sender, msg.value); } function changeProjectStatus(uint newStatus) public onlyOwner { require(projectStatus != newStatus, "Project has that state already, choose another!"); projectStatus = newStatus; emit NewStatusChange(newStatus); } event NewFundNotification( address sender, uint fundAmount ); event NewStatusChange( uint newStatus ); modifier onlyActiveProjects() { if (projectStatus == 1) { revert statusIsCosed(projectStatus); } _; } modifier notOwner() { require(owner != msg.sender, "Owners shouldnt send funds to its own projects!"); _; } modifier onlyOwner() { require(owner == msg.sender, "You must be the project owner!"); _; } function getGoal() public view returns(uint) { return goal; } function getStatus() public view returns(uint) { return projectStatus; } function getFunds() public view returns (uint) { return currentFunds; } }
Reto clase anterior
pragma solidity ^0.7.0; contract KickstarterPoor { string name; string description; string state = "opened"; uint256 goal; uint256 funds; address private owner; address payable creatorPayable; constructor(string memory _name, string memory _description, uint256 _goal) payable{ name = _name; description = _description; owner = msg.sender; creatorPayable = payable(owner); goal = _goal; funds = 0; } event newFunds( address sender, uint256 funds ); event projectState( uint256 goal, uint256 funds, string state ); function checkStatus () public view returns (string memory){ return state; } function getFunds() public view returns (uint256){ return funds; } modifier noFundOwner (){ require(msg.sender != owner, "You can't donate to yourself, I'll call the police."); _; } modifier onlyOwner (){ require(msg.sender == owner, "You are not admin, go away"); _; } function FundProyect() public payable noFundOwner{ creatorPayable.transfer(msg.value); funds += msg.value; emit newFunds(msg.sender,msg.value); } function changeProjectState(string calldata newState) public onlyOwner{ state = newState; emit projectState(goal,funds,state); } }
No estaría viendo dónde está mi error porque creo los eventos y los aplico en las funciones correspondientes, pero no veo los logs lanzados a la hora de compilar y ejecutarlas.
Revisa en la info de la transacción
Una cosa no me quedó clara. ¿Cuándo es mejor usar error y cuando es mejor usar require?
Cuando se hace revert la blockchain vuelve al estado original antes de correrse, como una especie del rollBack ?
Hola 👋🏼
Sí, con revert() se eliminan todos las cambios que haya generado el smart contract hasta ese momento.