Figment Datahub and Avalanche: Make an e-Voting dApp Using Figment Datahub, Avalanche, and Filebase
Learn make an e-Voting dApp Using Figment Datahub, Avalanche, and Filebase.
A dApp is a decentralized application that can operate autonomously, typically through the use of smart contracts, and runs on blockchain networks.
Figment DataHub is a platform that enables developers to create decentralized applications (dApps) using the powerful and unique features of blockchain technology without having to be experts on the wide variety of blockchain protocols.
Avalanche is a decentralized, open-source blockchain that has smart contract functionality.
In this guide, we’ll use Figment Datahub to create an API key that we’ll use to create a decentralized application on the Avalanche blockchain. We’ll also utilize S3FS-FUSE to mount a Filebase IPFS bucket locally to our system so that we can store our dApp source files.
- Perform a cross-chain swap via the Transfer AVAX Between X-Chain and C-Chain tutorial to get funds to your C-Chain address on the Fuji testnet.
This guide was written and tested using Ubuntu 20.04. Commands and workflow may vary depending on your operating system.

Then use the ‘Create App’ button to create your new Avalanche app.

We will reference this API key later.

To do this, navigate to console.filebase.com. If you don’t have an account already, sign up, then log in.
Select ‘Create Bucket’ in the top right corner to create a new bucket.

Bucket names must be unique across all Filebase users, be between 3 and 63 characters long, and can contain only lowercase characters, numbers, and dashes.

