본문 바로가기
WATTO 프로젝트/컨트렉트

WT_Token

by gun_poo 2022. 2. 12.

 배팅 컨트렉트가 포함된 stable coin

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";


contract WTToken is ERC20, Ownable {
uint256 _amountToken;
 
//배팅을 하는 플레이어에 대한 구조체
struct Player {
address addr;
// 주소
uint256 playerBetAmount;
// 베팅 금액
string vote;
// 후보에게 베팅
}
 
struct Game {
Player[] taker;
// 참여자 정보
uint256 amount;
// 배팅금액 총액
bool SerialStatus;
bool contentStatus;
//
}
mapping(uint => Game) rooms;
// rooms[0], rooms[1] 형식으로 접근할 수 있으며, 각 요소는 Game 구조체 형식입니다.
// mapping(uint => mapping(Game => Serials))rooms;
// rooms[0], rooms[1] 형식으로 접근할 수 있으며, 각 요소는 Game 구조체 형식입니다.
// mapping(uint => Serials) serials; // 각 회차마다 유효성검사를 위해서 맏ㄹ

uint roomLen = 0; // rooms의 키 값입니다. 방이 생성될 때마다 1씩 올라갑니다.
event total(uint256 roomNumber,uint price);
event userInfo(uint256 roomNumber, address user, string select,uint256 amount);
event check(uint onePay);
event countPeople(uint256 contentNumber, string result, uint256 totalCount,uint256 onePay);
event showContentNumber (uint256 num);


constructor() ERC20("WToken", "WT") {
}

//1.WTTOKEN 민팅, NWT 민팅과 동일하다
function mintToken(address to, uint256 amount) public onlyAuthorized returns (bool) {
require(to != address(0x0));
require(amount > 0);
_mint(to, amount);
_approve(to, msg.sender, allowance(to, msg.sender)+amount); // approve 추가
return true;
}

 



//transferFrom 함수로 인해 spender부분 때문에 만들어 놓은 함수, NWT_approveToken 해석 참고
function approveToken(address owner,address spender) onlyAuthorized public {
uint256 amount = balanceOf(owner);
_approve(owner,spender,amount);
}
//남아있는양을 업데이트 해준다.


// 교환해주는 함수

function exchange(address _to, uint256 price) public onlyAuthorized returns (bool) {
require(_to != address(0x0));
require(balanceOf(msg.sender) > 0);
_amountToken = (price * 1e18) / 1000; // 가라로 고정으로 설정 (원화)
require(balanceOf(msg.sender) >= _amountToken);
_transfer(msg.sender, _to, _amountToken);
return true;
}


// 베팅 함수들

// 1.베팅할 수 있는 방을 개설하는 함수.
function createContent () public onlyAuthorized returns (uint roomNum){
rooms[roomLen].SerialStatus = true;
rooms[roomLen].contentStatus = true;
roomNum = roomLen;
roomLen++;
emit showContentNumber(roomLen);
}


// 2. 유효성검사를 해주는 함수.
function validVoter(address voter, uint roomNum) view public onlyAuthorized returns(bool){
for(uint i=0; i<rooms[roomNum].taker.length; i++){
if(voter == rooms[roomNum].taker[i].addr){
return false;
}
}
return true;
}

// 3.투표하는 함수
function vote (uint roomNum,address user,string memory select) public onlyAuthorized {
uint price = 10e18;
require(rooms[roomNum].SerialStatus == true,"cloesed SerialContents");
require(rooms[roomNum].contentStatus == true,"cloesed AllContents");
require(validVoter(user,roomNum), "duplicate is not!!!");
require(msg.sender == user, "owner do not vote that");
require(balanceOf(user)>price,"you do not have money");
transferFrom(user,address(this),price);
rooms[roomNum].taker.push(Player(user,price,select));
rooms[roomNum].amount += price;
emit userInfo(roomNum,user,select,rooms[roomNum].amount);
}


//4.총 얼마만큼 투표했는지 확인하는 함수.
function checkAmount (uint roomNum) public {
emit total(roomNum,rooms[roomNum].amount);
}



// 5.시간이 종료되면 투표를 막을 수 있는 장치를 만들어 놨다.
function closeSerialContent(uint roomNum) public onlyAuthorized {
rooms[roomNum].SerialStatus=false;
}
function openSerialContent(uint roomNum) public onlyAuthorized {
rooms[roomNum].SerialStatus=true;
}
function closeContent(uint roomNum) public onlyAuthorized {
rooms[roomNum].contentStatus=false;
}
// 6.회차로인해서 다시 true로 변경해서 투표를 할 수 있게 합니다.

// 정답을 맞춘 인원이 몇명인지 계산해보자.
function CountWinner(string memory res, uint roomNum) public onlyAuthorized returns(uint256){
uint256 count;

for(uint i=0; i<rooms[roomNum].taker.length; i++){
if(keccak256(abi.encodePacked(res)) == keccak256(abi.encodePacked(rooms[roomNum].taker[i].vote))){
count++;
}
}

uint onePay = rooms[roomNum].amount / count;
emit countPeople(roomNum,res,count,onePay);
return onePay;
}


//정산해 주는 로직
function payOut(string memory res, uint roomNum) public onlyAuthorized {
 
require(rooms[roomNum].contentStatus == false,"still open contentsRoom");
 
uint256 payOne = CountWinner(res,roomNum);
for(uint i=0; i<rooms[roomNum].taker.length; i++){
if(keccak256(abi.encodePacked(res)) == keccak256(abi.encodePacked(rooms[roomNum].taker[i].vote))){
_transfer(address(this),rooms[roomNum].taker[i].addr,payOne);
  }
}
 
emit check(payOne);
}
}

