Build a DAO Smart Contract in Algorand using Beaker #1
Tags: algorand, blockchain, beginners, tutorial
Published: 2023-11-07 14:58:24

Decentralized Autonomous Organizations (DAOs) have gained significant attention in the blockchain space for their ability to facilitate decentralized decision-making and governance. Algorand, a blockchain platform known for its speed and security, offers a powerful environment for creating smart contracts, including DAOs. In this article, we'll walk through a sample DAO smart contract written in PyTeal, the smart contract language for Algorand. We'll explain each part of the contract and how it works.

## Step 1: Declare Variables and Initialize the Application

In this first section, we declare the necessary variables and initialize the application state.

```
from beaker import *
from pyteal import *

from beaker.lib.storage import BoxMapping
```


### Define a data structure for a proposal

```
class proposalStruct(abi.NamedTuple):
description: abi.Field[abi.String]
vote_yes: abi.Field[abi.Uint64]
vote_no: abi.Field[abi.Uint64]
status: abi.Field[abi.String]
```


### Define the smart contract's application state

```
class AppStateValue:
    memberCount = GlobalStateValue(
        stack_type=TealType.uint64,
        default=Int(0),
        descr="Number Member of DAO",
    )

    proposalCount = GlobalStateValue(
        stack_type=TealType.uint64,
        default=Int(0),
        descr="Number of Proposals"
    )

    memberDAO = LocalStateValue(
        stack_type=TealType.uint64,
        default=Int(0),
        descr="Member DAO or not",
    )

    check_voted = ReservedLocalStateValue(
        stack_type=TealType.uint64,
        max_keys=8,
        descr=("Check if a user voted in a proposal"),
    )

    proposals = BoxMapping(abi.Uint64, proposalStruct)
```


### Create an Algorand application called "Simple DAO" with the defined state

```
app = (
    Application("Simple DAO", state=AppStateValue())
    .apply(unconditional_create_approval, initialize_global_state=True)
    .apply(unconditional_opt_in_approval, initialize_local_state=True)
)
```


-   We declare a data structure `proposalStruct` to represent a proposal, which includes a description, vote counts (yes and no), and a status field.
-   The `AppStateValue` class defines the global and local state variables required for the DAO. It includes the number of members, the number of proposals, member status, a reserved local state value to check if a user voted, and a mapping to store proposals.
-   We create an Algorand application named "Simple DAO" with the defined state.

## Step 2: Join and Leave the DAO

Next, we define functions for joining and leaving the DAO.

###  Check member DAO status

```
@app.external
def check_member_dao(*, output: abi.Uint64) -> Expr:
    return output.set(app.state.memberDAO[Txn.sender()])
```


### Join the DAO

```
@app.external
def join_dao() -> Expr:
    return Seq(
        app.state.memberDAO[Txn.sender()].set(Int(1)),
        app.state.memberCount.increment(),
    )
```


### Leave the DAO

```
@app.external
def leave_dao() -> Expr:
    return Seq(
        app.state.memberDAO[Txn.sender()].set(Int(0)),
        app.state.memberCount.decrement(),
    )
```


-   `check_member_dao`: This function checks if the sender is a member of the DAO. If they are, it sets the output to `1`; otherwise, it sets it to `0`.
-   `join_dao`: This function allows a user to join the DAO. It sets their DAO membership status to `1` and increments the member count.
-   `leave_dao`: This function lets a user leave the DAO. It sets their DAO membership status to `0` and decrements the member count.

## Step 3: Create Proposals

We define a function to create proposals in the DAO.

### Create Proposal

```
@app.external
def create_proposal(descr: abi.String, *, output: proposalStruct) -> Expr:
    proposal_tuple = proposalStruct()
    proposalId = abi.Uint64()
    vote_yes = abi.Uint64()
    vote_no = abi.Uint64()
    status = abi.String()
    return Seq(
        vote_yes.set(Int(0)),
        vote_no.set(Int(0)),
        status.set("In Progress"),
        proposal_tuple.set(descr, vote_yes, vote_no, status),
        proposalId.set(app.state.proposalCount.get()),
        app.state.proposals[proposalId].set(proposal_tuple),
        app.state.proposalCount.increment(),
        app.state.proposals[proposalId].store_into(output),
    )
```


