Tienes todo el conocimiento necesario en Solidity para desarrollar tu primer contrato profesional. Es hora de poner a prueba todo lo aprendido en este curso.
Desafió sobre CrowdFunding
Un CrowdFunding es un mecanismo colectivo de financiación de proyectos donde múltiples usuarios realizan aportes económicos a una cuenta con el fin de que esta reciba inversión y el proyecto se desarrolle.
Por lo general, y para evitar estafas, dichos proyectos se encuentran aprobados por una plataforma y poseen un libro blanco que describe el equipo y los objetivos del mismo. Los usuarios que aportan capital suelen recibir incentivos en forma de tokens que pueden ser más valiosos en el futuro si el proyecto tiene éxito y recuperar la inversión.
Tu objetivo será desarrollar un contrato inteligente que permita:
Inicializar el contrato y crea una struct para almacenar los datos de una proyecto y administra el estado de cada uno con un enum.
Crear una función fundProject() que permita enviar ETH a un proyecto.
Crear la función changeProjectState() que permita cambiar el estado de un proyecto.
Utilizar modifiers para que solo el creador de un proyecto pueda modificarlo su estado.
Producir validaciones en tu contrato para que no pueda aportarse capital a un proyecto finalizado o evitar que se aporte 0 ETH.
Almacenar los proyectos en un Array y guarda cada contribución que un proyecto reciba en un Mappings.
Al finalizar el reto, comparte tu primer contrato inteligente y compara tu solución con la de otros estudiantes. Recuerda que existen distintas maneras de resolver este reto.
Si estaba dificil, pero una maravillosa experiencia para seguir aprendiendo
Proyecto finalizado :) CrowdFunding repositorio
Por fin, mi primer Smart Contract.
Que proyecto tan genial! Me gusto mucho la forma como se fue llevando :)
A los fines educativos esta bien, pero no veo como alguien pueda saber el indice interno del array donde esta el proyecto que quiere fondear.
Creo que habria sido mejor usar un mapping con el identificador que se pasa para crear el proyecto... amen q debe verificarse tambien que esa key no exista previamente.
Totalmente, pero al ser un curso de introducción me parece que complicaría mucho las cosas
Por que en las funciones de fundProject y changeProjectState se usa la linea
projects[projectIndex]= project
Es la forma de guardar los cambios en el struct del array?
Porque esta apuntando al proyecto que quiere fondear.
Si exacto es para actualizar al Proyecto que esta guardado en el arrray
Proyecto Terminado, dejo el repo con mis versiones.
Repositorio
Proyecto terminado
hola como estan, me podrian ayudar me lanza este error indicando que el struct contamina el mapping
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.8.2<0.9.0;contract crofunding{enum is_open { open, closed } struct contribution{ address contributor; uint value;} struct project { string id; string name; string description; address payable author; string string_state; is_open state; uint funds; uint funds_reising_goal;mapping(address=> uint ) funders;} project[]public new_projects;mapping(string=> contribution[])public contributions; event amount_funds(uint how_much_was_found, uint current_funds, uint how_much_missing, address funder, string project_id); event project_state( string notification_state, string project_id); event new_project_created( string id, string name, string description, uint funds_reising_goal); modifier only_author(uint project_index){require( msg.sender== new_projects[project_index].author,"Sorry but only the author can change the name of the project"); _;} modifier author_cant_fund(uint project_index){require( new_projects[project_index].author!= msg.sender,"Sorry but you cant fund your own project"); _;}functioncreate_project(string calldata id, string calldata name, string calldata description, uint funds_reising_goal)public{require(funds_reising_goal >0,"Fundraising goal must be greater than 0"); project memory new_project =project(id, name, description,payable(msg.sender), is_open.open,0, funds_reising_goal); new_projects.push(new_project); emit new_project_created(id, name, description, funds_reising_goal);}functionfund_project(uint project_index)public payable author_cant_fund(project_index){ project memory proyect_to_fund = new_projects[project_index];require(proyect_to_fund.state== is_open.open,"thanks you for trusting us but we closed the proyect");require(msg.value>0,"You can't transfer 0"); proyect_to_fund.author.transfer(msg.value); proyect_to_fund.funds+= msg.value; new_projects[project_index]= proyect_to_fund; contributions[proyect_to_fund.id].push(contribution(msg.sender, msg.value)); emit amount_funds(msg.value, proyect_to_fund.funds, proyect_to_fund.funds_reising_goal- proyect_to_fund.funds, msg.sender, proyect_to_fund.id);}functionchange_project_state(is_open new_state, uint project_index)publiconly_author(project_index){ project memory project_to_change_state = new_projects[project_index];require(new_state == is_open.open|| new_state == is_open.closed,"This state is not defined");require(new_state != project_to_change_state.state,"The state that you're trying it's already on"); project_to_change_state.state= new_state;if(new_state == is_open.open){ project_to_change_state.string_state="Open";}elseif(new_state == is_open.closed){ project_to_change_state.string_state="Closed";} new_projects[project_index]= project_to_change_state; emit project_state(new_projects.string_state, new_projects.id);}}
Por en la function createProject los parametros los guarda en calldata, a diferencia del constructor que teniamos que los recibia en memory?
La diferencia entre calldata y memory es que, ++en calldata la persona que hace el deploy asigna los datos++ , y ++en memory el usuario que interactua con el contrato asigna la memoria.++
Acá te dejo el enlace donde podes profundizar con ejemplos
Pero cuando la declaró con el tipo enum directamente en el parámetro "State", no me da error, debería de ser al revés, será que la consola no me está dando bien la indicación?
En este caso la comparación que estás haciendo es de dos enums, por lo que es válida
Porque si yo estoy declarando la variable en el parámetro con uint256 la consola me dice que el operador != no es compatible con los tipos enum?...!
Porque no puedes comparar dos valores que sean de distinto tipo, enum (project.state) y uint256 (change_State), necesitas que ambos tengan el mismo tipo para poder operarlos
No comprendo muy bien como pudo agregar al mapping varios contribuidores si es solo una llave, ¿Alguien me puede explicar porfavor?
El mapping relaciona una llave y un valor, este valor puede ser de cualquier tipo, por ejemplo un array de contribuidores, por lo tanto, cada vez que consultemos el mapping con cierta llave (id del proyecto), vamos a obtener la lista de las wallets que han aportado a ese proyecto.
porque de declaro el tipo struct Contribution dentro del mapping con los corchetes [ ]???, estas ahora declarando el tipo struct Contribution como un array???, osea que se transformo el tipo de dato???
Estoy intentando comprender ese mapping tambien...
segun analizo cada vez que fondeemos un proyecto le pasaremos el id del proyecto como llave, y en vez de reemplazar los datos lo que queremos es agregar un dato nuevo, para eso usamos un array de contribution....
Cada vez que fondeemos ese proyecto estaremos pasando el id y agregando un contributions, si queremos ver quien es y su aporte necesitamos pasarle el id del proyecto y el indixe del array.
Espero que haber entendido bien
para que y porque se coloca el projectIndex dentro de el parametro de los modifier??? que efecto tiene eso sobre el modifier???
el projectIndex nos permitirá encontrar dentro el array de proyectos cuál es el proyecto que estamos consultando y así validar su autor.
Hola. Tengo este problema en la Fila 19. NO puedo agregar el Array Project.
.
Podrían ayudarme por favor?
.
:thinking: podemos ver más de tu codigo? confirmaste que el nombre del struct fuera Project?
Si, el nombre del struct es Project.
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0<0.9.0;contract CrowdFunding{enumFundrisingState{Opened,Closed} struct Contribution{ string id; string name; string description; address payable author;FundrisingStateState; uint256 funds; uint256 fundrisingGoal;}Project[]public project;mapping(string=>Contribution[])publicContributions; event ProjectCreated( string projectId, string name, string description, uint256 fundrisingGoal
); event ProjectFunded( string projectId, uint256 value
); event ProjectStateChanged( string id, uint256 state
); modifier isAuthor(uint256 projectIndex){require(projects[projectIndex].author== msg.sender,"You need to be the project author"); _;} modifier isNotAuthor(uint256 projectIndex){require(projects[projectIndex].author!= msg.sender,"As author you can not fund your own project"); _;}functioncreateProject(string calldata id, string calldata name, string calldata description, uint256 fundrisingGoal)public{require(fundrisingGoal >0,"Fundrising must be higher than 0");Project memory project =project(id, name, description,payable(msg.sender),FundrisingState.Opened,0, fundrisingGoal); project.push(project); emit ProjectCreated(id, name, description, fundrisingGoal);}functionfundProject(uint256 projectIndex)public payable isNotAuthor(projectIndex){ project memory project = projects[projectIndex];require(project.state!=FundrisingState.Closed,"The project can not receive funds");require(msg.value>0,"Fund value must be higher than 0"); project.author.transfer(msg.value); project.funds+= msg.value; projects[projectIndex]= project; contributions[project.id].push(contribution(msg.sender, msg.value)); emit ProjectFunded(project.id, msg.value);}functionchangeProjectState(fundrisingGoal newState, uint256 projectIndex)publicisAuthor(projectIndex){ project memory project = projects[projectIndex];require(project.state!= newState,"New state must be different"); project.state= newState; projects[projectIndex]= project; emit ProjectStateChanged(project.id, newState);}}
Excelente curso, pero creo que falto que expliquen transfer y transferFrom para poder depositar y extraer token específicos desde y hacia el SM. Estará esto en algún curso futuro?
Gracias a esta clase, he comprendido como desde Remix se puede cear un proyecto y como se puede hacer un aporte al mismo desde otra address.
¿Se puede hacer un front-end para que muestre en forma más vistosa los proyectos creados y luego seleccionar a cual quiero aportar? ¿Tenes alguna clase donde explicas eso?
Gracias!
Javier
Hola Javier, claro que se puede hace, para esto se usa una librería llamada web3.js, puedes aprender más sobre web3.js en este curso https://platzi.com/cursos/frontend-dapps/
Gracias!!
Estuvo para darle un poco de tiempo el último reto.
Aunque, preferí variar un poco la implementación, comparto código.
// SPDX-License-Identifier: SEE LICENSE IN LICENSEpragma solidity >=0.8.0;contract crowdfunding {enumFundraisingState{Opened,Closed} struct Project{ uint id; string description; address payable author;//who create o represent the projectFundraisingState state; uint fundraisingGoal;// the goal of the funding uint funds;}mapping(string=>Project)public projects; uint idProjects =0; event NewProject( string nameProject, address authorProject, string descriptionProject, uint fundraisingGoal
);functioncreateProject(string memory _name, string memory _description, uint _fundraisingGoal)public{require(_fundraisingGoal >0,"Fundraising goal must be greater then 0");Project memory newProject =Project({id: idProjects,description: _description,author:payable(msg.sender),state:FundraisingState.Opened,fundraisingGoal: _fundraisingGoal,funds:0}); projects[_name]=newProject; emit NewProject(_name, msg.sender, _description, _fundraisingGoal); idProjects ++;}// mapping´s key is the project´s name event SendFunding( address funder, uint fund
); event RaisingFundGoal( string nameProject, uint fundRaising, string mensaje
); struct Funder{ address funder; uint amount;}mapping(string=>Funder)public funders;functionfundProject(string calldata nameProject)public payable {require(projects[nameProject].author!= msg.sender,"Author don't funding his ouw project");require(projects[nameProject].state!=FundraisingState.Closed,"The project can not receive funds, it's closed");require(msg.value>0,"Fund value must be greater then 0"); projects[nameProject].author.transfer(msg.value); projects[nameProject].funds+= msg.value; emit SendFunding(msg.sender, msg.value);if(projects[nameProject].funds>= projects[nameProject].fundraisingGoal){ emit RaisingFundGoal( nameProject, projects[nameProject].funds,"fundRaisingGoal reached");} funders[nameProject]=Funder(msg.sender, msg.value);} event ChangeState( string nameProject,FundraisingState newState
);functionchangeProjectState(FundraisingState newState, string calldata nameProject)public{require(msg.sender== projects[nameProject].author,"You sould be the author for this project"); projects[nameProject].state= newState; emit ChangeState(nameProject, newState);}}