function mintToken, approveToken => NWT 참고

function exchange 내 _transfer 함수

function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");

_beforeTokenTransfer(sender, recipient, amount);
  • _beforeTokenTransfer : 토큰 전송 전에 호출되는 후크
 
uint256 senderBalance = _balances[sender];
  • sender의 벨런스를 senderBalance로 변환
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
  • senderBalance의 양이 amount 보다 크거나 같은지 체크
unchecked {
_balances[sender] = senderBalance - amount;
}
  • unchecked {...}
  • require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");가 오버플로/언더플로 오류보다 트랜잭션이 실패한 이유에 대한 자세한 정보를 제공, 블록 내부의 잔액을 줄이는 unchecked는 가스 절약을 의미하며 확인은 이미 require 문에서 체크되었다! 우리는 구두쇠가 되어야한다... 가스비 절약을 습관화 하자...
_balances[recipient] += amount;
  • recipient의 총량을 amount 값 만큼 더해준다.

emit Transfer(sender, recipient, amount);

_afterTokenTransfer(sender, recipient, amount);
  • _afterTokenTransfer : 토큰 전송 후 호출되는 후크
}

배팅 struct, mapping, event

player 구조체

struct Player {
address addr;
// 플레이어 주소
uint256 playerBetAmount;
// 베팅 금액
string vote;
// 후보에게 베팅
}

Game 구조체

struct Game {
Player[] taker;
// 참여자 정보
uint256 amount;
// 배팅금액 총액
bool SerialStatus;
//content-serial
bool contentStatus;
//전체 방
}
 
mapping(uint => Game) rooms;
  • rooms[0], rooms[1] 형식으로 접근할 수 있으며, 각 요소는 Game 구조체 형식
uint roomLen = 0;
  • rooms의 키 값. 방이 생성될 때마다 1씩 올라간다.
event total(uint256 roomNumber,uint price);
  • checkAmount 함수에서 총 얼마나 투표했는지 확인
event userInfo(uint256 roomNumber, address user, string select,uint256 amount);
  • vote 함수에서 user가 roomNumber, 유저 주소, 투표 목록, 투표한 양 기록
event check(uint onePay);
  • payOut , 즉 정산 함수에서 기록
event countPeople(uint256 contentNumber, string result, uint256 totalCount,uint256 onePay);
  • CountWinner 함수
event showContentNumber (uint256 num);
  • createContent 함수에서 몇번째 방인지 기록해줌

function createContent 

function createContent () public onlyAuthorized returns (uint roomNum){
rooms[roomLen].SerialStatus = true;
rooms[roomLen].contentStatus = true;
  • 배팅 방이 열리게 되면 game 구조체의 SerialStatus, contentStatus의 상태를 true로 변경
roomNum = roomLen;
roomLen++;
  • 1개의 방이 열리면 roomLen을 1 더해준다.
emit showContentNumber(roomLen);
}

 


function vaildVoter