-   The `create_proposal` function allows a user to create a new proposal. It initializes vote counts, sets the status to "In Progress," and stores the proposal in the contract's state. The proposal's details and status are stored in the `output` variable.

## Step 4: Check and End Proposals

We define functions to check the status of proposals and end proposals when certain conditions are met.

### Check Proposal

```
@app.external
def check_proposal(proposalId: abi.Uint64, *, output: proposalStruct) -> Expr:
    return app.state.proposals[proposalId].store_into(output)
```


### End Proposal when total vote > 1/2 member Count

```
@app.external
def end_proposal(proposalId: abi.Uint64, *, output: abi.String) -> Expr:
    proposal = proposalStruct()
    description = abi.String()
    vote_yes = abi.Uint64()
    vote_no = abi.Uint64()
    status = abi.String()
    new_status = abi.String()

    total_dao_member = app.state.memberCount.get()
    app.state.proposals[proposalId].store_into(proposal)
    description.set(proposal.description)
    vote_yes.set(proposal.vote_yes)
    vote_no.set(proposal.vote_no)
    status.set(proposal.status)

    if status != "In Progress":
        return output.set("Proposal Ended")

    if 2 * (vote_yes + vote_no) < total_dao_member:
        return output.set("Not enough votes")

    if vote_yes < vote_no:
        return Seq(
            output.set("Proposal failed"),
            new_status.set("Proposal failed"),
            proposal.set(description, vote_yes, vote_no, new_status),
            app.state.proposals[proposalId].set(proposal),
        )

    return Seq(
        output.set("Proposal succeeded"),
        new_status.set("Proposal succeeded"),
        proposal.set(description, vote_yes, vote_no, new_status),
        app.state.proposals[proposalId].set(proposal),
    )
```


-   `check_proposal`: This function allows users to check the details of a specific proposal.
-   `end_proposal`: Users can end a proposal using this function if it has received enough votes. The function checks the vote counts and the total number of DAO members to determine if a proposal passes or fails.

## Step 5: Check Voted and Vote on Proposals

We define functions to check if a user has voted and to cast votes on proposals.

```
@app.external
def check_voted(proposalId: abi.Uint64, *, output: abi.Uint64) -> Expr:
    return output.set(app.state.check_voted[proposalId])
```


### Vote Function

```
@app.external
def vote(proposalId: abi.Uint64, vote_choice: abi.Uint8, *, output: abi.String) -> Expr:
    proposal = proposalStruct()
    description = abi.String()
    vote_yes = abi.Uint64()
    vote_no = abi.Uint64()
    status = abi.String()
    return Seq(
        proposal.decode(app.state.proposals[proposalId].get()),
        description.set(proposal.description),
        status.set(proposal.status),
        vote_yes.set(proposal.vote_yes),
        vote_no.set(proposal.vote_no),
        If(vote_choice.get() == Int(1))
        .Then(
            vote_yes.set(vote_yes.get() + Int(1)),
        )
        .Else(
            vote_no.set(vote_no.get() + Int(1)),
        ),
        proposal.set(description, vote_yes, vote_no, status),
        app.state.proposals[proposalId].set(proposal),
        output.set("Vote Successfully"),
    )
```


-   `check_voted`: This function allows users to check if they have already voted on a specific proposal.
-   `vote`: Users can cast their votes on proposals using this function. It increments the vote counts accordingly and updates the proposal's status.

Now, you have a complete step-by-step guide to building and interacting with the DAO smart contract on Algorand. You can opt-in to the contract, join the DAO, create and vote on proposals, check the status of proposals, and more, based on your use case.
## Next 
- Check only users who have joined the DAO can create proposals and vote.
- Check that users can only vote once.
- How to deploy and interact with dAppflow.