Sui.

Post

Share your knowledge.

article banner.
BigSneh.
Sep 04, 2025
Article

The Anatomy of a Secure DAO on Sui: Voting, Roles, and Objects

Decentralized Autonomous Organizations (DAOs) are a natural fit for Sui’s object-centric model. Unlike Ethereum-style smart contracts where governance often relies on storage mappings, Sui encourages structuring governance around Move objects. This leads to gas-efficient, modular, and secure designs.

In this post, we’ll explore how to design a secure DAO on Sui, focusing on voting, role-based permissions, and proposal execution.

Why Objects Instead of Mappings?

On Ethereum, DAOs often store proposals, votes, and roles in mappings (e.g., mapping(uint => Proposal)). This works, but updating global storage can become costly and prone to contention.

On Sui, each proposal and each role can be its own object:

Proposals are owned or shared objects.

Votes can be lightweight objects linked to proposals.

Roles are represented as capabilities — tokens that grant permissions.

This object-based model is inherently parallelizable and minimizes hotspots in shared state.


Core DAO Components

  1. DAO Root Object

Tracks governance parameters (quorum, proposal threshold, etc.).

Shared object, but kept tiny to reduce conflicts.

  1. Proposal Objects

Each proposal is a separate object created when a member submits one.

Stores metadata (description, deadline, vote counts).

  1. Vote Objects

Each vote is an owned object submitted by a member.

Proposal object tallies results by consuming or referencing votes.

  1. Roles via Capabilities

AdminCap: allows upgrading DAO parameters.

MemberCap: allows creating proposals and voting.

These capabilities are distributed at DAO creation and can later be delegated.

Sample Code: Roles + Proposal Voting

module dao::governance { use sui::object::{Self, UID}; use sui::tx_context::TxContext; use sui::transfer; use std::option;

/// Root DAO object
struct DAO has key {
    id: UID,
    quorum: u64,
    proposal_count: u64,
}

/// Capability for admin operations
struct AdminCap has key, store { id: UID }

/// Capability for members
struct MemberCap has key, store { id: UID }

/// Proposal object
struct Proposal has key {
    id: UID,
    proposer: address,
    description: vector<u8>,
    yes_votes: u64,
    no_votes: u64,
    executed: bool,
}

/// Initialize DAO with one admin
public entry fun init(ctx: &mut TxContext): (DAO, AdminCap) {
    let dao = DAO { id: object::new(ctx), quorum: 10, proposal_count: 0 };
    let cap = AdminCap { id: object::new(ctx) };
    (dao, cap)
}

/// Add a member capability (admin only)
public entry fun add_member(dao: &mut DAO, _admin: &AdminCap, ctx: &mut TxContext): MemberCap {
    MemberCap { id: object::new(ctx) }
}

/// Create a proposal
public entry fun propose(
    dao: &mut DAO,
    _member: &MemberCap,
    description: vector<u8>,
    ctx: &mut TxContext
): Proposal {
    dao.proposal_count = dao.proposal_count + 1;
    Proposal {
        id: object::new(ctx),
        proposer: tx_context::sender(ctx),
        description,
        yes_votes: 0,
        no_votes: 0,
        executed: false,
    }
}

/// Cast a vote
public entry fun vote(p: &mut Proposal, support: bool) {
    if (support) {
        p.yes_votes = p.yes_votes + 1;
    } else {
        p.no_votes = p.no_votes + 1;
    }
}

/// Execute a proposal (if quorum met)
public entry fun execute(dao: &DAO, p: &mut Proposal) {
    assert!(p.executed == false, 1);
    assert!(p.yes_votes >= dao.quorum, 2);
    p.executed = true;
    // DAO-specific logic (e.g., transfer funds, change params) goes here
}

}

Security Considerations

Resource Safety: Capabilities (AdminCap, MemberCap) enforce who can act — no need to track roles in global storage.

Replay Protection: Ensure votes can’t be double-counted by consuming Vote objects.

Gas Efficiency: Proposals and votes are independent objects, so transactions can run in parallel without clashing.

Upgradability: DAO parameters (e.g., quorum) live in the root DAO object, adjustable only by AdminCap.


Best Practices

  1. Keep the DAO root object small — don’t store vectors of all proposals or votes inside it.

  2. Use dynamic fields or references only when absolutely necessary.

  3. Push heavy analytics (e.g., full voting history) off-chain via events.

  4. Build for parallelism: independent proposals and votes should not block each other.

  5. Consider capability delegation if your DAO wants sub-committees or role hierarchies.

Conclusion

Sui’s object-based model enables DAOs that are more parallel, gas-efficient, and secure than traditional mapping-based governance. By structuring proposals, votes, and roles as objects, developers can create governance systems that scale gracefully while minimizing contention.

  • Sui
  • SDKs and Developer Tools
  • Transaction Processing
1
Share
Comments
.