Remix IDE에서 투표(Voting) 스마트 계약을 작성합니다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract Voting {
// 후보자 구조체
struct Candidate {
uint256 id;
string name;
uint256 voteCount;
}
// 투표 상태
enum State { Created, Voting, Ended }
address public admin;
State public state;
mapping(uint256 => Candidate) public candidates;
mapping(address => bool) public registeredVoters;
mapping(address => bool) public hasVoted;
uint256 public candidateCount;
uint256 public totalVotes;
// 이벤트 (WIA-FIN-007 감사 로그)
event CandidateAdded(uint256 indexed id, string name);
event VoterRegistered(address indexed voter);
event VoteCast(address indexed voter, uint256 indexed candidateId);
event StateChanged(State newState);
// 접근 제어 modifier
modifier onlyAdmin() {
require(msg.sender == admin, "Admin only");
_;
}
modifier inState(State _state) {
require(state == _state, "Invalid state");
_;
}
constructor() {
admin = msg.sender;
state = State.Created;
}
// 후보자 추가 (Created 상태에서만)
function addCandidate(string memory _name)
public onlyAdmin inState(State.Created)
{
candidateCount++;
candidates[candidateCount] = Candidate(candidateCount, _name, 0);
emit CandidateAdded(candidateCount, _name);
}
// 유권자 등록
function registerVoter(address _voter) public onlyAdmin {
require(!registeredVoters[_voter], "Already registered");
registeredVoters[_voter] = true;
emit VoterRegistered(_voter);
}
// 투표 시작
function startVoting() public onlyAdmin inState(State.Created) {
require(candidateCount > 0, "No candidates");
state = State.Voting;
emit StateChanged(State.Voting);
}
// 투표하기
function vote(uint256 _candidateId) public inState(State.Voting) {
require(registeredVoters[msg.sender], "Not registered voter");
require(!hasVoted[msg.sender], "Already voted");
require(_candidateId > 0 && _candidateId <= candidateCount, "Invalid candidate");
hasVoted[msg.sender] = true;
candidates[_candidateId].voteCount++;
totalVotes++;
emit VoteCast(msg.sender, _candidateId);
}
// 투표 종료
function endVoting() public onlyAdmin inState(State.Voting) {
state = State.Ended;
emit StateChanged(State.Ended);
}
// 당선자 조회
function getWinner() public view inState(State.Ended)
returns (string memory winnerName, uint256 winnerVotes)
{
uint256 maxVotes = 0;
uint256 winnerId = 0;
for (uint256 i = 1; i <= candidateCount; i++) {
if (candidates[i].voteCount > maxVotes) {
maxVotes = candidates[i].voteCount;
winnerId = i;
}
}
return (candidates[winnerId].name, maxVotes);
}
}
코드 핵심 포인트
enum State: 계약의 생애주기를 3단계로 관리
modifier inState: 특정 상태에서만 함수 실행 허용
- 모든 주요 동작에
event 발생 (WIA-FIN-007 준수)
- 다중
require로 입력값 검증