- 参赛球队一经设定不可改变,整个活动结束后无法投票;
- 全⺠均可参与,无权限控制;
- 每次投票为 1 ether,且只能选择一支球队;
- 每个人可以投注多次;
- 仅管理员公布最终结果,完成奖金分配,开奖后逻辑:
- winner 共享整个奖金池(一部分是自己的本金,一部分是利润);
- winner 需自行领取奖金(因为有手续费);
- 下一期自行开始
6 h! N- D' X0 v2 [$ l" q* n
- / ~0 H! d/ J% Y* B% ]" `4 ^' d# N
- // SPDX-License-Identifier: GPL-3.0
- 2 q/ T; U3 L. j. E0 _; u9 M7 }
- pragma solidity >=0.7.0 <0.9.0;" m2 d3 ]( W4 J6 r( U
- 1 z$ H- v& C5 z! g# `- v
- import "hardhat/console.sol";8 J' C$ D7 S$ I3 |* W; o3 h
- V, L; h2 @1 o' J3 g) i
- contract WorldCup {/ j) j Q" X/ E% e' G
- // 1. 状态变量:管理员、所有玩家、获奖者地址、第几期、参赛球队
- // 2. 核心方法:下注、开奖、兑现
- // 3. 辅助方法:获取奖金池金额、管理员地址、当前期数、参与人数、所有玩家、参赛球队
- 3 x8 v$ f/ B' M
- // 管理员4 g' ? I. u& r
- address public admin;
- // 第几期" e7 o5 Z- n5 s' E* U
- uint8 public currRound;0 f2 o# z6 @) b$ L! }/ J
- & H' N! ?4 d: N7 F2 c
- // 参赛球队, E( \/ W7 R, Z, m6 l3 e3 }4 n8 o
- string[] public countries = ["GERMANY", "FRANCH", "CHINA", "BRIZAL", "KOREA"];
- // 期数 => 玩家2 k/ f% k* U" H7 Z9 ?
- mapping(uint8 => mapping(address => Player)) players;9 X& U- G' F: Y0 N
- // 期数 => 投注各球队的玩家
- mapping(uint8 => mapping(Country => address[])) public countryToPlayers;+ x: {( g) s- X" J$ c$ P7 c
- // 玩家对应赢取的奖金
- mapping(address => uint256) public winnerVaults;
- 0 V3 }: U+ x# ^, I% E5 m2 k
- // 投注截止时间-使用不可变量,可通过构造函数传值,部署后无法改变5 [/ r$ d$ m. ~
- uint256 public immutable deadline;
- // 所有玩家待兑现的奖金8 _/ F! v h! ^2 N
- uint256 public lockedAmts;
- enum Country {
- GERMANY,
- FRANCH,
- CHINA,
- BRAZIL,
- KOREA
- }7 A3 i2 M4 G" l. L
- $ k7 e# c: O$ U+ ?
- event Play(uint8 _currRound, address _player, Country _country);8 g# s0 g# R E. d! S3 C# C" p* Q
- event Finialize(uint8 _currRound, uint256 _country);
- event ClaimReward(address _claimer, uint256 _amt);/ w+ g6 A; |4 e! \
- // 验证管理员身份8 e" c3 I+ G! @9 m
- modifier onlyAdmin {# h% d* a- s: u
- require(msg.sender == admin, "not authorized!");
- _;$ P4 @% o& e0 E7 b% ]" _
- }
- / F0 u& F2 J; G" [5 c
- // 玩家投注信息3 w/ W' z: k: f$ p8 y. M! C
- struct Player {
- // 是否开奖* r1 Z# ^" S( Z+ i* J+ `6 Z6 T9 |
- bool isSet;
- // 投注的球队份额; ~+ m6 P4 g1 K0 W* r
- mapping(Country => uint256) counts;
- } U; i4 H! D, W- c8 s& H
- / @* m1 U* Q. k, H* h9 D9 K
- constructor(uint256 _deadline) { c* x& t0 S4 ]# P4 ? J3 |
- admin = msg.sender;/ o' U# Y- U$ c% X3 u/ c
- require(_deadline > block.timestamp, "WorldCupLottery: invalid deadline!");* C2 o. I9 F. m$ r
- deadline = _deadline; i& F5 g1 t& j, L
- }
- // 下注过程
- function play(Country _selected) payable external {
- // 参数校验
- require(msg.value == 1 gwei, "invalid funds provided!");
- + i2 l, G7 F+ a" q
- require(block.timestamp < deadline, "it's all over!");/ {7 u" B. R. V% W
- // 更新 countryToPlayers- j) }& ?- W' Z5 E. z; G
- countryToPlayers[currRound][_selected].push(msg.sender);# R6 s6 T0 G: ^- q
- // 更新 players(storage 是引用传值,修改会同步修改原变量)
- Player storage player = players[currRound][msg.sender];2 X3 y) i6 R1 n( H# l4 ?' }" i
- // player.isSet = false;
- player.counts[_selected] += 1;7 o* U1 @4 @: P8 [+ W$ i
- emit Play(currRound, msg.sender, _selected);+ U3 ^" O* V+ z& G
- }/ I) p8 W% c3 q+ J
- // 开奖过程- A& {* p4 c7 O4 f& w0 e' c; S
- function finialize(Country _country) onlyAdmin external {
- // 找到 winners- p% P. v0 g# R# J* Z" z& b' X# S8 Q
- address[] memory winners = countryToPlayers[currRound][_country];
- // 分发给所有压中玩家的实际奖金
- uint256 distributeAmt;3 J/ y- i$ s) _+ A- R4 \
- 4 B) M4 J; E( Q% u: }
- // 本期总奖励金额(奖池金额 - 所有玩家待兑现的奖金)0 v; j1 E" j* t+ N p2 m
- uint currAvalBalance = getVaultBalance() - lockedAmts;
- console.log("currAvalBalance:", currAvalBalance, "winners count:", winners.length);
- for (uint i = 0; i < winners.length; i++) {
- address currWinner = winners[i];$ Y+ H5 \: D9 z$ i/ w4 f
- // 获取每个地址应该得到的份额
- Player storage winner = players[currRound][currWinner]; F" s) `1 J4 G8 g: K. `
- if (winner.isSet) {
- console.log("this winner has been set already, will be skipped!");
- continue;
- }" ?- B+ f; r4 W* F7 _
- # }+ o; ^# T" y3 ^/ X. b A& m: G: v
- winner.isSet = true;
- // 玩家购买的份额
- uint currCounts = winner.counts[_country];1 g; N- G4 u4 k" |0 Q! u5 E
- 2 E( s4 _; f# ^' {$ e& D+ E0 H
- // (本期总奖励 / 总获奖人数)* 当前地址持有份额
- uint amt = (currAvalBalance / countryToPlayers[currRound][_country].length) * currCounts;, U! p1 N7 n0 p% C* h
- // 玩家对应赢取的奖金
- winnerVaults[currWinner] += amt;
- distributeAmt += amt;' W0 ~+ ~5 `7 X0 D1 @7 k/ j0 T
- // 放入待兑现的奖金池8 x8 _. D& |/ F. T$ x O2 N
- lockedAmts += amt;
- console.log("winner:", currWinner, "currCounts:", currCounts);) f t: d6 j8 j8 k
- console.log("reward amt curr:", amt, "total:", winnerVaults[currWinner]);
- }
- # |6 S, n- P! e
- // 未分完的奖励即为平台收益2 o4 L8 `) g8 O
- uint giftAmt = currAvalBalance - distributeAmt;
- if (giftAmt > 0) {: h9 j S" a( L$ t
- winnerVaults[admin] += giftAmt;1 h# X5 }4 B2 S
- }
- emit Finialize(currRound++, uint256(_country));
- }
- // 奖金兑现- P+ Z j! j; u( y6 i+ f
- function claimReward() external {
- uint256 rewards = winnerVaults[msg.sender];
- require(rewards > 0, "nothing to claim!");5 }% q5 [/ j0 j* A2 c& Q0 d1 ?
- // 玩家领取完奖金置为 0
- winnerVaults[msg.sender] = 0;
- // 从待兑现奖金池中移除该玩家份额3 {5 Y# y0 E O0 F' H+ Z% G
- lockedAmts -= rewards;) u% y; Z# C. f: M3 G7 K/ l) g3 q P/ c
- (bool succeed,) = msg.sender.call{value: rewards}("");! x R% y7 S# ?. Q
- require(succeed, "claim reward failed!");3 E5 X4 k# B+ }: U# c) d
- , J. t4 M7 L; Q# e" b; L |
- console.log("rewards:", rewards);
- 3 e$ m+ B+ u0 m1 {4 x( ^7 v
- emit ClaimReward(msg.sender, rewards);8 D+ [/ f z6 R/ `
- }5 J$ d# A! g) Z) t1 O4 s% W |, B3 B
- // 获取奖池金额
- function getVaultBalance() public view returns(uint256 bal) {
- bal = address(this).balance;
- }1 D3 N y+ S* ^* ^- ~" B4 t% E
- // 获取当期下注当前球队的人数7 D2 U E: \! A1 w- S ~* a# t
- function getCountryPlayers(uint8 _round, Country _country) external view returns(uint256) {
- return countryToPlayers[_round][_country].length;* F: Q5 v L/ F: v. t
- } g) ]6 A( ?9 k/ q- M8 p% R
- 5 l% C1 e( [0 T# \) r% B
- // 获取当前玩家当期押注份额7 d; z I' Y; P2 X# N' h
- function getPlayerInfo(uint8 _round, address _player, Country _country) external view returns(uint256 _counts) { j" y6 b# Z) w$ a3 g
- return players[_round][_player].counts[_country];
- }
- }
. ^+ B$ _$ F. y1 e0 z
9 f( E4 h7 P6 B- s( p5 y! k
; L1 {9 I8 {! i, P2 x
- f/ b; Y- J0 F/ @3 A+ Y