---
# System prepended metadata

title: Voting System Tutorial

---


## Voting System Tutorial

### Create Objects/Define Data
 
#### Create the blockchain

<pre>
ignite scaffold chain <b>votingsystem</b> --no-module
cd votingsystem
</pre>

#### Create Module

<pre>
ignite scaffold module <b>votingsystem</b> --dep bank
</pre>

#### Create System Variables

- Election Counter
- Tokens Number Issued
- Max Tokens

<pre>
ignite scaffold single systemInfo electionCounter:uint tokensAllocated:uint tokensMax:uint --module votingsystem --no-message
</pre>

Edit proto/votingsystem/genesis.proto

<pre>
SystemInfo systemInfo = 2 <b>[(gogoproto.nullable) = false];</b>
</pre>

Run

<pre>
ignite generate proto-go
</pre>

Edit x/votingsystem/types/genesis.go

 **From:**

<pre>
func DefaultGenesis() *GenesisState {
	return &GenesisState{
	SystemInfo:     nil,
</pre>

**To**

<pre>
const DefaultIndex uint64 = 1
const NumberOfTokens uint64 = 0
const MaxTokensToIssue uint64 = 1

func DefaultGenesis() *GenesisState {
	return &GenesisState{
		SystemInfo:     SystemInfo{
			ElectionCounter: uint64(DefaultIndex),
			TokensAllocated: uint64(NumberOfTokens),
			TokensMax: uint64(MaxTokensToIssue),
		},
	},
</pre>

Edit x/votingsystem/genesis.go

**From**

<pre>
if genState.SystemInfo != nil {
        k.SetSystemInfo(ctx, *genState.SystemInfo)
}
</pre>

**To**

<pre>
if genState.SystemInfo != (types.SystemInfo{}) {
        k.SetSystemInfo(ctx, genState.SystemInfo)
}
</pre>

Also (around line 40)

**From**

<pre>
if found {
  genesis.SystemInfo = &systemInfo
}
</pre>

**To**

<pre>
if found {
  genesis.SystemInfo = systemInfo
}
</pre>

### Create Objects/Define Data
 
We need to define the structures that will store the data of the voting system

- Election  *(A thing for people to vote on. Only yes/no votes.)*
- Vote      *(A token that allows a vote. Transferrable.)*
- Ballot    *(A ballot that is tied to an election and must be cast by token holder.)*

#### Commands to create data structures

<pre>
ignite scaffold map vote owner nickname --module votingsystem --no-message
ignite scaffold map election desc voter startDate endDate --module votingsystem --no-message
ignite scaffold map ballot electionid:uint vote choice:bool --module votingsystem --no-message
</pre>

#### Commands to create data messages

<pre>
ignite scaffold message createElection desc startDate endDate --module votingsystem --response electionId
ignite scaffold message castBallot electionId:uint voteToken choice:bool --module votingsystem
ignite scaffold message issueVoteToken nickname --module votingsystem
ignite scaffold message transferVoteToken tokenIndex newTokenHolder nickname --module votingsystem
</pre>

#### Commands to create queries

<pre>
ignite scaffold query electionResult electionID --module votingsystem --response votesFor,votesAgainst
ignite scaffold query myVoteList --module votingsystem --response voteList
</pre>

- **Make method to create election**

Edit election file x/votingsystem/keeper/msg\_server\_create\_election.go

<pre>
   ctx := sdk.UnwrapSDKContext(goCtx)

    // Get and increment election counter
    systemInfo, found := k.Keeper.GetSystemInfo(ctx)
    if !found {
        panic("SystemInfo not found")
    }
    nextIndex := systemInfo.ElectionCounter
    nextIndex++
    newIndex := strconv.FormatUint(nextIndex, 10)

    // Get vote info

    // Check dates
    if false {
        return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Bid is not high enough")
    }

    startDate := msg.StartDate
    endDate := msg.EndDate

    thisElection := types.Election{
        Index: newIndex,
        Desc: msg.Desc,
        Voter: msg.Creator,
        StartDate: startDate,
        EndDate: endDate,
    }

    k.SetElection(ctx, thisElection);

    // Save the systemInfo number of tokens
    systemInfo.ElectionCounter = nextIndex;
    k.SetSystemInfo(ctx, systemInfo)

    return &types.MsgCreateElectionResponse{ElectionId: newIndex}, nil
</pre>

Edit election file x/votingsystem/keeper/msg\_server\_cast\_ballot.go

<pre>
ctx := sdk.UnwrapSDKContext(goCtx)

// Try getting a election from the store
ElectionId, electionValid := k.GetElection(ctx, strconv.FormatUint(uint64(msg.ElectionID), 10))
voteToken, voteValid := k.GetVote(ctx, msg.VoteToken)

if !electionValid || !voteValid {
    // Make custom error. This is just temporary
    return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "You've provided bad data")
}
if msg.Creator != msg.VoteToken {
    // Make custom error. This is just temporary
    return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "You've provided bad data")
}

externalAnswer, err := callExternalService(ElectionId.String())
if err != nil {
    log.Fatalf("did not connect: %v", err)
}
fmt.Println("From External Service: " + externalAnswer)

thisBallot := types.Ballot{
    Index: ElectionId.String(),
    Vote: voteToken.String(),
    Choice:  msg.Choice,
}

k.SetBallot(ctx, thisBallot);

return &types.MsgCastBallotResponse{}, nil
</pre>

Edit election file x/votingsystem/keeper/msg\_server\_issue\_vote\_token.go

<pre>
// In the import section
"strconv"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

// In the IssueVoteToken func
ctx := sdk.UnwrapSDKContext(goCtx)

    systemInfo, found := k.Keeper.GetSystemInfo(ctx)
    if !found {
        panic("SystemInfo not found")
    }
    nextIndex := systemInfo.TokensAllocated
    nextIndex++
    newTokenIndex := strconv.FormatUint(nextIndex, 10)

    if nextIndex > systemInfo.TokensMax {
        return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "All vote tokens have been issued")
    }

    newVote := types.Vote{
        Index: newTokenIndex,
        Owner: msg.Creator,
        Nickname: msg.Nickname,
    }

    k.SetVote(ctx, newVote)

    // Save the systemInfo number of tokens
    systemInfo.TokensAllocated = nextIndex;
    k.SetSystemInfo(ctx, systemInfo)

    return &types.MsgIssueVoteTokenResponse{}, nil
