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)
¿Qué son los Smart Contracts?
Qué aprenderás de Smart Contracts y Blockchain
¿Qué es un Smart Contract?
Proyecto del curso: contrato para crowdfunding
Ethereum
EVM (Ethereum Virtual Machine)
Solidity
¿Qué es Solidity?
Estructura de un contrato en Solidity
Variables de estado y variables globales
Estructuras de control
Funciones
Memory, storage y call data
Modificadores de funciones
Eventos
Errores
Struct types
Enum types
Arrays y mappings
Terminando nuestro contrato
Desplegando nuestro smart contract
Cómo desplegar nuestro contrato en Ropsten
Cómo desplegar nuestro contrato en Truffle
Conclusión y siguientes pasos
Conclusión
Aún no tienes acceso a esta clase
Crea una cuenta y continúa viendo este curso
Aportes 20
Preguntas 7
Errores
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”
Espero les sirva los comentarios que dejo, se los dejo con amor ❤️
// 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;
}
}
Felipe es un muy buen profesor y ha logrado transmitir muy bien su conocimiento. Las clases están siendo muy productivas.
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);
}
}
Funcion de los errores
**Esto es en parte aportes, código del curso y una pequeña parte de código propio espero que les sirva **
<// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract CrowFounding {
string public id;
string public name;
string public description;
address payable public author;
string public state = "Opened";
uint public funds;
uint public fundraisingGoal;
// constructor (solo se ejecutal al momento del despliegue)
constructor(
string memory _id,
string memory _name,
string memory _description,
uint _fundraisingGoal) {
id = _id;
name = _name;
description = _description;
fundraisingGoal = _fundraisingGoal;
author = payable(msg.sender);
}
// controlo q solo el dueño pueda cambiar el estado
modifier onlyOwner() {
require(msg.sender == author, "Only owner can change the project state.");
//la función es insertada en donde aparece este símbolo
_;
}
// controlo q el dueño no pueda aportar
modifier notOwner() {
require(msg.sender != author, "Owner cannot add to the proyect.");
//la función es insertada en donde aparece este símbolo
_;
}
event depositedAmount(
address contributor,
string projectId,
uint amount
);
event projectStateChange(
string id,
string state
);
// funcion para aportar
function fundProject() public payable notOwner {
author.transfer(msg.value);
funds += msg.value;
emit depositedAmount(msg.sender, id , msg.value);
}
// funcion para cambiar el estado del proyecto
function changeProjectState(string calldata newState) public onlyOwner {
state = newState;
emit projectStateChange( id, newState );
}
}
>
Para el reto ya tenia el contrato de fondeo con varios require
de esta manera:
//IF the project is still alive
require(Isfondeable, "The project is not longer available");
//If the goal is not reach yet
require(totalFunded < Goal, "The Goal is already reach");
//Check valid amount
require(msg.value != uint(0), "Please add a amount to contribute to the project");
//Check not exceed the goal
require(totalFunded + msg.value <= Goal, "unable to add more funds, check amount remaining for our goal");
Pero por tema del reto, agregue una validación con un if
y error
en el cambio de estado:
function ChangeProjectState(bool change) public OnlyOwner {
if(Isfondeable == change) {
revert IsSameState(change, "Can't change the state to same status");
}
Isfondeable = change;
emit ProjectStateEvent(change, id);
}
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);
}
}
aporto mi ejemplo al reto 4
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract CrowdFunding {
// variables (q uso en el contructor)
string public id;
string public name;
string public description;
address payable public author;
//variables ....q no uso en el costructor
//string public state = "Opened"; 0 open 1 close
uint public state = 0; // 0 open 1 close
uint256 public funds;
uint256 public fundraisingGoal;
// evento para saber quien aporto y cuanto queda
event ChangeFound(
address editor,
uint256 fundRestante
);
// constructor (solo se ejecutal al momento del despliegue)
constructor(
string memory _id,
string memory _name,
string memory _description,
uint256 _fundraisingGoal
) {
id = _id;
name = _name;
description = _description;
fundraisingGoal = _fundraisingGoal;
author = payable(msg.sender);
}
// controlo q solo el dueño pueda cambiar el estado
modifier onlyOwner() {
require(msg.sender == author, "Only owner can change the project state.");
//la función es insertada en donde aparece este símbolo
_;
}
// controlo q el dueño no pueda aportar
modifier notOwner() {
require(msg.sender != author, "Owner cannot add to the proyect.");
//la función es insertada en donde aparece este símbolo
_;
}
// funcion para aportar
function fundProject() public payable notOwner {
require(state == 0, "Proyect is closed");
require(msg.value > 0, "Not mouse acepted.");
author.transfer(msg.value);
funds += msg.value;
emit ChangeFound(msg.sender, fundraisingGoal - funds);
}
// funcion para cambiar el estado del proyecto
//function changeProjectState(string calldata newState) public onlyOwner {
function changeProjectState(uint newState) public onlyOwner {
require(newState == 0 || newState == 1, "Value not defined");
state = newState;
}
}
Me encantan estas clases, aunque al principio era un poco confuso la sintaxis de sollidity, ya me voy familiarizando de a poco
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract CrowdFunding2 {
string public id;
string public name;
string public description;
address payable public author;
uint256 public state = 0;
uint256 public funds;
uint256 public fundraisingGoal;
constructor(
string memory _id,
string memory _name,
string memory _description,
uint256 _fundraisingGoal
) {
id = _id;
name = _name;
description = _description;
fundraisingGoal = _fundraisingGoal;
author = payable(msg.sender);
}
modifier onlyOwner(){
require(msg.sender == author,"Just the owner can do this action");
_;
}
modifier excludeOwner(){
require(msg.sender != author,"Owner can not do this action");
_;
}
event projectFunded(uint256 lastfunds, uint256 income);
event stateChanged(address editor, uint256 newState);
error NoZeroFunds(uint256 unit);
function fundProject() public payable excludeOwner {
if(state == 1){
require(state == 1, "The contract is already closed, funds are not acepted");
}else if (state == 0){
if (msg.value == 0){
revert NoZeroFunds(msg.value);
}
author.transfer(msg.value);
funds += msg.value;
emit projectFunded(funds-msg.value, msg.value);
}
}
function changeProjectState(uint256 newState) public onlyOwner {
require(newState != state, "The contract is already in that state");
state = newState;
emit stateChanged(msg.sender, state);
}
}
Mi solución al reto #4 😃
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract crowfundingContract {
//Definir los datos iniciales de quien despliegue el contrato (i.e. el que empieza su crowfunding con la
//estructura de este contrato)
string public id;
string public projectName;
string public description;
string public status = "Opened";
uint public statusID = 0;
address payable public author;
uint public fundingGoal;
uint public funds;
event fundedBy (
string project,
address participant,
uint vulue
);
event changedBy (
string project,
address author,
string newStatus
);
// reto#4
error noFundSelected (uint funds, string err);
error statusAlreadySelected (uint status);
error notAnOption (uint status);
error projectClosed (string err);
// reto#4
//Crear el constructor que inicializa variables relevantes a quién inicie un corwfunding
constructor(string memory _id, string memory _projectName, string memory _description, uint _fundingGoal){
author = payable(msg.sender);
id = _id;
projectName = _projectName;
description = _description;
fundingGoal = _fundingGoal;
}
//Crear una función que permita a quién la llame aportar al crowfunding deseado
function fundProject() public payable fundRestriction {
if (statusID == 0){
if (msg.value == 0){
revert noFundSelected(msg.value, "You cannot fund with 0 wei!");
}else{
author.transfer(msg.value);
funds += msg.value;
emit fundedBy(id, msg.sender, msg.value);
}
}else{
revert projectClosed ("Project is currently closed, you cannot fund it!");
}
}
//Crear la función que permita cambiar el estado del proyecto
function changeProjectStatus(uint newStatus) public changeStatusRestriction {
if(newStatus != statusID){
if (newStatus == 0){
status = "Opened";
statusID = newStatus;
emit changedBy(id, msg.sender, status);
}else if (newStatus == 1){
status = "Closed";
statusID = newStatus;
emit changedBy(id, msg.sender, status);
}else{
revert notAnOption (newStatus);
}
}else {
revert statusAlreadySelected (newStatus);
}
}
//Crear el modificador para que sólo quién despliega el contraro en la red de ethereum pueda modificar el estado
//del proyecto a financiar
modifier changeStatusRestriction(){
require(
msg.sender == author,
"Only the owner can change the status"
);
_;
}
//Crear la función modifier que evita que el creador del crowfunding aporte a su propio proyecto
modifier fundRestriction(){
require(
msg.sender != author,
"You as the author cannot fund this project"
);
_;
}
}
Creo que por ahi puede ser…
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract CrowdFunding {
string public id;
string public name;
string public description;
address payable public author;
// string public projectState = "Open"; //por defecto abierto a recibir aporte
uint public funds;
uint256 public fundGoal; //define cuanto espero ganar con la ronda de levantamento de capital
uint public projectStatus;
constructor(string memory _id, string memory _name, string memory _description, uint256 _fundGoal){
id = _id;
name = _name;
description = _description;
fundGoal = _fundGoal;
// Owner Information
author = payable(msg.sender); //msg.sender por defecto no recibe ETH pero la convertimos a 'payable'
// Status 0 = "Close"
// Status 1 = "Open"
projectStatus = 1;
}
//registrar fondos del proyecto. Con 'event' compatimos los datos ala Blockchain
event ProjectFunded(string projectId, uint256 value);
event ProjectStateChanged(string id, uint state);
// solo podemos modificar el estado del projecto siendo el propietario
modifier onlyAuthor() {
require(author == msg.sender ,
"Only owner can change state of project"
);
_;
}
modifier noSelfFunds() {
require(
author != msg.sender,
"Autofinance is not available"
);
_;
}
//Manjeo de errores
error ProjectClosed(uint projectStatus);
error NoZeroFunds(uint amount);
//caulquier persona la puede ver y se puede enviar ETH sin problemas
function fundProject() public payable noSelfFunds {
//Project close
require(projectStatus != 1, "Project is already close. No more funds are collected");
//validacion para que no se puedan hacer aportes de 0
if(msg.value == 0){
revert NoZeroFunds(msg.value);
}
author.transfer(msg.value); //transfiere el valor del usuario al autor
funds += msg.value; //despues lo agrego a los fundos del proyecto para registrar el aporte
emit ProjectFunded(id, msg.value);
}
//modifica el estado del proyecto. Calldata ahorra gas, solo existe cuando se le llama a la funcion
function changeProjectState(uint newState) public onlyAuthor{
require(projectStatus != newState, "Project has already that state");
projectStatus = newState;
emit ProjectStateChanged(id, newState);
}
}
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);
}
}
}
}
Esta muy bueno el curso, como esta manejando la clase y el proyecto al mismo tiempo, asi llevamos la teoria vista aplicada en la practica y poco a poco va tomando mas complejidad el contrato 😄
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
¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.