-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
739 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# Workshop 6 - Voting System | ||
|
||
✔️ Write a decentralized voting system | ||
|
||
✔️ Deploy your smart contract on a local testnet using [Anvil](https://book.getfoundry.sh/anvil/) shipped with [Foundry](https://book.getfoundry.sh/) | ||
|
||
✔️ Display your smart contract on a dApp | ||
|
||
## Introduction | ||
|
||
A smart contract is a self-executing program that runs on a blockchain, automatically enforcing the terms of an agreement when predefined conditions are met. With smart contracts, it's possible to build decentralized systems, such as a transparent and secure voting system, where the rules are coded into the contract, and the results are immutable and verifiable by everyone. | ||
|
||
In this workshop we'll learn the basics of solidity with a classic use case: a voting system. At once, we'll use Foundry coupled with Anvil to deploy our contract on a local testnet and gradually integrate it on a front-end template. | ||
|
||
## Step 0: Initialization | ||
|
||
All the required information to install the workshop's dependencies are given in the [SETUP.md](./SETUP.md). To launch the dApp : | ||
|
||
- Clone the "dApp" folder, afterward: | ||
|
||
```bash | ||
cd dApp | ||
npm install | ||
npm run dev | ||
``` | ||
|
||
If everything went well, you should read "No proposals yet.", now let's code ! | ||
|
||
## Step 1 : Create the contract | ||
|
||
### 📑 **Description**: | ||
|
||
First, we need to create the core smart contract that will power our decentralized voting system. For now, you're just going to focus on the base of the smart contract: the constructor. | ||
|
||
### 📌 **Tasks**: | ||
|
||
Remove the files from `script/`, `src/` and `test` directories. Create `VotingSystem.sol` in the `src/` folder. | ||
|
||
- Create a structure named `Proposal` | ||
>💡 The structure should have 2 variables, `name` and `voteCount`. I let you guess their types. | ||
- On deployment, the contract should take an array of strings as parameters and store them in a `public` array of `Proposals`. | ||
|
||
### ✔️ **Validation**: | ||
|
||
After implementing this correctly, paste the `VotingSystem.t.sol` file from the `util/` folder from the repository and write: | ||
|
||
```bash | ||
forge test | ||
``` | ||
|
||
if the two tests are good, you can move on the second step ! | ||
|
||
## Step 2: Deploy and integrate it | ||
|
||
### 📑 **Description**: | ||
|
||
In this step you'll focus on the deployment and the integration part of your smart contract. For quick and easy deployment, you'll use [Anvil](https://book.getfoundry.sh/anvil/) to create a local testnet. | ||
|
||
### 📌 **Tasks**: | ||
|
||
- Using Anvil and forge, deploy your smart contract on a local testnet. | ||
- Paste the address of your deployed contract into the environment file at the root of the dApp folder. | ||
|
||
### ✔️ **Validation**: | ||
|
||
Once you have done this, you should now be able to see the parameters you entered when deploying the contract in the dApp. | ||
|
||
## Step 4: Code the contract | ||
|
||
### 📑 **Description**: | ||
|
||
Crucial step! I'll leave you to code the rest of the contract yourself, so you can see the results bit by bit. Feel free to redeploy your contract whenever you like with forge! | ||
|
||
### 📌 **Tasks**: | ||
|
||
- Create a structure `Voter` | ||
|
||
- Code the `vote` function. It should take an id in parameter. | ||
- Check if the voter had already voted and if the id is correct. | ||
- Add a `voteCount` for the proposal. | ||
|
||
> 💡 These functions require a wallet address. If you've written `anvil` before, you have account available to use. Put one of the private keys in the `.env` file. | ||
- Code the `winningProposal` function, returning the proposal with the most `voteCount`. | ||
|
||
### ✔️ **Validation**: | ||
|
||
If you can see all the proposals, vote for one and the winner is highlighted in green, congratulations, you have just made your first smart contract ! | ||
|
||
## Authors | ||
|
||
| [<img src="https://github.com/Sacharbon.png?size=85" width=85><br><sub>Sacha Dujardin</sub>](https://github.com/Sacharbon) | | ||
| :------------------------------------------------------------------------------------------------------------------------: | | ||
<h2 align=center> | ||
Organization | ||
</h2> | ||
|
||
<p align='center'> | ||
<a href="https://www.linkedin.com/company/pocinnovation/mycompany/"> | ||
<img src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white"> | ||
</a> | ||
<a href="https://www.instagram.com/pocinnovation/"> | ||
<img src="https://img.shields.io/badge/Instagram-E4405F?style=for-the-badge&logo=instagram&logoColor=white"> | ||
</a> | ||
<a href="https://twitter.com/PoCInnovation"> | ||
<img src="https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white"> | ||
</a> | ||
<a href="https://discord.com/invite/Yqq2ADGDS7"> | ||
<img src="https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white"> | ||
</a> | ||
</p> | ||
<p align=center> | ||
<a href="https://www.poc-innovation.fr/"> | ||
<img src="https://img.shields.io/badge/WebSite-1a2b6d?style=for-the-badge&logo=GitHub Sponsors&logoColor=white"> | ||
</a> | ||
</p> | ||
|
||
> 🚀 Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
VITE_ADDRESS="" | ||
VITE_PRIVATE_KEY_ACCOUNT="" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* eslint-env node */ | ||
require('@rushstack/eslint-patch/modern-module-resolution') | ||
|
||
module.exports = { | ||
root: true, | ||
'extends': [ | ||
'plugin:vue/vue3-essential', | ||
'eslint:recommended', | ||
'@vue/eslint-config-typescript', | ||
'@vue/eslint-config-prettier/skip-formatting' | ||
], | ||
parserOptions: { | ||
ecmaVersion: 'latest' | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
.DS_Store | ||
dist | ||
dist-ssr | ||
coverage | ||
*.local | ||
|
||
/cypress/videos/ | ||
/cypress/screenshots/ | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? | ||
|
||
*.tsbuildinfo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/prettierrc", | ||
"semi": false, | ||
"tabWidth": 2, | ||
"singleQuote": true, | ||
"printWidth": 100, | ||
"trailingComma": "none" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="vite/client" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<link rel="icon" href="/favicon.ico"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Vite App</title> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script type="module" src="/src/main.ts"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
{ | ||
"name": "front", | ||
"version": "0.0.0", | ||
"private": true, | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "run-p type-check \"build-only {@}\" --", | ||
"preview": "vite preview", | ||
"build-only": "vite build", | ||
"type-check": "vue-tsc --build --force", | ||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", | ||
"format": "prettier --write src/" | ||
}, | ||
"dependencies": { | ||
"viem": "^2.20.0", | ||
"vue": "^3.4.29" | ||
}, | ||
"devDependencies": { | ||
"@rushstack/eslint-patch": "^1.8.0", | ||
"@tsconfig/node20": "^20.1.4", | ||
"@types/node": "^20.14.5", | ||
"@vitejs/plugin-vue": "^5.0.5", | ||
"@vue/eslint-config-prettier": "^9.0.0", | ||
"@vue/eslint-config-typescript": "^13.0.0", | ||
"@vue/tsconfig": "^0.5.1", | ||
"eslint": "^8.57.0", | ||
"eslint-plugin-vue": "^9.23.0", | ||
"npm-run-all2": "^6.2.0", | ||
"prettier": "^3.2.5", | ||
"typescript": "~5.4.0", | ||
"vite": "^5.3.1", | ||
"vue-tsc": "^2.0.21" | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<script setup lang="ts"> | ||
import { win, getProposals, vote } from "./interact" | ||
import { ref, onMounted } from 'vue' | ||
import Card from "@/components/Card.vue"; | ||
const winner = ref<number>(-1 ) | ||
const proposals = ref() | ||
const loading = ref(true) | ||
const isVoting = ref(false) | ||
const selectedIndex = ref<number>(-1) | ||
const voteStatus = ref<string>('') | ||
function delay(ms: number) { | ||
return new Promise(resolve => setTimeout(resolve, ms)); | ||
} | ||
const refreshData = async () => { | ||
proposals.value = await getProposals() | ||
winner.value = await win() | ||
loading.value = false | ||
console.log(proposals.value) | ||
} | ||
onMounted(async () => { | ||
if (import.meta.env.VITE_ADDRESS) { | ||
await refreshData() | ||
} | ||
}) | ||
const toVote = (index: number) => { | ||
isVoting.value = true; | ||
selectedIndex.value = index; | ||
} | ||
const voting = async () => { | ||
try { | ||
await vote(selectedIndex.value) | ||
voteStatus.value = "You have successfully voted !" | ||
await delay(2000) | ||
voteStatus.value = "" | ||
isVoting.value = false | ||
loading.value = true | ||
await refreshData() | ||
} catch (error) { | ||
if (error.message.includes('Already voted.')) { | ||
voteStatus.value = "You have already voted." | ||
} else { | ||
voteStatus.value = "Internal network error." | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<header> | ||
<h1>Voting system</h1> | ||
</header> | ||
<div id="container"> | ||
<h2 v-if="isVoting">Do you want to vote ?</h2> | ||
<div id="cards" v-if="!loading" v-for="(proposal, index) in proposals"> | ||
<Card | ||
v-if="!isVoting || (isVoting && selectedIndex === index)" | ||
:name="proposal[0]" | ||
:voteCount="proposal[1]" | ||
:index="<number>index" | ||
:toVote="toVote" | ||
:winnerIndex="winner" | ||
/> | ||
</div> | ||
<p v-else>Not proposals yet.</p> | ||
<div id="choice" v-if="isVoting"> | ||
<button @click="voting">yes</button> | ||
<button @click="isVoting = false">no</button> | ||
<p>{{ voteStatus }}</p> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style scoped> | ||
header { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 10vh; | ||
} | ||
h1 { | ||
font-size: 3rem; | ||
font-weight: bold; | ||
} | ||
h2 { | ||
font-size: 1.8rem; | ||
} | ||
#container { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
height: 90vh; | ||
} | ||
#cards { | ||
display: flex; | ||
width: 100vw; | ||
justify-content: center; | ||
} | ||
button { | ||
padding: 10px; | ||
padding-inline: 20px; | ||
margin: 10px; | ||
font-size: 1rem; | ||
} | ||
#choice { | ||
text-align: center; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const abi = [{"inputs":[{"internalType":"string[]","name":"proposalName","type":"string[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"chairperson","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposals","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"voteCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voters","outputs":[{"internalType":"bool","name":"hasVoted","type":"bool"},{"internalType":"uint256","name":"votedProposalId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"winningProposal","outputs":[{"internalType":"uint256","name":"winningProposalId","type":"uint256"}],"stateMutability":"view","type":"function"}] |
Oops, something went wrong.