Skip to main content

Define RPC Methods and Messages

Defining Methods and Messages

In this step, we will first be implementing the RPC methods and message definitions of our Voting dApp in the BuildersDAO.proto file!

Proceed to open the src/Protobuf/contract/BuildersDAO.proto and overwrite the existing file contents with the following code snippet.

syntax = "proto3";

import "aelf/core.proto";
import "aelf/options.proto";
import "google/protobuf/empty.proto";
import "acs12.proto";

// The namespace of this class
option csharp_namespace = "AElf.Contracts.BuildersDAO";

service BuildersDAO {
// The name of the state class the smart contract is going to use to access
// blockchain state
option (aelf.csharp_state) = "AElf.Contracts.BuildersDAO.BuildersDAOState";
option (aelf.base) = "acs12.proto";

// Actions -> Methods that change state of smart contract
// This method sets up the initial state of our StackUpDAO smart contract
rpc Initialize(google.protobuf.Empty) returns (google.protobuf.Empty);

// This method allows a user to become a member of the DAO by taking in their
// address as an input parameter
rpc JoinDAO(aelf.Address) returns (google.protobuf.Empty);

// This method allows a user to create a proposal for other users to vote on.
// The method takes in a "CreateProposalInput" message which comprises of an
// address, a title, description and a vote threshold (i.e how many votes
// required for the proposal to pass)
rpc CreateProposal(CreateProposalInput) returns (Proposal);

// This method allows a user to vote on proposals towards a specific proposal.
// This method takes in a "VoteInput" message which takes in the address of
// the voter, specific proposal and a boolean which represents their vote
rpc VoteOnProposal(VoteInput) returns (Proposal);

// Views -> Methods that does not change state of smart contract
// This method allows a user to fetch a list of proposals that had been
// created by members of the DAO
rpc GetAllProposals(google.protobuf.Empty) returns (ProposalList) {
option (aelf.is_view) = true;
}

// aelf requires explicit getter methods to access the state value,
// so we provide these three getter methods for accessing the state
// This method allows a user to fetch a proposal by proposalId
rpc GetProposal (google.protobuf.StringValue) returns (Proposal) {
option (aelf.is_view) = true;
}

// This method allows a user to fetch the member count that joined DAO
rpc GetMemberCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) {
option (aelf.is_view) = true;
}

// This method allows a user to check whether this member is exist by address
rpc GetMemberExist (aelf.Address) returns (google.protobuf.BoolValue) {
option (aelf.is_view) = true;
}
}

// Message definitions
message Member {
aelf.Address address = 1;
}

message Proposal {
string id = 1;
string title = 2;
string description = 3;
repeated aelf.Address yesVotes = 4;
repeated aelf.Address noVotes = 5;
string status = 6; // e.g., "IN PROGRESS", "PASSED", "DENIED"
int32 voteThreshold = 7;
}

message CreateProposalInput {
aelf.Address creator = 1;
string title = 2;
string description = 3;
int32 voteThreshold = 4;
}

message VoteInput {
aelf.Address voter = 1;
string proposalId = 2;
bool vote = 3; // true for yes, false for no
}

message MemberList {
repeated Member members = 1;
}

message ProposalList {
repeated Proposal proposals = 1;
}

Understanding the Code

In this code snippet, we first defined the proto3 syntax version! Next, we proceed to import the necessary Protobuf definitions and libraries that are essential for our smart contract.

Next, we proceed to define the important RPC methods for our smart contract which are namely:

  • Initialize - This method sets up the initial state of our StackUpDAO smart contract
  • JoinDAO - This method allows a user to become a member of the DAO by taking in their address as an input parameter
  • CreateProposal - This method allows a user to create a proposal for other users to vote on. The method takes in a "CreateProposalInput" message which comprises of an address, a title, description and a vote threshold (i.e how many votes required for the proposal to pass)
  • VoteOnProposal - This method allows a user to vote on proposals towards a specific proposal. This method takes in a "VoteInput" message which takes in the address of the voter, specific proposal and a boolean which represents their vote
  • GetAllProposals - This method allows a user to fetch a list of proposals that had been created by members of the DAO

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

  • GetProposal - This method allows a user to fetch a proposal by proposalId
  • GetMemberCount - This method allows a user to fetch the member count that joined DAO
  • GetMemberExist - This method allows a user to check whether this member is exist by address

With our RPC methods defined, the code snippet next defines the following message definitions required to implement the functionalities of our Voting dApp smart contract.

Here are the following message definitions:

  • Member - Represents a DAO member with their address as an identifier
  • Proposal - Represents a proposal with fields such as a title, description, votes, status of the proposal and a vote threshold
  • CreateProposalInput - Represents the required fields to create a proposal such as the title, description and vote threshold
  • VoteInput - Represents the required fields to cast a vote on a proposal such as the proposal id to indicate the specific proposal and the boolean vote value where true represents yes and false represents no
  • MemberList - A list of DAO members
  • ProposalList - A list of proposals In summary, this smart contract allows users to initialize the DAO smart contract, join a DAO, create proposals for other users to vote on and allows members of the DAO to view all proposals!

Defining Contract State

In AElf, the contract state represents the data stored by a smart contract on the blockchain.

Open the src/BuildersDAOState.cs file.

Proceed to override the existing contents of the file with the following code snippet below.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using AElf.Sdk.CSharp.State;
using AElf.Types;

namespace AElf.Contracts.BuildersDAO
{
// The state class is access the blockchain state
public class BuildersDAOState : ContractState
{
public BoolState Initialized { get; set; }
public MappedState<Address, bool> Members { get; set; }
public MappedState<string, Proposal> Proposals { get; set; }
public Int32State MemberCount { get; set; }
public Int32State NextProposalId { get; set; }
}
}

Understanding the Code

In this code snippet, we have defined the following state variables when our voting smart contract has been initialized!

Here are some of the key state variables:

  • Members - Here, we define a mapping that associates each member to a boolean value to represent if the member has indeed joined the DAO
  • Proposals - Similarly, we define a mapping that associates each proposal to an id value so that our smart contract can identify and retrieve the specific proposal
  • MemberCountId and NextProposalId - Tracks the total number of members and proposals respectively of the DAO

With that, we have successfully implemented the service, message and state definitions of our smart contract!

Up next, we will implement the logic of our voting smart contract!