Set up a credentials file for S3FS at
${HOME}/.passwd-s3fs.
You will need to save your Filebase Access and Secret keys to this file and give it owner permissions. You can do so with the following commands:echo ACCESS_KEY_ID:SECRET_ACCESS_KEY > ${HOME}/.passwd-s3fs
chmod 600 ${HOME}/.passwd-s3fs
ACCESS_KEY_ID is your Filebase Access key, and SECRET_ACCESS_KEY is your Filebase Secret key. For more information on Filebase access keys, see here.
You can mount a Filebase IPFS bucket with the command:
s3fs mybucket /path/to/mountpoint -o passwd_file=${HOME}/.passwd-s3fs -o url=https://s3.filebase.com
- mybucket: name of your Filebase bucket
- /path/to/mountpoint
cd /path/to/mounted/bucket
mkdir evoting
cd evoting
npm init
npm install express dotenv @truffle/hdwallet-provider --save
truffle init
This command sets up our initial project structure, which includes a contracts folder that will house our smart contracts, and a migrations folder for deployment functions.
require('dotenv').config();
const HDWalletProvider = require("@truffle/hdwallet-provider");
//Account credentials from which our contract will be deployed
const mnemonic = process.env.MNEMONIC;
//API key of your Datahub account for Avalanche Fuji test network
const APIKEY = process.env.APIKEY;
module.exports = {
networks: {
fuji: {
provider: function() {
return new HDWalletProvider({mnemonic, providerOrUrl: `https://avalanche--fuji--rpc.datahub.figment.io/apikey/${APIKEY}/ext/bc/C/rpc`, chainId: "0xa869"})
},
network_id: "*",
gas: 3000000,
gasPrice: 470000000000,
skipDryRun: true
}
},
solc: {
optimizer: {
enabled: true,
runs: 200
}
}
}
- Input your Avalanche API key that we took note of earlier.
The format is as follows:
MNEMONIC="avalanche-wallet-mnemonic"
APIKEY="your-api-key"
pragma solidity >=0.4.21 <0.6.0;
contract Election {
//Structure of candidate standing in the election
struct Candidate {
uint id;
string name;
uint voteCount;
}
//Storing candidates in a map
mapping(uint => Candidate) public candidates;
//Storing address of those voters who already voted
mapping(address => bool) public voters;
//Number of candidates in standing in the election
uint public candidatesCount;
//Adding 2 candidates during the deployment of contract
constructor () public {
addCandidate("Filebase Robot");
addCandidate("Figment Robot");
}
//Private function to add a candidate
function addCandidate (string memory _name) private {
candidatesCount ++;
candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
}
//Public vote function for voting a candidate
function vote (uint _candidate) public {
require(!voters[msg.sender], "Voter has already Voted!");
require(_candidate <= candidatesCount && _candidate >= 1, "Invalid candidate to Vote!");
voters[msg.sender] = true;
candidates[_candidate].voteCount++;
}
}
This is a solidity smart contract that will allow us to view the candidates and their standing in the election.
const Election = artifacts.require("./Election.sol");
module.exports = function (deployer) {
deployer.deploy(Election);
};
truffle compile
Any time you make changes to the
Election.sol
file, you will need to recompile the contract.truffle migrate --network fuji
mkdir src
cd src
var express = require('express');
var app = express();
//JSON file for deployed contract and network information
const electionJSON = require('../build/contracts/Election.json')
require("dotenv").config();
app.use(express.static("./"));
app.get('/', (req,res) => {
res.send('index.html');
});
app.get('/electionJSON', (req,res) => {
res.send(electionJSON);
});
app.listen(process.env.PORT || 3000, () => {
console.log('Server started at 3000');
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Election</title>
</head>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<body>
<div style="width: 40%; margin: 50px auto" class="card">
<!-- Account address will be rendered here -->
<center id="account" style="margin-top: 20px"></center>
<!-- Loading will appear until blockchain data is loaded -->
<center id='loader' style="margin:20px;">Loading...</center>
<br><br>
<!-- Blockchain data would appear here -->
<div id="content" style="display:none" class="container" style="margin-top:30px;">
<!-- Table for fetching election data of the candidates -->
<table class="table table-bordered">
<tr>
<td>#</td>
<td>Name</td>
<td>Votes</td>
</tr>
<tbody id="candidateResults">
</tbody>
</table>
<!-- Form to submit vote to a candidate -->
<form onSubmit="App.castVote(); return false;" style="display:none">
<div class="form-group">
<label>Select Candidate</label>
<center>
<select class="form-control" id="candidatesSelect">
<option>Select here...</option>
</select><br>
<input type="submit" class="btn btn-primary" value="Vote">
</center>
</div>
</form>
<!-- This would appear and form will be hidden if the address has already voted -->
<div id="hasVoted" style="display:none; text-align: center">
<b>Thank you for voting !!!</b>
</div>
</div>
</div>
</body>
<!--jQuery CDN-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!--web3 module for interacting with blockchain-->
<script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/gh/ethereum/[email protected]/dist/web3.js"></script>
<!--Truffle Contract module for interacting with smart contract in javascript-->
<script src="https://rajranjan0608.github.io/ethereum-electionVoting/src/contract.js"></script>
<!--Our custom javascript code for interaction-->
<script type="module" language="javascript" src="index.js"></script>
</html>
You can change the styling of this code to reflect your candidate's names, different colors and themes, and even photos of each candidate.
// App would contain all the necessary functions for interaction
var App = {
loading: false,
contracts: {},
// Main function to be called first
load: async () => {
await App.loadWeb3();
await App.loadAccount();
await App.loadContract();
await App.render();
},
// Loading web3 on the browser
loadWeb3: async () => {
if(typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
App.web3Provider = web3.currentProvider;
}else {
window.alert("Please connect to Metamask");
}
if(window.ethereum) {
window.web3 = new Web3(ethereum);
try {
await ethereum.enable();
}catch (error) {
console.log(error);
}
}else if(window.web3) {
App.web3Provider = web3.currentProvider;
window.web3 = new Web3(web3.currentProvider);
}else{
console.log('Non-Ethereum Browser detected');
}
},
// This function would load account from Metamask to our dApp
loadAccount: async() => {
await web3.eth.getAccounts().then((result)=>{
App.account = result[0];
console.log(App.account);
});
},
// This function would help in loading contract to App.election
loadContract: async () => {
// Static pre-deployed contracts should be handled like this
const election = await $.getJSON('/electionJSON');
App.contracts.election = TruffleContract(election);
App.contracts.election.setProvider(App.web3Provider);
App.election = await App.contracts.election.deployed();
},
// This function will be called after the browser is ready for blockchain interaction
render: async() => {
if(App.loading) {
return;
}
App.setLoading(true);
$('#account').html(App.account);
App.renderCandidates();
App.setLoading(false);
},
// This will render blockchain data to the frontend.
renderCandidates: async() => {
var candidatesCount = await App.election.candidatesCount();
$("#candidateResults").html("");
$("#candidatesSelect").html("");
for(var i=1; i <= candidatesCount; i++) {
const candidate = await App.election.candidates(i);
const id = candidate[0];
const name = candidate[1];
const voteCount = candidate[2];
var candidateTemplate1 = "<tr>"+
"<td>" + id + "</td>" +
"<td>" + name + "</td>" +
"<td>" + voteCount + "</td>" +
"</tr>";
$("#candidateResults").append(candidateTemplate1);
var hasVoted = await App.election.voters(App.account);
if(!hasVoted) {
$("form").show();
$("#hasVoted").hide();
}else {
$("#hasVoted").show();
$("form").hide();
}
var candidateTemplate2 = "<option value='"+i+"'>" + name + "</option>";
$("#candidatesSelect").append(candidateTemplate2);
}
},
// This function will call vote() on Fuji testnet
castVote: async() => {
const candidateID = $("#candidatesSelect").val();
await App.election.vote(candidateID, { from: App.account });
App.renderCandidates();
},
setLoading: (boolean) => {
App.loading = boolean;
const loader = $('#loader');
const content = $('#content');
if(boolean) {
loader.show();
content.hide();
}else {
loader.hide();
content.show();
}
}
};
// Driver function to initiate the blockchain interaction
$(() => {
window.addEventListener('load', ()=>{
App.load();
});
});
window.App = App;
node server.js
27. You can view your dApp at http://localhost:3000/ and interact with the webpage we created with the files in the src
directory.
src
directory.
Congratulations! You’ve created an e-Voting app that is powered by blockchain and stored on decentralized storage!
If you have any questions, please join our Discord server, or send us an email at [email protected]