前言
最近卡塔爾世界杯如火如荼,讓我們一起來嘗試?yán)?solidity 語(yǔ)言做一個(gè)世界杯競(jìng)猜的 Dapp 實(shí)戰(zhàn)項(xiàng)目,本次實(shí)戰(zhàn)學(xué)習(xí)主要參考:https://github.com/dukedaily/solidity-expert,我會(huì)針對(duì)原始項(xiàng)目做更詳盡的注解,持續(xù)更新中…文章來源地址http://www.zghlxwxcb.cn/news/detail-690478.html
業(yè)務(wù)需求
- 參賽球隊(duì)一經(jīng)設(shè)定不可改變,整個(gè)活動(dòng)結(jié)束后無法投票;
- 全?均可參與,無權(quán)限控制;
- 每次投票為 1 ether,且只能選擇一支球隊(duì);
- 每個(gè)人可以投注多次;
- 僅管理員公布最終結(jié)果,完成獎(jiǎng)金分配,開獎(jiǎng)后邏輯:
- winner 共享整個(gè)獎(jiǎng)金池(一部分是自己的本金,一部分是利潤(rùn));
- winner 需自行領(lǐng)取獎(jiǎng)金(因?yàn)橛惺掷m(xù)費(fèi));
- 下一期自行開始
基礎(chǔ)合約實(shí)現(xiàn)
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "hardhat/console.sol";
contract WorldCup {
// 1. 狀態(tài)變量:管理員、所有玩家、獲獎(jiǎng)?wù)叩刂?、第幾期、參賽球?duì)
// 2. 核心方法:下注、開獎(jiǎng)、兌現(xiàn)
// 3. 輔助方法:獲取獎(jiǎng)金池金額、管理員地址、當(dāng)前期數(shù)、參與人數(shù)、所有玩家、參賽球隊(duì)
// 管理員
address public admin;
// 第幾期
uint8 public currRound;
// 參賽球隊(duì)
string[] public countries = ["GERMANY", "FRANCH", "CHINA", "BRIZAL", "KOREA"];
// 期數(shù) => 玩家
mapping(uint8 => mapping(address => Player)) players;
// 期數(shù) => 投注各球隊(duì)的玩家
mapping(uint8 => mapping(Country => address[])) public countryToPlayers;
// 玩家對(duì)應(yīng)贏取的獎(jiǎng)金
mapping(address => uint256) public winnerVaults;
// 投注截止時(shí)間-使用不可變量,可通過構(gòu)造函數(shù)傳值,部署后無法改變
uint256 public immutable deadline;
// 所有玩家待兌現(xiàn)的獎(jiǎng)金
uint256 public lockedAmts;
enum Country {
GERMANY,
FRANCH,
CHINA,
BRAZIL,
KOREA
}
event Play(uint8 _currRound, address _player, Country _country);
event Finialize(uint8 _currRound, uint256 _country);
event ClaimReward(address _claimer, uint256 _amt);
// 驗(yàn)證管理員身份
modifier onlyAdmin {
require(msg.sender == admin, "not authorized!");
_;
}
// 玩家投注信息
struct Player {
// 是否開獎(jiǎng)
bool isSet;
// 投注的球隊(duì)份額
mapping(Country => uint256) counts;
}
constructor(uint256 _deadline) {
admin = msg.sender;
require(_deadline > block.timestamp, "WorldCupLottery: invalid deadline!");
deadline = _deadline;
}
// 下注過程
function play(Country _selected) payable external {
// 參數(shù)校驗(yàn)
require(msg.value == 1 gwei, "invalid funds provided!");
require(block.timestamp < deadline, "it's all over!");
// 更新 countryToPlayers
countryToPlayers[currRound][_selected].push(msg.sender);
// 更新 players(storage 是引用傳值,修改會(huì)同步修改原變量)
Player storage player = players[currRound][msg.sender];
// player.isSet = false;
player.counts[_selected] += 1;
emit Play(currRound, msg.sender, _selected);
}
// 開獎(jiǎng)過程
function finialize(Country _country) onlyAdmin external {
// 找到 winners
address[] memory winners = countryToPlayers[currRound][_country];
// 分發(fā)給所有壓中玩家的實(shí)際獎(jiǎng)金
uint256 distributeAmt;
// 本期總獎(jiǎng)勵(lì)金額(獎(jiǎng)池金額 - 所有玩家待兌現(xiàn)的獎(jiǎng)金)
uint currAvalBalance = getVaultBalance() - lockedAmts;
console.log("currAvalBalance:", currAvalBalance, "winners count:", winners.length);
for (uint i = 0; i < winners.length; i++) {
address currWinner = winners[i];
// 獲取每個(gè)地址應(yīng)該得到的份額
Player storage winner = players[currRound][currWinner];
if (winner.isSet) {
console.log("this winner has been set already, will be skipped!");
continue;
}
winner.isSet = true;
// 玩家購(gòu)買的份額
uint currCounts = winner.counts[_country];
// (本期總獎(jiǎng)勵(lì) / 總獲獎(jiǎng)人數(shù))* 當(dāng)前地址持有份額
uint amt = (currAvalBalance / countryToPlayers[currRound][_country].length) * currCounts;
// 玩家對(duì)應(yīng)贏取的獎(jiǎng)金
winnerVaults[currWinner] += amt;
distributeAmt += amt;
// 放入待兌現(xiàn)的獎(jiǎng)金池
lockedAmts += amt;
console.log("winner:", currWinner, "currCounts:", currCounts);
console.log("reward amt curr:", amt, "total:", winnerVaults[currWinner]);
}
// 未分完的獎(jiǎng)勵(lì)即為平臺(tái)收益
uint giftAmt = currAvalBalance - distributeAmt;
if (giftAmt > 0) {
winnerVaults[admin] += giftAmt;
}
emit Finialize(currRound++, uint256(_country));
}
// 獎(jiǎng)金兌現(xiàn)
function claimReward() external {
uint256 rewards = winnerVaults[msg.sender];
require(rewards > 0, "nothing to claim!");
// 玩家領(lǐng)取完獎(jiǎng)金置為 0
winnerVaults[msg.sender] = 0;
// 從待兌現(xiàn)獎(jiǎng)金池中移除該玩家份額
lockedAmts -= rewards;
(bool succeed,) = msg.sender.call{value: rewards}("");
require(succeed, "claim reward failed!");
console.log("rewards:", rewards);
emit ClaimReward(msg.sender, rewards);
}
// 獲取獎(jiǎng)池金額
function getVaultBalance() public view returns(uint256 bal) {
bal = address(this).balance;
}
// 獲取當(dāng)期下注當(dāng)前球隊(duì)的人數(shù)
function getCountryPlayers(uint8 _round, Country _country) external view returns(uint256) {
return countryToPlayers[_round][_country].length;
}
// 獲取當(dāng)前玩家當(dāng)期押注份額
function getPlayerInfo(uint8 _round, address _player, Country _country) external view returns(uint256 _counts) {
return players[_round][_player].counts[_country];
}
}
文章來源:http://www.zghlxwxcb.cn/news/detail-690478.html
到了這里,關(guān)于世界杯競(jìng)猜項(xiàng)目Dapp-第一章(合約開發(fā))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!