Build a decentralized voting app with Choice Coin and Javascript algorand sdk   using NodeJS📨
Tags: javascript, algorand, tutorial, node
Published: 2022-01-20 23:39:09

[Choice Coin](https://choice-coin.com/) is an Algorand Standard Asset that powers Decentralized Decisions, a voting and governance software built directly on the Algorand Blockchain. Decentralized Decisions enables organizations to make governance decisions in an open and decentralized manner. 

In this Tutorial, we are going to build a decentralized voting application with javascript algorand sdk using NodeJs

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpwe59ydztd9lzdbkzs1.png)

# Requirements

* NPM and Node installed, download [HERE](https://phoenixnap.com/kb/install-node-js-npm-on-windows)
* A Purestake API key: [See Tutorial](https://developer.algorand.org/tutorials/getting-started-purestake-api-service/)
* Funded Testnet Accounts: [See Tutorial](https://developer.algorand.org/tutorials/create-account-testnet-javascript/)

# 1. Set up project folder and install algosdk

Create a new **project** folder, this can be done in the terminal with :


```
$ mkdir choice-coin-voting-app
```
After creating the project folder, enter the **directory** in your terminal


```
$ cd choice-coin-voting-app
```
To install dependencies in the project, you need to start an NPM instances using:


```
$ npm init -y
```
Create a new file, I will name mine `index.js`. This can also be done in the terminal with:



```
$ touch index.js
```
In the terminal, npm install both [AlgoSDK](https://github.com/algorand/js-algorand-sdk) and [Prompt-Sync](https://www.npmjs.com/package/prompt-sync)



```
$ npm install algosdk prompt-sync
```

[AlgoSDK](https://github.com/algorand/js-algorand-sdk) is the official JavaScript library for communicating with the Algorand network. It's designed for modern browsers and Node.js.
[Prompt-Sync](https://www.npmjs.com/package/prompt-sync) module is a function that creates prompting functions, this is the same thing with browsers' prompt but this works with NodeJs environment

In `index.js` file, import both modules


```
const algosdk = require('algosdk'); 
const prompt = require('prompt-sync')(); 
```

# 2. Configure Purestake API and Create client

Register for a [Purestake](https://developer.purestake.io/ ) developer account and get your API key to interact with algorand network.


```
const server = "https://testnet-algorand.api.purestake.io/ps2";
const port = "";
const token = {
  "X-API-Key": "YOUR API KEY", 
};
```
Create AlgodClient variable to start connection

```
const algodClient = new algosdk.Algodv2(token, server, port)
```


# 3. Recover Account And Enter Choice Coin Asset ID
* Create new testnet wallet address from either [myAlgoWallet](https://wallet.myalgo.com/) or [Algosigner](https://chrome.google.com/webstore/detail/algosigner/kmmolakhbgdlpkjkcjkebenjheonagdm?utm_source=googleads&utm_medium=adgroup5&utm_campaign=adgroup5&utm_id=adgroup5&gclid=Cj0KCQiAraSPBhDuARIsAM3Js4rUmz-VFZwht_OGGH4rSzjkIXwUC9oNktHWa2wTLvvRrNXFxXbdPKQaAqSQEALw_wcB), Copy and save your **25 mnemonic passphrase** and **do not share with anyone**.
* Fund the wallet address with some testnet algos [HERE](https://bank.testnet.algorand.network/).
* Opt In to **$Choice Coin** asset using ID **21364625** using [myAlgoWallet](https://wallet.myalgo.com/) or [Algosigner](https://chrome.google.com/webstore/detail/algosigner/kmmolakhbgdlpkjkcjkebenjheonagdm?utm_source=googleads&utm_medium=adgroup5&utm_campaign=adgroup5&utm_id=adgroup5&gclid=Cj0KCQiAraSPBhDuARIsAM3Js4rUmz-VFZwht_OGGH4rSzjkIXwUC9oNktHWa2wTLvvRrNXFxXbdPKQaAqSQEALw_wcB)
* Swap the testnet Algos to **$choice** on [Tinyman](https://testnet.tinyman.org/#/swap?asset_in=0&asset_out=21364625).

In `index.js` 


```

const mnemonic = "The mmemonic 25 characters seperated by a whitespace should be imported here"; 

const recoveredAccount = algosdk.mnemonicToSecretKey(mnemonic); 

const ASSET_ID = 21364625

const voting_address = "" 
```
In the constant `voting_address` input a newly created voting address wallet different from the one recovered that **$choice** voting amount can be sent to, **make sure $choice is opt-in to receive votes** just like earlier

# 4. Choosing Voting Option and Sending Vote

Create a voting function and send the voted amount from the `recoveredAccount` to the `voting_address` wallet depending on the candidate option being voted.

```
const chooseVotingOption = async () => {
    const candidateOption = prompt("Press 0 for candidate Zero or Press 1 for candidate One:") 
    
     const amount = prompt("Please enter Amount to commit to voting:");


    const params =  await algodClient.getTransactionParams().do()
    const encoder = new TextEncoder()

     if (!(candidateOption)) {
         console.log('Please select a valid candidate option');
     } else if (!Number(amount)) {
         console.log("Please Enter A valid Choice token amount to vote")
     }
      else  if (candidateOption == "0") {
            try {
                let txn = algosdk.makeAssetTransferTxnWithSuggestedParams(
                    recoveredAccount.addr,
                    voting_address,
                    undefined,
                    undefined,
                    Number(amount),
                    encoder.encode("Voting with Choice coin"),
                    ASSET_ID,
                    params

                )

        let signedTxn = txn.signTxn(recoveredAccount.sk);
        const response =  await algodClient.sendRawTransaction(signedTxn).do();
            if(response) {
              console.log(`You just voted for candidate Zero,Your voting ID: ${response.txId}`);
                
                waitForConfirmation(algodClient, response.txId);
            } else {
                console.log('error voting for candidate Zero, try again later')
            }
     
        }
        catch(error) {
            console.log("error voting for candidate Zero, Try again later");
        }
        
 } 


     else  if(candidateOption == "1"){
        try {
            let txn = algosdk.makeAssetTransferTxnWithSuggestedParams(
                recoveredAccount.addr,
                voting_address,
                undefined,
                undefined,
                Number(amount),
                encoder.encode("Voting with Choice coin"),
                ASSET_ID,
                params
            )
       let signedTxn = txn.signTxn(recoveredAccount.sk);
       const response =  await algodClient.sendRawTransaction(signedTxn).do();
            if(response) {
               console.log(`You just voted for candidate One,Your voting ID: ${response.txId}`);

                waitForConfirmation(algodClient, response.txId);
            } else {
               console.log('error voting for candidate one, try again later')
            }
        
        }
        catch(error) {
            console.log("Error voting for candidate One, Try again later");
        }
    
        }
        }
```


# 5. Wait For Confirmation To Sync Vote From Algorand Blockchain


```
const waitForConfirmation = async function (algodClient, txId) {
    let lastround = (await algodClient.status().do())['last-round'];
     while (true) {
        const pendingInfo = await algodClient.pendingTransactionInformation(txId).do();
        if (pendingInfo['confirmed-round'] !== null && pendingInfo['confirmed-round'] > 0) {
          //Got the completed Transaction
          console.log('Voting confirmed in round ' + pendingInfo['confirmed-round']);
          break;
        }
        lastround++;
        await algodClient.statusAfterBlock(lastround).do();
     }
 };
```
This function verify when the vote is being confirmed from algorand network.

# 6. Check $Choice Balance After Voting


```
const checkBalance = async () => {
    
  //get the account information
    const accountInfo =  await algodClient.accountInformation(recoveredAccount.addr).do();
    const assets =  accountInfo["assets"];
    
    //get choice amount from assets
     assets.map(asset => {
        if (asset['asset-id'] === ASSET_ID) {
            const amount = asset["amount"];
            const choiceAmount = amount / 100;
            console.log(
                `Account ${recoveredAccount.addr} has ${choiceAmount} $choice`
              );
              return;
        }  else {
            console.log(`Account ${recoveredAccount.addr} must opt in to Choice Coin Asset ID ${ASSET_ID}`);
          }
     })

  };
```
Check $Choice balance after voting has ended, if There is no choice asset ID in the account information. the function is stopped using `return` and a `console` message is shown to add choice asset ID to the acconut

# 7. Run The Full Code

The `index.js` would look like.


```
const algosdk = require('algosdk'); //importing algosdk
const prompt = require('prompt-sync')(); //importing nodeJs  prompt to enable prompt in a nodeJs environment

// open a purestaker api and get a unique API KEY
const server = "https://testnet-algorand.api.purestake.io/ps2";
const port = "";
const token = {
  "X-API-Key": "" //your API key gotten from purestake API, 
};
const algodClient = new algosdk.Algodv2(token, server, port); //connecting to algodclient

// create a testnet account with myalgowallet, keep the mmemonic key;
const mnemonic = "The mmemonic 25 characters seperated by a whitespace should be imported here";

// get account from mmemonic key;
const recoveredAccount = algosdk.mnemonicToSecretKey(mnemonic); 

//choice coin asset ID 
const ASSET_ID = 21364625

// voting address
const voting_address = "" //input a voting address wallet you can send choice to, make sure choice is opt-in to receive votes

//Press 1 to vote for candidate one and 0 to vote for candidate Zero

const chooseVotingOption = async () => {
    const candidateOption = prompt("Press 0 for candidate Zero or Press 1 for candidate One:") 
     const amount = prompt("Please enter Amount to commit to voting:");


    const params =  await algodClient.getTransactionParams().do(); //get params
    const encoder = new TextEncoder();  //message encoder

    // if there is no valid option 
     if (!(candidateOption)) {
         console.log('Please select a valid candidate option');
     } else if (!Number(amount)) {
         console.log("Please Enter A valid Choice token amount to vote")
     }
     // if your option is candidate zero
      else  if (candidateOption == "0") {
            try {
                let txn = algosdk.makeAssetTransferTxnWithSuggestedParams(
                    recoveredAccount.addr,
                    voting_address,
                    undefined,
                    undefined,
                    Number(amount),
                    encoder.encode("Voting with Choice coin"),
                    ASSET_ID,
                    params

                )

            let signedTxn = txn.signTxn(recoveredAccount.sk);
            const response =  await algodClient.sendRawTransaction(signedTxn).do();
            if(response) {
                console.log(`You just voted for candidate Zero,Your voting ID: ${response.txId}`);
                // wait for confirmation
                waitForConfirmation(algodClient, response.txId);
            } else {
                console.log('error voting for candidate Zero, try again later')
            }
     
        }
        catch(error) {
            console.log("error voting for candidate Zero, Try again later");
        }
        
 } 
 // if your option is candidate one

 else  if(candidateOption == "1"){
    try {
        let txn = algosdk.makeAssetTransferTxnWithSuggestedParams(
            recoveredAccount.addr,
            voting_address,
            undefined,
            undefined,
            Number(amount),
            encoder.encode("Voting with Choice coin"),
            ASSET_ID,
            params
        )
        let signedTxn = txn.signTxn(recoveredAccount.sk);
        const response =  await algodClient.sendRawTransaction(signedTxn).do();
        if(response) {
            console.log(`You just voted for candidate One,Your voting ID: ${response.txId}`);
            // wait for confirmation
            waitForConfirmation(algodClient, response.txId);
        } else {
            console.log('error voting for candidate one, try again later')
        }
    
    }
    catch(error) {
        console.log("Error voting for candidate One, Try again later");
    }

    }
    }

chooseVotingOption();

//verification function
const waitForConfirmation = async function (algodClient, txId) {
    let lastround = (await algodClient.status().do())['last-round'];
     while (true) {
        const pendingInfo = await algodClient.pendingTransactionInformation(txId).do();
        if (pendingInfo['confirmed-round'] !== null && pendingInfo['confirmed-round'] > 0) {
          //Got the completed Transaction
          console.log('Voting confirmed in round ' + pendingInfo['confirmed-round']);
          break;
        }
        lastround++;
        await algodClient.statusAfterBlock(lastround).do();
     }
 };
 

// check account balance
const checkBalance = async () => {
    
    
  //get the account information
    const accountInfo =  await algodClient.accountInformation(recoveredAccount.addr).do();
    const assets =  accountInfo["assets"];
    
    //get choice amount from assets
     assets.map(asset => {
        if (asset['asset-id'] === ASSET_ID) {
            const amount = asset["amount"];
            const choiceAmount = amount / 100;
            console.log(
                `Account ${recoveredAccount.addr} has ${choiceAmount} $choice`
              );
              return;
        }  else {
            console.log(`Account ${recoveredAccount.addr} must opt in to Choice Coin Asset ID ${ASSET_ID}`);
          }
     })

  };

checkBalance();
```
In Conclusion, We made a voting application with choice coin and JavaScript algorand SDK using NodeJS. You can check the full code on [Github](https://github.com/Samuellyworld/Choice-Coin-JS-Tutorial)