Skip to main content

Implement Voting Smart Contract Logic

In this step, we will be implementing the logic of our voting smart contract in the src/BuildersDAO.cs file!

Checking Smart Contract Logics

Proceed to overwrite the existing contents of the current file with the following code snippet.

using System.Collections.Generic;
using System.Security.Principal;
using AElf.Sdk.CSharp;
using AElf.Sdk.CSharp.State;
using AElf.Types;
using Google.Protobuf.WellKnownTypes;

namespace AElf.Contracts.BuildersDAO
{
public class BuildersDAO : BuildersDAOContainer.BuildersDAOBase
{
const string author = "REPLACE PLACEHOLDER HERE";

// Implement Initialize Smart Contract Logic
public override Empty Initialize(Empty input) { }

// Implement Join DAO Logic
public override Empty JoinDAO(Address input) { }

// Implement Create Proposal Logic
public override Proposal CreateProposal(CreateProposalInput input) { }

// Implement Vote on Proposal Logic
public override Proposal VoteOnProposal(VoteInput input) { }

// Implement Get All Proposals Logic
public override ProposalList GetAllProposals(Empty input) { }

// Implement Get Proposal Logic
public override Proposal GetProposal(StringValue input) { }

// Implement Get Member Count Logic
public override Int32Value GetMemberCount(Empty input) { }

// Implement Get Member Exist Logic
public override BoolValue GetMemberExist(Address input) { }
}
}

To begin, let’s first fill in the author string variable with a unique name.

danger

Aelf sidechain does not recognise duplicate smart contracts hence we will be using the author variable as the unique identifier for our voting smart contract!

as the aelf sidechain does not recognise duplicate smart contracts hence we will be using the author variable as the unique identifier for our voting smart contract!

Implementing Initialize Function

Next, proceed to head over to the comment // Implement Initialize Smart Contract Logic where we will be implementing the Initialize functionality!

In this function, we will first check if the smart contract has been already initialized and return if the smart contract has already been initialized!

Next, we will define a hardcoded proposal for demonstration purposes, parsing in the necessary parameters such as the id, title, description and more!

Next, we will update our Proposals state variable with our hardcoded proposal and increment the proposalId.

With a better understanding of how the function works, proceed to populate the Initialize function with the following code snippet.

// Implement Initialize Smart Contract Logic
public override Empty Initialize(Empty input)
{
Assert(!State.Initialized.Value, "already initialized");
var initialProposal = new Proposal
{
Id = "0",
Title = "Proposal #1",
Description = "This is the first proposal of the DAO",
Status = "IN PROGRESS",
VoteThreshold = 1,
};
State.Proposals[initialProposal.Id] = initialProposal;
State.NextProposalId.Value = 1;
State.MemberCount.Value = 0;

State.Initialized.Value = true;

return new Empty();
}

Implementing Join DAO Function

Next, proceed to head over to the comment // Implement Join DAO Logic where we will be implementing the Join DAO functionality!

In this function, we will first check if the member is already part of the DAO by checking the Members mapped state variable. If the member is not part of the DAO, we will then update the Members state variable to true based on the user’s address.

We will then increment the membersCount state variable to represent the latest count of members in the DAO!

This function will be implemented by you all. After completed the implementation, you can go to the next page to check if your implementation matches the reference code.

// Implement Join DAO Logic
public override Empty JoinDAO(Address input)
{
// Based on the address, determine whether the address has joined the DAO. If it has, throw an exception
// If the address has not joined the DAO, then join and update the state's value to true
// Read the value of MemberCount in the state, increment it by 1, and update it in the state
// Using 'return null' to ensure the contract compiles successfully. Please update it to the correct return value when implementing
return null;
}

Implementing Create Proposal Function

Next, proceed to head over to the comment // Implement Create Proposal Logic where we will be implementing the Create Proposal functionality!

In this function, we first check if the user is a member of the DAO as only members of the DAO are able to create proposals!

Next, we will create a new proposal object parsing in the fields from the "CreateProposalInput" message definition from our BuildersDAO.proto file.

We will then update the Proposals state variable with the newly created proposal object, increment the NextProposalId state variable and return the newly created proposal object.

With a better understanding of how the function works, proceed to populate the CreateProposal function with the following code snippet.

