Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- TypeScript
- goerli
- next.js
- Sass
- wallet
- S3
- Codestates
- Blockchain
- keccak256
- 자바스크립트
- methoidID
- 코딩테스트
- set-cookie
- 자료구조
- incentive
- 블록체인
- 다중서명계약
- geth
- scss
- next-connect
- JavaScript
- ts-loader
- 스마트컨트랙트
- @debug
- webpack
- 해쉬테이블
- currentTarget
- CA불러오기
- HTMLFormElement
- Goerlifaucet
Archives
- Today
- Total
Minwook’s portfolio
3인조직 solidity 스마트 컨트랙트 작성 본문
구현할 기능
1. 조직의 구성원은 최대 3명.
2. 모든 조직원은 관리자의 권한을 갖는다.
3. 관리자는 외부인을 관리자로 추천할 수 있다.
4. 관리자는 투표를 통하여 구성원을 해임 시킬 수 있다.
수도코드
1. 스마트 컨트랙트 발행자가 첫번째 오너가 된다.
2. 오너는 다른 사람을 추천해서 구성원에 포함시킬 수 있다.
3. 구성원이 3명 이상일경우 더이상 구성원을 추가 할 수 없다.
4. 구성원이 3명 이상일시 관리자를 해임자를 선출하여 투표를 진행할 수 있다.
5. 모든 구성원이 투표를 완료하면 개표를 진행 할 수 있다.
6. 과반수 이상 찬성하면 해임이 되고, 공석이 된다.
필요한 함수
Read(2)
- 투표상황 조회(투표 진행중, 완료)
- 각 투표자의 투표상태(투표미완료, 완료)
Write(4)
- 후보자추천
- 해임자추천
- 투표(찬성, 반대)
- 개표
코드작성
1. 라이센스와 컴파일할 솔리디티 버전을 작성
추후 ERC-20에 적용시킬 코드이기 때문에 SPDX는 MIT로
컴파일 버전은 0.8.14이상으로 작성하겠습니다
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;
2. 컨트랙트에 필요한 변수선언
contract OwnerHelper {
// 관리자가 변경되었을 때 주소와 새로운 관리자의 주소 로그를 남김
event OwnershipTransferred(address indexed preOwner, address indexed nextOwner);
address private _theOwner; // 최초 관리자
// 투표의 상태는 투표미완료, 완료 두가지
enum VoteStatus {
STATUS_NOT_VOTED, STATUS_VOTED
}
// 찬성, 반대 두가지만 던질 수 있다.
enum Vote {
disagree, agree
}
// 투표 결과 상태
enum VoteResult {
STATUS_END, STATUS_PENDING
}
struct Voting {
address Dismsissal; // 해임 대상
VoteResult voteResult; // 투표 결과 상태
}
Voting _voting;
// 관리자 구조체
struct Owner {
address addr; // 관리자 주소
VoteStatus voteStatus; // 관리자 투표 상태
Vote vote; // 관리자 투표
}
Owner [] _ownerGroup; //관리자 그룹을 배열로 선언
}
3. constructor 작성
이 계약을 최초 배포한 사람은 최초 관리자가 된다.
constructor() {
_theOwner = msg.sender;
// msg.sender를 _theOwne로 선언
Owner memory _firstOwner = Owner({
addr: _theOwner,
vote: Vote.disagree, // 투표 기본값은 반대
voteStatus: VoteStatus.STATUS_NOT_VOTED
});
_ownerGroup.push(_firstOwner); // 관리자 배열에 넣는다
}
4. 투표상태, 관리자배열 조회(Read) 함수 작성
// 투표(_voting) : 후보자 + 투표 결과 상태 리턴
function votingStatus() public view virtual returns (Voting memory) {
return _voting;
}
// 관리자 배열 리턴
function ownerGroup() public view virtual returns (Owner [] memory) {
return _ownerGroup;
}
5. 유효성 검사를 위한 함수 및 modifier 작성
// 관리자 그룹 안에 있는지 조회하는 함수
// 관리자이면 true
function ownerGroupInclude(address _candidate) view private returns (bool){
for(uint i=0; i<_ownerGroup.length; i++) {
if(_ownerGroup[i].addr == _candidate) return true;
} return false;
}
modifier ownerGroupNotInCheck(address _candidate) {
require(!ownerGroupInclude(_candidate),"Already Candidate Includes OwnerGroup");
_;
}
modifier onlyOwner(address _sender) {
require(ownerGroupInclude(_sender), "Not Includes OwnerGroup! Permission Denied");
_;
}
// 관리자가 세명인지 아닌지 검사하는 함수
// 세 명일 경우 해임 대상이 있어야 하고 세 명 미만일 경우 해임 대상 불필요
// 3명 이상일때 false
function ownerGroupLengthCheck() view private returns (bool){
if(_ownerGroup.length >=3) return false;
else return true;
}
modifier ownerUnderThree() {
require(ownerGroupLengthCheck(),"ownerGroup Length Over 3! Please use recommandCandidate Function");
_;
}
modifier ownerOverThree() {
require(!ownerGroupLengthCheck(),"ownerGroup Length Under 3! Please use recommandDismissal Function");
_;
}
//찬,반 유효성 검사
modifier isVaildVote(Vote _vote) {
require((_vote == Vote.agree) || (_vote == Vote.disagree));
_;
}
//현재 투표가 진행 중인지 확인
modifier votingIsVaild() {
require(_voting.voteResult == VoteResult.STATUS_PENDING, "The vote is not in progress.");
_;
}
//개표전 모든 관리자가 투표를 마쳤는지 확인
function ownerFinishVotingCheck () private view returns (bool){
for(uint i=0;i<_ownerGroup.length;i++) {
if(_ownerGroup[i].voteStatus != VoteStatus.STATUS_VOTED) {
return false;
}
}
return true; // 다 투표 했을 경우 허가
}
modifier ownerFinishVoting () {
require(ownerFinishVotingCheck(),"Voting is still in progress");
_;
}
6. 관리자 추천 함수 작성
//1. 제안자는 관리자 그룹에 존재,
//2. 후보자는 ownerGroup에 없어야 하고
//3. 관리자 그룹은 세 명미만이여야 함.
//4. 투표 진행중이 아니어야함
function recommandCandidate(address _candidate) public
onlyOwner(msg.sender)
ownerGroupNotInCheck(_candidate)
ownerUnderThree() {
require(_voting.voteResult != VoteResult.STATUS_PENDING,"Already Voting is in progress");
Owner memory _newOwner = Owner({
addr: _candidate,
vote: Vote.disagree,
voteStatus: VoteStatus.STATUS_NOT_VOTED
});
_ownerGroup.push(_newOwner);
}
7. 해임자 추천 함수 작성
//1. 제안자는 관리자 그룹에 존재
//2. 해임자는 ownerGroup에 존재(제안자가 스스로 해임가능)
//3. 관리자 그룹은 세명 이상이어야 함
//4. 투표중인 상태여야함
function recommandDismissal(address _target) public
onlyOwner(msg.sender)
ownerOverThree() {
//해임자는 ownerGroup에 있어야 함
require(ownerGroupInclude(_target), "Dismissed Person Not In OwnerGroup");
// 후보자가 이미 있는 경우 투표가 이미 시작한 경우 필터
require(_voting.voteResult != VoteResult.STATUS_PENDING,"Already Voting is in progress");
//투표 시작
_voting = Voting({
Dismsissal : _target,
voteResult : VoteResult.STATUS_PENDING
});
}
8. 투표 함수작성
//1. 투표자는 관리자 그룹에 존재,
//2. 유효한 투표(찬,반)해야 되고(무효표 불가)
//3. 현재 투표가 진행중이어야 함
function ownerVoting (Vote _vote) public
onlyOwner(msg.sender)
isVaildVote(_vote)
votingIsVaild() {
//자신의 투표 상태와 투표를 바꿈
for(uint i=0;i<_ownerGroup.length;i++) {
if(msg.sender == _ownerGroup[i].addr) {
_ownerGroup[i].vote = _vote;
_ownerGroup[i].voteStatus = VoteStatus.STATUS_VOTED;
break;
}
}
}
9. 개표함수 작성
//1. 개표 요청 또한 OwnerGroup에 속한 사람만 가능,
//2. 투표가 진행중인 상태
//3. 개표 요청 시 모든 관리자들이 투표를 마친 상태여야 함
function requestBallotCounting() public
onlyOwner(msg.sender)
ownerFinishVoting() {
uint agree = 0; // 찬성
uint disagree = 0; // 반대
for(uint i=0;i<_ownerGroup.length;i++) {
if(_ownerGroup[i].vote == Vote.agree) {
agree++;
} else {
disagree++;
}
}
//과반수 찬성시 해임
if(agree >= disagree) {
for(uint i=0; i<_ownerGroup.length; i++) {
if(_voting.Dismsissal == _ownerGroup[i].addr) {
delete _ownerGroup[i];
//삭제 후 하나씩 당기기 : delete만 하면 0x00,0,0으로 남음
for(uint j=i;j<_ownerGroup.length-1;j++) {
_ownerGroup[j] = _ownerGroup[j+1];
}
//마지막값 pop
_ownerGroup.pop();
break;
}
}
}
//_voting 투표진행상태 초기화
_voting.voteResult = VoteResult.STATUS_END;
_voting.Dismsissal = address(0x00); //해임자
//owner 투표 상태 및 투표 초기화
for(uint i=0;i<_ownerGroup.length;i++) {
_ownerGroup[i].voteStatus = VoteStatus.STATUS_NOT_VOTED;
_ownerGroup[i].vote = Vote.disagree;
}
}
전체코드
/*
* read (2)
* 1) 투표상황 조회
* 2) 투표자 상태
*
* write (4)
* 1) 후보자추천
* 2) 해임자추천
* 3) 투표
* 4) 개표
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;
contract OwnerHelper {
// 관리자가 변경되었을 때 주소와 새로운 관리자의 주소 로그를 남김
event OwnershipTransferred(address indexed preOwner, address indexed nextOwner);
address private _theOwner; // 최초 관리자
// 투표의 상태
enum VoteStatus {
STATUS_NOT_VOTED, STATUS_VOTED
}
// 각 관리자가 찬성, 반대 상태
enum Vote {
disagree, agree
}
// 투표 결과 상태
enum VoteResult {
STATUS_END, STATUS_PENDING
}
struct Voting {
address Dismsissal; // 해임 대상
VoteResult voteResult; // 투표 결과 상태
}
Voting _voting;
// 관리자 구조체
struct Owner {
address addr; // 관리자 주소
VoteStatus voteStatus; // 관리자 투표 상태
Vote vote; // 관리자 투표
}
Owner [] _ownerGroup; //관리자를 배열로 선언
// 이 계약을 배포한 사람은 최초 관리자가 됨
constructor() {
_theOwner = msg.sender;
// 관리자 배열에 넣고 기본값은 반대
Owner memory _firstOwner = Owner({
addr: _theOwner,
vote: Vote.disagree,
voteStatus: VoteStatus.STATUS_NOT_VOTED
});
_ownerGroup.push(_firstOwner);
}
// 투표(_voting) : 후보자 + 투표 결과 상태 리턴
function votingStatus() public view virtual returns (Voting memory) {
return _voting;
}
// 관리자 배열 리턴
function ownerGroup() public view virtual returns (Owner [] memory) {
return _ownerGroup;
}
// 관리자 그룹 안에 있는지 조회
// 관리자이면 true
function ownerGroupInclude(address _candidate) view private returns (bool){
for(uint i=0; i<_ownerGroup.length; i++) {
if(_ownerGroup[i].addr == _candidate) return true;
} return false;
}
modifier ownerGroupNotInCheck(address _candidate) {
require(!ownerGroupInclude(_candidate),"Already Candidate Includes OwnerGroup");
_;
}
modifier onlyOwner(address _sender) {
require(ownerGroupInclude(_sender), "Not Includes OwnerGroup! Permission Denied");
_;
}
// 관리자가 세명인지 아닌지 검사하는 함수
// 세 명일 경우 해임 대상이 있어야 하고 세 명 미만일 경우 해임 대상 불필요
// 3명 이상일때 false
function ownerGroupLengthCheck() view private returns (bool){
if(_ownerGroup.length >=3) return false;
else return true;
}
modifier ownerUnderThree() {
require(ownerGroupLengthCheck(),"ownerGroup Length Over 3! Please use recommandCandidate Function");
_;
}
modifier ownerOverThree() {
require(!ownerGroupLengthCheck(),"ownerGroup Length Under 3! Please use recommandDismissal Function");
_;
}
//찬,반 유효성 검사
modifier isVaildVote(Vote _vote) {
require((_vote == Vote.agree) || (_vote == Vote.disagree));
_;
}
//현재 투표가 진행 중인지 확인
modifier votingIsVaild() {
require(_voting.voteResult == VoteResult.STATUS_PENDING, "The vote is not in progress.");
_;
}
//개표 요청 전 모든 관리자가 투표를 마쳤는지 확인
function ownerFinishVotingCheck () private view returns (bool){
for(uint i=0;i<_ownerGroup.length;i++) {
if(_ownerGroup[i].voteStatus != VoteStatus.STATUS_VOTED) {
return false;
}
}
return true; // 다 투표 했을 경우 허가
}
modifier ownerFinishVoting () {
require(ownerFinishVotingCheck(),"Voting is still in progress");
_;
}
//1) 관리자 추천
//1. 제안자는 관리자 그룹에 존재,
//2. 후보자는 ownerGroup에 없어야 하고
//3. 관리자 그룹은 세 명미만이여야 함.
//4. 투표 진행중이 아니어야함
function recommandCandidate(address _candidate) public
onlyOwner(msg.sender)
ownerGroupNotInCheck(_candidate)
ownerUnderThree() {
require(_voting.voteResult != VoteResult.STATUS_PENDING,"Already Voting is in progress");
Owner memory _newOwner = Owner({
addr: _candidate,
vote: Vote.disagree,
voteStatus: VoteStatus.STATUS_NOT_VOTED
});
_ownerGroup.push(_newOwner);
}
//2) 해임자 투표
//1. 제안자는 관리자 그룹에 존재
//2. 해임자는 ownerGroup에 존재(제안자가 스스로 해임가능)
//3. 관리자 그룹은 세명 이상이어야 함
//4. 투표중인 상태여야함
function recommandDismissal(address _target) public
onlyOwner(msg.sender)
ownerOverThree() {
//해임자는 ownerGroup에 있어야 함
require(ownerGroupInclude(_target), "Dismissed Person Not In OwnerGroup");
// 후보자가 이미 있는 경우 투표가 이미 시작한 경우 필터
require(_voting.voteResult != VoteResult.STATUS_PENDING,"Already Voting is in progress");
//투표 시작
_voting = Voting({
Dismsissal : _target,
voteResult : VoteResult.STATUS_PENDING
});
}
//3) 관리자 개인 투표
//1. 투표자는 관리자 그룹에 존재,
//2. 유효한 투표(찬,반)해야 되고(무효표 불가)
//3. 현재 투표가 진행중이어야 함
function ownerVoting (Vote _vote) public
onlyOwner(msg.sender)
isVaildVote(_vote)
votingIsVaild() {
//자신의 투표 상태와 투표를 바꿈
for(uint i=0;i<_ownerGroup.length;i++) {
if(msg.sender == _ownerGroup[i].addr) {
_ownerGroup[i].vote = _vote;
_ownerGroup[i].voteStatus = VoteStatus.STATUS_VOTED;
break;
}
}
}
//4) 개표
//1. 개표 요청 또한 OwnerGroup에 속한 사람만 가능,
//2. 투표가 진행중인 상태
//3. 개표 요청 시 모든 관리자들이 투표를 마친 상태여야 함
function requestBallotCounting() public
onlyOwner(msg.sender)
ownerFinishVoting() {
uint agree = 0; // 찬성
uint disagree = 0; // 반대
for(uint i=0;i<_ownerGroup.length;i++) {
if(_ownerGroup[i].vote == Vote.agree) {
agree++;
} else {
disagree++;
}
}
//과반수 찬성시 해임
if(agree >= disagree) {
for(uint i=0; i<_ownerGroup.length; i++) {
if(_voting.Dismsissal == _ownerGroup[i].addr) {
delete _ownerGroup[i];
//삭제 후 하나씩 당기기 : delete만 하면 0x00,0,0으로 남음
for(uint j=i;j<_ownerGroup.length-1;j++) {
_ownerGroup[j] = _ownerGroup[j+1];
}
//마지막값 pop
_ownerGroup.pop();
break;
}
}
}
//_voting 투표진행상태 초기화
_voting.voteResult = VoteResult.STATUS_END;
_voting.Dismsissal = address(0x00); //해임자
//owner 투표 상태 및 투표 초기화
for(uint i=0;i<_ownerGroup.length;i++) {
_ownerGroup[i].voteStatus = VoteStatus.STATUS_NOT_VOTED;
_ownerGroup[i].vote = Vote.disagree;
}
}
}
위 컨트랙트 작성은 코드스테이츠 BEB 6기 동기생인 김현우님의 도움을 받았습니다.
apfl99 - Overview
apfl99 has 9 repositories available. Follow their code on GitHub.
github.com
'Codestates 블록체인 부트캠프 6기' 카테고리의 다른 글
Incentive Community Project 회고 (0) | 2022.11.09 |
---|---|
Opensea Clone Coding Project 회고 (0) | 2022.10.26 |
Local에서 Remix 연결하기 + 팁! (0) | 2022.09.20 |
docker container geth 설치 (0) | 2022.09.13 |
Eth-LightWallet를 이용한 keystore와 address 생성 (0) | 2022.08.30 |
Comments