Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

足球竞猜智能合约源代码

仙翁童子子os
455 0 0
业务需求& P; j1 @/ m* j! J
  • 参赛球队一经设定不可改变,整个活动结束后无法投票;
  • 全⺠均可参与,无权限控制;
  • 每次投票为 1 ether,且只能选择一支球队;
  • 每个人可以投注多次;
  • 仅管理员公布最终结果,完成奖金分配,开奖后逻辑:
  • winner 共享整个奖金池(一部分是自己的本金,一部分是利润);
  • winner 需自行领取奖金(因为有手续费);
  • 下一期自行开始/ f& V' y) `- ~3 ^. l5 A

! M6 W% g4 |0 ?2 _) K: Q* U+ @* {$ q% x4 Y( D3 g

! l% j; b+ m: {4 w9 u* c
  1. ) I. {' m% h/ Y5 ~1 R) [$ t% t
  2. // SPDX-License-Identifier: GPL-3.05 j- N# m% K* `1 V; G0 I6 A9 y

  3. 7 @0 A3 q2 ~' k% ?* G
  4. pragma solidity >=0.7.0 <0.9.0;3 p% c  j( c# E' E7 g; {* A5 _

  5. $ H: l8 j. C- t! ^- [- m5 i( `
  6. import "hardhat/console.sol";
    5 V$ \# j, l+ N& m2 W; g
  7. 9 R. ]7 O. t# O7 E: I  O
  8. contract WorldCup {1 z7 f# S1 r3 F' `6 ~
  9.     // 1. 状态变量:管理员、所有玩家、获奖者地址、第几期、参赛球队5 e8 w6 D/ k" c  e0 y2 j
  10.     // 2. 核心方法:下注、开奖、兑现
    ; S4 R) {$ ?- |8 U3 I: O8 T$ `
  11.     // 3. 辅助方法:获取奖金池金额、管理员地址、当前期数、参与人数、所有玩家、参赛球队6 J) @- `4 s# l. {6 }

  12. 6 ~6 S  v; B* l, G: ?3 i
  13.     // 管理员+ V, N. U4 ~. B2 y0 t
  14.     address public admin;5 P* }$ |8 B  `0 W, B( J3 Q/ A4 _7 }7 [0 a
  15.     // 第几期% M/ Z& v, l) V$ @: v, E
  16.     uint8 public currRound;) D  K4 g. T* j) C
  17. ( Q5 |) @5 i# h
  18.     // 参赛球队; E0 W6 X+ Q# i2 |) d
  19.     string[] public countries = ["GERMANY", "FRANCH", "CHINA", "BRIZAL", "KOREA"];* @! {+ n+ g! u' [6 D
  20.     // 期数 => 玩家5 x0 r* T( n' l& d/ D: h
  21.     mapping(uint8 => mapping(address => Player)) players;, |, l6 U5 u# l
  22.     // 期数 => 投注各球队的玩家" L# _4 ~/ r4 t1 a
  23.     mapping(uint8 => mapping(Country => address[])) public countryToPlayers;
    4 L$ U: m0 F" q
  24.     // 玩家对应赢取的奖金
    % V3 `( U. m2 {/ Q. D( d; ]+ I  G
  25.     mapping(address => uint256) public winnerVaults;
    6 G. W6 O0 W# w+ C' X% B

  26. & K6 Q$ ?" |2 u# ]. g4 Z
  27.     // 投注截止时间-使用不可变量,可通过构造函数传值,部署后无法改变
    0 P; a) C: C. s7 O2 e' Q8 T
  28.     uint256 public immutable deadline;
    $ H1 u# J! D/ U7 |/ v3 Q. y
  29.     // 所有玩家待兑现的奖金
    ' z; Y) ^# Y, P7 p! f/ P; q, c5 B9 {
  30.     uint256 public lockedAmts;* P! Z5 j2 Y% p& z) ?! Z$ ^
  31.   U+ m! C: ]. d  X& u. H0 S
  32.     enum Country {' @( _2 t- N2 s5 @) m  A
  33.         GERMANY,
    2 D8 Q' ]4 b/ d
  34.         FRANCH,
    & v" I' i4 O1 e6 f& y
  35.         CHINA,0 ~/ C8 a# [/ a( S' h
  36.         BRAZIL," M' q7 A1 N7 r# t
  37.         KOREA4 K4 |) n5 _% Y/ G
  38.     }
    / A" C2 O1 t% Q; j
  39. . I1 x, W5 G0 F  j3 A3 d
  40.     event Play(uint8 _currRound, address _player, Country _country);' F0 P: A4 F% s8 D: [$ t3 G
  41.     event Finialize(uint8 _currRound, uint256 _country);5 X* S' Z9 B' o9 t0 R! G
  42.     event ClaimReward(address _claimer, uint256 _amt);
    * V& M, v- ?8 k2 ^
  43. + N, ?8 n5 F) I% o3 r8 d
  44.     // 验证管理员身份; H& b2 U9 U# p( x/ }
  45.     modifier onlyAdmin {
    # S4 x% G7 s. {8 n0 Q8 ~) r
  46.         require(msg.sender == admin, "not authorized!");
    # N% O! x- s7 T1 c
  47.         _;
    . c0 x9 e/ U( N3 o
  48.     }
    8 w4 \* x! G5 K

  49. " l* a6 ]" b! B! R5 ]7 q- S5 V
  50.     // 玩家投注信息: f% A0 n& {  |& c$ S* }9 g1 n
  51.     struct Player {4 b/ O- N9 _6 |
  52.         // 是否开奖) R: I- o  n& N+ E
  53.         bool isSet;
    / P/ d- N- F6 u' Q7 u3 f* @$ C) g
  54.         // 投注的球队份额
    $ ?' A. X; p! }
  55.         mapping(Country => uint256) counts;/ r+ l  `% a6 y: Y4 v8 ~
  56.     }
    ' R& p# q; ^- q/ B

  57. 9 b1 t6 u7 c2 u3 F
  58.     constructor(uint256 _deadline) {
    & |3 ^  w3 L1 H& ]8 y
  59.         admin = msg.sender;
      t& g4 b! P. ?7 H
  60.         require(_deadline > block.timestamp, "WorldCupLottery: invalid deadline!");3 D$ U; f( ^9 r, h
  61.         deadline = _deadline;
    * I. e5 l6 c: U3 f" j
  62.     }4 @: b) b0 I. Z. d5 r# k

  63. ; V+ A4 W) I7 h5 o/ ~) \2 \
  64.     // 下注过程1 Z( C' _8 b  Y
  65.     function play(Country _selected) payable external {! U- {4 j: p; n+ p1 F
  66.         // 参数校验
    * X9 T( M6 ~* V; c8 \. q* k
  67.         require(msg.value == 1 gwei, "invalid funds provided!");+ `2 y# F& t# `9 z5 D+ V
  68. : u' Z7 q5 D; y0 F  [- t
  69.         require(block.timestamp < deadline, "it's all over!");' k$ Z' P9 A/ k5 n8 M# R
  70. : X, O, W) r) P" f0 }' ]2 }
  71.         // 更新 countryToPlayers
    * b2 j; L2 @7 _2 X* o3 m0 N
  72.         countryToPlayers[currRound][_selected].push(msg.sender);
    , x' m, ~( ?4 B- @
  73.         // 更新 players(storage 是引用传值,修改会同步修改原变量)
    3 B2 s# f; ?# m" r  a2 I8 `
  74.         Player storage player = players[currRound][msg.sender];
    ( C7 o1 u% u9 Q
  75.         // player.isSet = false;" h  R! {& ~/ G& ]! }
  76.         player.counts[_selected] += 1;
    5 f* }8 G( D6 \2 h
  77. 1 O$ F6 `: D* p
  78.         emit Play(currRound, msg.sender, _selected);! p, {$ ?# \- O5 i. o
  79.     }
    ( S! V( Z4 g; P2 [+ a( {

  80. ) i' V9 Z) S/ i+ v) A5 I% j" I
  81.     // 开奖过程
    9 }% I! V( g% u/ s
  82.     function finialize(Country _country) onlyAdmin external {
    3 L9 H( b# G/ f& s7 j% d
  83.         // 找到 winners3 _% V- ~' u! |/ H5 ]- p$ y
  84.         address[] memory winners = countryToPlayers[currRound][_country];; E7 M/ n0 D2 d/ J2 M
  85.         // 分发给所有压中玩家的实际奖金3 D2 u) D% m5 _# r+ p% z
  86.         uint256 distributeAmt;
    , F( |7 A9 G: @# [# `2 N( i7 n( O

  87. 5 M$ n, l7 x6 I0 _3 o5 `
  88.         // 本期总奖励金额(奖池金额 - 所有玩家待兑现的奖金)# B' P" J% P: ]0 f
  89.         uint currAvalBalance = getVaultBalance() - lockedAmts;
    7 P* e$ a8 W$ B- \! \
  90.         console.log("currAvalBalance:", currAvalBalance, "winners count:", winners.length);; {9 K& L* Q. a! O# S- h
  91. - b: [4 }# |! p! h
  92.         for (uint i = 0; i < winners.length; i++) {) j! Y" x/ t$ l
  93.             address currWinner = winners[i];) p' q% y) D: \

  94. " W" `' p" p7 k9 ~
  95.             // 获取每个地址应该得到的份额
    7 i' d) W4 [) e% j. k
  96.             Player storage winner = players[currRound][currWinner];
    1 ]* `* H8 S2 D: x
  97.             if (winner.isSet) {' v4 f; Y6 Y6 j" W
  98.                 console.log("this winner has been set already, will be skipped!");) j4 q6 Q0 X, b# {# v# \
  99.                 continue;
    $ ~: T9 M0 X' Q- b- i' O( M
  100.             }9 Z0 W" }6 o4 U: j5 V
  101. 5 i9 @( a+ I$ j+ M1 I6 h$ r
  102.             winner.isSet = true;" S5 _+ v5 x/ F$ k+ ?
  103.             // 玩家购买的份额, V8 P1 g) ?" o  t( `. ~
  104.             uint currCounts = winner.counts[_country];
    1 c" l0 ], C1 R# W- E5 ?
  105. 7 j& q7 a, m7 }7 |7 A
  106.             // (本期总奖励 / 总获奖人数)* 当前地址持有份额, W) L+ w6 {) V- G% j
  107.             uint amt = (currAvalBalance / countryToPlayers[currRound][_country].length) * currCounts;
    8 H2 f. Z: e  d$ X  g9 K/ j8 F
  108.             // 玩家对应赢取的奖金( Z. u+ b$ H( T
  109.             winnerVaults[currWinner] += amt;
      |1 r0 l! g9 y: n% X, t4 s" ~
  110.             distributeAmt += amt;, p# ^- Z  h) e( f3 Y
  111.             // 放入待兑现的奖金池
    * |  M+ d' \" N) ^" t; j
  112.             lockedAmts += amt;' [+ T. r! U% Q

  113. 1 Q+ w7 X: [  {- |
  114.             console.log("winner:", currWinner, "currCounts:", currCounts);# J) C: ^0 N: r2 Q' Z3 i# `
  115.             console.log("reward amt curr:", amt, "total:", winnerVaults[currWinner]);4 k- o" X( i# j: @- u0 u" D( D
  116.         }
    ! X8 k4 q( V2 T& x5 Q
  117. ( P, M- w9 f7 T2 w% C, n: M, t
  118.         // 未分完的奖励即为平台收益; V" J& M" _, m
  119.         uint giftAmt = currAvalBalance - distributeAmt;/ n# E. B1 \1 r1 s) p
  120.         if (giftAmt > 0) {
    ! v, Z, w1 Z# _# n: d
  121.             winnerVaults[admin] += giftAmt;
    5 S& O6 o3 \3 K; O) x
  122.         }
    / \; e+ a, @, X
  123. 7 }6 \+ T9 O+ S9 n$ K* U
  124.         emit Finialize(currRound++, uint256(_country));
    8 ^+ A; X: a9 Y! h
  125.     }/ s7 @7 j$ g9 Q% O4 H

  126. ; P5 A+ ?) P; G5 G* x1 f. F
  127.     // 奖金兑现8 ]& d* w* E* Q4 p: v# O
  128.     function claimReward() external {1 w4 R3 ]' X* {
  129.         uint256 rewards = winnerVaults[msg.sender];
    + y$ d. Q- |! p8 V  b
  130.         require(rewards > 0, "nothing to claim!");
    & g; i3 F4 p$ u, ?- Y; z1 W
  131. / E- J+ m# C9 z3 i
  132.         // 玩家领取完奖金置为 0  v# p( B7 L& t! |/ _3 b* S% W. a
  133.         winnerVaults[msg.sender] = 0;
    6 L# D; ~7 {' J9 l  G- T! l7 Z  g/ d
  134.         // 从待兑现奖金池中移除该玩家份额7 r5 l) P. B2 B7 t2 s
  135.         lockedAmts -= rewards;; J9 E: |3 L: T. O2 Q
  136.         (bool succeed,) = msg.sender.call{value: rewards}("");
    % U8 k. F+ t+ H) Q
  137.         require(succeed, "claim reward failed!");& T9 G1 T( k8 X: C. u
  138. 6 P  O* _+ S; B$ T# {$ J
  139.         console.log("rewards:", rewards);9 X) X0 m6 b2 S$ @. T) T& w; y
  140. , N8 U+ l, F, \6 P3 n. E
  141.         emit ClaimReward(msg.sender, rewards);
    / N1 P9 [, O( z+ F! l( v
  142.     }
    9 d4 l3 u  M: k$ ~0 o; T
  143. 4 h. h/ q! `9 w: S2 l
  144.     // 获取奖池金额  l' B, S- c. Y1 i6 B. P. b
  145.     function getVaultBalance() public view returns(uint256 bal) {
    # C5 V) \5 q! L( ~; ]0 X6 R7 l# ^
  146.         bal = address(this).balance;1 v! o+ r3 ?9 F3 I1 |) o
  147.     }& d9 ~0 G: |0 z( c

  148. & J& @  C2 b* v4 t
  149.     // 获取当期下注当前球队的人数8 F# {! x- D3 }) T
  150.     function getCountryPlayers(uint8 _round, Country _country) external view returns(uint256) {
    : d$ [$ y$ l0 V8 ~/ ]
  151.         return countryToPlayers[_round][_country].length;. \0 `/ Y4 ]0 k2 H( g7 \6 y
  152.     }
      [% G* m! Y- R6 V' ]  u

  153. 8 S. I1 w# ^$ p$ \5 J
  154.     // 获取当前玩家当期押注份额  F& y2 E6 }/ H4 M3 X8 D
  155.     function getPlayerInfo(uint8 _round, address _player, Country _country) external view returns(uint256 _counts) {
    8 E% B+ V9 _& Z1 I4 |! }. Y6 c
  156.         return players[_round][_player].counts[_country];
    # v( y% ^, \  A- v8 x& e2 ?5 i3 @
  157.     }% L0 d5 C6 d1 _! k* X
  158. }
复制代码

, s9 H# C- Z" K+ m  P- u
, C! v: a2 V& p7 S+ `0 i3 U+ J: |2 I8 }4 s8 N( `2 J& p+ ~

8 k* X1 @9 m" M! f1 ?0 ]
1 U& `5 R& ^6 q5 X( |" H
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    4