일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- API
- 웹
- geth구현
- 프론트엔드
- blockchain
- Solidity
- 앵커링
- 블록체인 구조와 이론
- Klaytn API Service
- 제어자
- 블록체인
- SCN
- ethereum
- 클레이튼
- Klaytn
- Web
- 이더리움
- 서비스체인
- javascript
- 솔리디티
- web3.js
- nodejs
- Geth
- vue
- vue.js
- 접근제어자
- 프레임워크
- kas
- web3
- frontend
- Today
- Total
BloCCat
이더리움 기반 Crowd Funding Dapp 본문
프로젝트 개요
1. 프로젝트 설명
사용자(후원자 또는 자영업자)는 이더리움 네트워크에 계정을 등록한 참여자이어야 한다.
후원을 받기위해 자신의 정보와 목표 후원 금액, 후원 기간을 제출하면 입력하면 운영자는 이 자영업자의 Funding Contract를 배포하고, 토큰을 발행한다.
후원자는 사이트에서 자영업자의 사연을 보고 후원 여부를 결정한다.
후원할 때, 자신의 계정 주소와 비밀번호(Private key)를 입력하고 얼마나 후원할지 입력한다.
후원을 마치면 사이트는 후원에 대한 보상으로 후원자에게 토큰을 발행한다.
자영업자는 목표금액 만큼 후원금이 모이면 제약없이 즉이 인출이 가능하다.
동작과정
1.준비 및 실행
1) 네트워크
테스트를 위해 이더리움 메인 네트워크가 아닌 테스트 네트워크(가나슈)를 사용한다.
$ ./ganache-2.5.4-linux-x86_64.AppImage
2) 컨트랙트 배포
사이트에서 쓰여질 Token Contract와 후원을 원하는 자영업자의 Funding Contract를 배포한다.
추가로 각 펀딩 컨트랙트들은 후원자들에게 후원받을시 보상으로 토큰을 줘야하므로 Token Contract로 부터 토큰을 분배받아야 한다.
* 컴파일과 배포는 Remix(https://remix.ethereum.org/)에서 이루어진다.
3) Web Server 설정 및 실행
Ganache Network와의 통신과 배포된 Contract 사용을 위해 Contract의 CA와,ABI, 그리고 자영업자(후원 받을 사람) 와 후원자의 EOA를 등록 후 실행한다.
이때 서버는 Node.js를 이용하며 Ganache Network와의 연결은 Web3.js를 사용하며 토큰 생성 계정(mainAdrees), 3명의 자영업자(crowdFundingContractAddress) 를 등록한다.
위와 같은 설정을 마친 후, 서버를 실행하는데 이때 npm을 사용한다.
$ npm start
2.후원
1) 메인화면 상단 메뉴의 “Donate” 를 클릭하거나 대표 후원사례의 “More”를 클릭하면 후원을 기다리는 자들의 리스트를 확인할 수 있다.
2) 후원 목록 페이지에서 참여하기 버튼을 클릭하면 후원 결정 페이지로 이동하며, 계정 주소와 후원금액, 비밀번호(Private Key)를 입력 즉시 후원이 완료된다.
(이때 별도의 회원가입 절차 없이 네트워크 참여 계정이라면 누구나 후원이 가능하다)
3) 정보를 입력하고 “Donate Now” 버튼을 누르면 후원이 완료된다.
- 후원이 완료되면 후원자에겐 후원에 대한 보상으로 후원한 금액의 1대1 비율의 양만큼 토큰(ZAST)을 받는다.
- 목표 모금액 30Ether중 12Ether가 모였으므로 모금 진행률이 40%로 변경된 것을 확인할 수 있다.
4) 같은 방법, 다른 계정으로 목표 모금액 달성
- 이 계정은 18Ether를 후원하여 18개의 토큰(ZAST)을 받은 것을 확인할 수 있다
- 12Ether를 후원받은 상태에서 18Ether를 추가로 다른 계정에게 후원 받아 총 후원 진행률이 100%가 된 것을 확인할 수 있다.
- 이 자영업자는 처음 100개의 토큰(ZAST)가 있었는데 첫번째 후원자에게 12개, 두번째 후원자에게 18개의 토큰을 후원 보상으로 지급하여 현재 70개의 토큰이 남아있는 것 또한 확인할 수 있다.
3.인출
1) 상단 메뉴의 “Makers” 버튼을 클릭하면 장영업자 리스트 페이지로 이동한다.
- 아래 “Withdraw ETH” 버튼을 누르면 인출 페이지로 이동한다.
2) 인출
- 위 페이지의 “보유 Ether”는 저 사람, 즉 자영업자의 보유 Ether 이다
3) 인출 전 비밀번호 입력
- 그림(11) 인출 페이지에서 “Withdraw” 버튼을 누르면 비밀번호 입력란이 나온다.
- 비밀번호는 자영업자 본인 확인을 위해 입력하며 자영업자의 Pirvate Key를 입력한다.
4) 인출 완료
- 인출 성공 후 자영업자의 보유 Ether는 기존 100Ether에서 모금액인 30Ether를 받아 총 130Ether를 보유하게된 것을 알 수 있다.
Smart Contract
pragma solidity ^0.4.18;
contract WalletCompatibleToken {
string public name;
string public symbol;
uint8 public decimals;
mapping (address => uint256) public balanceOf;
event Transfer(address _from, address _to, uint _value);
constructor() public {
name = "ZZASE TOKEN";
symbol = "ZAST";
decimals = 0;
balanceOf[msg.sender] = 10000; // Give the creator all initial tokens
}
function transfer(address _to, uint256 _value) external {
if (balanceOf[msg.sender] < _value) revert();
if (balanceOf[_to] + _value < balanceOf[_to]) revert();
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
emit Transfer(msg.sender,_to,_value);
}
}
pragma solidity ^0.4.18;
interface token {
function transfer(address receiver, uint amount) external;
}
contract CrowdFund {
address public owner;
address public producer;
uint public goalAmount;
uint public totalAmount;
uint public deadline;
uint public price;
token public tokenReward;
mapping(address => uint256) public balanceOf;
bool public goalReached;
bool public ended;
event GoalReached(address ownerAddress, uint amountRaisedValue);
event FundTransfer(address backer, uint amount, bool isContribution);
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
modifier afterDeadline() {
require(now >= deadline);
_;
}
constructor(
address _tokenAddress,
address _producerAddress
) public {
owner = msg.sender;
producer = _producerAddress;
goalAmount = 30 * 1 ether;
deadline = now + (3 * 1 minutes);
price = 1 * 1 ether;
tokenReward = token(_tokenAddress);
totalAmount = 0;
goalReached = false;
ended = false;
}
function() payable external {
require(!ended);
uint amount = msg.value;
balanceOf[msg.sender] += amount;
totalAmount += amount;
tokenReward.transfer(msg.sender, amount / price);
emit FundTransfer(msg.sender, amount, true);
}
function checkGoalReached() external afterDeadline {
require(!ended);
if(totalAmount >= goalAmount) {
goalReached = true;
emit GoalReached(owner, totalAmount);
}
ended = true;
}
function safeWithdrawal() external afterDeadline {
if(!goalReached) {
uint amount = balanceOf[msg.sender];
balanceOf[msg.sender] = 0;
if(amount > 0) {
if(msg.sender.send(amount)) {
emit FundTransfer(msg.sender, amount, false);
} else {
balanceOf[msg.sender] = amount;
}
}
}
if(goalReached && owner == msg.sender) {
if(producer.send(totalAmount)) {
emit FundTransfer(producer, totalAmount, false);
} else {
goalReached = false;
}
}
}
function getGoalAmount() public view returns (uint){
return goalAmount;
}
function kill() public onlyOwner {
selfdestruct(owner);
}
}