Crowd Fund - demonoved/A-trial-run-in-solidity GitHub Wiki

// SPDX-License-Identifier: MIT
// compiler version must be greater than or equal to 0.8.20 and less than 0.9.0
pragma solidity ^0.8.23;
// 1. пользователь создает компанию
// 2. пользователи могут внести залог, передав свой токен в компанию
// 3. после окончания компании создатель компании может претендовать на средства, если общая сумма залога превышает цель компании
// 4. в противном случае, если компания не достигла своей цели, пользователи могут отозвать свой залог.
interface IERC20 {
    function transfer (address, uint) external returns (bool);
    function transferFrom (address, address, uint) external returns (bool);
}
contract CrowdFund {
    event Launch(
        uint id,
        address indexed creator,
        uint goal,
        uint32 startAt,
        uint32 endAt
    );
    event Cancel(uint id);
    event Pledge(uint indexed id, address indexed caller, uint amount);
    event Unpledge(uint indexed id, address indexed caller, uint amount);
    event Claim(uint id);
    event Refund(uint id, address indexed caller, uint amount);

    struct Campaign{
        //создатель компании
        address creator;
        //количество токенов для привлечения
        uint goal;
        //общая сумма залога
        uint pledged;
        //временная метка начала компании
        uint32 startAt;
        //временная отметка окончания компании
        uint32 endAt;
        //true если цель была достигнута и создатель забрал токены
        bool claimed;
    }
    IERC20 public immutable token;
    //общее количество созданных компаний 
    //так же используется для генерации id для новых компаний
    uint public  count;
    //сопоставление id с компанией
    mapping(uint => Campaign) public campaigns;
    //сопоставление с индефикатором компании => залогодателем => сумма залога 
    mapping(uint => mapping(address => uint)) public pledgedAmount;

    constructor(address _token) {
        token = IERC20(_token);
    }
    function launch (uint _goal, uint32 _startAt, uint32 _endAt) external {
        require(_startAt >= block.timestamp, "start at < now");
        require(_endAt >= _startAt, "end at < start at");
        require(_endAt <= block.timestamp + 90 days, "end at > max duration");
        count += 1;
        campaigns[count] = Campaign({
            creator: msg.sender,
            goal: _goal,
            pledged: 0,
            startAt: _startAt,
            endAt: _endAt,
            claimed: false
        });
        emit Launch(count, msg.sender, _goal, _startAt, _endAt);
    }
    function cancel (uint _id) external{
        Campaign memory campaign = campaigns[_id];
        require(campaign.creator == msg.sender, "not creator");
        require(block.timestamp < campaign.startAt, "started");

        delete campaigns[_id];
        emit Cancel(_id);
    }
    function pledge (uint _id, uint _amount) external {
        Campaign storage campaign = campaigns[_id];
        require(block.timestamp >= campaign.startAt, "not started");
        require(block.timestamp <= campaign.endAt, "ended");
        campaign.pledged += _amount;
        pledgedAmount[_id][msg.sender] += _amount;
        token.transferFrom(msg.sender, address(this), _amount);
        emit Pledge(_id, msg.sender, _amount);
    }
    function unpledge(uint _id, uint _amount) external {
        Campaign storage campaign = campaigns[_id];
        require(block.timestamp <= campaign.endAt, "ended");
        campaign.pledged -= _amount;
        pledgedAmount[_id][msg.sender] -= _amount;
        token.transfer(msg.sender, _amount);
        emit Unpledge(_id, msg.sender, _amount);
    }
    function claim (uint _id) external {
        Campaign storage campaign = campaigns[_id];
        require(campaign.creator == msg.sender, "not creator");
        require(block.timestamp > campaign.endAt, "not ended");
        require(campaign.pledged >= campaign.goal, "pledged < goal");
        require(!campaign.claimed, "claimed");
        campaign.claimed = true;
        token.transfer(campaign.creator, campaign.pledged);
        emit Claim(_id);
    }
    function refund(uint _id) external {
        Campaign memory campaign = campaigns[_id];
        require(block.timestamp > campaign.endAt, "not ended");
        require(campaign.pledged < campaign.goal, "pledged >= goal");
        uint bal = pledgedAmount[_id][msg.sender];
        pledgedAmount[_id][msg.sender] = 0;
        token.transfer(msg.sender, bal);
        emit Refund(_id, msg.sender, bal);
    }

}

Данный код представляет собой контракт на языке программирования Solidity для создания и управления кампаниями сбора средств (краудфандинга) на платформе Ethereum. Он позволяет пользователям запускать свои кампании для сбора средств в виде токенов ERC20 и управлять ими. Подробно разберем, что делает каждый пункт в коде:

1. Определение интерфейса IERC20 Интерфейс для взаимодействия с токенами ERC20, который содержит две функции:

  • transfer(address, uint) – для перевода токенов ДРУГОМУ пользователю от контракта.
  • transferFrom(address, address, uint) – для перевода токенов ОТ пользователя ДРУГОМУ адресу.

2. Объявление контракта CrowdFund Это основной контракт, который содержит логику краудфандинга.

3. Описание событий (Events) Эти события предоставляют возможность внешнему миру отслеживать изменения в блокчейне, связанные с отдельными действиями в кампаниях (запуск, отмена, взносы и т.д.).

4. Структура Campaign Описывает сущность кампании со следующими атрибутами:

  • creator – адрес создателя кампании;
  • goal – цель сбора средств в токенах;
  • pledged – сумма уже внесенных средств;
  • startAt и endAt – временные метки начала и окончания кампании;
  • claimed – флаг, указывающий, были ли средства уже получены создателем после успешного краудфандинга.

5. Переменные состояния

  • token – ссылаются на контракт ERC20, который будет использоваться в кампаниях;
  • count – общее количество созданных кампаний;
  • campaigns – отображение, которое связывает уникальный идентификатор с объектом кампании;
  • pledgedAmount – отображение для отслеживания суммы средств, вложенных каждым участником в каждую кампанию.

6. Функция launch (запуск) Позволяет пользователю создать новую кампанию, устанавливая цели и временные рамки.

7. Функция cancel (отмена) Дает создателю кампании возможность отменить кампанию до ее старта.

8. Функция pledge (взнос) Позволяет пользователям внести свой вклад (залог) в кампанию, передав токены в контракт.

9. Функция unpledge (изъятие залога) Позволяет участникам отозвать свой вклад до окончания кампании, если они решат изменить свое мнение.

10. Функция claim (заявка на получение средств) Даёт возможность создателю кампании забрать вложенные средства после окончания кампании, если цель сбора достигнута.

11. Функция refund (возврат средств) Позволяет участникам получить возврат средств после окончания кампании, если цель финансирования не была достигнута.

В целом, контракт реализует систему краудфандинга, где пользователи могут начать кампанию с определенной финансовой целью, другие пользователи могут внести свои токены в поддержку, и в зависимости от исхода, либо создатель кампании получает собранные средства, либо вкладчики могут вернуть свои вложения.