- 参赛球队一经设定不可改变,整个活动结束后无法投票;
- 全⺠均可参与,无权限控制;
- 每次投票为 1 ether,且只能选择一支球队;
- 每个人可以投注多次;
- 仅管理员公布最终结果,完成奖金分配,开奖后逻辑:
- winner 共享整个奖金池(一部分是自己的本金,一部分是利润);
- winner 需自行领取奖金(因为有手续费);
- 下一期自行开始8 `6 R! m" V# a" N- a1 a V
) S& V7 j9 ^, ~6 |2 i
* K. ~! ]8 ?0 |# |0 E8 g' `- G
- / a u9 Z. D& W; j- L
- // SPDX-License-Identifier: GPL-3.0* [# J# ^# l' I/ D- W& L. F
- pragma solidity >=0.7.0 <0.9.0;
- . i! Z% W. [: o: u& E9 K* ], X
- import "hardhat/console.sol";
- contract WorldCup {
- // 1. 状态变量:管理员、所有玩家、获奖者地址、第几期、参赛球队
- // 2. 核心方法:下注、开奖、兑现3 |5 a/ m2 N$ D- ~, B; j5 F& {
- // 3. 辅助方法:获取奖金池金额、管理员地址、当前期数、参与人数、所有玩家、参赛球队
- 9 V9 r2 C% r* u! ?2 ^+ F: R" I. m
- // 管理员. G' k; q& E3 t% {" i2 C
- address public admin;' f7 [0 B C# p, u, G( p% H0 H* J
- // 第几期
- uint8 public currRound;
- # F0 F$ ]" Q2 P/ H# Z) ?0 [
- // 参赛球队
- string[] public countries = ["GERMANY", "FRANCH", "CHINA", "BRIZAL", "KOREA"];
- // 期数 => 玩家
- mapping(uint8 => mapping(address => Player)) players;8 G6 Q7 n Q8 A! C! a# x/ s- \' A" E4 n
- // 期数 => 投注各球队的玩家
- mapping(uint8 => mapping(Country => address[])) public countryToPlayers;+ X7 [% P$ B* b2 L, X k
- // 玩家对应赢取的奖金1 \1 M5 P4 L# F7 V: C
- mapping(address => uint256) public winnerVaults;: }! y2 A/ o0 c! k/ W
- // 投注截止时间-使用不可变量,可通过构造函数传值,部署后无法改变; u" ]& e" L* D% d# B' n/ K
- uint256 public immutable deadline;; g# }2 ` L# O" V$ i
- // 所有玩家待兑现的奖金, h) H ?/ U/ g2 _) `
- uint256 public lockedAmts;3 E0 k6 Y; W# B2 o& w
- enum Country {1 d! w9 K% U1 l8 Y. \, E
- GERMANY,
- FRANCH,
- CHINA,
- BRAZIL,7 U/ i3 ~0 h: @: |& u% {
- KOREA
- }! I& |$ U0 D Y2 m4 o
- 6 G1 [& w7 a) u/ A9 x
- event Play(uint8 _currRound, address _player, Country _country);
- event Finialize(uint8 _currRound, uint256 _country);1 N7 k% i. _; g, p6 Y3 w, d4 r
- event ClaimReward(address _claimer, uint256 _amt);9 H) _ W* S: S. k
- - B r: u, d U" W M0 [& K# ?
- // 验证管理员身份 A3 y) E& s! w7 r/ K+ w
- modifier onlyAdmin {7 A' k6 T- D3 A! o2 w# v) Y6 }
- require(msg.sender == admin, "not authorized!");
- _;
- }
- 6 L+ n2 |4 A3 P; \7 F1 G+ T) m( }
- // 玩家投注信息
- struct Player {
- // 是否开奖( N) E% @' p( `. J# [% S" V
- bool isSet;
- // 投注的球队份额
- mapping(Country => uint256) counts;& p/ C0 @) I4 v, {- l; q; {
- }+ N, F9 z; T$ k7 c
- constructor(uint256 _deadline) {
- admin = msg.sender;8 H; ^* L- Y" |1 O6 W L0 s
- require(_deadline > block.timestamp, "WorldCupLottery: invalid deadline!");1 g+ h8 U5 W- ^7 ^% d. z
- deadline = _deadline;. T- m2 [ u, v5 l; [5 [: s" N
- }
- , }- \3 k) r+ U2 Z" X" b7 D
- // 下注过程
- function play(Country _selected) payable external {
- // 参数校验* Z( ?) N( l. m8 p |( M* h
- require(msg.value == 1 gwei, "invalid funds provided!");, `; ?) ?( L9 x+ S( [
- require(block.timestamp < deadline, "it's all over!");
- & X0 p7 V1 g0 w
- // 更新 countryToPlayers
- countryToPlayers[currRound][_selected].push(msg.sender);
- // 更新 players(storage 是引用传值,修改会同步修改原变量)
- Player storage player = players[currRound][msg.sender];. v& z) t7 ^ B
- // player.isSet = false;! y2 X. x1 G) U' E/ G2 p
- player.counts[_selected] += 1;& q( {1 [) c ~( a4 M
- emit Play(currRound, msg.sender, _selected);2 U9 P# w- o2 v* ~0 ^5 L% w7 G
- }+ [3 i+ @, `) j# l9 e/ K+ q% C
- 9 u% I5 j+ u" }& c, {: d- `
- // 开奖过程
- function finialize(Country _country) onlyAdmin external {
- // 找到 winners
- address[] memory winners = countryToPlayers[currRound][_country];
- // 分发给所有压中玩家的实际奖金7 f% h( a. b9 t9 A$ I) m
- uint256 distributeAmt;
- 5 X3 T4 f" q7 Q5 E% l
- // 本期总奖励金额(奖池金额 - 所有玩家待兑现的奖金)
- uint currAvalBalance = getVaultBalance() - lockedAmts;1 c! x+ e6 V* J c. B& M1 i/ w% X
- console.log("currAvalBalance:", currAvalBalance, "winners count:", winners.length);( A1 y, {3 t2 o7 D3 `0 a: _. F$ M7 W9 j
- ! f$ U) R H9 F4 ]$ p
- for (uint i = 0; i < winners.length; i++) {
- address currWinner = winners[i];
- * k8 M$ z( [# L" H2 |$ U
- // 获取每个地址应该得到的份额
- Player storage winner = players[currRound][currWinner];( ?# f" I; Q5 L% U# m/ E5 M' ~
- if (winner.isSet) {& r+ f+ n) C2 S6 d9 \7 z
- console.log("this winner has been set already, will be skipped!");
- continue;( ^ j. v2 K8 C6 Y, o* x9 B7 P% V' }' u
- }% A8 q# U9 e' x) }/ R
- winner.isSet = true;
- // 玩家购买的份额
- uint currCounts = winner.counts[_country];
- % @9 I1 @9 D# t2 R5 {* d. n# R0 R
- // (本期总奖励 / 总获奖人数)* 当前地址持有份额
- uint amt = (currAvalBalance / countryToPlayers[currRound][_country].length) * currCounts;$ A ^7 x3 B0 Z
- // 玩家对应赢取的奖金
- winnerVaults[currWinner] += amt;; M$ h2 h8 L" r# Q- @ Z. }8 y" x' p
- distributeAmt += amt;3 J+ l9 N) w( \; ]- Z1 {
- // 放入待兑现的奖金池: R6 G$ t1 n& u! U' M
- lockedAmts += amt;( s$ _2 V v3 q& @" w. e
- console.log("winner:", currWinner, "currCounts:", currCounts);
- console.log("reward amt curr:", amt, "total:", winnerVaults[currWinner]);
- }. r/ `' {9 L0 e; S+ P( u
- " i& F$ H! f a
- // 未分完的奖励即为平台收益- E1 X4 G" o8 A, G6 M$ y3 A
- uint giftAmt = currAvalBalance - distributeAmt;
- if (giftAmt > 0) {
- winnerVaults[admin] += giftAmt;
- }
- emit Finialize(currRound++, uint256(_country));
- }
- // 奖金兑现
- function claimReward() external {
- uint256 rewards = winnerVaults[msg.sender];7 v' i7 l" O* d6 W l& y: n1 ]
- require(rewards > 0, "nothing to claim!");+ Y9 \- l5 A, n4 O) v9 e
- // 玩家领取完奖金置为 0
- winnerVaults[msg.sender] = 0;
- // 从待兑现奖金池中移除该玩家份额
- lockedAmts -= rewards;1 _+ I5 X0 g9 _6 l
- (bool succeed,) = msg.sender.call{value: rewards}("");
- require(succeed, "claim reward failed!");6 z9 g0 k# C& _
- $ [1 f+ ~9 R0 ]. y
- console.log("rewards:", rewards);6 S2 b* [: x* {! V: o5 F: E5 W3 _
- , N" L) [- C6 C7 _. X
- emit ClaimReward(msg.sender, rewards);
- }" c) A' n8 ^; u7 I! O
- // 获取奖池金额% g2 G- O0 } _ U/ o+ v
- function getVaultBalance() public view returns(uint256 bal) {
- bal = address(this).balance;) {9 w% m7 o! j+ o
- }
- // 获取当期下注当前球队的人数
- function getCountryPlayers(uint8 _round, Country _country) external view returns(uint256) {
- return countryToPlayers[_round][_country].length;
- }! D7 }7 L% |& y
- $ o. M( J" l* j% c
- // 获取当前玩家当期押注份额; U3 _2 S; Y9 Y5 @+ S$ Q/ O
- function getPlayerInfo(uint8 _round, address _player, Country _country) external view returns(uint256 _counts) {' [( ]$ o6 M( U# y9 c
- return players[_round][_player].counts[_country];
- }
- }
$ y: {6 ?. B1 M. {. {. @' \" i
2 e# `& Y1 H% }# E9 Z