이번 포스팅에서는 킥스타터(Kickstarter), 와디즈(Wadiz)와 같은 크라우드 펀딩 서비스 앱을 위한 컨트랙트를 작성해보고자 한다. 심플하게 작성된 컨트랙트이며 핵심적인 기능들만을 담아보고자 하였다.
< 목차 >
- 컨트랙트 설명
- contract Fundraising
1. 컨트랙트 설명
- 크라우드 펀딩 프로젝트 주최자 (owner) : 돈을 모금하려는 자.
- 목표 금액 (targetAmount) : 모금하려는 돈의 총 금액.
- 크라우드 펀딩 프로젝트에 기부한 모든 사람들의 목록 (donation; mapping 데이터 타입; key).
- 참여한 사람들의 기부 금액 (donation; mapping 데이터 타입; value).
- 기부금의 누적 총액 (raisedAmount).
- 데드라인 (finishTime) : 목표액 도달까지 프로젝트가 가지고 있는 시간.
데드라인에 도달했을 때 기부금 총액이 목표액보다 많거나 같으면, 모아진 모든 자금이 프로젝트 소유자에게 전달된다.
하지만 반대로 기부금 총액이 기부 목표액보다 적으면, 기부자 명단 안에 있는 사람들은 전액을 환불 받게 된다.
2. contract Fundraising
우선 Fundraising 컨트랙트의 전체 코드는 다음과 같다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
contract Fundraising {
uint256 public targetAmount;
address public owner;
mapping(address => uint256) public donations;
uint256 public raisedAmount = 0;
uint256 public finishTime = block.timestamp + 2 weeks;
constructor(uint256 _targetAmount) {
targetAmount = _targetAmount;
owner = msg.sender;
}
/* Function */
receive() external payable {
require(block.timestamp < finishTime, 'This campaign is over');
donations[msg.sender] += msg.value;
raisedAmount += msg.value;
}
function withdrawDonations() external {
require(msg.sender == owner, 'Funds will only be released to the owner');
require(raisedAmount >= targetAmount, 'The project did not reach the goal');
require(block.timestamp > finishTime, 'The campaign is not over yet');
payable(owner).transfer(raisedAmount);
}
function refund() external {
require(block.timestamp > finishTime, 'The campaign is not over yet');
require(raisedAmount < targetAmount, 'The campaign reached the goal');
require(donations[msg.sender] > 0, 'You did not donate to this campaign');
uint256 toRefund = donations[msg.sender];
donations[msg.sender] = 0;
payable(msg.sender).transfer(toRefund);
}
}
👉 상태변수
- targetAmount : 목표 금액을 나타내는 상태변수.
- owner : 해당 컨트랙트 소유자(크라우드 펀딩 프로젝트 주최자)의 계정을 나타내는 상태변수. (address 타입)
- donations : 크라우드 펀딩 프로젝트에 참여한 사람들과 그들의 기부 금액을 mapping(address => uint256) 타입으로 나타낸 상태변수.
- raisedAmount : 기부금 누적 총액을 나타내는 상태변수.
- finishTime : 데드라인을 나타내는 상태변수.
참고로 finishTime 상태변수를 만들 때 block.timestamp 값을 사용한 것을 확인할 수 있다. 여기서 block 객체는 컨트랙트가 배포될 때 EVM에서 정의되는 객체이며 컨트랙트를 업로드하는 블록의 정보를 포함하고 있다. (주간 단위인 weeks 역시 EVM에 의해 제공되는 단위이다.)
👉 constructor
constructor(uint256 _targetAmount) {
targetAmount = _targetAmount;
owner = msg.sender;
}
constructor( ) 를 통해 컨트랙트 배포 시점에 상태변수의 값을 초기화 할 수 있다. 여기서는 인자값으로 _targetAmount를 전달하여 목표 금액을 나타내는 상태변수 값을 초기화 해주었으며 owner 상태변수 값으로는 msg.sender를 할당해 컨트랙트 배포 계정이 크라우드 펀딩 프로젝트의 주최자가 되게끔 하였다.
👉 function
// EOA에서 해당 컨트랙트로 돈을 송금하면 receive 함수가 실행된다.
receive() external payable { // external 함수는 컨트랙트 외부에서만 호출할 수 있다.
// require() : 조건이 참인지 여부를 확인
require(block.timestamp < finishTime, 'This campaign is over');
donations[msg.sender] += msg.value;
raisedAmount += msg.value;
}
EOA에서 Fundraising 컨트랙트로 이더를 송금하면 receive( ) 함수가 실행된다. 이 때 receive( ) 함수의 속성으로 반드시 payable을 명시해주도록 하자. require( ) 함수를 통해 프로젝트가 진행중인 조건에서만 함수가 실행되게끔 하였다. require( ) 함수를 사용했기 때문에 조건이 참이 아닐 경우, 메시지와 함께 오류를 발생시키고 코드 실행을 중지한다. 여기서 사용된 block.timestamp는 크라우드 펀딩 참여자가 컨트랙트로 이더를 보낼 때 발생한 트랜잭션을 저장하는데 사용된 블록의 timestamp 이다.
- donations[msg.sender] += msg.value : 크라우드 편딩 참여자를 등록하고 기부 금액을 mapping.
- raisedAmount += msg.value : 기부금 누적액 업데이트.
// 크라우드 펀딩 프로젝트 주최자에게 자금을 풀어주는 기능
function withdrawDonations() external {
require(msg.sender == owner, 'Funds will only be released to the owner');
require(raisedAmount >= targetAmount, 'The project did not reach the goal');
require(block.timestamp > finishTime, 'The campaign is not over yet');
// payable 함수를 사용해 owner에게 자금을 방출
payable(owner).transfer(raisedAmount);
}
withdrawDonations( ) 함수는 크라우드 펀딩 프로젝트의 기간이 만료되었을 때 프로젝트 주최자에게 모금된 기금을 풀어주는 기능을 수행하는 함수이다. require( ) 함수를 통해 다음의 항목들을 체크한 다음, payable( ).transfer( ) 메소드를 사용해 owner 계정으로 raisedAmount 만큼의 이더를 전송한다.
- require(msg.sender == owner) : 모금액 인출을 요청하는 사람은 크라우드 펀딩 프로젝트 주최자여야만 한다.
- require(raisedAmount >= targetAmount) : 모금액이 목표액과 같거나 혹은 더 많아야만 한다.
- require(block.timestamp > finishTime) : 크라우드 펀딩 프로젝트 기간이 종료되어야만 인출을 요청할 수 있다.
// 크라우드 펀딩 참여자들에게 환불해주는 기능 (크라우드 펀딩 기간이 끝났고 목표액에 도달하지 못한 경우)
function refund() external {
require(block.timestamp > finishTime, 'The campaign is not over yet');
require(raisedAmount < targetAmount, 'The campaign reached the goal');
require(donations[msg.sender] > 0, 'You did not donate to this campaign');
uint256 toRefund = donations[msg.sender];
donations[msg.sender] = 0;
// 기부액을 참여자에게 송금하여 전액 환불.
payable(msg.sender).transfer(toRefund);
}
refund( ) 함수는 크라우드 펀딩 기간이 종료되었고 모금액이 목표액에 도달하지 못했을 때 크라우드 펀딩에 참여한 사람들에게 환불해주는 기능을 수행하는 함수이다. withdrawDonations( ) 함수에서와 마찬가지로 require( ) 함수를 사용해 다음의 항목들을 체크한 다음, payable( ).transfer( ) 메소드를 사용해 크라우드 펀딩에 참여했던 사람들이 자신이 기부했던 금액 전부를 환불 받을 수 있게끔 하였다.
- require(block.timestamp > finishTime) : 크라우드 펀딩 프로젝트가 종료된 시점 이후에만 환불이 가능하다.
- require(raisedAmount < targetAmount) : 목표 금액에 도달하지 못했을 경우에만 환불이 가능하다.
- require(donations[msg.sender] > 0) : 환불을 요청하는 참여자는 기부한 금액이 존재해야만 한다.
'Ethereum' 카테고리의 다른 글
Ethereum/이더리움 - NFT / SaleToken 컨트랙트 (0) | 2022.08.02 |
---|---|
Ethereum/이더리움 - NFT / contract ERC721 (0) | 2022.07.27 |
Ethereum/이더리움 - NFT / Remix로 컨트랙트 배포하기 / OpenSea에 NFT 올리기 (2) | 2022.07.26 |
Ethereum/이더리움 - OpenZeppelin / 토큰 컨트랙트 / 스왑 컨트랙트 (2) | 2022.07.23 |
Ethereum/이더리움 - 인터페이스 & ERC-20 / 토큰 발행하기 (0) | 2022.07.22 |