function validVoter(address voter, uint roomNum) view public onlyAuthorized returns(bool){
for(uint i=0; i<rooms[roomNum].taker.length; i++){
  • 0부터 총 taker 길이만큼 반복
if(voter == rooms[roomNum].taker[i].addr){
  • if문으로 voter가 rooms[roomNum].taker.addr랑 겹치는지 안겹치는지 확인
return false;
 
  • 겹치면 false 반환
}
}
return true;
}

function vote

function vote (uint roomNum,address user,string memory select) public onlyAuthorized {
uint price = 10e18;
require(rooms[roomNum].SerialStatus == true,"cloesed SerialContents");
require(rooms[roomNum].contentStatus == true,"cloesed AllContents");
require(validVoter(user,roomNum), "duplicate is not!!!");
require(msg.sender == user, "owner do not vote that");
require(balanceOf(user)>price,"you do not have money");
transferFrom(user,address(this),price);
  • ERC-20 TransferFrom
  • user는 이 컨트렉트에게 price 만큼 전송한다
rooms[roomNum].taker.push(Player(user,price,select));
  • rooms[roomNum].taker에 Player(user,price,select)를 push
rooms[roomNum].amount += price;
  • rooms[roomNum]의 amount, 총 집계량에 price 만큼 추가
emit userInfo(roomNum,user,select,rooms[roomNum].amount);
  • userInfo를 기록
}

 


ERC-20 TransferFrom

function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
  • function _approve(address owner, address spender, uint256 currentAmount, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            require(currentAmount == _allowances[owner][spender], "ERC20: invalid currentAmount");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, currentAmount, amount);
        }
     
  • _approve에서는 내가 토큰을 양도할 상대방(spender)에게 양도할 값(amount)를 allowances에 기록
}
return true;
}
  • transferFrom은 양도를 수행하는 거래 대행자(msg.sender)가 sender가 허락해준 값만큼 상대방(recipient)에게 토큰을 이동
  • 이동을 위해 _transfer 함수를 실행
  • _transfer에서는 양도를 하는 sender의 잔고를 amount만큼 줄이고, recipient의 잔고를 amount만큼 늘림
  • _transfer함수 실행이 완료되고, require를 모두 통과한다면 currentAllowance를 체크하여 _approve 함수를 실행

function checkAmount

  • web에서 해당 배팅 방에 총 얼마나 집계되었는지 카운트 하기 위한 함수
function checkAmount (uint roomNum) public {
emit total(roomNum,rooms[roomNum].amount);
}

function closeSerialContent, openSerialContent, closeContent

  • 작은 방을 열고 닫고, 큰 구조의 방을 닫는 함수
 
function closeSerialContent(uint roomNum) public onlyAuthorized {
rooms[roomNum].SerialStatus=false;
}
function openSerialContent(uint roomNum) public onlyAuthorized {
rooms[roomNum].SerialStatus=true;
}
function closeContent(uint roomNum) public onlyAuthorized {
rooms[roomNum].contentStatus=false;
}

function CountWinner

  • 우승자를 맞춘 인원 체크
function CoupntWinner(string memory res, uint roomNum) public onlyAuthorized returns(uint256){
uint256 count;

for(uint i=0; i<rooms[roomNum].taker.length; i++){
if(keccak256(abi.encodePacked(res)) == keccak256(abi.encodePacked(rooms[roomNum].taker[i].vote))){
count++;
}
}

uint onePay = rooms[roomNum].amount / count;
emit countPeople(roomNum,res,count,onePay);
return onePay;

}

function payOut

  • 정산하는 함수
function payOut(string memory res, uint roomNum) public onlyAuthorized {
 
require(rooms[roomNum].contentStatus == false,"still open contentsRoom");
 
uint256 payOne = CountWinner(res,roomNum);
for(uint i=0; i<rooms[roomNum].taker.length; i++){
if(keccak256(abi.encodePacked(res)) == keccak256(abi.encodePacked(rooms[roomNum].taker[i].vote))){
_transfer(address(this),rooms[roomNum].taker[i].addr,payOne);
}
}
 
emit check(payOne);
}

CountWinner, payOut 함수 프론트 단 수정 후 리뷰 예정

'WATTO 프로젝트 > 컨트렉트' 카테고리의 다른 글

NFT 경매  (0) 2022.02.13
NFT 컨트렉트  (0) 2022.02.13
Token_Swap  (0) 2022.02.13
NWT_Token  (0) 2022.02.12
사용 컨트렉트 소개  (0) 2022.02.12

댓글