투표 DApp 코딩 실습, 메타마스크(MetaMask) 기초 설명
2021. 10. 12. 19:33ㆍBlockchain/Solidity
실습 순서
- 솔리디티 코드 작성
- 솔리디티 코드 컴파일
- 컴파일 결과 배포 // = 블럭에 내용 추가, 가나슈 재부팅 하면 여기서부터 다시 작업
- 컴파일 결과를 js코드로 내용 불러올 수 있는지 테스트
- html에서 javascript web3 활용하여 내용 불러오기
실습 시작
- vsCode터미널 2개 열기 // 1: Ganache, 2: 작업 공간
- 터미널 1 >> ganache-cli --host 0.0.0.0 // 가나슈 실행
- Voting.sol 파일 생성 // 솔리디티 코드 작성
pragma solidity ^0.8.0; contract Voting { /* 필요 기능 1 후보자 초기화 2 각 후보자에게 투표 가능 3:후보자들의 각 투표 개수 */ // ['ingoo1', 'ingoo2', 'ingoo3'] string[] public candidateList; mapping(string => uint) public voteReceived; constructor(string[] memory _candidateNames) public { candidateList = _candidateNames; } function voteForCandidate(string memory _candidate) public { voteReceived[_candidate] += 1; } // 후보자 명을 넣어주면 결과값이 투표 개수를 function totalVotesFor(string memory _candidate) view public returns(uint){ return voteReceived[_candidate]; } // String 비교 function validCandidate(string memory _candidate) view public returns(bool) { // string to byte // keccake256() 메서드 안에 byte 값 넣기 for (uint i = 0; i < candidateList.length; i++) { if (keccak256(bytes(candidateList[i])) == keccak256(bytes(_candidate))) { return true; } } return false; } /* arr = ['1', '2', '3']; searchText = '4' // 완전탐색 0(n) function check(txt) { for (let i = 0; i < arr.length; i++); { if (arr[i] == txt) { return true; } return false; } } console.log(check(serachText)); */ }
- soljs --abi --bin [파일명] // 솔리디티 코드 컴파일
* Voting_sol_Voting.abi, Voting_sol_Voting.bin 자동 생성 됨 -
여기까지 작업하고 node deploy로 실행 // newContract.options.address 값 가져오기const Web3 = require('web3'); const fs = require('fs'); const ABI = JSON.parse(fs.readFileSync('./Voting_sol_Voting.abi').toString()); const BYTECODE = fs.readFileSync('./Voting_sol_Voting.bin').toString(); let web3 = new Web3('http://127.0.0.1:8545'); // 블록 생성 할 때 솔리디티 컴파일한 abi파일을 인자값으로 const deployContract = new web3.eth.Contract(ABI); deployContract.deploy({ // 배포를 할 때 솔리디티 컴파일한 byte값을 넣음 data: BYTECODE, arguments: [['ingoo1', 'ingoo2', 'ingoo3'].map(name => web3.utils.asciiToHex(name))], // { } 대괄호 없으면 return 생략 가능 }) .send({ from: '0x7fE68fFb0395EFb89D3F9f163215eAe3C92f823d', // Available Accounts 첫번째, 가나슈 껐다키면 수정 gas: '6721975', }, (error, result) => { console.log(error); }) .then(newContract=>{ console.log(newContract.options.address); })
const deployContract부터 주석 처리 후 뒤에 아래 내용 추가
여기까지 작업하고 node deploy 재실행const contract = new web3.eth.Contract(ABI, '0x31dc15fd778b1d3bd66f9f621f8797dd1a0fe1d5') // Contract created, 가나슈 껐다키면 수정 contract.methods.voteForCandidate('ingoo1').send({ from: '0x71a1ef22df159fD4B9147d6B21eC04cD0511A583', // Available Accounts 중 0번 제외 아무거나, 가나슈 껐다키면 수정 }) contract.methods.totalVotesFor('ingoo1').call().then(data => { console.log(data); })
실행 할 때마다 블록이 추가되어 숫자가 하나씩 쌓이는게 확인 됨 // 0, 1, 2.....
가나쉬에서는 GAS가 계속 소모되고 있음 (아래)
* 가나슈를 재부팅 하면 쌓였던 블록들이나 배포했던 내용들이 다 사라지고, 컴파일도 다시 해야함
* 코드도 수정이 필요하며 해당 내용은 아래 index.js 파일 내 주석으로 달아 놓음 - index.html 파일 생성 후 web3 라이브러리 가져오고, index.js 연결
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>투표 DApp</title> </head> <body> <ul> <li> <span id="candidate1"></span> <span id="candidateCount1"></span> </li> <li> <span id="candidate2"></span> <span id="candidateCount2"></span> </li> <li> <span id="candidate3"></span> <span id="candidateCount3"></span> </li> </ul> <input type="text" id="candidateName" /> <button id="btn">투표</button> </body> <!-- https://github.com/ChainSafe/web3.js web3의 라이브러리를 가져옴 --> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script> <script type="text/javascript" src="./index.js"></script> </html>
- index.js 파일 생성
/* 가나쉬 연결 */ const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); const ABI = JSON.parse(`[{"inputs":[{"internalType":"string[]","name":"_candidateNames","type":"string[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"candidateList","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_candidate","type":"string"}],"name":"totalVotesFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_candidate","type":"string"}],"name":"validCandidate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_candidate","type":"string"}],"name":"voteForCandidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"voteReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`); // .abi 파일 내용 복붙 const deployAddress = `0x31dc15fd778b1d3bd66f9f621f8797dd1a0fe1d5`; // Contract created, 가나슈 껐다키면 수정 let VotingContract = new web3.eth.Contract(ABI, deployAddress); let candidates = { // 테스트를 위해 하드코딩 "ingoo1": "candidate1", "ingoo2": "candidate2", "ingoo3": "candidate3" }; window.addEventListener('DOMContentLoaded', init); async function init() { let candidateNames = Object.keys(candidates); // ['ingoo1','ingoo2','ingoo3'] for (let i = 0; i < candidateNames.length; i++) { let name = candidateNames[i];//ingoo1 const nameElement = document.querySelector(`#${candidates[name]}`); // #candidate1 nameElement.innerHTML = name; const countElement = document.querySelector(`#candidateCount${i + 1}`); countElement.innerHTML = await VotingContract.methods.totalVotesFor(`ingoo${i + 1}`).call(); } // await VotingContract.methods.vodeForCandiodate('ingoo1').send({from:'0x71a1ef22df159fD4B9147d6B21eC04cD0511A583'}); // VotingContract.methods.totalVotesFor('ingoo1').call().then(data=>{ // console.log(data); // }) } const btn = document.querySelector('#btn'); btn.addEventListener('click', btnEvent); async function btnEvent() { let candidateName = document.querySelector(`#candidateName`).value; // console.log(candidateName); await VotingContract.methods.voteForCandidate(candidateName).send({ from: '0x71a1ef22df159fD4B9147d6B21eC04cD0511A583' }) // Available Accounts 중 0번 제외 아무거나, 가나슈 껐다키면 수정 let candidateCount = await VotingContract.methods.totalVotesFor(candidateName).call(); let number = candidateName.charAt(candidateName.length - 1) let countElement = document.querySelector(`#candidateCount${number}`); countElement.innerHTML = candidateCount; }
- index.html 파일 라이브 서버로 열기
- input box에 ingoo3 입력 후 투표 버튼 누르면 트랜잭션이 발생하여 터미널도 갱신되며, html 페이지에서 오른쪽 숫자도 올라가는걸 확인 할 수 있음
메타마스크(MetaMask)
- 메타마스크의 특징
* 핫 월렛
* 비수탁형 지갑
- 메타마스크의 역할
* 암호화폐의 지갑으로 코인마다 지갑이 다르기때문에 여러개의 주소를 보관해줌
* 보관한 주소를 조회할 수도 있으며, 다른 주소로 코인을 보낼 수도 있음
- 핫 월렛(HotWallet) : 온라인 지갑
- 콜드 월렛(ColdWallet) : 오프라인 지갑
- 수탁형(trustee) 지갑 : 거래소에 저장 된 지갑, 제 3자에 의해서 보관 됨
- 비수탁형(non-trustee) 지갑 : 사용자가 직접 관리하는 지갑
'Blockchain > Solidity' 카테고리의 다른 글
스마트 컨트랙트(Smart Contract) (0) | 2021.10.11 |
---|---|
솔리디티(Solidity) (0) | 2021.10.08 |