// Implement Create Proposal Logic
public override Proposal CreateProposal(CreateProposalInput input)
{
Assert(State.Members[input.Creator], "Only DAO members can create proposals");
var proposalId = State.NextProposalId.Value.ToString();
var newProposal = new Proposal
{
Id = proposalId,
Title = input.Title,
Description = input.Description,
Status = "IN PROGRESS",
VoteThreshold = input.VoteThreshold,
YesVotes = { }, // Initialize as empty
NoVotes = { }, // Initialize as empty
};
State.Proposals[proposalId] = newProposal;
State.NextProposalId.Value += 1;
return newProposal; // Ensure return
}

Implementing Vote On Proposal Function

Next, proceed to head over to the comment // Implement Vote on Logic where we will be implementing the Vote on Proposal functionality!

In this function, we will first perform the following checks:

  • Check if the member is already a member of the DAO as only DAO members can vote on a proposal
  • Check if the proposal to be voted on exists otherwise return an error message that the proposal is not found
  • Check if the member has already voted on the specific proposal as only DAO members can only vote once on the proposal If the member passes the following checks, we will then store the member’s vote and update the proposal State.

At the same time, we will also check if the updated votes count has reached the current vote threshold whereby if the yesVotes count reaches the vote threshold, the status of the proposal will be updated to "PASSED".

Similarly, if the noVotes count has reached the threshold, the status of the proposal will be updated to "DENIED".

With a better understanding of how the function works, proceed to populate the VoteOnProposal function with the following code snippet.

// Implement Vote on Proposal Logic
public override Proposal VoteOnProposal(VoteInput input)
{
Assert(State.Members[input.Voter], "Only DAO members can vote");
var proposal = State.Proposals[input.ProposalId]; // ?? new proposal
Assert(proposal != null, "Proposal not found");
Assert(
!proposal.YesVotes.Contains(input.Voter) && !proposal.NoVotes.Contains(input.Voter),
"Member already voted"
);

// Add the vote to the appropriate list
if (input.Vote)
{
proposal.YesVotes.Add(input.Voter);
}
else
{
proposal.NoVotes.Add(input.Voter);
}

// Update the proposal in state
State.Proposals[input.ProposalId] = proposal;

// Check if the proposal has reached its vote threshold
if (proposal.YesVotes.Count >= proposal.VoteThreshold)
{
proposal.Status = "PASSED";
}
else if (proposal.NoVotes.Count >= proposal.VoteThreshold)
{
proposal.Status = "DENIED";
}

return proposal;
}

Implementing Get All Proposals Function

Next, proceed to head over to the comment // Implement Get All Proposals Logic which allows us to retrieve the list of proposals created by members of the DAO!

This is implemented by first creating a new ProposalList object as defined from the message definition from the BuildersDAO.proto file.

Next, we will fetch and loop through the Proposals state variable and update the ProposalList object with the proposal objects and finally return the list of proposals!

This function will be implemented by you all as well. After completed the implementation, you can go to the next page to check if your implementation matches the reference code.

// Implement Get All Proposals Logic
public override ProposalList GetAllProposals(Empty input)
{
// Create a new list called ProposalList
// Start iterating through Proposals from index 0 until the value of NextProposalId, read the corresponding proposal, add it to ProposalList, and finally return ProposalList
// Using 'return null' to ensure the contract compiles successfully. Please update it to the correct return value when implementing
return null;
}

Implementing Get Proposal / Get Member Count / Get Member Exist Functions

Aelf requires explicit getter methods to access the state value, so we provide these three getter methods for accessing the state.

These three methods are used to retrieve different states respectively.

  • // Implement Get Proposal Logic which allows us to retrieve the proposal created by proposalId. Pass the proposalId to call this method, And use the proposalId as the key to query the corresponding value from State.Proposals and return the value.
  • // Implement Get Member Count Logic which allows us to retrieve the member count. Pass the empty parameter to call this method, And return the value of MemberCount in the State.
  • // Implement Get Member Exist Logic which allows us to retrieve the value of whether member exists. Pass the address to call this method, And use the address as the key to query the corresponding value from State.Members and return the value.
// Implement Get Proposal Logic
public override Proposal GetProposal(StringValue input)
{
var proposal = State.Proposals[input.Value];
return proposal;
}

// Implement Get Member Count Logic
public override Int32Value GetMemberCount(Empty input)
{
var memberCount = new Int32Value {Value = State.MemberCount.Value};
return memberCount;
}

// Implement Get Member Exist Logic
public override BoolValue GetMemberExist(Address input)
{
var exist = new BoolValue {Value = State.Members[input]};
return exist;
}

With that, we have implemented all the functionalities of our Voting dApp smart contract!

In the next step, we will compile our smart contract and deploy our written smart contract to the aelf sidechain!