# Polling app
This tutorial creates a blockchain poll application.
This tutorial builds understanding as it walks you through creating a blockchain app, adding and modifying types for a transaction, editing messages, and designing the front-end app.
You will learn how to
- Create a simple blockchain poll application
- Design a front-end app that lets an end user sign in, create polls, cast votes, and see voting results
- Add logic to require funds to execute the create poll transaction
- Modify a REST endpoint
- Modify CLI transactions
- Add module components to the front-end app
- Add a bank keeper to the module
- Modify a message
This tutorial requires Starport (opens new window) v0.17.0.
Important The tutorial is based on this specific version of Starport and is not supported for other versions.
The Starport tool is the easiest way to build a blockchain and accelerates chain development.
/usr/local/bin, run the following command:
When the installation succeeds, you see this message:
# Voting App Goals
Create a blockchain poll app with a voting module. The app requires that the app end user can:
- Sign in
- Create polls
- Cast votes
- See voting results
Design the app so that you can:
Collect transaction fees:
- The create poll transaction fee is 200 tokens.
- Voting is free.
Restrict transactions only to signed in poll app end users.
# Build your Blockchain App
Use Starport to scaffold the blockchain app and the voting module.
Important In the code examples throughout this tutorial, when you see
cosmonaut be sure to substitute it with cosmonaut. You need to do this in some of the Vue and REST API examples later. The app end user in this tutorial is alice.
# Build the new blockchain
To scaffold a new blockchain named voter:
A new directory named
voter is created in your current directory.
voter directory contains a working blockchain app and all of the code you need to build and launch a blockchain-based app, including these files and directories:
appcontains files that connect all of the moving parts of your application
cmdis responsible for the
voterddaemon that starts and interacts with the app
protocontains the protobuf types
vuecontains the web user interface as shown at the top of this tutorial
xcontains the Cosmos SDK
Cosmos SDK modules are the building blocks of apps. If you are new to Cosmos SDK modules, see Introduction to SDK Modules (opens new window).
# Launch the voter app
To launch the app from the
voter project directory:
The following output is returned, along with any errors that might show up in your application. Two default users and their mnemonic pass phrases are created.
Congratulations! You successfully created and launched a blockchain application.
# Add a Poll Transaction
The voter app doesn't do anything yet, so the next step is to add some types. Adding types generates files that implement create, read, update, and delete (CRUD) functionality for a custom new type.
The voting applications has two types of entities: polls and votes.
# Add the poll type
A poll type has a
title and a list of
In a new terminal window, run the following command in the
After the poll type is successfully created, you see:
With this command, you generated the code that handles the creation of
# View the Front-end User Interface
To see the front-end app form for creating polls, Starport includes a
While running your blockchain, change into the
vue directory, install the dependencies and start your frontend.
It takes a few minutes to rebuild the app, so give it a couple of seconds. If your
localhost:8080 is already in use, your app can be viewed on the next available port.
# Sign in as Alice
On the front-end app, sign in as end user Alice. The mnemonic passphrases for Alice and Bob were printed in the console after you ran the
starport chain serve command.
After you are signed in as Alice, you can import an existing wallet that was created with the app. The wallet in the voter app can handle multiple accounts, so give your wallet a descriptive name. Using a descriptive wallet name helps you recognize this wallet in future transactions. For this example, naming this wallet
voter makes sense.
- Click Access Wallet and then click Import existing wallet.
- Enter the passphrase for Alice that was output to your console when you launched the voter app with the
starport servecommand and click Next
- Name your wallet
voterand enter a password.
- Click Done.
Now you want to view the custom
poll type you created earlier.
# View the Poll Type
To view the newly created
poll transaction type, click the Custom Type navigation point on the web browser front-end app.
To see the workflow to create a poll, enter an example value for the title and poll options. A new object is created and displayed next to the new poll form. You have successfully created an object and stored it on the blockchain!
This object, however, does not look and work exactly like a poll. You need to be able to add option fields and store them as an array. You want the option fields to display as interactive buttons.
Take a look at some of the files modified by the
starport scaffold list command.
# Modify the Protobuffer Types
To have multiple options in the poll, you must change the value
string options in the Protobuffer definitions.
- In the
proto/voterdirectory, open the
- To allow passing an array of strings, add the keyword
proto/voter/tx.proto file, update the CRUD (Create, Read, Update and Delete) types for the poll transaction.
- Add the keyword
string optionsfor the
# Modify the Poll Transaction Message
Navigate to the
message_poll.go file at
x/voter/types/message_poll.go that defines a message that creates a poll.
- To store the options as a list instead of a string, replace
options stringin the
And also in the
# About the Poll Keeper
To write anything to a blockchain or perform any other state transition, a client makes an HTTP POST request. In our case, the voter web app is the client.
The handler creates an unsigned transaction that contains an array of messages. The client then signs the transaction and sends it to http://localhost:1317/txs (opens new window). The application processes the transaction by sending each message to a corresponding handler, in our case
A handler then calls a
CreatePoll function that is defined in
x/voter/keeper/poll.go that writes the poll data into the store.
# Modify the CLI Transaction
A poll app end user can also interact with your application by using a command line interface.
The CLI definition is available at
This command generates a transaction with a create poll message, signs the transaction using the private key of app end user
alice, and broadcasts the transaction to the blockchain. Remember,
alice is one of two users that this tutorial created by default.
The modification you need to make is to change a line that reads arguments from the console.
In the function
You end up with the following function:
Now make similar changes for the function
These changes assume that all arguments after the first one represent a list of options.
You end up with the following function:
Run and reset the app state of your blockchain with:
# Add the Votes
At this point, you have created a blockchain that lets app end users create polls. Now it's time to enable the app end users to cast votes on an existing poll.
To create the vote type:
This command creates a vote type transaction with:
- A poll ID
- An option that is a string representation of the selected answer
Now, restart the application. Remember to use the
--reset-once flag to recognize the code changes.
Each time you reset the application state, all of the data from your previously created state is not saved.
Each time the app restarts, the app end users alice and bob receive new passphrases and new tokens. Make sure to update the wallet accounts in the front-end app after you reset the state of the blockchain.
Now that you have made all the required changes to the app, take a look at the client-side application.
# Front-end Application
Starport automatically generated a basic front end for the app. For convenience, Vue.js (opens new window) framework is used with Vuex (opens new window) for state management. Because all features of the app are exposed through an HTTP API, you can build clients using any language or framework.
For the front-end app, you can focus on the content of these directories:
These directories contain the code for the page templates of your app.
Handles sending transactions and receiving data from your blockchain and the
@tendermint/vue(opens new window)directory that contains UI components, like buttons and forms. This directory contains the generated protobuffer file definitions that were defined in the
vue/src/store/generated/cosmonaut/voter/cosmonaut.voter.voter/index.jshas the generated transactions
MsgDeletePollthat use the CosmJS (opens new window) library for handling wallets, creating, signing and broadcasting transactions and defines a Vuex store.
# Add the Voter Module Component to the Front End
Navigate to the
Since you don't need the default form component, replace these two lines in
with two new components and the
To import the component, add the import statements in the
<script>tag after the template code. The
Now you can start creating the PollForm and PollList components.
# Create the PollForm Component
Note: Some of the following steps depend on one another. If you look at your front-end app before you have updated all of the components that depend on one another, the front-end app might not load. Don't worry if the front-end app doesn't load at this point, this expected behavior happens because you have not yet completed code updates for all of the dependencies. Just complete the steps. Everything should work fine after the tutorial is completed and the pieces are wired up correctly.
For the PollForm, create a new file
Add this code to give the PollForm component a title and two buttons:
Refresh the page.
Sign in as an app end user with a password.
Create a new poll. It takes a few seconds to process the transaction.
Now, visit http://localhost:1317/voter/poll (opens new window). This endpoint is defined in
# Create the Poll List Component
Create a new
PollList.vuefile for the component in
PollList component you just created lists every poll, including the options for that poll as buttons. Selecting an option triggers a
submit method that broadcasts a transaction with a create vote message and fetches data back from your application.
Two components are still missing from your app to make look more like a voting poll. Now you can add the
AppText.vue UI options.
# Add the Options Component
# Add the Poll List Text Component
Now you can add the text for the poll list in
# Update the Front-end App
vue/src/App.vue to fetch the votes.
App.vue file handles the transactions of the components. Modify the code in the
<script> tag to look like:
By now you should be able to see the same front-end app UI that you saw in the first screenshot. Try creating polls and casting votes. You might notice that it's possible to cast multiple votes for one poll. This activity is not what you want, so you can fix that behavior.
# Access the API
To fix this issue, you first have to understand how data is stored in your application.
Think of the data storage as a lexicographically ordered key-value store. You can loop through the entries, filter by key prefix, add, update, and delete entries. It is easier to visualize the store as JSON.
When you create a poll and cast a vote, this is the resulting JSON:
See the API and JSON output of your created poll endpoint at http://localhost:1317/cosmonaut/voter/voter/poll (opens new window).
To see the votes, go to the API endpoint at http://localhost:1317/cosmonaut/voter/voter/vote (opens new window).
The endpoint paths are defined by the cosmonaut that you used when bootstrapping the application with Starport, together with the module name. So, if your GitHub user name is
cosmonaut, then you can find:
- The poll endpoint at
- The API endpoint at
Looking into this data, you can see that the combination of
pollID is what you are looking for.
# Limit to One Vote per User
Each account should be allowed to have only 1 vote per pollID.
The logic for access to a certain transaction is in the
keeper directory. For the votes transaction logic, open the
msg_server_vote.go file at
x/voter/keeper/msg_server_vote.go, and modify the
CreateVote function to check if the account has already voted and to return an error when a subsequent vote is cast.
After you restart the app, a front-end app user can cast only 1 vote per poll.
# Introducing a Fee for Creating Polls
Add the logic for the transaction so that creating a poll costs 200 tokens.
You already require users to have accounts registered. Each app end user has tokens on balance. The only thing you need to do is to send coins from the app end user's account to a module account before you create a poll.
# Add the Bank Keeper to the Voter Module
First, load the
expected_keepers in the
x/voter/types/expected_keepers.go file to define all of the bank functions you want to make available in your module.
Second, add the
bankKeeper keeper to the
x/voter/keeper/keeper.go file. Add it to the
type and the
NewKeeper function as follows:
Finally, add the bank module in the New function to the
voterKeeper in the
app.go file in
In the keeper loading section, replace:
Now, you are ready to use all of the bank functions that you added to the expected keepers file. The next step is to define the transaction to require the funds to execute the create poll transaction.
# Modify the Create Poll Message with the Price
Modify the msg at
The fee payment occurs before
k.AppendPoll so if an end user does not have enough tokens, the application raises an error and does not proceed with creating a poll.
The import statement requires
"github.com/tendermint/tendermint/crypto". Be sure to add this repo to the import statement if your text editor didn't do that for you.
Now, restart the app and try creating several polls to see how the transaction affects your token balance.
Congratulations, you have built a blockchain voting application.