Team Members:
- Daiana Bilbao (hello_dayana#4030)
- Kevin Le (water1925#7425)
- Christina Polyukh (cpolyukh#5660)
We are deploying a ballot smart contract with proposals to vote on, and writing scripts to interact with the smart contract in the following ways:
- Deploy the ballot smart contract
- Query proposals
- Give voting rights to a wallet
- Cast a vote to the ballot
- Delegate vote to another user
What is your favorite cryptocurrency?
- Bitcoin
- Ethereum
- Avalanche
- Solana
- Algorand
- Oasis
- Cardano
- Dogecoin
- Polkadot
- Polygon
- Chainlink
- Shiba Inu
The ballot has been deployed on the Goerli test network.
Contract Address 0x16d0b05942f2d3b861dcd4544f7773b489410d0f Link
Transaction hash 0x2d2770a4c9b95b514ddce446e8e9a84fe62ce3c1cf6578a5ec256216d275bc12 Link
Call the ballot contract and iterate through the proposals array. Print each proposal converted from bytes32 to string.
console.log("Ballot: What's your favorite cryptocurrency out of the following:")
for (let i = 0; i < 12; i++) {
const proposal = await ballotContract.proposals(i);
const proposalString = ethers.utils.parseBytes32String(proposal.name);
console.log(`${i}: ${proposalString}`);
}
console.log(`End of proposals`);
yarn ts-node ./scripts/Ballot/queryProposals.ts 0x16d0b05942f2d3b861dcd4544f7773b489410d0f
Ballot: What's your favorite cryptocurrency out of the following:
0: Bitcoin
1: Ethereum
2: Avalanche
3: Solana
4: Algorand
5: Oasis
6: Cardano
7: Dogecoin
8: Polkadot
9: Polygon
10: Chainlink
11: Shiba Inu
End of proposals
Only the chairperson (creator of the Ballot smart contract) can give voting rights to other users.
const chairpersonAddress = await ballotContract.chairperson();
if (chairpersonAddress !== signer.address)
throw new Error("Caller is not the chairperson for this contract");
console.log(`Giving right to vote to ${voterAddress}`);
const tx = await ballotContract.giveRightToVote(voterAddress);
console.log("Awaiting confirmations");
await tx.wait();
console.log(`Transaction completed. Hash: ${tx.hash}`);
yarn ts-node ./scripts/Ballot/giveVotingRights.ts 0x16d0b05942f2d3b861dcd4544f7773b489410d0f 0x2d923D4846b958b19662c1b3d2c686b4b8B2AaDF
Attaching ballot contract interface to address 0x16d0b05942f2d3b861dcd4544f7773b489410d0f
Giving right to vote to 0x2d923D4846b958b19662c1b3d2c686b4b8B2AaDF
Awaiting confirmations
Transaction completed. Hash: 0xaaf076f67624c6247fbf800efd759e8d5e68991337a3ae6e6379b2716492b5c8
Wallet addresses with voting rights can cast votes to the ballot, to vote for one of the proposals.
let proposalNum = parseInt(proposal);
console.log(`Casting vote for proposal #${proposalNum} for account ${voter}`);
const voteTx = await ballotContract.vote(proposal);
console.log("Awaiting confirmations");
await voteTx.wait();
console.log(`Transaction completed. Hash: ${voteTx.hash}`);
yarn ts-node ./scripts/Ballot/castVote.ts 0x16d0b05942f2d3b861dcd4544f7773b489410d0f 11
Using address 0xf5e2e431864DCd48A5b00713CEf9ab919c539213
Wallet balance 0.05
Attaching ballot contract interface to address 0x16d0b05942f2d3b861dcd4544f7773b489410d0f
Casting vote for proposal #11 for account 0xf5e2e431864DCd48A5b00713CEf9ab919c539213
Awaiting confirmations
Transaction completed. Hash: 0x47d1b4813158d2388be2307163bda01efa14fd06c545d8c6fe5ba979b21a2601
Accounts with voting rights can delegate their vote to another account. Doing so will add their voting weight to their delegate. The Delegate will then have increased voting weight.
const voterInfo = await ballotContract.voters(voterAddress);
const toInfo = await ballotContract.voters(toAddress);
console.log(`Delegating ${voterAddress}'s vote to ${toAddress} account.`);
console.log(
`VoterInfo - Weight: ${voterInfo.weight}, Voted: ${voterInfo.voted}, Delegate: ${voterInfo.delegate}, Vote: ${voterInfo.vote}`
);
console.log(
`ToInfo - Weight: ${toInfo.weight}, Voted: ${toInfo.voted}, Delegate: ${toInfo.delegate}, Vote: ${toInfo.vote}`
);
const delegateTx = await ballotContract.delegate(toAddress);
delegateTx.wait();
console.log("Awaiting confirmations");
await delegateTx.wait();
console.log(`Transaction completed. Hash: ${delegateTx.hash}`);
yarn ts-node ./scripts/Ballot/delegateVote.ts 0x16d0b05942f2d3b861dcd4544f7773b489410d0f 0x858C60547fE069724B017c8e42c4b27BE4F151C6
Using address 0x9c1F4Ff1aE57e959128f0356CE15BFE01E3A14E0
Wallet balance 0.02
Attaching ballot contract interface to address 0x16d0b05942f2d3b861dcd4544f7773b489410d0f
Delegating 0x9c1F4Ff1aE57e959128f0356CE15BFE01E3A14E0's vote to 0x858C60547fE069724B017c8e42c4b27BE4F151C6 account.
VoterInfo - Weight: 1, Voted: false, Delegate: 0x0000000000000000000000000000000000000000, Vote: 0
ToInfo - Weight: 1, Voted: false, Delegate: 0x0000000000000000000000000000000000000000, Vote: 0
Awaiting confirmations
Transaction completed. Hash: 0xcf84c42242939c33d6af51323096ce5740b0d5b64da58ef89f9bd27c3729eddb
Call the ballot contract and iterate through the proposals array. Print each proposal and vote count.
console.log("Results for the ballot asking: What's your favorite cryptocurrency out of the following:")
for (let i = 0; i < 12; i++) {
const proposal = await ballotContract.proposals(i);
const proposalString = ethers.utils.parseBytes32String(proposal.name);
console.log(`${i}: ${proposalString} received ${proposal.voteCount} votes`);
}
console.log(`End of proposals`);
yarn run ts-node ./scripts/Ballot/queryResults.ts 0x16d0b05942F2D3b861DCd4544f7773b489410d0f
Attaching ballot contract interface to address 0x16d0b05942F2D3b861DCd4544f7773b489410d0f
Results for the ballot asking: What's your favorite cryptocurrency out of the following:
0: Bitcoin received 0 votes
1: Ethereum received 0 votes
2: Avalanche received 0 votes
3: Solana received 0 votes
4: Algorand received 0 votes
5: Oasis received 1 votes
6: Cardano received 0 votes
7: Dogecoin received 0 votes
8: Polkadot received 0 votes
9: Polygon received 0 votes
10: Chainlink received 0 votes
11: Shiba Inu received 3 votes
From the delegate's account, they cast a vote for proposal 2. Since they are someone's delegate, their vote counts as 2 votes.
yarn ts-node ./scripts/Ballot/castVote.ts 0x16d0b05942f2d3b861dcd4544f7773b489410d0f 2
yarn run ts-node ./scripts/Ballot/queryResults.ts 0x16d0b05942F2D3b861DCd4544f7773b489410d0f
Attaching ballot contract interface to address 0x16d0b05942F2D3b861DCd4544f7773b489410d0f
Results for the ballot asking: What's your favorite cryptocurrency out of the following:
0: Bitcoin received 0 votes
1: Ethereum received 0 votes
2: Avalanche received 2 votes
3: Solana received 0 votes
4: Algorand received 0 votes
5: Oasis received 1 votes
6: Cardano received 0 votes
7: Dogecoin received 0 votes
8: Polkadot received 0 votes
9: Polygon received 0 votes
10: Chainlink received 0 votes
11: Shiba Inu received 3 votes
- More on Ether.js functions and utilities
- Bytes32 and Strings conversion
- Helper functions inside test scripts
- Unit tests structure and nested tests
import { expect } from "chai";
import { ethers } from "hardhat";
// eslint-disable-next-line node/no-missing-import
import { Ballot } from "../../typechain";
const PROPOSALS = ["Proposal 1", "Proposal 2", "Proposal 3"];
function convertStringArrayToBytes32(array: string[]) {
const bytes32Array = [];
for (let index = 0; index < array.length; index++) {
bytes32Array.push(ethers.utils.formatBytes32String(array[index]));
}
return bytes32Array;
}
describe("Ballot", function () {
let ballotContract: Ballot;
this.beforeEach(async function () {
const ballotFactory = await ethers.getContractFactory("Ballot");
ballotContract = await ballotFactory.deploy(
convertStringArrayToBytes32(PROPOSALS)
);
await ballotContract.deployed();
});
describe("when the contract is deployed", function () {
it("has the provided proposals", async function () {
for (let index = 0; index < PROPOSALS.length; index++) {
const proposal = await ballotContract.proposals(index);
expect(ethers.utils.parseBytes32String(proposal.name)).to.eq(
PROPOSALS[index]
);
}
});
it("has zero votes for all proposals", async function () {});
it("sets the deployer address as chairperson", async function () {});
it("sets the voting weight for the chairperson as 1", async function () {});
});
describe("when the chairperson interacts with the giveRightToVote function in the contract", function () {
it("gives right to vote for another address", async function () {});
it("can not give right to vote for someone that has voted", async function () {});
it("can not give right to vote for someone that has already voting rights", async function () {});
});
describe("when the voter interact with the vote function in the contract", function () {});
describe("when the voter interact with the delegate function in the contract", function () {});
describe("when the an attacker interact with the giveRightToVote function in the contract", function () {});
describe("when the an attacker interact with the vote function in the contract", function () {});
describe("when the an attacker interact with the delegate function in the contract", function () {});
describe("when someone interact with the winningProposal function before any votes are cast", function () {});
describe("when someone interact with the winningProposal function after one vote is cast for the first proposal", function () {});
describe("when someone interact with the winnerName function before any votes are cast", function () {});
describe("when someone interact with the winnerName function after one vote is cast for the first proposal", function () {});
describe("when someone interact with the winningProposal function and winnerName after 5 random votes are cast for the proposals", function () {});
});- Running a script with yarn and node, ts-node and/or hardhat
- Ballot deployment script
- Passing arguments
- Passing variables to the deployment script
- Environment files
- Providers
- Connecting to a testnet with a RPC Provider
- Running scripts on chain
- Script for giving voting rights to a given address
- Dealing with transactions in scripts
https://www.typescripttutorial.net/typescript-tutorial/typescript-hello-world/
https://nodejs.org/docs/latest/api/process.html#processargv
https://docs.ethers.io/v5/api/providers/
https://docs.ethers.io/v5/api/contract/contract-factory/
import { ethers } from "hardhat";
const PROPOSALS = ["Proposal 1", "Proposal 2", "Proposal 3"];
async function main() {
console.log("Deploying Ballot contract");
console.log("Proposals: ");
PROPOSALS.forEach((element, index) => {
console.log(`Proposal N. ${index + 1}: ${element}`);
});
// TODO
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});yarn run hardhat .\scripts\Ballot\deployment.ts
yarn run ts-node --files .\scripts\Ballot\deployment.ts "arg1" "arg2" "arg3
- Create Github Issues with your questions about this lesson
- Read the references
- Finish covering other operations with scripts
- Form groups of 3 to 5 students
- Structure scripts to
- Deploy
- Query proposals
- Give vote right passing an address as input
- Cast a vote to a ballot passing contract address and proposal as input and using the wallet in environment
- Delegate my vote passing user address as input and using the wallet in environment
- Query voting result and print to console
- Publish the project in Github
- Run the scripts with a set of proposals, cast and delegate votes and inspect results
- Write a report detailing the addresses, transaction hashes, description of the operation script being executed and console output from script execution for each step (Deployment, giving voting rights, casting/delegating and querying results).
- (Extra) Use TDD methodology