Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

足球竞猜智能合约源代码

仙翁童子子os
454 0 0
业务需求* t1 m& u/ t. u9 L5 v' [
  • 参赛球队一经设定不可改变,整个活动结束后无法投票;
  • 全⺠均可参与,无权限控制;
  • 每次投票为 1 ether,且只能选择一支球队;
  • 每个人可以投注多次;
  • 仅管理员公布最终结果,完成奖金分配,开奖后逻辑:
  • winner 共享整个奖金池(一部分是自己的本金,一部分是利润);
  • winner 需自行领取奖金(因为有手续费);
  • 下一期自行开始
    7 p: e- ^, \+ p9 C/ j" c& q) J

  N( C0 X: d. p# q2 t
! x0 o' ^; \( |) e% T5 k+ L
# _% N  q3 h+ [! ?5 `6 L

  1. : {* }% K" n& T9 x2 H
  2. // SPDX-License-Identifier: GPL-3.0( ?' X5 F+ R. y( t6 g5 l( \
  3. * D: Z! ]! n: P
  4. pragma solidity >=0.7.0 <0.9.0;8 c5 @) |8 }* E3 W
  5. * R5 d4 y  Z" ?& S0 t8 N
  6. import "hardhat/console.sol";
    4 ?) o% `; N" x+ U: v6 k: N
  7. " s- {. s; w+ n- j' j. U& K, F
  8. contract WorldCup {
    , D1 w2 a, Q' R- Q3 K. q
  9.     // 1. 状态变量:管理员、所有玩家、获奖者地址、第几期、参赛球队
    % w$ u) `2 W4 F3 X
  10.     // 2. 核心方法:下注、开奖、兑现
    ; j/ `* x5 g7 [: u$ \; x
  11.     // 3. 辅助方法:获取奖金池金额、管理员地址、当前期数、参与人数、所有玩家、参赛球队% J5 p- n- t  n
  12. 9 E2 P5 E- P* r6 j+ P
  13.     // 管理员
    5 B' o$ }1 S+ d) i' C, N6 j
  14.     address public admin;5 e6 z8 g4 F' u( m/ n
  15.     // 第几期
    : E: |- X+ e/ j0 U
  16.     uint8 public currRound;* d0 Q6 z* ]/ K7 I; W
  17. 7 P' N4 E/ A8 E% B- H( k) P
  18.     // 参赛球队, q) \. ?( E( T9 [8 o
  19.     string[] public countries = ["GERMANY", "FRANCH", "CHINA", "BRIZAL", "KOREA"];! R% G: T8 K/ l7 r; Q
  20.     // 期数 => 玩家. S; a; w% ^. n# T
  21.     mapping(uint8 => mapping(address => Player)) players;
    " v- h! \  H+ d! Q
  22.     // 期数 => 投注各球队的玩家- Z$ d/ M( ?3 [8 _6 O+ G: S2 A
  23.     mapping(uint8 => mapping(Country => address[])) public countryToPlayers;6 y+ R4 i0 S' r) c5 L* U! `
  24.     // 玩家对应赢取的奖金
    , M* q- [+ t, R9 f6 }( _5 F6 i
  25.     mapping(address => uint256) public winnerVaults;" P( Z& j5 T2 T+ M+ H- [8 |
  26.   T2 A$ y" [# A& [* l
  27.     // 投注截止时间-使用不可变量,可通过构造函数传值,部署后无法改变' v/ G4 ^6 \& A+ _/ d8 V% F
  28.     uint256 public immutable deadline;
    9 E' |' S5 \! Z8 ^# s/ E" g! N
  29.     // 所有玩家待兑现的奖金1 t6 [$ b1 M' H+ w- X
  30.     uint256 public lockedAmts;( E6 ~! z/ Y. \7 d( z# p8 t( w( [8 O

  31. - T/ f0 y+ Z: g- {/ O% F$ {
  32.     enum Country {
    & P! J) q. A3 Q- B
  33.         GERMANY,! w3 i/ ^, s+ w5 H
  34.         FRANCH,5 a6 G7 ^* G# Z& C: D9 K+ R
  35.         CHINA,
    3 ]" N# v- s% E7 F# \
  36.         BRAZIL,+ x, I8 c  n: _/ U$ Y
  37.         KOREA' G- g+ z' I+ c% u3 d
  38.     }
    - N: k9 h+ @2 q& ]7 z
  39. " I( u! [- J* P, H' Q8 G. ]' X$ m
  40.     event Play(uint8 _currRound, address _player, Country _country);) t* C$ o, U7 [
  41.     event Finialize(uint8 _currRound, uint256 _country);
    ; }7 f/ h% @2 C1 I
  42.     event ClaimReward(address _claimer, uint256 _amt);
    & p+ x4 F/ p: q. h& q

  43. / d8 [* X% \& P
  44.     // 验证管理员身份
    2 V: S* P/ ^/ b* ]
  45.     modifier onlyAdmin {: v* _4 J  I2 G8 @1 e( u
  46.         require(msg.sender == admin, "not authorized!");
    0 z, y8 F1 H% k* J4 N
  47.         _;
    2 t' h* {! j) V3 S( f1 z1 U
  48.     }
    3 U( l' t+ j! D1 x8 w. A" F1 M" ^3 p9 `
  49. ' X! d% F0 J! V* N5 `* ]6 O
  50.     // 玩家投注信息$ v0 r, P) H7 W0 Y
  51.     struct Player {
    & M* J0 U9 r. k7 M7 B& f3 m
  52.         // 是否开奖8 T; `" @1 I* O1 V3 v7 ~7 g
  53.         bool isSet;9 f. T$ f, |  y0 T3 k; ?
  54.         // 投注的球队份额3 I+ t  X# |& L% B
  55.         mapping(Country => uint256) counts;
    $ e: Q) @0 q" y
  56.     }
    " d8 s6 q# ], r' E$ ?/ N: o

  57. 5 d  I8 x* q$ R8 Q
  58.     constructor(uint256 _deadline) {  i  }' e7 Z% k1 R3 l& N( O
  59.         admin = msg.sender;
    / s/ K2 G: X, g; \8 x
  60.         require(_deadline > block.timestamp, "WorldCupLottery: invalid deadline!");6 p, g' |8 R4 Q. x' X( G/ m
  61.         deadline = _deadline;9 d; a+ g- n! O9 H4 t2 Q
  62.     }; s6 H  c! H. H% f+ _* ?% d
  63. : [0 p2 l) r  u$ f6 t9 V7 W+ F8 O& ?
  64.     // 下注过程1 N; G1 ~8 I) p7 u# w/ {* U! J
  65.     function play(Country _selected) payable external {
    % Y# Z3 T' w* P
  66.         // 参数校验
    * G/ L8 g+ u; m2 m2 E( P
  67.         require(msg.value == 1 gwei, "invalid funds provided!");
    + X9 h8 E' L" v

  68. * r7 W. A& L1 Z& P5 d. A; n. h" E
  69.         require(block.timestamp < deadline, "it's all over!");
    " E' a9 @1 R, T" ]3 ^! t7 Y
  70. ! a4 `3 U( U9 D
  71.         // 更新 countryToPlayers, m# E, f& r: J5 r6 U8 t# @
  72.         countryToPlayers[currRound][_selected].push(msg.sender);
    $ Q6 `2 k2 t, V% N
  73.         // 更新 players(storage 是引用传值,修改会同步修改原变量): @% m- ^6 C* A; h2 S5 Y
  74.         Player storage player = players[currRound][msg.sender];
    $ ]( e5 t3 G5 }0 g6 m% ?
  75.         // player.isSet = false;
    & _+ t/ s9 M4 F0 N' K: j5 \
  76.         player.counts[_selected] += 1;9 Z- p' O8 L, U6 f- [' B$ z4 y
  77. 3 F% w: N) O1 x( F4 @) Q# {$ o
  78.         emit Play(currRound, msg.sender, _selected);
    / ^/ U: I1 a( O- ]0 ?
  79.     }
    6 S) V2 Z/ G* V1 v5 Y3 s8 U
  80. - v7 Y. M/ I  F3 W: _: f
  81.     // 开奖过程
    * b, h) w; _; D' V# T% t( E& \
  82.     function finialize(Country _country) onlyAdmin external {
      [1 o* s' G2 x8 i
  83.         // 找到 winners
    ) b: T# g" @1 ^
  84.         address[] memory winners = countryToPlayers[currRound][_country];
    ' |6 z- c: f! A. ]6 w  \& h5 m
  85.         // 分发给所有压中玩家的实际奖金8 V9 e$ a9 l3 F1 g& w6 Q$ e1 E
  86.         uint256 distributeAmt;$ a1 T4 S) P9 s. p
  87. ) A, t0 ^' ]9 {+ d2 K$ w+ L
  88.         // 本期总奖励金额(奖池金额 - 所有玩家待兑现的奖金)
    # t2 D/ M! c* u: Y$ C: E5 V/ m
  89.         uint currAvalBalance = getVaultBalance() - lockedAmts;
    / _; I( A! U7 J. a8 l# N" P9 C
  90.         console.log("currAvalBalance:", currAvalBalance, "winners count:", winners.length);
    8 u" {( G" N: U1 [2 V6 N

  91. * o) X% n, O5 I- p& O
  92.         for (uint i = 0; i < winners.length; i++) {& g# b% B5 [/ }! M
  93.             address currWinner = winners[i];
    ( N: E0 f4 h3 G9 {& \- A

  94. ( v+ n# {9 ?9 L- L8 K# ~8 v/ F
  95.             // 获取每个地址应该得到的份额/ F' s" ]3 E7 T* r5 b# r
  96.             Player storage winner = players[currRound][currWinner];
    ' x' }" {7 I  J0 }4 U6 A
  97.             if (winner.isSet) {# d! S, s2 Z! {" a/ A( z' l6 D- U
  98.                 console.log("this winner has been set already, will be skipped!");+ z$ M' X" W0 m4 ]3 j( K$ L" w
  99.                 continue;* g2 J& G; {0 b
  100.             }- G0 J3 a8 w( r" h5 S, c/ z7 _

  101. ! E, F; i- x( o: N  r% e4 E
  102.             winner.isSet = true;
    2 e* l4 l0 e; r& [
  103.             // 玩家购买的份额
    + g+ O, k' s5 A) ]
  104.             uint currCounts = winner.counts[_country];* ]5 J6 k, `, W1 }6 R4 o9 L

  105. 7 g+ Z* V1 w) T6 V
  106.             // (本期总奖励 / 总获奖人数)* 当前地址持有份额
    $ J9 o/ a4 J" k6 {% `5 _: X7 Y
  107.             uint amt = (currAvalBalance / countryToPlayers[currRound][_country].length) * currCounts;# d# d* }* S# N
  108.             // 玩家对应赢取的奖金
    & ?! ~) m  H( g7 ]
  109.             winnerVaults[currWinner] += amt;
    4 R7 M  B2 Y. x1 y
  110.             distributeAmt += amt;, B% n6 H& K' I3 L& V; w9 g' p
  111.             // 放入待兑现的奖金池
    2 I; C. b* y  Z# @: o! \
  112.             lockedAmts += amt;/ n2 p1 T5 V3 h" I
  113. 8 z: l  ]# {: f' _% T. [. |1 N
  114.             console.log("winner:", currWinner, "currCounts:", currCounts);9 R; E* v, C- ^2 k; i4 G) W1 D
  115.             console.log("reward amt curr:", amt, "total:", winnerVaults[currWinner]);7 u0 I; w7 E3 E  h
  116.         }' u$ _2 X' I+ R  ]' D5 X

  117. ( P3 d5 ~0 U7 G) X
  118.         // 未分完的奖励即为平台收益& r6 `/ U* z/ i! u) t
  119.         uint giftAmt = currAvalBalance - distributeAmt;
    : ~$ P- _! m) G0 C3 W
  120.         if (giftAmt > 0) {& x/ ?5 e. D1 z8 q; P
  121.             winnerVaults[admin] += giftAmt;; h3 }/ E  A0 b+ ~5 i
  122.         }
    " b% g( k* d% t7 q' }/ m

  123. 0 s/ S2 O7 x5 v% o5 u
  124.         emit Finialize(currRound++, uint256(_country));
    * i" E  u4 M$ r8 C/ \# N: J! P. S
  125.     }5 V' N, W: Z( y- S# g+ `
  126. . J+ p$ I; g0 H
  127.     // 奖金兑现
      l/ t1 v* W6 |% y+ w% m- q
  128.     function claimReward() external {( l+ }2 g/ E! j/ G$ @; }# q
  129.         uint256 rewards = winnerVaults[msg.sender];
    & v( s1 {6 H1 \; ]4 L1 x' {1 b  v
  130.         require(rewards > 0, "nothing to claim!");
    - R$ W$ j* H$ D7 k& x  j4 U" H2 `

  131. 8 C% \# a3 j' S: {0 g; u) \% u3 }1 L
  132.         // 玩家领取完奖金置为 0- D, o0 }& {( u9 D
  133.         winnerVaults[msg.sender] = 0;
    + }0 l6 M3 W8 I* X) }/ u
  134.         // 从待兑现奖金池中移除该玩家份额7 D! r$ t8 M: [) k$ J
  135.         lockedAmts -= rewards;
    9 u; G8 F3 V9 d( \- v# H
  136.         (bool succeed,) = msg.sender.call{value: rewards}("");
      n6 a8 M6 l7 Q! \. j
  137.         require(succeed, "claim reward failed!");
    2 T- Q7 F6 m* t- p# }  L  B. C
  138. 4 B$ Y+ m% l6 Z; J
  139.         console.log("rewards:", rewards);
    & P% n, o  P3 i4 ]
  140. ( g# n: g, D/ {' b4 h
  141.         emit ClaimReward(msg.sender, rewards);
    * `  E( u0 ?7 e6 q- J
  142.     }
    & t5 M$ M, s- Y& F% o% b* k- q
  143. 0 w' t$ w% v2 f8 H
  144.     // 获取奖池金额
    " c  g9 i! y) x' n. [" g
  145.     function getVaultBalance() public view returns(uint256 bal) {
    & D( F4 M" W2 e% j% l
  146.         bal = address(this).balance;9 P' l( n) m& }
  147.     }
    7 r9 z# D. M2 z( t6 F" H
  148. * r" M7 O) y* S- W
  149.     // 获取当期下注当前球队的人数
    + ?9 p6 i5 C0 K5 Q& ^
  150.     function getCountryPlayers(uint8 _round, Country _country) external view returns(uint256) {
    & ^9 m  V( A6 [! z0 ~: @( S
  151.         return countryToPlayers[_round][_country].length;
    * W: [* o; p0 p0 Z" @! t0 K
  152.     }$ X$ h. }. Z+ N+ G; I

  153. 5 L' Y# J* P8 l1 v- {
  154.     // 获取当前玩家当期押注份额4 M, n4 k3 a1 p; t* h' K. Z
  155.     function getPlayerInfo(uint8 _round, address _player, Country _country) external view returns(uint256 _counts) {
    - I- v5 l+ ^3 A% c
  156.         return players[_round][_player].counts[_country];4 }9 E1 O1 v( M8 x& b$ p+ e
  157.     }) n! _% \* V/ V% t$ W
  158. }
复制代码
' y) Y2 A' E; X% A+ X* B

; F. @. W$ V1 T, ?- y
* e- p+ e% l# Q* }) V  G. k/ ~5 V( d" D1 O/ {9 T* G
  Q: k/ R- _1 M2 H7 I
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

仙翁童子子os 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    4