Lottery DApp 개발 Lottery React웹 프론트[3]
web3js - filter
수수료를 계산해 특정 주소와 owner에게 전하는 함수이다. 여기서는 간단하게 수수료를 0으로 지정하였다.
function transferAfterPayingFee(address payable addr, uint256 amount) internal returns(uint256) {
uint256 fee = 0;
uint256 amountWithoutFee = amount - fee;
//transfer to addr
addr.transfer(amountWithoutFee);
//transfer to owner
owner.transfer(fee);
return amountWithoutFee;
}
정답을 가져오는 함수를 만들어준다. mode가 트루일 경우에는 블록해쉬값을 리턴할 것이고 false일 경우 우리가 커스텀한 answer를 리턴한다.
bool private mode = false; //false = use answer for test, true = use real block hash
bytes32 public answerForTest;
만들어 준 후
function getAnswerBlockHash(uint256 answerBlockNumber) internal view returns(bytes32 answer){
return mode ? blockhash(answerBlockNumber) : answerForTest;
}
정답을 지정하는 settor도 만들어 준다.
function setAnswerForTest(bytes32 answer) public returns (bool result){
require(msg.sender == owner, "only owner can set the answer for test mode");
answerForTest = answer;
return true;
}
/**
* @dev 베팅결과값을 확인하고 팟머니를 분배한다.
* 정답 실패 : 팟머니 축적, 정답 맞춤 : 팟머니 획득, 한글자 맞춤 또는 정답확인불가 : 베팅금액만 획득
*/
function distribute() public {
// head 부터 tail 까지 도는 loop
uint256 cur;
uint256 transferAmount;
BetInfo memory b;
BlockStatus currentBlockStatus;
BettingResult currentBettingResult; //결과값
for(cur = _head; cur < _tail; cur++){
b = _bets[cur];
currentBlockStatus = getBlockStatus(b.answerBlockNumber);
if(currentBlockStatus == BlockStatus.Checkable){
bytes32 answerBlockHash = getAnswerBlockHash(b.answerBlockNumber);
//blockhash가 랜덤 값으로 나와서 테스트하기 좋지 않음
currentBettingResult = isMatch(b.challenges, getAnswerBlockHash(b.answerBlockNumber));
//win bettor gets pot
if(currentBettingResult == BettingResult.win){
//transfer pot
transferAmount = transferAfterPayingFee(b.bettor, _pot + BET_AMOUNT);
//pot = 0; 으로 초기화
_pot = 0;
//emit win
emit WIN(cur, b.bettor, transferAmount, b.challenges, answerBlockHash[0], b.answerBlockNumber);
}
//fail bettor's momey goes pot
if(currentBettingResult == BettingResult.fail){
//pot = pot+bet_amount
_pot = _pot + BET_AMOUNT;
//emit fail
emit FAIL(cur, b.bettor, 0, b.challenges, answerBlockHash[0], b.answerBlockNumber);
}
//하나만 맞췄을 경우 refund bettor's money
if(currentBettingResult == BettingResult.draw){
// transfer only bet_amount
transferAmount = transferAfterPayingFee(b.bettor, BET_AMOUNT);
//emit draw
emit DRAW(cur, b.bettor, transferAmount, b.challenges, answerBlockHash[0], b.answerBlockNumber);
}
}
if(currentBlockStatus == BlockStatus.NotRevealed){
break;
}
if(currentBlockStatus == BlockStatus.BlockLimitPassed){
transferAmount = transferAfterPayingFee(b.bettor, BET_AMOUNT);
emit REFUND(cur, b.bettor, transferAmount, b.challenges, b.answerBlockNumber);
}
popBet(cur);
}
_head = cur;
}
emit의 event들을 작성해준다.
event WIN(uint256 index, address bettor, uint256 amount, byte challenges, byte answer, uint256 answerBlockNumber);
event FAIL(uint256 index, address bettor, uint256 amount, byte challenges, byte answer, uint256 answerBlockNumber);
event DRAW(uint256 index, address bettor, uint256 amount, byte challenges, byte answer, uint256 answerBlockNumber);
event REFUND(uint256 index, address bettor, uint256 amount, byte challenges, uint256 answerBlockNumber);
마지막으로 이 함수를 작성해준다.
//이 로터리에서는 운영자가 아닌 사용자가 계속 답을 확인하면서 베팅을 하는
// 로터리를 구현할 것이기 때문에 이 함수를 사용할거임
function betAndDistribute(byte challenges) public payable returns(bool result){
bet(challenges);
distribute();
return true;
}
###두 글자 다 맞춰서 이겼을 경우
describe('when the answer is checkable', function() {
it('should give the user the pot when the answer matches', async () => {
//두 글자 다 일치했을 때
await lotery.setAnswerForTest('0xab66ec4675534645e7f83e0c9d369356db51a012a0746e2f0e82df38a345b38b', {from:deployer});
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //1 -> 4
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //2 -> 5
await lotery.betAndDistribute('0xab',{from:user1, value:betAmount}); //3 -> 6 //win
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //4 -> 7
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //5 -> 8
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //6 -> 9
let potBefore = await lotery.getPot(); // ==0.01eth
let user1BalanceBefore = await web3.eth.getBalance(user1);
let receipt7 = await lotery.betAndDistribute('0xef',{form:user2,value:betAmount}); //7 -> 10 //여기에서 win확인 가능 user1에게 pot이 간다
let potAfter = await lotery.getPot();// win : ==0 eth
let user1BalanceAfter = await web3.eth.getBalance(user1); //==balancebefore + 0.015 eth
//console.log(user1BalanceBefore.toString(), new web3.utils.BN('10000000000000000').toString());
//pot 확인
assert.equal(potBefore.toString(), new web3.utils.BN('10000000000000000').toString());
assert.equal(potAfter.toString(), new web3.utils.BN('0').toString());
user1BalanceBefore = new web3.utils.BN(user1BalanceBefore);
assert.equal(user1BalanceBefore.add(potBefore).add(betAmountBN).toString(), new web3.utils.BN(user1BalanceAfter).toString());
})
3번째에서 정답 2글자를 다 맞히는 경우를 넣어준다.
여기서 주의할 점은 user1으로 부터라는 것을 유의하여야 한다. 나는 까먹고 다 user2로 해서 equal값이 기대하는
것과 달라 오류가 났었다.
3번째에서 이겼으므로 정답블록을 확인할 수 있는 블록번호는 6이다.
이 블록6번을 확인할려면 블록 7번이 mining되어야 확인 할 수 있으므로 7번 블록까지 betAndDistribute함수를 이용해 베팅해준다.
pot에는 앞에 두명이 fail하였으므로 2 x 0.005 eth 를 가지고 있다. 3번 블록이 이겼으므로 이 pot값을 다 가져가 값이 0이 된다.
따라서 user1은 pot머니 + 베팅한 금액을 얻게 된다.
그 결과
user1이 이겨서 eth를 얻은 것을 확인 할 수 있다.
###한글자만 맞춰서 이겼을 경우
it('should give the user the amount he bet when a single character matches', async () => {
//한글자 맞췄을 때
await lotery.setAnswerForTest('0xab66ec4675534645e7f83e0c9d369356db51a012a0746e2f0e82df38a345b38b', {from:deployer});
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //1 -> 4
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //2 -> 5
await lotery.betAndDistribute('0xaf',{from:user1, value:betAmount}); //3 -> 6 //draw
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //4 -> 7
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //5 -> 8
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //6 -> 9
let potBefore = await lotery.getPot(); // ==0.01eth
let user1BalanceBefore = await web3.eth.getBalance(user1);
let receipt7 = await lotery.betAndDistribute('0xef',{form:user2,value:betAmount}); //7 -> 10 //여기에서 확인 가능
let potAfter = await lotery.getPot();// draw : ==0.01 eth
let user1BalanceAfter = await web3.eth.getBalance(user1); //==balancebefore + 0.005 eth
//draw
assert.equal(potBefore.toString(), potAfter.toString());
user1BalanceBefore = new web3.utils.BN(user1BalanceBefore);
assert.equal(user1BalanceBefore.add(betAmountBN).toString(), new web3.utils.BN(user1BalanceAfter).toString());
})
한 글자만 맞췄을 경우에는 자신이 베팅한 금액만 다시 돌려 받으므로 pot머니는 전과 후가 같다 그리고 user1의 잔고는 베팅한 금액만 더해지면 된다.
테스트한 결과
###맞추지 못한 경우
it.only('should get the eth of the user when the answer does not matches at all', async () => {
// 꽝
await lotery.setAnswerForTest('0xab66ec4675534645e7f83e0c9d369356db51a012a0746e2f0e82df38a345b38b', {from:deployer});
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //1 -> 4
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //2 -> 5
await lotery.betAndDistribute('0xef',{from:user1, value:betAmount}); //3 -> 6 //fail
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //4 -> 7
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //5 -> 8
await lotery.betAndDistribute('0xef',{from:user2, value:betAmount}); //6 -> 9
let potBefore = await lotery.getPot(); // ==0.01eth
let user1BalanceBefore = await web3.eth.getBalance(user1);
let receipt7 = await lotery.betAndDistribute('0xef',{form:user2,value:betAmount}); //7 -> 10 //여기에서 확인 가능
let potAfter = await lotery.getPot();// fail : ==0.015 eth
let user1BalanceAfter = await web3.eth.getBalance(user1); //==balancebefore
//fail
assert.equal(potBefore.add(betAmountBN).toString(), potAfter.toString());
user1BalanceBefore = new web3.utils.BN(user1BalanceBefore);
assert.equal(user1BalanceBefore.toString(), new web3.utils.BN(user1BalanceAfter).toString());
});
지면 베팅한 금액이 pot에 들어간다.
테스트한 결과
web3js - filter
web3js - send & call
React js 환경 설정
Distribute 함수설계 & 테스트
Distribute 함수설계 & isMath함수 구현 및 테스트
Bet 함수 구현&테스트
Bet 함수 구현&테스트
Lotery domain과 queue설계
Lotery 를 테스트할 테스트 파일을 test폴더 안에 만들어준다.
왜 제목이 안보이지; 너무어렵다 블로긍 ㅠㅡㅜ 다들 맥으로 설명해오앵애ㅐㅐㅐㅐㅐㅐㅐㅐㅐㅐ