No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Desafió para el contrato de CrowdFunding

18/21
Recursos

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.


Contribución creada por: Kevin Fiorentino (Platzi Contributor).

Aportes 14

Preguntas 11

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Dificil el ultimo desafio, tuve que apoyarme con material de otros e internet xD

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

contract CrowdFunding {

    struct Contribution {
        address contributor;
        uint amount;
    }

    enum Status { Active, Inactive }
    struct Project {
        string id;
        string name;
        string description;
        uint fundraisingGoal;
        address payable wallet;
        address owner;
        uint funds;
        Status status;
    }

    Project project;
    Project[] public projectCluster;
    
    mapping(string => Contribution[]) public contributions;
    uint private initialFunds = 0;
    
    event NewFundNotification(address sender, uint fundAmount);
    
    event NewStatusChange(Status newStatus);

    modifier ownerNotSendFunds() {
        require(project.owner != msg.sender, "Owners shouldnt send funds to its own projects!");
        _;
    }

    modifier onlyOwnerModifyStates() {
         require(project.owner == msg.sender, "You must be the project owner!");
         _;
    }

    constructor(string memory _id, string memory _name, string memory _description, uint _fundraisingGoal) {
        project = Project(
            _id,
            _name,
            _description,
            _fundraisingGoal,
            payable(msg.sender),
            msg.sender,
            initialFunds,
            Status.Active
        );
        projectCluster.push(project);
    }

    function createProject(string memory _id, string memory _name, string memory _description, uint _fundraisingGoal) public {
        project = Project(
            _id,
            _name,
            _description,
            _fundraisingGoal,
            payable(msg.sender),
            msg.sender,
            initialFunds,
            Status.Active
        );
        projectCluster.push(project);
    }

    function fundProject(uint index) public payable ownerNotSendFunds {
        Project memory project = projectCluster[index];
        require(project.status == Status.Active, "This project state is Closed!");
        require(msg.value > 0, "Funds can not be Zero!");

        project.wallet.transfer(msg.value);
        project.funds += msg.value;
        projectCluster[index] = project;
        contributions[project.id].push(Contribution(msg.sender, msg.value));
        emit NewFundNotification(msg.sender, msg.value);
    }

    function changeProjectStatus(Status newStatus) public onlyOwnerModifyStates {
        require(project.status != newStatus, "Project has that state already, choose another!");
        project.status = newStatus;
        emit NewStatusChange(newStatus);
    }

}

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.

Proyecto Terminado, dejo el repo con mis versiones.
Repositorio

Proyecto terminado

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 LICENSE

pragma solidity >= 0.8.0;

contract crowdfunding {
    enum FundraisingState {Opened, Closed}
    
    struct Project {
        uint id;
        string description;
        address payable author; //who create o represent the project
        FundraisingState 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
    );

    function createProject(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;

    function fundProject(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
    );

    function changeProjectState(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);
    }
}

Wow! genial, gracias!!

Super interesante el último challenge.

RESUMEN CLASE 18:
TERMINANDO NUESTRO CONTRATO

RETO 7:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract CrowdFunding {
    enum FundraisingState {
        Opened,
        Closed
    }

    struct Contribution {
        address contributor;
        uint256 value;
    }

    struct Project {
        string id;
        string name;
        string description;
        address payable author;
        FundraisingState state;
        uint256 funds;
        uint256 fundraisingGoal;
    }

    Project[] public projects;
    mapping(string => Contribution[]) public contributions;

    event ProjectCreated(
        string projectId,
        string name,
        string description,
        uint256 fundraisingGoal
    );

    event ProjectFunded(string projectId, uint256 value);

    event ProjectStateChanged(string id, FundraisingState 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"
        );
        _;
    }

    function createProject(
        string calldata id,
        string calldata name,
        string calldata description,
        uint256 fundraisingGoal
    ) public {
        require(fundraisingGoal > 0, "fundraising goal must be greater than 0");
        Project memory project = Project(
            id,
            name,
            description,
            payable(msg.sender),
            FundraisingState.Opened,
            0,
            fundraisingGoal
        );
        projects.push(project);
        emit ProjectCreated(id, name, description, fundraisingGoal);
    }

    function fundProject(uint256 projectIndex)
        public
        payable
        isNotAuthor(projectIndex)
    {
        Project memory project = projects[projectIndex];
        require(
            project.state != FundraisingState.Closed,
            "The project can not receive funds"
        );
        require(msg.value > 0, "Fund value must be greater 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);
    }

    function changeProjectState(FundraisingState newState, uint256 projectIndex)
        public
        isAuthor(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);
    }
}