</pre>

Edit election file x/votingsystem/keeper/msg\_server\_transfer\_vote\_token.go

<pre>
// In import
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

// In TransferVoteToken func
ctx := sdk.UnwrapSDKContext(goCtx)

// If the message didn't come from the token holder, error out
voteToken, voteValid := k.GetVote(ctx, msg.TokenIndex)
if !voteValid {
    return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Token not found")
}

if msg.Creator != voteToken.Owner {
    return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Non-Owner tried to tranfer token")
}

newVote := types.Vote{
    Index: voteToken.Index,
    Owner: msg.NewTokenHolder,
    Nickname: msg.Nickname,
}

k.SetVote(ctx, newVote)

return &types.MsgTransferVoteTokenResponse{}, nil
</pre>

#### Add Error Messages

Add some error messages (x/votingsystem/types/errors.go)

<pre>
ErrInvalidDeadline = sdkerrors.Register(ModuleName, 1109, "Deadline cannot be parsed: %s")
</pre>

#### Add Unit Tests

Make a list of test files and sample content

<pre>
Sample test code
</pre>

Add external program call to ballot casting

Edit election file x/votingsystem/keeper/msg\_server\_cast\_ballot.go

**Add**

<pre>
// At top, in import section
	"strconv"
    "flag"
    "log"
    "time"
	"fmt"

	"google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"

// Inside CastBallot Method, before "thisBallot := types.Ballot"
externalAnswer, err := callExternalService(ElectionId.String())
if err != nil {
    log.Fatalf("did not connect: %v", err)
}
fmt.Println("From external service: " + externalAnswer)

// At bottom
func callExternalService(dataToSend string) (string, error) {
    var (
        addr = flag.String("addr", "localhost:50051", "the address to connect to")
        name = flag.String("name", dataToSend, "Name to greet")
    )

    flag.Parse()
    // Set up a connection to the server.
    conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    
    c := types.NewGreeterClient(conn)

    // Contact the server and print out its response.
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    
    r, err := c.SayHello(ctx, &types.HelloRequest{Name: *name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.GetMessage())
    return r.GetMessage(), nil
}
</pre>

Create proto file proto/votingsystem/greeter.proto

**Add**

<pre>
syntax = "proto3";
package votingsystem.votingsystem;

option go_package = "votingsystem/x/votingsystem/types";

service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
</pre>


## Set up the queries

Edit the file proto/votingsystem/query.proto

**Change**

<pre>
message QueryMyVoteListResponse {
  string voteList = 1;
}
</pre>

**To**

<pre>
message QueryMyVoteListResponse {
  repeated string vote = 1 [(gogoproto.nullable) = false];
  cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
</pre>

Run command

<pre>
ignite generate proto-go
</pre>

Edit file x/votingsystem/keeper/grpc\_query\_my\_vote\_list.go

<pre>
// Body of MyVotingList, after ctx line

// Define a variable that will store a list of posts
var votes []*types.Vote

// Get the key-value module store using the store key (in this case store key is "chain")
store := ctx.KVStore(k.storeKey)

// Get the part of the store that keeps posts (using vote key, which is "Vote-value-")
voteStore := prefix.NewStore(store, []byte(types.VoteKey))

pageRes, err := query.Paginate(voteStore, req.Pagination, func(key []byte, value []byte) error {
    var vote types.Vote
    if err := k.cdc.Unmarshal(value, &vote); err != nil {
        return err
    }
    votes = append(votes, &vote)

    return nil
})

return &types.QueryMyVoteListResponse{Vote: votes}, nil
</pre>

Edit file x/votingsystem/keeper/grpc\_query\_election\_result.go

<pre>
// Add to import section
"strconv"

// Body of ElectionResult, after ctx line

// Define a variable that will store a list of posts
var ballotFor int = 0
var ballotAgainst int = 0

// Get the key-value module store using the store key (in this case store key is "chain")
store := ctx.KVStore(k.storeKey)

// Get the part of the store that keeps posts (using ballot key, which is "Ballot-value-")
ballotStore := prefix.NewStore(ballot, []byte(types.BallotKey))

// Terrible way to do this. Need to find way to just query what I want instead of everything
pageRes, err := query.Paginate(ballotStore, req.Pagination, func(key []byte, value []byte) error {
    var ballot types.Ballot
    if err := k.cdc.Unmarshal(value, &vote); err != nil {
        return err
    }

    if ballot.ElectionId == msg.ElectionId {
        if ballot.Choice == "true" {
            ballotFor++
        } else {
            ballotAgainst++
        }
    }

    return nil
})

return &types.QueryElectionResultResponse{VotesFor: strconv.Itoa(ballotFor), VotesAgainst: strconv.Itoa(ballotAgainst)}, nil
</pre>


Start up your new blockchain(zone)

<pre>
ignite chain serve
</pre>
