> ## Documentation Index
> Fetch the complete documentation index at: https://whalescorp.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Смарт-контракт DAO

# Общие данные

**Ссылка:** [https://github.com/tonwhales/nominators-dao](https://github.com/tonwhales/nominators-dao\\)

**Лицензия:** [MIT](https://opensource.org/licenses/MIT)

**Язык:** [Tact](https://tact-lang.org/)

# Что такое DAO-контракт

Это DAO-смарт-контракт, который автоматически:

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

# Как это работает

* Фиксированные доли участников

Есть список участников с долями, например 10%, 15%, 5% и так далее. Когда в DAO поступают деньги, будь то доход, вывод из пула или другой источник, контракт автоматически распределяет их строго по этим долям. Никакого ручного расчета, кому сколько положено: все зашито в коде.

* Общий банк и минимальный баланс

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

* Голосование по предложениям

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

* Для принятия решения определенное количество участников должно проголосовать за или против, обычно это около двух третей, но не менее двух голосов.
* Один участник — один голос по каждому конкретному предложению.
* Если набрано достаточно голосов "за", контракт автоматически выполняет действия, заложенные в предложение. Если набрано достаточно голосов "против", предложение считается отклоненным.
* Не доверие людям, а доверие к коду

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

# Что контракт дает партнерам

* Прозрачное и автоматическое распределение доходов между всеми участниками по четким правилам.
* Формализованное принятие решений: любые важные действия проходят через голосование, результат которого нельзя "подкрутить".
* Минимальную операционную нагрузку: не нужны ручные выплаты и ручной контроль, все реализовано в коде.
* Предсказуемость и устойчивость: контракт защищен от "обнуления" за счет минимального баланса и проверок баланса перед рискованными действиями.

# Структура и основные сущности

**DAOWithSplitter** — основной DAO-контракт.\
Хранит:

* `managable: Address` — адрес управляемого стейкинг-контракта или пула.
* `members: map[Int]Int` — набор участников:
  * Ключ — хэш адреса участника, только basechain, `workchain = 0`
  * Значение — вес или доля участника в виде целого числа
* `denominator: Int` — общий знаменатель для расчета долей, сумма всех весов участников не превышает это значение.
* `withdrawFee: Int` — фиксированная сумма, отправляемая в `managable` при запросе на вывод стейка.

**Proposal** — вспомогательный контракт для голосования по одному предложению.\
Хранит:

* `owner: Address` — адрес DAO, то есть инициатор и владелец предложения.
* `members: map[Int]Int` — локальную копию долей участников на момент создания предложения.
* `votesNeeded: Int` — количество голосов, необходимое для принятия решения.
* `agreed, disagreed` — счетчики голосов "за" и "против".
* `proposal: map[Int]ProposedMessage` — набор сообщений, которые будут исполнены в случае принятия предложения.

**ProposedMessage** — описание одного исходящего сообщения, которое DAO отправит после успешного голосования:

* `to: Address, value: Int, mode: Int, bounce: Bool, body: Cell`.

## Ключевые константы

* `PROPOSAL_MINIMUM_BALANCE` — минимальный баланс, необходимый для безопасного создания предложения.
* `DAO_MINIMUM_BALANCE` — минимальный баланс на счете DAO, ниже которого вывод средств недопустим.

# Логика голосования и предложений

1. Создание предложения, сообщение `CreateProposal`
   1. Отправитель должен быть участником DAO, то есть его хэш должен присутствовать в `members`.
   2. На счете сообщения `ctx.value` должно быть больше, чем `2 × PROPOSAL_MINIMUM_BALANCE`, чтобы покрыть деплой и работу контракта предложения.
2. Голосование в контракте `Proposal` через `receive("Agree")` и `receive("Disagree")`
   1. Голосовать могут только адреса из basechain, то есть `wc == 0`, которые присутствуют в `members` и имеют ненулевой вес.
   2. Один участник может проголосовать только один раз: после голосования его вес в `members` обнуляется.
   3. Если количество голосов "за" больше либо равно `votesNeeded`:
      1. Вызывается `executeProposal()`
      2. Контракт `Proposal` отправляет в DAO сообщение `ExecuteProposal` с картой `messages`.
   4. Если количество голосов "против" больше либо равно `votesNeeded`:
      1. Вызывается `terminateProposal()`
      2. В DAO уходит текстовое сообщение `"Terminated"`.
3. Исполнение предложения в DAO через `receive(proposal: ExecuteProposal)`
   1. DAO проверяет, что отправитель является корректным контрактом предложения.
   2. Пересчитывает `StateInit` ожидаемого `Proposal` на основе текущего состояния DAO и полученной карты `messages`.
   3. Сравнивает `ctx.sender` с адресом, вычисленным из этого `StateInit`.

* Если проверка проходит, DAO последовательно отправляет все `ProposedMessage`.
* Каждая запись в `messages` становится обычным `send()` с указанными полями.

# Механика распределения

`splitAndSend(value: Int)`

* Вычисляет `splittable = value / denominator * denominator` и распределяет только эту "делимую" часть.
* Если `splittable ≤ 0`, ничего не делает.
* Итеративно проходит по словарю `members` через `nativeDictGetMin` и `nativeDictGetNext`.
* Для каждого участника:
  * извлекает `memberShare`,
  * преобразует хэш-ключ в `Address` basechain.
* Затем отправляет участнику:
  * `value = splittable / denominator * memberShare`
  * в `body` комментарий вида `"X/Y of Z (whales revenue share)"`, где:
    * `X` — доля участника,
    * `Y` — знаменатель,
    * `Z` — общая распределяемая сумма.

# Входящие сообщения в DAO

`"Topup DAO"` — пополнение DAO без дополнительной логики, сообщение просто принимается.\
`"Terminated"` — служебное сообщение для синхронизации с контрактом предложения, дополнительных действий не выполняется.\
`"Withdraw"` — отправитель должен быть участником DAO.\
Проверяется условие: `myBalance() - withdrawFee > DAO_MINIMUM_BALANCE`.

* Если условие выполнено:
  * DAO отправляет в `managable` сообщение `WithdrawStake` с:
    * случайным `queryId`,
    * фиксированным `gasLimit`,
    * `stake = 0`, поскольку фактический размер стейка определяется внешним контрактом.

`"Gift"` — любой пользователь может отправить средства с этим текстом.

* DAO сохраняет `DAO_MINIMUM_BALANCE` как неснижаемый остаток, а остальную часть, то есть `ctx.value - DAO_MINIMUM_BALANCE`, если она положительна, распределяет через `splitAndSend`.

`WithdrawStakeResponse` и `WithdrawStakeDelayed` — это сообщения, которые приходят от `managable` после завершения операций по выводу стейка.

* По логике они обрабатываются аналогично `"Gift"`:
  * из входящей суммы вычитается `DAO_MINIMUM_BALANCE`,
  * оставшаяся сумма, если она больше нуля, распределяется между участниками через `splitAndSend`.

# Get-методы

`memberShare(addr: Address): Int` — возвращает вес участника по его адресу. Если адрес не относится к участникам, выбрасывается ошибка.\
`membersCount(): Int` — считает количество записей в словаре `members`.\
`minimumVotes(): Int` — возвращает минимальное число голосов, необходимое для принятия решения.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.