Me retó bastante este proyecto. Le agregué algunas validaciones adicionales que no pidieron. Se los dejo por si quieren revisar una forma diferente de abordar el problema.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract Crowdfunding {
    //Array of projects
    CrowdfundingProject [] projects;

    //Mapping of donations
    mapping (string => Donation[]) donations;

    //Crowdfunding Project structure
    struct CrowdfundingProject{
        address payable projectAddress;
        string projectName;
        uint256 fundraisingGoal;
        uint256 moneyReceived;
        ProjectState projectState;
    }

    struct Donation{
        address contributor;
        uint256 donation;
    }

    //Enum - Project States 
    enum ProjectState {Opened, Closed}

    //Modifier to validate the project with given name belong to sender address
    modifier onlyOwner(string calldata _projectName){
        bool isProjectOwner = false;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName)) && projects[i].projectAddress == msg.sender){
                isProjectOwner = true;
            }
        }
        require(isProjectOwner == true, "Only owner can do this action");
        _;
    }

    //Modifier to validate the project with given name does not belong to sender address
    modifier notOwnProject(string calldata _projectName) {
        bool isOwnProject = false;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName)) && projects[i].projectAddress == msg.sender){
                isOwnProject = true;
            }
        }
        require(isOwnProject == false, "Owner cannot fund his own project");
        _;
    }

    //Event when a project is funded
    event FundedProject(
        address contributor,
        uint256 donation
    );

    //Event when the project state changed
    event ProjectStateChanged(
        address author,
        ProjectState newState
    );

    //Function to create a project
    function createProject (string calldata projectName, uint256 _fundraisingGoal) public {
        CrowdfundingProject memory crowdfundingProject = CrowdfundingProject(payable(msg.sender), projectName, _fundraisingGoal, 0, ProjectState.Opened);
        projects.push(crowdfundingProject);

    }

    //Function to fund a project
    function fundProject (string calldata _projectName) public notOwnProject (_projectName) payable{
        require(bytes(_projectName).length>0, "The project name is mandatory");
        require(msg.value > 0, "The donation should be greater than zero");

        uint index;
        bool foundProject = false;
        CrowdfundingProject memory currentProject;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName))){
                currentProject = projects[i];
                foundProject = true;
                index = i;
            }
        }

        emit ProjectStateChanged(msg.sender, currentProject.projectState);

        require(foundProject == true, "The project was not found");
        require(currentProject.projectState == ProjectState.Opened, "Crodwfunding is closed");
        require (currentProject.moneyReceived + msg.value <= currentProject.fundraisingGoal, "Fundarising is almost full. Fund with less money.");
        currentProject.projectAddress.transfer(msg.value);
        currentProject.moneyReceived = currentProject.moneyReceived + msg.value;
        //Update array and mapping
        projects[index] = currentProject;
        donations[_projectName].push(Donation(msg.sender, msg.value));
        emit FundedProject(msg.sender, msg.value);
    }

    //Function to change the project state
    function changeProjectState(string calldata _projectName) public onlyOwner (_projectName) {

        uint index;
        CrowdfundingProject memory currentProject;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName))){
                currentProject = projects[i];
                index = i;
            }
        }

        if(currentProject.projectState == ProjectState.Opened){
            currentProject.projectState = ProjectState.Closed;
            projects[index] = currentProject;
        }else{
            currentProject.projectState = ProjectState.Opened;
            projects[index] = currentProject;
        }
        emit ProjectStateChanged(msg.sender, currentProject.projectState);
    }

    //Function to get all projects
    function getAllProjects() public view returns (CrowdfundingProject [] memory) {
        return projects;
    }

    //Function to get donations for a specific project
    function getDonationsForProject(string calldata _projectName) public view returns (Donation[] memory) {
        return donations[_projectName];
    }

}

si alguno le llega a pasar en la funcion de createProject tienen que declararla como “public”

Mi desarrollo del contrato! Un gusto aprender con ustedes 😃

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract crowfundingContract {

    enum Status { Opened, Closed }
    struct ProjectData {
        string name;
        string description;
        string status;
        uint fundingGoal;
        uint funds;
        Status statusID;
    }
    struct ContributorData {
        address contributor;
        uint contributedEther;
    }
    
    ProjectData[] public projects;
    
    mapping (uint => ContributorData[]) public contributors;
    mapping (uint => address) public projectOwner;

    event fundedBy (
        string project,
        address participant,
        uint vulue
    );
    event changedBy (
        string project,
        address author,
        string newStatus
    );

    function createProject(string memory _projectName, string memory _description, uint _fundingGoal) public {
        projects.push(ProjectData(_projectName,_description,"Opened",_fundingGoal,0,Status.Opened));
        uint id = projects.length -1;
        projectOwner[id] = msg.sender;
    }

    function fundProject(uint projectIndex) public payable fundRestriction (projectIndex) {
        ProjectData memory selectedProject = projects[projectIndex];
        require(selectedProject.statusID == Status.Opened, "Project is closed! you cannot fund");
        require(msg.value > 0, "You cannot fund 0 wei!");
        address payable author = payable(projectOwner[projectIndex]);
        author.transfer(msg.value);
        selectedProject.funds += msg.value;
        contributors[projectIndex].push(ContributorData(msg.sender,msg.value));

        emit fundedBy(selectedProject.name, msg.sender, msg.value);
    }

    function changeProjectStatus(Status newStatus, uint projectIndex) public changeStatusRestriction(projectIndex) {
        ProjectData memory selectedProject = projects[projectIndex];
        require(newStatus != selectedProject.statusID, "Status already selected!");
        if(newStatus == Status.Opened){
            selectedProject.status = "Opened";
        }else{
            selectedProject.status = "Closed";
        }
        selectedProject.statusID = newStatus;

        emit changedBy(selectedProject.name, msg.sender, selectedProject.status);
    }

    modifier changeStatusRestriction(uint projectIndex){
        require(
            msg.sender == projectOwner[projectIndex],
            "Only the owner can change the status"
        );
        _;
    }

    modifier fundRestriction(uint projectIndex){
        require(
            msg.sender != projectOwner[projectIndex],
            "You as the author cannot fund this project"
        );
        _;
    }
}