- 参赛球队一经设定不可改变,整个活动结束后无法投票;
- 全⺠均可参与,无权限控制;
- 每次投票为 1 ether,且只能选择一支球队;
- 每个人可以投注多次;
- 仅管理员公布最终结果,完成奖金分配,开奖后逻辑:
- winner 共享整个奖金池(一部分是自己的本金,一部分是利润);
- winner 需自行领取奖金(因为有手续费);
- 下一期自行开始# ^$ r4 L3 V# \+ Q. \
- 4 ^9 W/ j6 Y" D$ i7 H+ J) c ^# h
- // SPDX-License-Identifier: GPL-3.0
- pragma solidity >=0.7.0 <0.9.0;4 a7 B7 e1 ?% z+ c( ?! ^/ s1 L
- " i' X a" l6 i6 l0 `! p/ \
- import "hardhat/console.sol";
- , a, s: W* f( y! a# T2 ?6 c2 k
- contract WorldCup {
- // 1. 状态变量:管理员、所有玩家、获奖者地址、第几期、参赛球队' s, r: w) H- h! O2 e
- // 2. 核心方法:下注、开奖、兑现9 o$ `7 z* O* r* G# Q1 h- A
- // 3. 辅助方法:获取奖金池金额、管理员地址、当前期数、参与人数、所有玩家、参赛球队 ]- H, h/ }% \
- 3 E% Z7 @+ Y3 r1 B P* o
- // 管理员( Y/ k; l$ h6 Y. E
- address public admin;8 R7 F; H; I& l' S
- // 第几期
- uint8 public currRound;1 U% T4 u8 `$ N, C' v/ _
- - x% C8 s6 F% F5 y& `3 J
- // 参赛球队
- string[] public countries = ["GERMANY", "FRANCH", "CHINA", "BRIZAL", "KOREA"];
- // 期数 => 玩家2 u# U7 u, v+ K% L' q- d" s
- mapping(uint8 => mapping(address => Player)) players;
- // 期数 => 投注各球队的玩家2 H) R# q% [+ s3 i
- mapping(uint8 => mapping(Country => address[])) public countryToPlayers;
- // 玩家对应赢取的奖金
- mapping(address => uint256) public winnerVaults;6 [2 H& {3 `) q p; j- o: A/ V' y, e
- . J5 O4 u8 }2 G- r
- // 投注截止时间-使用不可变量,可通过构造函数传值,部署后无法改变/ V8 M/ _" ^- G7 H6 y- Y. N5 v
- uint256 public immutable deadline;
- // 所有玩家待兑现的奖金( L0 c5 S+ r7 ]' G7 B2 _
- uint256 public lockedAmts;! r. @& W* r- I6 s+ e
- enum Country {2 P) a( }$ X7 e
- GERMANY,
- FRANCH,. L/ ~7 U9 @# T
- CHINA,6 K. H! _) m8 |. } b
- BRAZIL,& E' h* d) K* H
- KOREA, Q3 c( L7 V4 l" `$ z* D
- }) D, Z) ]4 j' l
- 9 A4 T/ j+ L. |7 d
- event Play(uint8 _currRound, address _player, Country _country);; O" s% Y$ _9 S
- event Finialize(uint8 _currRound, uint256 _country);
- event ClaimReward(address _claimer, uint256 _amt);/ d5 e0 R6 ?7 t8 ^- f
- 2 |8 F& N9 {) @/ G4 W
- // 验证管理员身份
- modifier onlyAdmin {
- require(msg.sender == admin, "not authorized!");
- _;
- }
- ; Q( Y7 n* I6 {2 a) G
- // 玩家投注信息
- struct Player {
- // 是否开奖
- bool isSet;# v( i6 S; b+ P
- // 投注的球队份额3 n" K- q' }- a7 F3 d: ]
- mapping(Country => uint256) counts;
- }& H7 K6 P$ T9 n: Y
- constructor(uint256 _deadline) {
- admin = msg.sender;" ? F. y1 c7 F& D$ D
- require(_deadline > block.timestamp, "WorldCupLottery: invalid deadline!");& T( W4 o4 H% i
- deadline = _deadline;
- }/ F* b; D5 R: l. H3 p
- 6 i* j1 k- f3 k s3 ]
- // 下注过程+ c8 ?- L, b8 H8 u* m7 H6 C
- function play(Country _selected) payable external {
- // 参数校验$ \( p9 Q8 C \7 u& X
- require(msg.value == 1 gwei, "invalid funds provided!");' R- C1 f6 h. `% k* t
- require(block.timestamp < deadline, "it's all over!");! Q! Z3 Y/ U5 g0 ]- c: V
- // 更新 countryToPlayers+ |0 b; Q" [# h
- countryToPlayers[currRound][_selected].push(msg.sender);2 i, v, @' v- ]/ Y
- // 更新 players(storage 是引用传值,修改会同步修改原变量)
- Player storage player = players[currRound][msg.sender];) Y1 |, Z5 f4 u) f" I, M
- // player.isSet = false;) n5 {; ~$ s: _" Q' a
- player.counts[_selected] += 1;
- 7 s0 ~( `( t2 m. \8 n" I
- emit Play(currRound, msg.sender, _selected);
- }* Y c# ]! Q, v% Y
- 7 V# i( ^7 C: f5 Z6 A- }, z8 [! F# ~
- // 开奖过程
- function finialize(Country _country) onlyAdmin external {1 U3 N0 g/ o, p$ n' S
- // 找到 winners$ B) H. r$ R4 K2 L2 ?7 i
- address[] memory winners = countryToPlayers[currRound][_country];
- // 分发给所有压中玩家的实际奖金
- uint256 distributeAmt;
- // 本期总奖励金额(奖池金额 - 所有玩家待兑现的奖金)
- uint currAvalBalance = getVaultBalance() - lockedAmts;
- console.log("currAvalBalance:", currAvalBalance, "winners count:", winners.length);0 g( g$ E2 x1 ^
- % y- D5 v) Z7 R& C& B8 B2 u. [
- for (uint i = 0; i < winners.length; i++) {# l+ D' y2 [0 S2 a J5 _1 X0 i$ m
- address currWinner = winners[i];
- // 获取每个地址应该得到的份额
- Player storage winner = players[currRound][currWinner];
- if (winner.isSet) {
- console.log("this winner has been set already, will be skipped!");
- continue;6 f7 ~' v7 @: }3 ^5 M% V
- }
- $ X$ \" K' m* e/ Q& n( u1 N3 L
- winner.isSet = true;; ] t3 w( W- V) _
- // 玩家购买的份额% j9 a0 y) P p2 b% c; o; H
- uint currCounts = winner.counts[_country];/ o) v3 |& A* E$ Y ?2 n. d
- // (本期总奖励 / 总获奖人数)* 当前地址持有份额
- uint amt = (currAvalBalance / countryToPlayers[currRound][_country].length) * currCounts;- S, {% M: i y: J9 [
- // 玩家对应赢取的奖金: e1 o# b$ U# ^+ K$ @9 p, N7 K$ ^6 z
- winnerVaults[currWinner] += amt;
- distributeAmt += amt;
- // 放入待兑现的奖金池) n* x4 g1 r8 [9 w. y+ G, T; z3 S
- lockedAmts += amt;5 e0 a) [) r. X" ?% [
- console.log("winner:", currWinner, "currCounts:", currCounts);- h8 S E& E# k1 R
- console.log("reward amt curr:", amt, "total:", winnerVaults[currWinner]);
- }8 L: s, ^" w3 C' x
- 6 A2 ]0 f+ h9 |% P, D: K9 R
- // 未分完的奖励即为平台收益* d% P8 ^4 i! h0 `$ ~. e
- uint giftAmt = currAvalBalance - distributeAmt;
- if (giftAmt > 0) {7 ?. V9 ^5 ~( ~3 L& D0 E) A5 t) @
- winnerVaults[admin] += giftAmt;
- }# p G* B1 `/ ^4 N4 G! ?' U
- emit Finialize(currRound++, uint256(_country));- r6 P) F, Q- X3 S; E0 |
- }+ H% \, f9 O' l
- 2 i# z% X. }, R9 u
- // 奖金兑现+ y. b1 a, D( {
- function claimReward() external {
- uint256 rewards = winnerVaults[msg.sender];
- require(rewards > 0, "nothing to claim!");. Y2 C. ?3 ~# C1 \) J8 j
- // 玩家领取完奖金置为 0
- winnerVaults[msg.sender] = 0;
- // 从待兑现奖金池中移除该玩家份额& O. v( B! Q1 }6 V- l
- lockedAmts -= rewards;3 v: |( w9 j3 `8 p6 ?$ U& h
- (bool succeed,) = msg.sender.call{value: rewards}("");
- require(succeed, "claim reward failed!");) q4 R) d, h1 P
- console.log("rewards:", rewards);' z* q' i5 E/ t* ~
- emit ClaimReward(msg.sender, rewards);% z" Q* I. `. |$ ]* J7 d- P
- }
- // 获取奖池金额9 D$ b2 q% {' e- r7 z9 y
- function getVaultBalance() public view returns(uint256 bal) {
- bal = address(this).balance;
- }' n6 T7 O" k, U. U
- // 获取当期下注当前球队的人数6 i& D3 x9 z2 U! _% N* ?9 O) E# Y
- function getCountryPlayers(uint8 _round, Country _country) external view returns(uint256) {
- return countryToPlayers[_round][_country].length;
- }
- // 获取当前玩家当期押注份额' Z# t" c9 l$ x8 x+ `
- function getPlayerInfo(uint8 _round, address _player, Country _country) external view returns(uint256 _counts) {4 N( k' w* u) C
- return players[_round][_player].counts[_country];: G! u& L- w% U+ b9 O( \& `) q G: R
- }+ I3 e! ] s6 E. s: u/ E$ q
- }
' p5 s& O% ^1 b, T8 z8 |
. {8 O2 q/ q/ O1 m: ^/ A% r
5 p0 T. i: \6 F# W& F4 m