Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLOIP-9: Treasury governance & community share #10

Open
Dexaran opened this issue Feb 22, 2024 · 17 comments
Open

CLOIP-9: Treasury governance & community share #10

Dexaran opened this issue Feb 22, 2024 · 17 comments

Comments

@Dexaran
Copy link
Member

Dexaran commented Feb 22, 2024


cloip: 9
title: Trustless Treasury model for Callisto Network
author: Dexaran (@Dexaran) dexaran@callisto.network
status: Draft
category: Meta
created: 22 February, 2024

Abstract

This CLOIP supersedes CLOIP-7: Trustless Treasury model as the previous Treasury keyholders resigned from their rights. In this proposal we will host a discussion to allocate a share of Treasury that previously belonged to Callisto Enterprise to the community.

Proposal

@Upaut1 proposed to create a community-governed smart-contract that will receive payments from Treasury in CLOIP-7 #7 (comment)

I propose to redirect up to 50% Treasury (previously Callisto Enterprise's share) to the community contract. However few points have to be addressed first:

  • Keyholders
  • Contract implementation
  • Voting method
@Upaut1
Copy link

Upaut1 commented Mar 4, 2024

@Dexaran и все члены сообщества

Предлагаю для начала определиться с основным контрактом Казначейства. Так как долей сообщества будет распоряжаться другой контракт, на основе голосования, с ним мы можем определиться немного позднее, когда утвердим контракт Казначейства.

Мое предложение следующее, мне бы не хотелось чтобы контракт Казначейства был перегружен логикой, хотелось бы чтобы он был прост и его код легко воспринимался читающими.
Основная задача которую должен исполнять контракт Казначейства, это хранить и распределять свой баланс между получателями, а именно Callisto Network (50%) и контрактом Сообщества (50%) и давать им распоряжаться своим балансом. Отмечу, что контракт казначейства не должен делать выплаты на адреса Callisto Network и на контракт Сообщества по умолчанию, вместо этого он должен делать выплаты на те адреса что требуют от него доливики.

Callisto Network и контракт Сообщества - далее "получатели"

Предлагаю следующие требования к контракту Казначейства:

  • Иметь возможность принимать на свой баланс CLO и распределять его среди владельцев, по установленным процентам
  • Иметь возможность принимать на свой баланс любые токены стандартов ERC20 и ERC223.
  • 100% всех поступивших токенов передавать в распоряжение сообщества.
  • Получатели должны иметь возможность, единолично, изменять свой адрес в Казначействе
  • Получатель должен иметь право передавать часть своего процента, или полностью весь процент, под управление второго получателя.
  • Иметь возможность отправлять CLO на указанный получателем адрес
  • Иметь возможность отправлять любой токен на указанный сообществом адрес
  • Иметь функцию возвращающую текущий процент и баланс в CLO, по заданному получателю
  • Иметь функцию возвращающую баланс контракта Казначейства в заданном токене

Данный контракт я уже написал и развернул его в Callisto Mainnet, для вашего дальнейшего ознакомления с его кодом. Контракт полностью верифицирован.

https://02.callisto.network/address/0xbD62acE2BC78B7ABdEb84E00fdBCa54544929d0b/contracts

На тот случай если обозреватель перестанет работать или не работает, я опубликую код контракта еще и здесь для ознакомления.
Разумеется адреса callistoNetwork и callistoCommunity должны быть изменены!

`// All rights reserved.
// SPDX-License-Identifier: No License (None)
// Разработчик: Upaut

pragma solidity ^0.8.16;

interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
}

contract Treasury {

struct RecipientData { // структура описывающая данные каждого получателя
    uint256 percent; // установленный процент получателя
    uint256 balance; // баланс получателя
}

address public callistoNetwork = 0xeBE894814554c8382EA6a24CcDdf1527407A24f6; // Первый получатель
address public callistoCommunity = 0xfc8f3324B6D71d14BfD625ECdbD18f3bc29A9935; // Второй получатель

mapping(address => RecipientData) private recipients; // связь получателей с их структурой данных

event ChangeRecipient(address indexed _old, address indexed _new);
event TransferPercent(address indexed _sender, uint _percent);
event TransferCLO(address indexed _sender, address indexed _recipient, uint _value);


modifier onlyRecipients() {
    require((msg.sender == callistoNetwork) || (msg.sender == callistoCommunity), "Only recipient");
    _;
}

constructor()
{
    // Проценты имеют decimal = 18
    recipients[callistoNetwork].percent = 50 * 1e18; // 50% от трежери для CallistoNetwork
    recipients[callistoCommunity].percent = 50 * 1e18; // 50% от трежери для сообщества Callisto
}


function changeRecepient(address _newRecepient) external onlyRecipients // изменение адреса получателя. Новый получатель сохраняет за собой право на проценты и баланс старого получателя
{
    uint256 _percent = recipients[msg.sender].percent; // процент старого получателя
    uint256 _balance = recipients[msg.sender].balance; // баланс старого получателя
    delete recipients[msg.sender]; // удаляем данные старого получателя

    // Инициализируем данные нового получателя
    recipients[_newRecepient] = RecipientData(_percent, _balance);
    (callistoNetwork, callistoCommunity) = msg.sender == callistoNetwork ? (_newRecepient, callistoCommunity) : (callistoNetwork, _newRecepient);

    emit ChangeRecipient(msg.sender, _newRecepient); // логируем изменение получателя
}


function transferPercent(uint256 _percent) external onlyRecipients // передача части процента под управление второго получателя
{
    uint256 _tekPercent = recipients[msg.sender].percent; // текущий процент получателя
    require(_percent <= _tekPercent); // проверяем что отправитель имеет в распоряжении передаваемые проценты
    
    _balanceDistribution(); // получатели делят нераспределенный баланс трежери

    // передаем управление процентом второму получателю
    (recipients[callistoNetwork].percent, recipients[callistoCommunity].percent) = msg.sender == callistoNetwork ? (_tekPercent - _percent, recipients[callistoCommunity].percent + _percent) : (recipients[callistoNetwork].percent + _percent, _tekPercent - _percent);
    emit TransferPercent(msg.sender, _percent); // логируем передачу процента отправителем
}


function transferCLO(address _recipient, uint256 _value) external onlyRecipients // отправка CLO
{
    require((_recipient != address(0x00)) && (_recipient != address(this))); // получателем не может быть нулевой адрес, или контракт трежери
    _balanceDistribution(); // получатели делят нераспределенный баланс трежери

    uint256 _tekBalance = recipients[msg.sender].balance; // текущий баланс отправителя
    require(_value <= _tekBalance); // проверяем что отправитель имеет в распоряжении достаточный баланс

    recipients[msg.sender].balance = _tekBalance - _value; // корректировка баланса отправителя
    payable(_recipient).transfer(_value); // отправляем CLO получателю

    emit TransferCLO(msg.sender, _recipient, _value); // логируем отправку CLO
}


function transferToken(address _token, address _recipient, uint256 _value) external // отправка токена
{
    require(msg.sender == callistoCommunity); // Распоряжаться любыми токенами разрешено только сообществу
    require((_recipient != address(0x00)) && (_recipient != address(this))); // получателем не может быть нулевой адрес, или контракт трежери
    IERC20(_token).transfer(_recipient, _value); // отправляем токен получателю
}



function _balanceDistribution() private { // распределение CLO в трежери, между получателями
    uint256 _cn = recipients[callistoNetwork].balance; // текущий баланс callistoNetwork
    uint256 _cc = recipients[callistoCommunity].balance; // текущий баланс callistoCommunity
    uint256 _contractBalance = address(this).balance - (_cn + _cc); // получаем нераспределенный баланс нативной монеты у контракта трежери
    uint256 _cnShare = recipients[callistoNetwork].percent * _contractBalance / (100 * 1e18); // рассчитываем долю callistoNetwork в свободных CLO у контракта
    recipients[callistoNetwork].balance = _cn + _cnShare; // передаем эту долю в распоряжение callistoNetwork
    recipients[callistoCommunity].balance = _cc + (_contractBalance - _cnShare); // остаток переходит под управление callistoCommunity
}


function getRecipientData(address _recipient) public view returns (RecipientData memory) // Возвращаем текущий процент и текущий баланс CLO у получателя
{   
    RecipientData memory _result;
    uint256 _cn = recipients[callistoNetwork].balance; // текущий баланс callistoNetwork
    uint256 _cc = recipients[callistoCommunity].balance; // текущий баланс callistoCommunity
    uint256 _contractBalance = address(this).balance - (_cn + _cc); // получаем нераспределенный баланс нативной монеты у контракта трежери
    uint256 _share = recipients[_recipient].percent * _contractBalance / (100 * 1e18); // рассчитываем долю запрошенного получателя
    _result = RecipientData(recipients[_recipient].percent, recipients[_recipient].balance + _share); // получаем текущий процент и весь доступный баланс для запрошенного получателя
    return (_result); // возвращаем массив с инфой по токенам
}


function getBalanceToken(address _token) public view returns (uint256) // Возвращаем текущий процент и текущий баланс CLO у получателя
{   
    uint _balance = IERC20(_token).balanceOf(address(this)); // получаем баланс этого контракта в запрошенном токене
    return (_balance);
}


// Контракт принимает пожертвования в нативной монете, любые токены ERC223 и ERC20
// Любые пожертвования в токенах ERC223 и ERC20 переходят под управление сообществом в полном объеме

receive() external payable {} // принимаем CLO. Он будет распределен между получателями согласно установленным процентам

function tokenReceived(address _from, uint _value, bytes memory _data) public returns (bytes4) { // принимаем токены ERC223
    return this.tokenReceived.selector; // возвращаем селектор этой функции
}

}`

@Dexaran
Copy link
Member Author

Dexaran commented Mar 11, 2024

Предлагаю для начала определиться с основным контрактом Казначейства. Так как долей сообщества будет распоряжаться другой контракт, на основе голосования, с ним мы можем определиться немного позднее, когда утвердим контракт Казначейства.

I have no objections against this Treasury contract except the token donations. Why would community gain 100% of the donated tokens and not the other recipients?

@Dexaran
Copy link
Member Author

Dexaran commented Mar 11, 2024

I would like this CLOIP to host a discussion for the community Treasury share primarily. Updating the Treasury to make it a smart-contract is a cumbersome process, it will require a hardfork and it will happen (or not happen) in September most likely.

Community Treasury however can get funds right now. As soon as we finalize the community Treasury contract and discuss the proposal I'll be able to manually deposit the funds from the current Treasury balance to the community Treasury without any further delays.

@Upaut1
Copy link

Upaut1 commented Mar 12, 2024

I have no objections against this Treasury contract except the token donations. Why would community gain 100% of the donated tokens and not the other recipients?

Считаю что другие получатели для пожертвований должны иметь свои собственные адреса, чтобы иметь возможность единолично получать и распоряжаться тем, что конкретно им дали.
Но если средства поступают на контракт Казначейства, то распоряжаться этими средствами должно сообщество!

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

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

В третьих, и наверное самое главное для понимания, никто не запрещает другим получателям состоять в сообществе и иметь право голоса для управления этими токенами.

@Upaut1
Copy link

Upaut1 commented Mar 12, 2024

I would like this CLOIP to host a discussion for the community Treasury share primarily. Updating the Treasury to make it a smart-contract is a cumbersome process, it will require a hardfork and it will happen (or not happen) in September most likely.

Community Treasury however can get funds right now. As soon as we finalize the community Treasury contract and discuss the proposal I'll be able to manually deposit the funds from the current Treasury balance to the community Treasury without any further delays.

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

Что касается контракта DAO Governance, то общие мысли пока следующие:

Контракт должен иметь:

  1. Общее количество добавленных участников
  2. Общее количество проведенных голосований (меняется транзакцией исполнения решения по голосованию)
  3. Время отведенное на голосование (может изменяться через голосование, полагаю в диапазоне 3-7 дней достаточно)
  4. Количество нативной монеты. Плата за выставления вопроса на голосование (может изменяться через голосование). Если вопрос на голосование выдвигает адрес не являющийся участником данного контракта, он платит цену увеличенную в 10 раз.
  5. Мэппинг с доверенными контрактами, в которых сообщество принимает решение (первоначально 2 адреса это контракт Казначейства и контракт ДАО)
  6. Мэппинг на структуру данных участника (адрес, имя, количество_участий_в_голосованиях)
  7. Мэппинг на структуру данных Proposal (IDhash, Количество нативной монеты (4), Общее количество добавленных участников (1), контракт, селектор_функции, данные).
    ...

Наверное я зря это описываю, проще просто идею высказать

Изначально в контракте DAO Governance инициализирован первый участник. Он начнет создавать предложения на голосования по добавлению новых участников (новые участники должны сделать запрос о входе в ДАО в телеграм чатах и подтвердить свой адрес транзакцией).
Каждый раз при добавлении нового участника ему присваивается "Количество_участий_в_голосованиях" = "Общее количество проведенных голосований".
Каждый раз когда участник голосует за какое-либо предложение он увеличивает свое "Количество_участий_в_голосованиях" на 2, но не более чем текущее "Общее количество проведенных голосований". Объясняю почему на 2, 1 - за текущее голосование и 1 - чтобы снять с себя 1 очко штрафа за голосования что он пропускал.
Если разница между "Общее количество проведенных голосований" и "Количество_участий_в_голосованиях" составит 100 и более, то такого участника можно удалить из контракта ДАО без голосования.
Все голосующие участники, после проведения голосования запрашивают награду, за свой голос. Это значение равное "Плата за выставления вопроса на голосование" деленная на количество голосовавших. Так сказать система поощрения голосующих. Не голосовавшие в место поощрения "из коробки" получат +1 штрафа, что приближает их к удалению из контракта ДАО (очки штрафа начинают сниматься если участник начинает голосовать).

Само предложение на голосование должно:

  • Иметь время окончания голосования
  • Фиксировать количество участников, если все проголосуют, то голосование завершается досрочно.
  • Иметь данные (количество CLO за выставление предложения, текущее количество участников на момент создания предложения, ID этого предложения и все данные для низкоуровнего вызова функции call)

Я еще обдумываю детали, но думаю общую суть уже понять можно

@Upaut1
Copy link

Upaut1 commented Mar 14, 2024

I would like this CLOIP to host a discussion for the community Treasury share primarily. Updating the Treasury to make it a smart-contract is a cumbersome process, it will require a hardfork and it will happen (or not happen) in September most likely.

Итак, в общем предложение по контракту сообщества (далее GovernanceDAO) следующее:

GovernanceDAO должен:

  • управлять любым сторонним контрактом, при помощи низкоуровнего вызова call, на основании решения по завершению голосования (1 участник = 1 голос). Если большинство за исполнение предложения, то оно исполняется.

  • Уметь принимать предложения на голосование, от любого человека, не зависимо от того состоит он в DAO или нет.

  • Брать оплату за добавление предложения на голосование. Причем таких расценок две, первая для тех кто состоит в DAO, вторая для тех кто не состоит в DAO (они разумеется платят намного больше). Такие оплаты это мотивация всем голосующим, по завершению голосования эта оплата будет распределена среди проголосовавших (не важно как голосовали "за" или "против"). Установка расценок меняется при помощи голосования.

  • Новые участники DAO добавляются при помощи голосования. Первоначально добавляться будут проверенные люди из телеграм чатов Callisto, которые имеют в чате высокую активность. При добавлении участника в DAO, контракт сохранит его адрес кошелька и имя взятое из телеграма.

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

  • Каждый участник имеет данные по своей активности в голосованиях (сколько было голосований после его добавление в DAO и сколько раз он голосовал). Исключать участников из DAO также возможно через голосование.

  • Каждое предложение должно иметь deadline (крайний срок принятия голосов). Сколько времени отводить на голосование решают члены DAO при помощи голосования. Допустимые сроки отводимые на голосование, которые можно установить от 3 до 14 дней. Если за предложение досрочно проголосовали все существующие участники DAO, оно завершается досрочно и переходит к принятию решения об исполнении.

  • Каждый участник DAO может 1 раз голосовать за каждое предложение.

  • контракт должен уметь возвращать список всех (либо часть списка) участников DAO с их данными по голосованиям

  • контракт должен уметь возвращать список всех (либо часть списка) выставленных предложений с их данными

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

Я написал данный контракт, развернул и верифицировал его, для более детального ознакомления
https://02.callisto.network/address/0x23DAA8d59dE6C07c38F0E8B27E709851135dC651/contracts

На тот случай если обозреватель перестанет работать или не работает, я опубликую код контракта еще и здесь для ознакомления

`// All rights reserved.
// SPDX-License-Identifier: No License (None)
// Разработчик: Upaut

pragma solidity ^0.8.16;

contract GovernanceDAO {

struct UserData { // структура описывающая данные каждого участника
    uint256 index; // индекс участника
    uint256 votes; // сколько раз всего голосовал участник
    uint256 entered; // после какого голосования добавился участник
    address userAddr; // адрес участника (упростит задачу frontend)
    string name; // имя участника
}

struct ProposalData { // структура описывающая данные каждого предложения для голосования
    uint256 id; // уникальный номер предложения (упростит задачу frontend)
    uint256 time; // Дата создания предложения
    uint256 reward; // награда для участников голосования
    uint256 deadLine; // крайний срок голосования
    address owner; // адрес инициатора предложения
    uint8 status; // текущий статус предложения (1 - голосование, 2 - завершено, 3 - исполнено)
    string comment; // комментарий к предложению
    address[] vocesYes; // сколько участников проголосовало за предложение
    address[] vocesNo; // сколько участников проголосовало против предложения

    address to; // вызываемый контракт при исполнении данного предложения
    bytes data; // данные транзакции
}

uint256 public total_voting; // всего голосований
uint256 public total_close_voting; // всего завершенных голосований    
uint256 public total_user; // всего пользователей в DAO
uint256 public expire_period = 3 days; // сколько дней отведено на голосование (возможное значение от 3 до 14 дней)
uint256 public min_payment_DAO = 100 * 1e18; // минимальная оплата за вынесение предложения на голосование для членов DAO.
uint256 public min_payment_other = 10000 * 1e18; // минимальная оплата за вынесение предложения на голосование для не членов DAO.

mapping(uint256 => address) private indexes; // адреса участников по индексу
mapping(address => UserData) private users; // связь пользователей с их структурой данных
mapping(uint256 => ProposalData) private proposals; // список всех предложений
mapping(uint256 => mapping(address => bool)) private rewards; // награды за голосование

event CreateProposal(uint indexed _id, uint _reward); // ID предложения и награда за голосование
event Vote(address indexed _sender, uint indexed _id, bool _answer); // Участник, ID предложения, решение
event ChangeStatus(uint indexed _id, uint8 _status); // ID предложения, новый статус
event Claim(address indexed _sender, uint indexed _id, uint _pay); // Участник, ID предложения, сколько было выплачено

modifier onlyThis() {
    require(address(this) == msg.sender, "Only GovernanceDAO");
    _;
}

constructor(address _firstUser, string memory _name)
{
    ++total_user; // увеличиваем количество участников
    indexes[total_user] = _firstUser; // привязываем адрес участника к индексу
    users[_firstUser] = UserData(total_user, 0, 0, _firstUser, _name); // инициализация данных участника
}


function setExpirePeriod(uint256 _period) external onlyThis { // установить новый период голосования
    if((_period >= 3 days) && (_period <= 14 days)) expire_period = _period; // новый период должен быть в диапазоне от 3 до 14 дней (включительно)
}

function setMinPayments(uint256 _min_payment_DAO, uint256 _min_payment_other) external onlyThis { // установить новые минимальные сборы за вынесение предложения на голосование
    min_payment_DAO = _min_payment_DAO; // минимальная оплата за вынесение предложения на голосование для членов DAO.
    min_payment_other = _min_payment_other; // минимальная оплата за вынесение предложения на голосование для не членов DAO.
}

function addUserInDAO(address _user, string calldata _name) external onlyThis { // добавить нового участника
    if(_user == address(0)) return; // участник не может иметь нулевой адрес
    if (users[_user].index == 0){ // только новый участник 
        ++total_user; // увеличиваем количество участников
        indexes[total_user] = _user; // привязываем адрес участника к индексу
        users[_user] = UserData(total_user, 0, total_close_voting, _user, _name); // инициализация данных участника
    }
}

function delUserInDAO(address _user) external onlyThis { // удалить участника из DAO
    uint256 _ind = users[_user].index; // индекс удаляемого участника
    if (_ind > 0){ // только существующий участник 
        if(_ind != total_user){ // индекс участника непоследний
            indexes[_ind] = indexes[total_user]; // привязываем адрес последнего участника к новому индексу
            users[indexes[_ind]].index = _ind; // меняем индекс в данных последнего участника
        }
        delete indexes[total_user]; // обнуляем последний индекс
        delete users[_user]; // удаляем данные участника
        --total_user; // уменьшаем количество участников
    }
}



function createProposal(address _contract, bytes calldata _data, string calldata _comment) payable external { // создание предложения
    // _contract - адрес контракта, который будет вызван при принятии этого предложения
    // _data - данные вызова (функция и аргументы), закодированные через encodeABI()
    // _comment - краткий комментарий к предложению, возможно ссылка на github с proposal

    if(users[msg.sender].index > 0){ // адрес который выдвигает предложение состоит в DAO
        require(msg.value >= min_payment_DAO, "small payment"); // проверка оплаты предложения
    }
    else{ // адрес который выдвигает предложение не состоит в DAO
        require(msg.value >= min_payment_other, "small payment"); // проверка оплаты предложения
    }

    ++total_voting; // идентификатор нового голосования
    proposals[total_voting].id = total_voting; // уникальный номер предложения
    proposals[total_voting].time = block.timestamp; // Дата создания предложения
    proposals[total_voting].reward = msg.value; // награда для участников голосования
    proposals[total_voting].deadLine = block.timestamp + expire_period; // крайний срок голосования
    proposals[total_voting].owner = msg.sender; // адрес инициатора предложения
    proposals[total_voting].status = 1; // текущий статус предложения
    proposals[total_voting].comment = _comment; // комментарий к предложению
    proposals[total_voting].to = _contract; // вызываемый контракт при исполнении данного предложения
    proposals[total_voting].data = _data; // данные транзакции

    emit CreateProposal(total_voting, msg.value); // логируем ID предложения и награду за голосование
}


function vote(uint _id, bool _answer) external { // голосование
    // _id - идентификатор предложения
    // _answer - ваш ответ (0 - отклонить предложение, 1 - одобрить предложение)

    require(proposals[_id].time > 0, "proposal does not exist"); // проверяем что предложение существует
    if(proposals[_id].status > 1) return; // голосование по данному предложению завершено
    if(block.timestamp > proposals[_id].deadLine){ // время голосования истекло
        proposals[_id].status = 2; // голосование завершено
        ++total_close_voting; // увеличиваем количество завершенных голосований
        emit ChangeStatus(_id, 2); // ID предложения, новый статус
        return;
    }

    uint _index = users[msg.sender].index; // получим индекс голосующего в списке DAO
    require(_index > 0, "you are not a member"); // проверяем что голосующий состоит в DAO
    require(!rewards[_id][msg.sender], "you have already voted"); // проверяем что голосующий, ранее не голосовал за это предложение

    rewards[_id][msg.sender] = true; // подтверждаем принятие голоса c фиксацией выплаты за это голосование
    users[msg.sender].votes++; // увеличиваем количество всех голосований участника

    emit Vote(msg.sender, _id, _answer); // логируем участника, ID предложения, принятое решение

    if(_answer){ // голос отдан за принятие предложения
        proposals[_id].vocesYes.push(msg.sender);
    }
    else{ // голос отдан за отклонение предложения
        proposals[_id].vocesNo.push(msg.sender);
    }


    uint _amount_votes = proposals[_id].vocesYes.length + proposals[_id].vocesNo.length; // количество проголосовавших
    if(_amount_votes == total_user){ // если все участники DAO проголосовали, то голосование досрочно завершаем
        proposals[_id].status = 2; // голосование завершено
        ++total_close_voting; // увеличиваем количество завершенных голосований
        emit ChangeStatus(_id, 2); // ID предложения, новый статус
    }
}


function execute(uint _id) external { // Исполнение предложения
    // _id - идентификатор предложения
    require(proposals[_id].status == 2, "proposal is not completed"); // только завершенное предложение можно привести к исполнению
    proposals[_id].status = 3; // меняем статус на состояние "Исполнено"
    if(proposals[_id].vocesYes.length > proposals[_id].vocesNo.length){ // если большинство проголосовало за принятия предложения, то приводим его в исполнение
        _execute(proposals[_id].to, proposals[_id].data);
    }
    emit ChangeStatus(_id, 3); // ID предложения, новый статус
}

function _execute(address _to, bytes memory _data) private {
    (bool success,) = _to.call(_data);
    require(success, "Execute error");
}


function claim(uint _id) external { // Получить вознаграждение за проведенное голосование
    // _id - идентификатор предложения
    require(proposals[_id].status > 1, "proposal is not completed"); // награду можно требовать только когда голосование завершено
    require(rewards[_id][msg.sender], "no payment"); // проверяем имеет ли отправитель право на выплату
    rewards[_id][msg.sender] = false; // фиксируем выплату отправителю

    uint _amount_votes = proposals[_id].vocesYes.length + proposals[_id].vocesNo.length; // количество проголосовавших
    uint _pay = proposals[_id].reward / _amount_votes; // расчет выплаты для каждого участника
    payable(msg.sender).transfer(_pay); // выплата
    emit Claim(msg.sender, _id, _pay); // Участник, ID предложения, сколько было выплачено
}



// ФУНКЦИИ ЧТЕНИЯ

function getUser(address _user) public view returns (UserData memory) { // Возвращаем данные конкретного пользователя по его адресу
    return (users[_user]);
}

function getUsersList(uint _start_index, uint _amount) public view returns (UserData[] memory) { // Возвращаем данные нескольких пользователей из списка
    UserData[] memory _result = new UserData[](_amount);  
    for(uint i; i < _amount; i++) _result[i] = getUser(indexes[_start_index++]);
    return (_result);
}

function getProposal(uint _id) public view returns (ProposalData memory) { // Возвращаем данные конкретного предложения по его ID
    return (proposals[_id]);
}

function getProposalsList(uint _start_ID, uint _amount) public view returns (ProposalData[] memory) { // Возвращаем данные нескольких предложений из списка
    ProposalData[] memory _result = new ProposalData[](_amount);  
    for(uint i; i < _amount; i++) _result[i] = getProposal(_start_ID++);
    return (_result);
}

function checkClaim(uint _id, address _user) public view returns (bool) { // Проверяет наличие вознаграждения для указанного адреса, в указанном предложении
    return (rewards[_id][_user]);
}

function getClaimList(address _user, uint _start_ID, uint _amount) public view returns (uint [] memory, bool [] memory) { // Возвращаем массив предложений и массив наград для указанного адреса
    // _user - адрес для которого смотрим награды
    // _start_ID - с какого предложения начинать вести поиск наград
    // _amount - количество просматриваемых предложений
    uint[] memory _proposalID = new uint[](_amount);
    bool[] memory _claim = new bool[](_amount);

    for(uint i; i < _amount; i++){
        _proposalID[i] = _start_ID;
        _claim[i] = rewards[_start_ID][_user];
        ++_start_ID;
    } 
    return (_proposalID, _claim);
}

}
`

@Dexaran
Copy link
Member Author

Dexaran commented Mar 19, 2024

Here is a copy of the code provided by @Upaut1 https://gist.github.com/Dexaran/ade3480b4717a3c94816957bfbdb4872

@Dexaran
Copy link
Member Author

Dexaran commented Mar 19, 2024

My last concern is to agree on a list of the initial community contract members. While it is clear that they will be added one by one according to the rules of the smart-contract (by the initial creator) it would be good to nominate at least three or four existing members of the community.

@Dexaran Dexaran mentioned this issue Mar 28, 2024
@Upaut1
Copy link

Upaut1 commented Mar 29, 2024

@Dexaran
На разработку UI для контракта DAO требуются средства в количестве 1500 USDT, предпочтительно в сети Polygon, адрес получателя 0xeBE894814554c8382EA6a24CcDdf1527407A24f6

Контракты Treasury, GovernanceDAO, а также UI будут с лицензией GPLv3.

@Dexaran
Copy link
Member Author

Dexaran commented Apr 1, 2024

@Dexaran На разработку UI для контракта DAO требуются средства в количестве 1500 USDT, предпочтительно в сети Polygon, адрес получателя 0xeBE894814554c8382EA6a24CcDdf1527407A24f6

Контракты Treasury, GovernanceDAO, а также UI будут с лицензией GPLv3.


@Dexaran
To develop a UI for the DAO contract, funds in the amount of 1500 USDT are required, preferably on the Polygon network, recipient address 0xeBE894814554c8382EA6a24CcDdf1527407A24f6

Treasury, GovernanceDAO, and UI contracts will be licensed under GPLv3.

Alright, this proposal will be funded.

Unfortunately I'm unable to process the transfer of USDT via Polygon at the moment, the USDT will be delivered via BSC as soon as Bybit will approve my withdrawal.

@Dexaran
Copy link
Member Author

Dexaran commented Apr 2, 2024

@Upaut1
Copy link

Upaut1 commented Apr 12, 2024

@Dexaran
Рад сообщить что создание DAO успешно завершено.

Контракты Treasury, GovernanceDAO, а также UI с открытым исходным кодом и имеют лицензии GPLv3
https://github.com/YaStaer/CallistoDAO_UI

Мы также хорошо протестировали взаимодействие контракта GovernanceDAO со всеми возможными функциями контрактов Treasury и GovernanceDAO, а также взаимодействие со сторонними контрактами. Все тесты были проведены в Callisto Testnet. Адреса развернутых тестовых контрактов, в которых производились тесты, следующие:
Treasury - https://testnet-explorer.callisto.network/address/0xf82533EC6F73C56549e21B24a93B9f1612A99C67/contracts
GovernanceDAO - https://testnet-explorer.callisto.network/address/0xb356A5a5710Cac1677854f1b95608D1d4B4B417d/contracts

В настоящее время контракты Treasury и GovernanceDAO развернуты в сети Callisto Network по следующим адресам:
Treasury - https://02.callisto.network/address/0x5633F30748A435322030fB02F1097CFe9D311bA4/contracts
GovernanceDAO - https://02.callisto.network/address/0x810059e1406dEDAFd1BdCa4E0137CbA306c0Ce36/contracts
Пользовательский интерфейс DAO доступен по следующему адресу:
https://callisto-dao.2bears.exchange/
Со всеми действующими членами DAO можно ознакомиться через пользовательский интерфейс. Несмотря на то, что DAO было запущено всего несколько часов назад, к нему уже подключилось 8 проверенных членов сообщества. Мы продолжаем его формировать из активных и проверенных членов сообщества Callisto Network.

Теперь о ВАЖНОМ:

  1. Контракт Treasury распределяет казну следующим образом
    50% - управляются действующим DAO
    50% - второй получатель, в настоящее время это я. @Dexaran мне необходимо получить от тебя адрес, которому я передам права на второго получателя. После чего контракт Treasury будет готов принимать казну

  2. Когда человек выдвигает предложение на голосование, то он может оставлять комментарий к своему предложению. Такие комментарии сохраняются в блокчейне. Если у человека имеется предложение, которое требует значительного пояснения, то логично чтобы он его описал на github и в комментарий создаваемого предложения просто указал ссылку на него. Прощу тебя определить такое место где будут создаваться подобные описания к создаваемым предложениям.

@Dexaran
Copy link
Member Author

Dexaran commented Apr 25, 2024

The Callisto DAO codes are now located here: https://github.com/EthereumCommonwealth/CallistoDAO

Please send your pull requests to the repo in EthereumCommonwealth and lets keep Callisto core contracts there from now on. I'm ready to transfer the Community Treasury share to the Callisto Community DAO contract.

@Dexaran
Copy link
Member Author

Dexaran commented Apr 25, 2024

@Upaut1

@Dexaran мне необходимо получить от тебя адрес, которому я передам права на второго получателя. После чего контракт Treasury будет готов принимать казну

@Dexaran I need to get an address from you to which I will transfer the rights to the second recipient. After which the Treasury contract will be ready to accept treasury

Please use the New Treasury address: 0x3264Fb22a50ecadc6DFd0F0e1938a0eef965C491

@Dexaran
Copy link
Member Author

Dexaran commented Apr 25, 2024

@Upaut1

Когда человек выдвигает предложение на голосование, то он может оставлять комментарий к своему предложению. Такие комментарии сохраняются в блокчейне. Если у человека имеется предложение, которое требует значительного пояснения, то логично чтобы он его описал на github и в комментарий создаваемого предложения просто указал ссылку на него. Прощу тебя определить такое место где будут создаваться подобные описания к создаваемым предложениям.

When someone creates a new proposal there is a possibility to attach a comment there. Such comments will be stored on-chain. If the proposal requires a long explanation/description then it would be a good idea to place these descriptions somewhere on github and put a link to the proposal in the github comment. I'm proposing to create such a github space for proposal descriptions.

Let's use the original "Proposals" repo and create github issues for each new proposal
https://github.com/EthereumCommonwealth/Proposals/issues

@Upaut1
Copy link

Upaut1 commented Apr 26, 2024

Please use the New Treasury address: 0x3264Fb22a50ecadc6DFd0F0e1938a0eef965C491

В контракте "Treasury" права второго получателя переданы адресу 0x3264Fb22a50ecadc6DFd0F0e1938a0eef965C491, теперь он распоряжается 50% поступающего CLO. Так что контракт "Treasury" полностью готов к приему казны

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants