General Data
Link: https://github.com/tonwhales/nominators-dao License: MIT Language: TactWhat is a DAO contract
This is a DAO smart contract that automatically:- stores a common “bank” of funds,
- distributes incoming revenue among participants according to predefined shares,
- gives participants the ability to vote on important actions (proposals),
- manages an external staking contract (or pool) through which revenue is earned.
How it works
- Fixed participant shares
- Common bank and minimum balance
- Voting on proposals
- For decision-making, a certain number of participants need to vote for or against (approximately two-thirds, but minimum two).
- One participant — one vote per specific proposal.
- If enough “for” votes are collected — the contract automatically executes the actions embedded in the proposal. If “against” — the proposal is considered rejected.
- No trust in people, trust in code
What the contract gives to partners
- Transparent and automatic revenue distribution among all participants according to clear rules.
- Formalized decision-making: any important actions go through voting, the result of which cannot be “rigged”.
- Minimal operational burden: no manual payments and manual control — everything is implemented in code.
- Predictability and stability: the contract is protected from “zeroing out” through minimum balance and balance checks before risky actions.
Structure and main entities
DAOWithSplitter - main DAO contract.Stores:
managable: Address— address of the managed staking contract / pool.members: map[Int]Int— set of participants:- Key — hash of participant’s address (basechain only,
workchain = 0); - Value — “weight”/share of participant (integer).
- Key — hash of participant’s address (basechain only,
denominator: Int— common denominator for share calculation (sum of all member weights ≤ denominator).withdrawFee: Int— fixed amount sent tomanagablewhen requesting stake withdrawal.
Stores:
owner: Address— DAO address (initiator/owner of the proposal).members: map[Int]Int— local copy of participant shares at the time of proposal creation.votesNeeded: Int— number of votes needed for decision-making.agreed, disagreed— counters for “for” and “against” votes.proposal: map[Int]ProposedMessage— set of messages to be executed in case of acceptance.
to: Address, value: Int, mode: Int, bounce: Bool, body: Cell.
Key constants
PROPOSAL_MINIMUM_BALANCE— minimum balance required for safe proposal creation.DAO_MINIMUM_BALANCE— minimum balance on DAO account, below which fund withdrawal is not allowed.
Voting and proposal logic
- Proposal creation (
CreateProposalmessage)- Sender must be a DAO participant (
memberscontains their hash). - Message account (
ctx.value) must have > 2 × PROPOSAL_MINIMUM_BALANCE — to cover deployment and proposal contract operation.
- Sender must be a DAO participant (
- Voting in
Proposalcontractreceive("Agree")andreceive("Disagree"):- Only allowed for basechain addresses (
wc == 0) present inmemberswith non-zero weight. - One participant can vote only once — after voting, their weight in
membersis zeroed. - If number of “for” votes ≥
votesNeeded:executeProposal()is called:Proposalcontract sendsExecuteProposalmessage withmessagesmap to DAO.
- If number of “against” votes ≥
votesNeeded:terminateProposal()is called:- text message
"Terminated"goes to DAO.
- text message
- Only allowed for basechain addresses (
- Proposal execution in DAO (
receive(proposal: ExecuteProposal))- DAO checks that sender is the correct proposal contract:
- Recalculates
StateInitof expectedProposalbased on current DAO state and receivedmessages; - Compares
ctx.senderwith address computed from thisStateInit.
- compares
ctx.senderwith address computed from thisStateInit.- If check passes, DAO sequentially sends all
ProposedMessage:- Each entry in
messagesbecomes a regularsend()with specified fields.
- Each entry in
- If check passes, DAO sequentially sends all
Distribution mechanics
splitAndSend(value: Int)
- Calculates
splittable = value / denominator * denominatorand distributes only this “divisible” part. - If
splittable ≤ 0, does nothing. - Iteratively traverses
membersdictionary vianativeDictGetMin/nativeDictGetNext: - For each participant:
- Extracts
memberShare. - Converts hash key to
Address(basechain).
- Extracts
- Sends to participant:
value = splittable / denominator * memberShare.- In
body— comment like:"X/Y of Z (whales revenue share)", where:X— participant’s share,Y— denominator,Z— total distributed amount.
Incoming messages to DAO
"Topup DAO" - DAO top-up without additional logic; message is simply accepted."Terminated" - service message for synchronization with proposal contract; no additional actions performed."Withdraw" - sender must be a DAO participant.Checks that:
myBalance() - withdrawFee > DAO_MINIMUM_BALANCE.
- If condition is met:
- DAO sends
WithdrawStakemessage tomanagablewith:- random
queryId, - fixed
gasLimit, stake = 0(actual stake size is determined by external contract).
- random
- DAO sends
"Gift" - any user can send funds with this text.
- DAO retains
DAO_MINIMUM_BALANCEas minimum balance, the rest (ctx.value - DAO_MINIMUM_BALANCE) if positive is distributed viasplitAndSend.
WithdrawStakeResponse and WithdrawStakeDelayed - these messages come from managable after stake withdrawal operations completion.
- Similar to
"Gift":DAO_MINIMUM_BALANCEis subtracted from incoming value,- Remaining amount (if > 0) is distributed among participants via
splitAndSend.
Get methods
memberShare(addr: Address): Int - returns participant’s weight by their address. If address is not a participant — throws error.membersCount(): Int - counts number of entries in members dictionary.minimumVotes(): Int - returns minimum number of votes needed for decision-making.