From 63d8afe82f41b98c21bd45ea18b4c1b98acecc8c Mon Sep 17 00:00:00 2001 From: sacharbon Date: Wed, 28 Aug 2024 20:50:23 +0200 Subject: [PATCH 1/7] feat(p2p/voting-system): add v1 --- p2p/7.Voting-system/README.md | 119 ++++++++++++++++++ p2p/7.Voting-system/dApp/.env | 2 + p2p/7.Voting-system/dApp/.eslintrc.cjs | 15 +++ p2p/7.Voting-system/dApp/.gitignore | 30 +++++ p2p/7.Voting-system/dApp/.prettierrc.json | 8 ++ p2p/7.Voting-system/dApp/env.d.ts | 1 + p2p/7.Voting-system/dApp/index.html | 13 ++ p2p/7.Voting-system/dApp/package.json | 35 ++++++ p2p/7.Voting-system/dApp/public/favicon.ico | Bin 0 -> 4286 bytes p2p/7.Voting-system/dApp/src/App.vue | 114 +++++++++++++++++ p2p/7.Voting-system/dApp/src/abi.ts | 1 + p2p/7.Voting-system/dApp/src/assets/base.css | 86 +++++++++++++ p2p/7.Voting-system/dApp/src/assets/main.css | 1 + .../dApp/src/components/Card.vue | 48 +++++++ p2p/7.Voting-system/dApp/src/interact.ts | 79 ++++++++++++ p2p/7.Voting-system/dApp/src/main.ts | 7 ++ p2p/7.Voting-system/dApp/tsconfig.app.json | 14 +++ p2p/7.Voting-system/dApp/tsconfig.json | 11 ++ p2p/7.Voting-system/dApp/tsconfig.node.json | 19 +++ p2p/7.Voting-system/dApp/vite.config.ts | 16 +++ p2p/7.Voting-system/setup.md | 82 ++++++++++++ p2p/7.Voting-system/util/VotingSystem.t.sol | 38 ++++++ 22 files changed, 739 insertions(+) create mode 100644 p2p/7.Voting-system/README.md create mode 100644 p2p/7.Voting-system/dApp/.env create mode 100644 p2p/7.Voting-system/dApp/.eslintrc.cjs create mode 100644 p2p/7.Voting-system/dApp/.gitignore create mode 100644 p2p/7.Voting-system/dApp/.prettierrc.json create mode 100644 p2p/7.Voting-system/dApp/env.d.ts create mode 100644 p2p/7.Voting-system/dApp/index.html create mode 100644 p2p/7.Voting-system/dApp/package.json create mode 100644 p2p/7.Voting-system/dApp/public/favicon.ico create mode 100644 p2p/7.Voting-system/dApp/src/App.vue create mode 100644 p2p/7.Voting-system/dApp/src/abi.ts create mode 100644 p2p/7.Voting-system/dApp/src/assets/base.css create mode 100644 p2p/7.Voting-system/dApp/src/assets/main.css create mode 100644 p2p/7.Voting-system/dApp/src/components/Card.vue create mode 100644 p2p/7.Voting-system/dApp/src/interact.ts create mode 100644 p2p/7.Voting-system/dApp/src/main.ts create mode 100644 p2p/7.Voting-system/dApp/tsconfig.app.json create mode 100644 p2p/7.Voting-system/dApp/tsconfig.json create mode 100644 p2p/7.Voting-system/dApp/tsconfig.node.json create mode 100644 p2p/7.Voting-system/dApp/vite.config.ts create mode 100644 p2p/7.Voting-system/setup.md create mode 100644 p2p/7.Voting-system/util/VotingSystem.t.sol diff --git a/p2p/7.Voting-system/README.md b/p2p/7.Voting-system/README.md new file mode 100644 index 00000000..db2a173e --- /dev/null +++ b/p2p/7.Voting-system/README.md @@ -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 + +| [
Sacha Dujardin](https://github.com/Sacharbon) | +| :------------------------------------------------------------------------------------------------------------------------: | +

+Organization +

+ +

+ + + + + + + + + + + + +

+

+ + + +

+ +> 🚀 Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories. \ No newline at end of file diff --git a/p2p/7.Voting-system/dApp/.env b/p2p/7.Voting-system/dApp/.env new file mode 100644 index 00000000..e3ab26fc --- /dev/null +++ b/p2p/7.Voting-system/dApp/.env @@ -0,0 +1,2 @@ +VITE_ADDRESS="" +VITE_PRIVATE_KEY_ACCOUNT="" diff --git a/p2p/7.Voting-system/dApp/.eslintrc.cjs b/p2p/7.Voting-system/dApp/.eslintrc.cjs new file mode 100644 index 00000000..6f40582d --- /dev/null +++ b/p2p/7.Voting-system/dApp/.eslintrc.cjs @@ -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' + } +} diff --git a/p2p/7.Voting-system/dApp/.gitignore b/p2p/7.Voting-system/dApp/.gitignore new file mode 100644 index 00000000..8ee54e8d --- /dev/null +++ b/p2p/7.Voting-system/dApp/.gitignore @@ -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 diff --git a/p2p/7.Voting-system/dApp/.prettierrc.json b/p2p/7.Voting-system/dApp/.prettierrc.json new file mode 100644 index 00000000..66e23359 --- /dev/null +++ b/p2p/7.Voting-system/dApp/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "printWidth": 100, + "trailingComma": "none" +} \ No newline at end of file diff --git a/p2p/7.Voting-system/dApp/env.d.ts b/p2p/7.Voting-system/dApp/env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/p2p/7.Voting-system/dApp/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/p2p/7.Voting-system/dApp/index.html b/p2p/7.Voting-system/dApp/index.html new file mode 100644 index 00000000..a8885448 --- /dev/null +++ b/p2p/7.Voting-system/dApp/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/p2p/7.Voting-system/dApp/package.json b/p2p/7.Voting-system/dApp/package.json new file mode 100644 index 00000000..f166bff8 --- /dev/null +++ b/p2p/7.Voting-system/dApp/package.json @@ -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" + } +} diff --git a/p2p/7.Voting-system/dApp/public/favicon.ico b/p2p/7.Voting-system/dApp/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2 GIT binary patch literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S literal 0 HcmV?d00001 diff --git a/p2p/7.Voting-system/dApp/src/App.vue b/p2p/7.Voting-system/dApp/src/App.vue new file mode 100644 index 00000000..7836a0ba --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/App.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/p2p/7.Voting-system/dApp/src/abi.ts b/p2p/7.Voting-system/dApp/src/abi.ts new file mode 100644 index 00000000..74db4a2e --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/abi.ts @@ -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"}] diff --git a/p2p/7.Voting-system/dApp/src/assets/base.css b/p2p/7.Voting-system/dApp/src/assets/base.css new file mode 100644 index 00000000..8816868a --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/assets/base.css @@ -0,0 +1,86 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/p2p/7.Voting-system/dApp/src/assets/main.css b/p2p/7.Voting-system/dApp/src/assets/main.css new file mode 100644 index 00000000..a1e1556c --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/assets/main.css @@ -0,0 +1 @@ +@import './base.css'; \ No newline at end of file diff --git a/p2p/7.Voting-system/dApp/src/components/Card.vue b/p2p/7.Voting-system/dApp/src/components/Card.vue new file mode 100644 index 00000000..bba0ea52 --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/components/Card.vue @@ -0,0 +1,48 @@ + + + + + \ No newline at end of file diff --git a/p2p/7.Voting-system/dApp/src/interact.ts b/p2p/7.Voting-system/dApp/src/interact.ts new file mode 100644 index 00000000..f32e7de4 --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/interact.ts @@ -0,0 +1,79 @@ +import {createPublicClient, createWalletClient, http} from 'viem' +import { localhost } from 'viem/chains' +import { abi } from "./abi" +import {privateKeyToAccount} from "viem/accounts"; + +let address = null +let account = null + +if (import.meta.env.VITE_ADDRESS) { + address = import.meta.env.VITE_ADDRESS +} + +if (import.meta.env.VITE_PRIVATE_KEY_ACCOUNT) { + account = privateKeyToAccount(import.meta.env.VITE_PRIVATE_KEY_ACCOUNT) +} + +const chain = { + ...localhost, + id: 31337 +} + +const publicClient = createPublicClient({ + chain, + transport: http() +}) + +const client = createWalletClient({ + chain, + transport: http() +}) + +export async function win() { + try { + const result = await publicClient.readContract({ + address, + abi, + functionName: "winningProposal", + }) + return Number(result) + } catch (error) { + return null + } +} + +async function retrieveProposal(id: number) { + return await publicClient.readContract({ + address, + abi, + functionName: 'proposals', + args: [id] + }) +} + +export async function getProposals() { + const proposals = []; + let proposalCount = 0; + + while (true) { + try { + const proposal = await retrieveProposal(proposalCount) + proposals.push(proposal) + proposalCount++ + } catch (error) { + break; + } + } + return proposals; +} + +export async function vote(id: number) { + const { request } = await publicClient.simulateContract({ + account, + address, + abi, + functionName: 'vote', + args: [id] + }) as { request: any } + await client.writeContract(request) +} diff --git a/p2p/7.Voting-system/dApp/src/main.ts b/p2p/7.Voting-system/dApp/src/main.ts new file mode 100644 index 00000000..98c094e0 --- /dev/null +++ b/p2p/7.Voting-system/dApp/src/main.ts @@ -0,0 +1,7 @@ +import './assets/main.css' + +import { createApp } from 'vue' +// @ts-ignore +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/p2p/7.Voting-system/dApp/tsconfig.app.json b/p2p/7.Voting-system/dApp/tsconfig.app.json new file mode 100644 index 00000000..e14c754d --- /dev/null +++ b/p2p/7.Voting-system/dApp/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/p2p/7.Voting-system/dApp/tsconfig.json b/p2p/7.Voting-system/dApp/tsconfig.json new file mode 100644 index 00000000..66b5e570 --- /dev/null +++ b/p2p/7.Voting-system/dApp/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/p2p/7.Voting-system/dApp/tsconfig.node.json b/p2p/7.Voting-system/dApp/tsconfig.node.json new file mode 100644 index 00000000..f0940630 --- /dev/null +++ b/p2p/7.Voting-system/dApp/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*" + ], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/p2p/7.Voting-system/dApp/vite.config.ts b/p2p/7.Voting-system/dApp/vite.config.ts new file mode 100644 index 00000000..5c45e1d9 --- /dev/null +++ b/p2p/7.Voting-system/dApp/vite.config.ts @@ -0,0 +1,16 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + } +}) diff --git a/p2p/7.Voting-system/setup.md b/p2p/7.Voting-system/setup.md new file mode 100644 index 00000000..a9e54fbc --- /dev/null +++ b/p2p/7.Voting-system/setup.md @@ -0,0 +1,82 @@ +# Setup - Foundry, VSCode extension & Node + +[Foundry](https://book.getfoundry.sh/) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. We will need it throughout the workshop. + +## NodeJS + +To run the DApp locally, you need to install: +- [nodejs](https://github.com/nodejs/node): JavaScript runtime +- [npm](https://www.npmjs.com/): node package manager +- [yarn](https://yarnpkg.com/): a better npm + +## Download foundry + +- Open your terminal and type + +```bash +curl -L https://foundry.paradigm.xyz | bash +``` + +This will download foundryup. + +- Then, you can download foundry by running `foundryup` +- If everything went fine you should be able to use `forge`, `anvil`, `chisel` and `cast`. +- If you are on macos you will need to install `libusb` with + +```bash +brew install libusb +``` + +After the installation, run the following command to ensure it has been properly installed on your computer: + +```bash +forge --version +``` + +It should print your current version. + +If you have some troubles during the installation, you can refer to the [official documentation](https://book.getfoundry.sh/getting-started/installation). + +## Create a foundry project + +Once everything is done, you can create a new project using + +```bash +forge init new_project +cd new_project +``` + +This should create a new directory with a brand new foundry project + +If you already have a repository, you might need to add + +```bash +--no-commit +``` + +The first thing you wants to do is set the solidity version of this project in the `foundry.toml` file wich is the configuration file of foundry. + +You can do this by adding in the "[profile.default]" section: + +```toml +solc_version = "0.8.20" +``` + +## VSCode Integration + +I recommand you to install [solidity vscode extension](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity), it is an extension that simplifies development in Solidity. + +Also, I recommand you to use the extension formatter. It will format your code on save, which allows you to have a clean codebase. To do so: + +- Create a `.vscode/settings.json` file with this content + +```json +{ + "editor.formatOnSave": true, + "[solidity]": { + "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" + } +} +``` + +then [go back to the exercises](./README.md) 🚀 \ No newline at end of file diff --git a/p2p/7.Voting-system/util/VotingSystem.t.sol b/p2p/7.Voting-system/util/VotingSystem.t.sol new file mode 100644 index 00000000..3bff6848 --- /dev/null +++ b/p2p/7.Voting-system/util/VotingSystem.t.sol @@ -0,0 +1,38 @@ +pragma solidity 0.8.26; + +import "forge-std/Test.sol"; +import "../src/VotingSystem.sol"; + +contract VotingSystemTest is Test { + VotingSystem public votingSystem; + string[] public proposalNames; + + function setUp() public { + proposalNames = new string[](3); + proposalNames[0] = "Proposal 1"; + proposalNames[1] = "Proposal 2"; + proposalNames[2] = "Proposal 3"; + + votingSystem = new VotingSystem(proposalNames); + } + + function testProposalCreation() public view { + for (uint i = 0; i < proposalNames.length; i++) { + (string memory name, uint voteCount) = votingSystem.proposals(i); + assertEq(name, proposalNames[i]); + assertEq(voteCount, 0); + } + } + + function testProposalCount() public view { + uint proposalCount = 0; + while (true) { + try votingSystem.proposals(proposalCount) returns (string memory, uint) { + proposalCount++; + } catch { + break; + } + } + assertEq(proposalCount, proposalNames.length); + } +} \ No newline at end of file From 2e746947c0238cede14077b5908a05be1b907d84 Mon Sep 17 00:00:00 2001 From: Sacha Dujardin Date: Thu, 5 Sep 2024 07:03:18 +0200 Subject: [PATCH 2/7] feat(README.md): add documentation --- p2p/7.Voting-system/README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/p2p/7.Voting-system/README.md b/p2p/7.Voting-system/README.md index db2a173e..40924a3c 100644 --- a/p2p/7.Voting-system/README.md +++ b/p2p/7.Voting-system/README.md @@ -51,6 +51,10 @@ forge test if the two tests are good, you can move on the second step ! +### 📚 **Documentation**: + + - [Constructors](https://docs.soliditylang.org/en/v0.8.27/contracts.html#constructors) + ## Step 2: Deploy and integrate it ### 📑 **Description**: @@ -60,12 +64,16 @@ In this step you'll focus on the deployment and the integration part of your sma ### 📌 **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. +- Paste the address of your deployed contract into the environment file at the root of the dApp folder named `VITE_ADDRESS`. ### ✔️ **Validation**: Once you have done this, you should now be able to see the parameters you entered when deploying the contract in the dApp. +### 📚 **Documentation**: + +- [Anvil](https://book.getfoundry.sh/anvil/) + ## Step 4: Code the contract ### 📑 **Description**: @@ -80,7 +88,7 @@ Crucial step! I'll leave you to code the rest of the contract yourself, so you c - 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. +> 💡 These functions require a wallet address. If you've did the previous step correctly, 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`. @@ -88,6 +96,10 @@ Crucial step! I'll leave you to code the rest of the contract yourself, so you c 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 ! +## To go further + +You've just created a simple voting system smart contract ! If you want to go further you can add some feature to your contract and styling the dApp ! + ## Authors | [
Sacha Dujardin](https://github.com/Sacharbon) | @@ -116,4 +128,4 @@ Organization

-> 🚀 Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories. \ No newline at end of file +> 🚀 Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories. From f8018c81d2be8f4487d1b3902b8ff45fea2a233a Mon Sep 17 00:00:00 2001 From: Sacha Dujardin Date: Thu, 5 Sep 2024 07:35:25 +0200 Subject: [PATCH 3/7] fix(setup.md): end with a single newline character --- p2p/7.Voting-system/setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/7.Voting-system/setup.md b/p2p/7.Voting-system/setup.md index a9e54fbc..6ce7eacb 100644 --- a/p2p/7.Voting-system/setup.md +++ b/p2p/7.Voting-system/setup.md @@ -79,4 +79,4 @@ Also, I recommand you to use the extension formatter. It will format your code o } ``` -then [go back to the exercises](./README.md) 🚀 \ No newline at end of file +then [go back to the exercises](./README.md) 🚀 From 8451116f3bf955c2ef1aacc89b488c8c279aff30 Mon Sep 17 00:00:00 2001 From: Sacha Dujardin Date: Thu, 5 Sep 2024 07:37:25 +0200 Subject: [PATCH 4/7] fix(README.md): fix dead link --- p2p/7.Voting-system/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/7.Voting-system/README.md b/p2p/7.Voting-system/README.md index 40924a3c..ec623d74 100644 --- a/p2p/7.Voting-system/README.md +++ b/p2p/7.Voting-system/README.md @@ -14,7 +14,7 @@ In this workshop we'll learn the basics of solidity with a classic use case: a v ## 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 : +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: From f72d0bb8d102a7053a22e22955cbbee57bb3eb06 Mon Sep 17 00:00:00 2001 From: Sacha Dujardin Date: Fri, 27 Sep 2024 23:55:57 +0200 Subject: [PATCH 5/7] Update README.md --- p2p/7.Voting-system/README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/p2p/7.Voting-system/README.md b/p2p/7.Voting-system/README.md index ec623d74..9d3412f8 100644 --- a/p2p/7.Voting-system/README.md +++ b/p2p/7.Voting-system/README.md @@ -36,10 +36,18 @@ First, we need to create the core smart contract that will power our decentraliz 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. +- Setup the file by adding this header + ```solidity + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.26; + ``` + +- Create a contract named `VotingSystem` +- Declare a structure named `Proposal` +>💡 The structure should have 2 variables, `name` and `voSteCount`. 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`. +>💡 A constructor is an optional function that is executed upon contract deployment, you must create one to complete the previous task. ### ✔️ **Validation**: @@ -53,7 +61,8 @@ if the two tests are good, you can move on the second step ! ### 📚 **Documentation**: - - [Constructors](https://docs.soliditylang.org/en/v0.8.27/contracts.html#constructors) +- [Header](https://docs.soliditylang.org/en/latest/layout-of-source-files.html) +- [Constructors](https://docs.soliditylang.org/en/v0.8.27/contracts.html#constructors) ## Step 2: Deploy and integrate it From 67df9d1e6b1fefac50c398f35ff8a46b2219baef Mon Sep 17 00:00:00 2001 From: Sacha Dujardin Date: Sat, 28 Sep 2024 00:13:02 +0200 Subject: [PATCH 6/7] Update README.md --- p2p/7.Voting-system/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/p2p/7.Voting-system/README.md b/p2p/7.Voting-system/README.md index 9d3412f8..ca5e6a35 100644 --- a/p2p/7.Voting-system/README.md +++ b/p2p/7.Voting-system/README.md @@ -73,6 +73,8 @@ In this step you'll focus on the deployment and the integration part of your sma ### 📌 **Tasks**: - Using Anvil and forge, deploy your smart contract on a local testnet. +>💡 On a successful deployment, contract address appear next to the `deployed to` field. + - Paste the address of your deployed contract into the environment file at the root of the dApp folder named `VITE_ADDRESS`. ### ✔️ **Validation**: @@ -82,6 +84,7 @@ Once you have done this, you should now be able to see the parameters you entere ### 📚 **Documentation**: - [Anvil](https://book.getfoundry.sh/anvil/) +- [Deploy a Smart Contract local on Anvil with Foundry in 2 min](https://youtu.be/e5QmJaamdPE) ## Step 4: Code the contract @@ -91,9 +94,8 @@ Crucial step! I'll leave you to code the rest of the contract yourself, so you c ### 📌 **Tasks**: -- Create a structure `Voter` - -- Code the `vote` function. It should take an id in parameter. +- Create a structure `Voter` containing a boolean named `voted` and an uint `id`. +- Code the `vote` function. It should take the id of the proposal in parameter. - Check if the voter had already voted and if the id is correct. - Add a `voteCount` for the proposal. From 78929b7d690e8c3904618c9e495d60043ee966fe Mon Sep 17 00:00:00 2001 From: sacharbon Date: Sat, 28 Sep 2024 00:49:36 +0200 Subject: [PATCH 7/7] fix(voting-system): enhance ui --- p2p/7.Voting-system/dApp/src/App.vue | 2 +- p2p/7.Voting-system/dApp/src/components/Card.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/7.Voting-system/dApp/src/App.vue b/p2p/7.Voting-system/dApp/src/App.vue index 7836a0ba..7db9b891 100644 --- a/p2p/7.Voting-system/dApp/src/App.vue +++ b/p2p/7.Voting-system/dApp/src/App.vue @@ -67,7 +67,7 @@ const voting = async () => { :winnerIndex="winner" /> -

Not proposals yet.

+

No proposals yet.

diff --git a/p2p/7.Voting-system/dApp/src/components/Card.vue b/p2p/7.Voting-system/dApp/src/components/Card.vue index bba0ea52..364ad334 100644 --- a/p2p/7.Voting-system/dApp/src/components/Card.vue +++ b/p2p/7.Voting-system/dApp/src/components/Card.vue @@ -14,7 +14,7 @@ const backgroundColor = computed(() => { }) const textColor = computed(() => { - return props.index === props.winnerIndex ? 'white' : 'black' + return props.index === props.winnerIndex ? 'white' : undefined })