ETH Price: $2,456.24 (-1.27%)

Contract

0x37E64d7Fbeb040eb32A4801B46D4810bcB7b04e3
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Destroy181058672023-09-10 12:06:47420 days ago1694347607IN
0x37E64d7F...bcB7b04e3
0 ETH0.000280839.19349107
Settle Bet180478332023-09-02 9:07:11428 days ago1693645631IN
0x37E64d7F...bcB7b04e3
0 ETH0.0006252111.7561159
Play Coin Flip180478322023-09-02 9:06:59428 days ago1693645619IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0004448310.76425541
Settle Bet180072752023-08-27 16:45:35433 days ago1693154735IN
0x37E64d7F...bcB7b04e3
0 ETH0.0009366517.54721729
Play Etheroll180072742023-08-27 16:45:23433 days ago1693154723IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0006628815.70553269
Settle Bet180072732023-08-27 16:45:11433 days ago1693154711IN
0x37E64d7F...bcB7b04e3
0 ETH0.0009306917.43556843
Play Etheroll180072722023-08-27 16:44:59433 days ago1693154699IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0006635815.72221286
Settle Bet180072702023-08-27 16:44:35433 days ago1693154675IN
0x37E64d7F...bcB7b04e3
0 ETH0.0010727318.84962198
Play Two Dice180072692023-08-27 16:44:23433 days ago1693154663IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0010470315.9570719
Settle Bet180072682023-08-27 16:44:11433 days ago1693154651IN
0x37E64d7F...bcB7b04e3
0 ETH0.0009720318.64877937
Play Two Dice180072672023-08-27 16:43:59433 days ago1693154639IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0008381116.24946685
Settle Bet180072662023-08-27 16:43:47433 days ago1693154627IN
0x37E64d7F...bcB7b04e3
0 ETH0.0011063119.43605982
Play Two Dice180072652023-08-27 16:43:35433 days ago1693154615IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0007945316.37672774
Settle Bet180072632023-08-27 16:43:11433 days ago1693154591IN
0x37E64d7F...bcB7b04e3
0 ETH0.0010516519.29351506
Play Dice180072622023-08-27 16:42:59433 days ago1693154579IN
0x37E64d7F...bcB7b04e3
0.02 ETH0.0007872318.06858429
Settle Bet180072612023-08-27 16:42:47433 days ago1693154567IN
0x37E64d7F...bcB7b04e3
0 ETH0.0009901118.1649908
Play Dice180072602023-08-27 16:42:35433 days ago1693154555IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0007489417.18990801
Settle Bet180072582023-08-27 16:42:11433 days ago1693154531IN
0x37E64d7F...bcB7b04e3
0 ETH0.0010150219.08158834
Play Coin Flip180072572023-08-27 16:41:59433 days ago1693154519IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0007204917.4347717
Settle Bet180072572023-08-27 16:41:59433 days ago1693154519IN
0x37E64d7F...bcB7b04e3
0 ETH0.0011781422.14761113
Play Coin Flip180072562023-08-27 16:41:47433 days ago1693154507IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0006989316.91302759
Settle Bet179994502023-08-26 14:31:11434 days ago1693060271IN
0x37E64d7F...bcB7b04e3
0 ETH0.0008877916.63194247
Play Etheroll179994492023-08-26 14:30:59434 days ago1693060259IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0006254714.81929683
Settle Bet179994462023-08-26 14:30:23434 days ago1693060223IN
0x37E64d7F...bcB7b04e3
0 ETH0.0009708517.34191022
Play Two Dice179994452023-08-26 14:30:11434 days ago1693060211IN
0x37E64d7F...bcB7b04e3
0.01 ETH0.0006523414.1267626
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
181058672023-09-10 12:06:47420 days ago1694347607
0x37E64d7F...bcB7b04e3
1.32463311 ETH
180478332023-09-02 9:07:11428 days ago1693645631
0x37E64d7F...bcB7b04e3
0.018 ETH
180072752023-08-27 16:45:35433 days ago1693154735
0x37E64d7F...bcB7b04e3
1 wei
180072732023-08-27 16:45:11433 days ago1693154711
0x37E64d7F...bcB7b04e3
1 wei
180072702023-08-27 16:44:35433 days ago1693154675
0x37E64d7F...bcB7b04e3
1 wei
180072682023-08-27 16:44:11433 days ago1693154651
0x37E64d7F...bcB7b04e3
0.0162 ETH
180072662023-08-27 16:43:47433 days ago1693154627
0x37E64d7F...bcB7b04e3
0.0162 ETH
180072632023-08-27 16:43:11433 days ago1693154591
0x37E64d7F...bcB7b04e3
1 wei
180072612023-08-27 16:42:47433 days ago1693154567
0x37E64d7F...bcB7b04e3
0.027 ETH
180072582023-08-27 16:42:11433 days ago1693154531
0x37E64d7F...bcB7b04e3
0.018 ETH
180072572023-08-27 16:41:59433 days ago1693154519
0x37E64d7F...bcB7b04e3
1 wei
179994502023-08-26 14:31:11434 days ago1693060271
0x37E64d7F...bcB7b04e3
1 wei
179994462023-08-26 14:30:23434 days ago1693060223
0x37E64d7F...bcB7b04e3
1 wei
179994452023-08-26 14:30:11434 days ago1693060211
0x37E64d7F...bcB7b04e3
1 wei
179994422023-08-26 14:29:35434 days ago1693060175
0x37E64d7F...bcB7b04e3
1 wei
179994412023-08-26 14:29:23434 days ago1693060163
0x37E64d7F...bcB7b04e3
0.0228 ETH
179994392023-08-26 14:28:59434 days ago1693060139
0x37E64d7F...bcB7b04e3
0.0228 ETH
179994362023-08-26 14:28:11434 days ago1693060091
0x37E64d7F...bcB7b04e3
1 wei
179994342023-08-26 14:27:47434 days ago1693060067
0x37E64d7F...bcB7b04e3
0.018 ETH
179994332023-08-26 14:27:35434 days ago1693060055
0x37E64d7F...bcB7b04e3
1 wei
179925502023-08-25 15:18:47435 days ago1692976727
0x37E64d7F...bcB7b04e3
1 wei
179918582023-08-25 12:58:47435 days ago1692968327
0x37E64d7F...bcB7b04e3
0.01125 ETH
179918562023-08-25 12:58:23435 days ago1692968303
0x37E64d7F...bcB7b04e3
1 wei
179918042023-08-25 12:47:47435 days ago1692967667
0x37E64d7F...bcB7b04e3
0.02025 ETH
179916932023-08-25 12:25:23435 days ago1692966323
0x37E64d7F...bcB7b04e3
0.027 ETH
View All Internal Transactions
Loading...
Loading
Contract Self Destruct called at Txn Hash 0x0c397bb60a376488919a6025ad779ef2795c8580338cb1a35f99ec375919c735


Contract Source Code Verified (Exact Match)

Contract Name:
Dice9

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion, MIT license
File 1 of 16 : Dice9.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { TinyStrings, TinyString } from "./TinyStrings.sol";
import { Math } from "./Math.sol";
import { Options } from "./Options.sol";
import { BetStorage } from "./BetStorage.sol";
import { GameOptions, GameOption } from "./GameOptions.sol";
import { PackedBets, PackedBet } from "./PackedBets.sol";
import { VRF } from "./VRF.sol";

/**
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * *                                                                                                                                   * *
 * *                                                      Welcome to dice9.win!                                                        * *
 * *                                                                                                                                   * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Summary
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * Inspired by many projects in the Ethereum ecosystem, this smart contract implements a set of robust, provably fair games of chance.
 * The users can play one of four available games, wagering cryptocurrency at the odds they choose and even take part in Jackpot rolls!
 *
 * Coin Flip
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * This game allows to choose a side of the coin – heads or tails – and bet on it. Once the bet settles, the winning amount is paid if the
 * side matches the one chosen by the player.
 *
 * Dice
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * This game allows to choose 1 to 5 numbers of a dice cube and wins if a dice roll ends up with one of those numbers. The more numbers
 * are chosen the higher is the chance of winning, but the multiplier is less.
 *
 * Two Dice
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * This game allows to choose 1 to 11 numbers representing the sum of two dice. Similar to Dice game, if two dice add up to one of the
 * numbers chosen, the winnings are paid back.
 *
 * Etheroll
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * This game allows to place a bet on a number in 3 to 97 range, and if the random number produced (from 1..100 range) is less or equal
 * than the chosen one, the bet is considered a win.
 *
 * Winnings
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * If a bet wins, all the funds (including Jackpot payment, if it was eligible and won the Jackpot) are paid back to the address which
 * made the bet. Due to legal aspects, we do not distribute the winnings to other address(es), other currencies and so on.
 *
 * Jackpots
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * If a bet exceeds a certain amount (the UI will display that), a tiny Jackpot fee is taken on top of default House commission and the
 * bet automatically plays for Jackpot. Jackpots are events that have 0.1% chance of happening, but if they do, the bet gets an extra
 * portion of the winnings determined by Jackpot logic. The Jackpot rolls are completely independent from the games themselves, meaning
 * if a bet that lost a game can still get a Jackpot win.
 *
 * Commisions
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * In order to maintain the game, which includes paying for bet resolution transactions, servers for the website, our support engineers
 * and developers, the smart contract takes a fee from every bet. The specific amounts can be seen below in the constants named
 * HOUSE_EDGE_PERCENT, HOUSE_EDGE_MINIMUM_AMOUNT and JACKPOT_FEE.
 *
 * Questions?
 * ---------------------------------------------------------------------------------------------------------------------------------------
 * Please feel free to refer to our website at https://dice9.win for instructions on how to play, support channel, legal documents
 * and other helpful things.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * *                                                                                                                                   * *
 * *                                               Good luck and see you at the tables!                                                * *
 * *                                                                                                                                   * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
contract Dice9 {
  // Extension functions
  using TinyStrings for TinyString;
  using TinyStrings for string;
  using GameOptions for GameOption;
  using PackedBets for PackedBet;
  using Math for bool;

  // What percentage does the smart conract take as a processing fee
  uint constant internal HOUSE_EDGE_PERCENT = 1;
  // The minimum amount of the fee the contract takes (this is required to support small bets)
  uint constant internal HOUSE_EDGE_MINIMUM_AMOUNT = 0.001 ether;
  // The minimum amount wagered that makes the bet play for Jackpot
  uint constant internal MIN_JACKPOT_BET = 0.1 ether;
  // The fee taken from player's bet as a contribution to Jackpot fund
  uint constant internal JACKPOT_FEE = 0.001 ether;
  // The probability of any eligible bet to win a Jackpot
  uint constant internal JACKPOT_MODULO = 1000;
  // The target number to be rolled to win the jackpot
  uint constant internal JACKPOT_WINNING_OUTCOME = 888;
  // The denominator of jackpotMultiplier value
  uint constant internal JACKPOT_MULTIPLIER_BASE = 8;
  // The maximum number of jackpot bets to consider when testing for locked funds.
  uint constant internal JACKPOT_LOCK_COUNT_CAP = 5;
  // The maximum payment the jackpot can win if the multiplier is kept default
  uint constant internal JACKPOT_LOCK_MULTIPLIER = 4 * JACKPOT_FEE * JACKPOT_MODULO;
  // The paytable of the jackpot: each 2 octets specify the multiplier of the base jackpot to be paid
  uint constant internal JACKPOT_PAYTABLE = 0x0000000000000000000000000404040408080808101010101010101010202040;
  // The number of slots in the Jackpot paytable.
  uint constant internal JACKPOT_PAYTABLE_SLOTS_COUNT = 20;
  // The denominator of the values from JACKPOT_PAYTABLE
  uint constant internal JACKPOT_PAYTABLE_BASE = 16;
  // The number of epochs to pass before the bet becomes refundable
  uint constant internal REFUNDABLE_BET_EPOCHS_DISTANCE = 2;

  // The value indicating the maximum potential win a bet is allowed to make. We have to cap that value to avoid
  // draining the contract in a single bet by whales who put huge bets for high odds.
  uint72 public maxProfit;
  // The mutlplier of the jackpot payment, set by the house.
  uint40 public jackpotMultiplier;
  // The number of not-yet-settled bets that are playing for jackpot
  uint32 public jackpotBetCount;
  // The total number of funds potentially due to be paid if all pending bets win
  uint80 public lockedInBets;

  // The RSA modulus values as 4 uint256 integers. Please refer to the end of the contract for the non-interactive
  // proof of FIXME FIXME FIXME.
  uint immutable public modulus0;
  uint immutable public modulus1;
  uint immutable public modulus2;
  uint immutable public modulus3;

  // The address allowed to tweak system settings of the contract.
  address payable internal owner;

  // The bets placed by the players that are kept in contract's storage.
  BetStorage.Bets internal bets;

  /**
   * The event that gets logged when a bet is placed.
   *
   * @param vrfInputHash the hash of bet attributes (see VRF.sol).
   * @param player the address that placed the bet.
   * @param playerNonce the seq number of the player's bet against this contract.
   * @param amount the amount of bet wagered.
   * @param packedBet the game and options chosen (see PackedBet.sol)
   * @param humanReadable a human-readable description of bet (e.g. "CoinFlip heads")
   */
  event Placed(bytes32 indexed vrfInputHash, address player, uint playerNonce, uint amount, uint packedBet, string humanReadable);

  /**
   * The event that gets logged when a bet is won.
   *
   * @param vrfInputHash the hash of bet attributes (see VRF.sol).
   * @param player the address that placed the bet.
   * @param playerNonce the seq number of the player's bet against this contract.
   * @param payment the amount the bet pays as winnings.
   * @param jackpotPayment the amount the bet pays as jackpot winnings.
   * @param humanReadable a human-readable description of outcome (e.g. "CoinFlip heads heads 888")
   */
  event Won(bytes32 indexed vrfInputHash, address player, uint playerNonce, uint payment, uint jackpotPayment, string humanReadable);

  /**
   * The event that gets logged when a bet is won.
   *
   * @param vrfInputHash the hash of bet attributes (see VRF.sol).
   * @param player the address that placed the bet.
   * @param playerNonce the seq number of the player's bet against this contract.
   * @param payment the amount the bet pays as a consolation.
   * @param jackpotPayment the amount the bet pays as jackpot winnings.
   * @param humanReadable a human-readable description of outcome (e.g. "CoinFlip heads tails 887")
   */
  event Lost(bytes32 indexed vrfInputHash, address player, uint playerNonce, uint payment, uint jackpotPayment, string humanReadable);

  /**
   * The event that gets logged when somebody attemps to settle the same bet twice.
   *
   * @param player the address that placed the bet.
   * @param playerNonce the seq number of the player's bet against this contract.
   */
  event Duplicate(address player, uint playerNonce);

  /**
   * The event that gets logged when somebody attemps to settle non-existed (already removed?) bet.
   *
   * @param player the address that placed the bet.
   * @param playerNonce the seq number of the player's bet against this contract.
   */
  event Nonexistent(address player, uint playerNonce);

  /**
   * The event that gets logged when the House updates the maxProfit cap.
   *
   * @param value the new maxProfit value.
   */
  event MaxProfitUpdated(uint value);

  /**
   * The event that gets logged when the House updates the Jackpot multiplier.
   *
   * @param value the new jackpotMultiplier value.
   */
  event JackpotMultiplierUpdated(uint value);

  // The error logged when a player attempts to bet on both CoinFlip outcomes at the same time.
  error CoinFlipSingleOption();
  // The error logged when a player attempts to bet on multiple outcomes in Etheroll.
  error EtherollSingleOption();
  // The error logged when the contract receives the bet it might be unable to pay out.
  error CannotAffordToLoseThisBet();
  // The error logged when the contract receives the bet that can win more than maxProfit amount.
  error MaxProfitExceeded();
  // The error logged when the contract receives malformed batch of ecrypted bets to settle.
  error NonFullVRFs();
  // The error logged when the contract receives malformed encrypted bet.
  error StorageSignatureMismatch(address player, uint playerNonce);
  // The error logged when the contract receives a bet with 0 or 100+ winning probability (e.g. betting on all dice outcomes at once)
  error WinProbabilityOutOfRange(uint numerator, uint denominator);
  // The error logged when somebody attempts to refund the bet at the wrong time.
  error RefundEpochMismatch(address player, uint playerNonce, uint betEpoch, uint currentEpoch);

  /**
   * The modifier checking that the transaction was signed by the creator of the contract.
   */
  modifier onlyOwner {
    require(msg.sender == owner, "Only owner can do this");
    _;
  }

  /**
   * Constructs the new instance of the contract by setting the default values for contract settings
   * and recording RSA modulus values.
   *
   * @param m0 modulus0 chunk to initialize the contract with.
   * @param m1 modulus1 chunk to initialize the contract with.
   * @param m2 modulus2 chunk to initialize the contract with.
   * @param m3 modulus3 chunk to initialize the contract with.
   */
  constructor(uint m0, uint m1, uint m2, uint m3) payable {
    modulus0 = m0;
    modulus1 = m1;
    modulus2 = m2;
    modulus3 = m3;

    owner = payable(msg.sender);

    maxProfit = uint72(0 ether);
    jackpotMultiplier = 8;
  }

  /**
   * The entry point function to place a bet in a CoinFlip game.
   *
   * The first parameter is not used in any way by the smart contract and can be ignored – it's sole purpose
   * is to make Dice9 frontend find player's bets faster; it does not affect the logic in any way.
   *
   * The second parameter is a string containing "heads", "tails", "0" or "1" - indicating the side of the coin
   * the player is willing to put a bet on.
   *
   * @param options human-readable string of options to lace a bet on.
   */
  function playCoinFlip(uint /* unusedBetId */, string calldata options) external payable {
    (uint mask,) = Options.parseOptions(options.toTinyString(), 0, 1);

    // make sure there is a single option selected
    if (!Math.isPowerOf2(mask)) {
      revert CoinFlipSingleOption();
    }

    placeBet(msg.sender, msg.value, GameOptions.toCoinFlipOptions(mask));
  }

  /**
   * The entry point function to place a bet in a Dice game.
   *
   * The first parameter is not used in any way by the smart contract and can be ignored – it's sole purpose
   * is to make Dice9 frontend find player's bets faster; it does not affect the logic in any way.
   *
   * The second parameter is a string containing number(s) in the range of 1..6, e.g. "1" or "4,5,6" indicating
   * the dice outcome(s) the user is willing to place a bet on.
   *
   * @param options human-readable string of options to lace a bet on.
   */
  function playDice(uint /* unusedBetId */, string calldata options) external payable {
    (uint mask,) = Options.parseOptions(options.toTinyString(), 1, 6);
    placeBet(msg.sender, msg.value, GameOptions.toDiceOptions(mask));
  }

  /**
   * The entry point function to place a bet in a TwoDice game.
   *
   * The first parameter is not used in any way by the smart contract and can be ignored – it's sole purpose
   * is to make Dice9 frontend find player's bets faster; it does not affect the logic in any way.
   *
   * The second parameter is a string containing number(s) in the range of 2..12, e.g. "2" or "8,12" indicating
   * the sum of two dice roll(s) the user is willing to place a bet on.
   *
   * @param options human-readable string of options to lace a bet on.
   */
  function playTwoDice(uint /* unusedBetId */, string calldata options) external payable {
    (uint mask,) = Options.parseOptions(options.toTinyString(), 2, 12);
    placeBet(msg.sender, msg.value, GameOptions.toTwoDiceOptions(mask));
  }

  /**
   * The entry point function to place a bet in a Etheroll game.
   *
   * The first parameter is not used in any way by the smart contract and can be ignored – it's sole purpose
   * is to make Dice9 frontend find player's bets faster; it does not affect the logic in any way.
   *
   * The second parameter is a string containing number(s) in the range of 3..97, e.g. "5" or "95" indicating
   * the number the user is willing to place a bet on.
   *
   * @param options human-readable string of options to lace a bet on.
   */
  function playEtheroll(uint /* unusedBetId */, string calldata options) external payable {
    (uint mask, uint option) = Options.parseOptions(options.toTinyString(), 3, 97);

    // make sure there is a single option selected
    if (!Math.isPowerOf2(mask)) {
      revert EtherollSingleOption();
    }

    placeBet(msg.sender, msg.value, GameOptions.toEtherollOptions(option));
  }

  /**
   * The generic all-mighty function processing all the games once the input parameters have been read and validated
   * by corresponding playXXX public methods.
   *
   * Accepting player's address, bet amount and GameOption instance describing the game being played, the function
   * stores the bet information in the contract's storage so that it can be settled by a Croupier later down the road.
   *
   * The function performs a few boring, but very important checks:
   *  1. It makes sure that all the bets currently accepted will be payable, even if all of them win (since we do not know upfront).
   *     If the contract sees that the potential winnings from pending bets exceed contract's balance, it would refrain from accepting the bet.
   *  2. If checks that the current bet will not win "too much" – a value depicted by maxProfit - a fair limitation kept in place to avoid
   *     situations when a single whale drains the contract in one lucky bet and everyone else has to wait until the House tops the contract up.
   *     Please mind this value is kept reasonably high so you should rarely run into such a limitation.
   *  3. It makes sure the player does not place "non-sensial" bets, like all Dice numbers or no sides in CoinFlip.
   *
   * If everything goes well, the contract storage is updated with the new bet and a log entry is recorded on the blockchain so that the
   * player can validate the parameters of the bet accepted.
   *
   * @param player the address of the player placing a bit.
   * @param amount the amount of the bet the player wagers.
   * @param gameOptions the choice(s) and the game type selected by the player.
   */
  function placeBet(address player, uint amount, GameOption gameOptions) internal {
    // check if the bet plays for jackpot
    bool playsForJackpot = amount >= MIN_JACKPOT_BET;

    // pack the bet into an instance of PackedBet
    PackedBet packedBet = PackedBets.pack(amount, gameOptions, playsForJackpot);

    // extract the bet information with regards to ods to compute the winAmount
    (uint numerator, uint denominator,, TinyString humanReadable) = gameOptions.describe();
    // consider this bet wins: how big the win is going to be?
    uint winAmount = computeWinAmount(amount, numerator, denominator, playsForJackpot);

    // add the potential win to lockedInBets so that the contract always knows how much it owns in the worst case
    uint80 newLockedInBets = lockedInBets + uint80(winAmount);
    // increment the number of bets playing for a Jackpot to keep track of those too
    uint32 newJackpotBetCount = jackpotBetCount + uint32(playsForJackpot.toUint());

    // compute the amount the contract has to have available to pay if everyone wins and compare that to the current balance
    if (computeTotalLockedInBets(newLockedInBets, newJackpotBetCount, uint(jackpotMultiplier)) > address(this).balance) {
      // ok, we potentially owe too much and cannot accept this bet
      revert CannotAffordToLoseThisBet();
    }

    // check if the winning amount of the bet sans the amount wagered exceeds the maxProfit limit
    if (winAmount > amount + maxProfit) {
      // ok, the win seems to be too big - crash away
      revert MaxProfitExceeded();
    }

    // all seems good - just store the bet in contract's storage
    uint playerNonce = BetStorage.storePackedBet(bets, player, packedBet);

    // append "jckpt" string if the bet plays for jackpot
    if (playsForJackpot) {
      humanReadable = humanReadable.append(TinyStrings.SPACE).append(GameOptions.JCKPT_STR);
    }

    // compute VRF input hash - a hash of all bet attributes that would uniquely identify this bet
    bytes32 vrfInputHash = VRF.computeVrfInputHash(player, playerNonce, packedBet.withoutEpoch());

    // update the locked counters in the contract
    lockedInBets = newLockedInBets;
    jackpotBetCount = newJackpotBetCount;

    // log the bet being placed successfully
    emit Placed(vrfInputHash, player, playerNonce, amount, packedBet.toUint(), humanReadable.toString());
  }

  /**
   * This is main workhorse of the contract: the routine that settles the bets previously placed by the players.
   *
   * It is expected that a Croupier (i.e. our software having access to the secret encryption key) would invoke this function,
   * passing some number of ecnrypted bets. The RSA VRF utilities (see VRF.sol) would validate the authenticity of the ecrypted data
   * received (e.g. check that the bets are encrypted with the specific secret key).
   *
   * If the authencity is confirmed, the routine would use the encrypted text as the source of entropy – essentially, treat
   * the encrypted bet data as a huge number. Since the contract uses pretty strong and battle-tested encryption (RSA 1024 bits), this
   * number is guaranteed to be unpredictable and uniformely distributed. The only party which can produce this number is, of course,
   * the Croupier – the possession of the secret key is required to calculate the number. The Croupier, in its turn, cannot tamper
   * with the bet attributes (since the contract keeps track of what players bet on) and has to create a number for every bet submitted.
   * Since the key is fixed, every bet attributes get a single, unique number from the croupier.
   *
   * More technical details are available in VRF.sol.
   *
   * @param vrfs the blob of VRF(s) chunks to use for bet settlement.
   */
  function settleBet(bytes calldata vrfs) external {
    // first and foremost, make sure there is a whole number of VRF chunks in the calldata
    if (vrfs.length % VRF.RSA_CIPHER_TEXT_BYTES != 0) {
      // there is not – just revert, no way to even try, it is coming from a malicious actor
      revert NonFullVRFs();
    }

    // iterate over callback bytes in chunks of RSA_CIPHER_TEXT_BYTES size
    for (uint start = 0; start < vrfs.length; start += VRF.RSA_CIPHER_TEXT_BYTES) {
      // get the current slice of VRF.RSA_CIPHER_TEXT_BYTES bytes
      bytes calldata slice = vrfs[start:start+VRF.RSA_CIPHER_TEXT_BYTES];

      // use VRF.decrypt library to decrypt, validate and decode bet structure encoded in this particular chunk of calldata
      // unless the function reverts (which it would do shall there be anything wrong), it would return a fully decoded bet along
      // with vrfHash parameter – this is going to act as out entropy source
      (bytes32 vrfHash, bytes32 vrfInputHash, address player, uint playerNonce, PackedBet packedBet) = VRF.decrypt(modulus0, modulus1, modulus2, modulus3, slice);

      // get the (supposedly the same) bet from bet storage – it is trivial since we have both player and playerNonce values
      PackedBet ejectedPackedBet = BetStorage.ejectPackedBet(bets, player, playerNonce);

      // check if the bet is not blank
      if (ejectedPackedBet.isEmpty()) {
        // it is blank – probably already settled, so just carry on
        emit Nonexistent(player, playerNonce);
        continue;
      }

      // check if the bet's amount is set to zero – we are using this trick (see BetStorage.sol) to mark bets
      // which have already been handled
      if (ejectedPackedBet.hasZeroAmount()) {
        // it has been settled already, so just carry on
        emit Duplicate(player, playerNonce);
        continue;
      }

      // at this point it looks like the bet is fully valid – let's make sure the contract storage contains
      // exactly the same attributes as the decrypted data; we just need to be mindful that decrypted bets don't
      // contain any epoch information, so we ignore it for comparisons
      if (!ejectedPackedBet.withoutEpoch().equals(packedBet)) {
        // this is a pretty suspicious situation: the decrypted attributes do not match the data in the storage, as if
        // someone would try to settle a bet and alter its parameters at the same time. we don't like this and we crash
        revert StorageSignatureMismatch(player, playerNonce);
      }

      // at this point the bet seems valid, matches it decrypted counterpart and is ready to be settled

      // first of all, decode the bet into its attributes...
      (uint winAmount, bool playsForJackpot, uint betDenominator, uint betMask, TinyString betDescription) = describePackedBet(packedBet);
      // ...and pass those attributes to compute the actual outcome
      (bool isWin, uint payment, uint jackpotPayment, TinyString outcomeDescription) = computeBetOutcomes(uint(vrfHash), winAmount, playsForJackpot, betDenominator, betMask, betDescription);

      // unlock potential winnings amount (since we are processing the bet now)
      lockedInBets -= uint80(winAmount);
      // decrement the number of bets participating in Jackpot
      jackpotBetCount -= uint32(playsForJackpot.toUint());

      // did the bet win?
      if (isWin) {
        // yes! congratulations, log the information onto the blockchain
        emit Won(vrfInputHash, player, playerNonce, payment, jackpotPayment, outcomeDescription.toString());
      } else {
        // nope :( it is ok, you can try again – log the information onto the blockchain
        emit Lost(vrfInputHash, player, playerNonce, payment, jackpotPayment, outcomeDescription.toString());
      }

      // compute the total payment
      uint totalPayment = payment + jackpotPayment;

      // invoke the actual funds transfer and revert if it fails for any reason
      (bool transferSuccess,) = player.call{value: totalPayment + Math.toUint(totalPayment == 0)}("");
      require(transferSuccess, "Transfer failed!");
    }
  }

  /**
   * A publicly available function used to request a refund on a bet if it was not processed.
   *
   * The player or the House can refund any unprocessed bet during every other 8-hour window following
   * the bet.
   *
   * The logic of the time constraint is as follows:
   *  1. The day is split into 4 hour windows, i.e. 00:00-08:00, 08:00-16:00, 16:00-24:00 etc
   *  2. The smart contract keeps track of the window number the bet was placed in. For example, if
   *     the bet is placed at 02:15, it will be attributed to 00:00-08:00 window.
   *  3. The player can request the refund during every other 8-hour window following the bet.
   *     For example, if the bet is placed at 02:15, one can refund it during 16:00-20:00, or
   *     during 04:00-12:00 (next day), but not at 12:00 the same day.
   *
   * The refund window logic is a bit convoluted, but it is kept this way to minimise the gas requirements
   * imposed on all the bets going through the system. It is in House's best interests to make sure
   * this function is never needed – if all the bets are processed in a timely manner, noone would ever
   * invoke this. We decided to keep this in, however, to assure the players the funds will never end up
   * locked up in the contract, even if the Croupier stops revealing all the bets forever.
   *
   * @param player the address of the player that made the bet. Must match the sender's address or the contract owner.
   * @param playerNonce the playerNonce of the bet to refund
   */
  function refundBet(address player, uint playerNonce) external {
    // make sure a legit party is asking for a refund
    require(((msg.sender == player) || (msg.sender == owner)), "Only the player or the House can do this.");

    // extract the bet from the storage
    PackedBet ejectedPackedBet = BetStorage.ejectPackedBet(bets, player, playerNonce);

    // check if the bet is not blank
    if (ejectedPackedBet.isEmpty()) {
      // it is blank – probably already settled, so just carry on
      emit Nonexistent(player, playerNonce);
      return;
    }

    // check if the bet's amount is set to zero – we are using this trick (see BetStorage.sol) to mark bets
    // which have already been handled
    if (ejectedPackedBet.hasZeroAmount()) {
      // it has been settled already, so just carry on
      emit Duplicate(player, playerNonce);
      return;
    }

    // get the bet's and current epochs – those would be integers from 0..3 range denoting the number of
    // the 4 hour windows corresponding to the epochs
    (uint betEpoch, uint currentEpoch) = ejectedPackedBet.extractEpochs();
    // compute the distance between two epochs mod 4
    uint epochDistance = (currentEpoch + PackedBets.EPOCH_NUMBER_MODULO - betEpoch) & PackedBets.EPOCH_NUMBER_MODULO_MASK;

    // check if bet's epoch is good for refund
    if (epochDistance < REFUNDABLE_BET_EPOCHS_DISTANCE) {
      // we actually have to revert here since we have just modified the storage and are no taking any action
      revert RefundEpochMismatch(player, playerNonce, betEpoch, currentEpoch & PackedBets.EPOCH_NUMBER_MODULO_MASK);
    }

    // unlock the funds since the bet is getting refunded
    (uint winAmount, bool playsForJackpot,,,) = describePackedBet(ejectedPackedBet);
    lockedInBets -= uint80(winAmount);
    jackpotBetCount -= uint32(playsForJackpot.toUint());

    // send the funds back
    (,uint amount,) = ejectedPackedBet.unpack();
    (bool transferSuccess,) = player.call{value: amount}("");
    require(transferSuccess, "Transfer failed!");
  }

  /**
   * Being given an instance of PackedBet, decodes it into a set of parameters, specifically:
   *  1. What would the payment amount to if the bet wins.
   *  2. Whether the bet should play for Jackpot.
   *  3. What is the bet's game's denominator (2 for Coin Flip, 6 for Dice etc).
   *  4. What were the options chosen by the user (a.k.a. bet mask).
   *  5. What is the human-readable description of this bet.
   *
   * These parameters can further be utilised during processing or refunding of the bet.
   *
   * @param packedBet an instance of PackedBet to decode.
   *
   * @return winAmount how much the bet should pay back if it wins.
   *         playsForJackpot whether the bet should take part in a Jackpot roll.
   *         betDenominator the denominator of the game described by this bet.
   *         betMask the options the user chose in a form of a bitmask.
   *         betDescription the human-readable description of the bet.
   */
  function describePackedBet(PackedBet packedBet) internal pure returns (uint winAmount, bool playsForJackpot, uint betDenominator, uint betMask, TinyString betDescription) {
    // unpack the packed bet to get to know its amount, options chosen and whether it should play for Jackpot
    (GameOption gameOptions, uint amount, bool isJackpot) = packedBet.unpack();
    // unpack the GameOption instance into bet attributes and gather human-readable wager description at the same time
    (uint numerator, uint denominator, uint mask, TinyString description) = gameOptions.describe();

    // compute the amount of money this bet would pay if it is winning
    winAmount = computeWinAmount(amount, numerator, denominator, isJackpot);
    // return true if the bet plays for jackpot
    playsForJackpot = isJackpot;
    // transfer other attributes to the result
    betDenominator = denominator;
    betMask = mask;
    betDescription = description;
  }

  /**
   * Being given the entropy value and bet parameters, computes all the outcomes of the bet, speficically:
   *  1. The amount won, if the bet wins
   *  2. The amount of Jackpot payment, if the bet wins the jackpot
   *  3. The human-readable representation of the outcome (e.g. "Dice 1,2,3 2 888" or "CoinFlip tails heads 555")
   *
   * The incoming entropy integer is split into 3 chunks: game-dependent entropy, jackpot-dependent entropy and jackpot payment entropy.
   * All these values are taken from different parts of the combined entropy to make sure there is no implicit dependency between
   * out come values.
   *
   * @param entropy the RNG value to use for deciding the bet.
   * @param winAmount how much the bet should pay back if it wins.
   * @param playsForJackpot whether the bet should take part in Jackpot roll.
   * @param denominator the denominator of the game described by this bet.
   * @param mask the options the user chose in a form of a bitmask.
   * @param description the human-readable description of the bet.
   *
   * @return isWin the flag indicating whether the bet won on the primary wager.
   *         payment the amount of the winnings the bet has to pay the player.
   *         jackpotPayment the amount of the Jackpot winnings paid by this bet.
   *         outcomeDescription the human-readable description of the bet's result.
   */
  function computeBetOutcomes(uint entropy, uint winAmount, bool playsForJackpot, uint denominator, uint mask, TinyString description) internal view returns (bool isWin, uint payment, uint jackpotPayment, TinyString outcomeDescription) {
    // compute game specific entropy
    uint gameOutcome = entropy % denominator;

    // decide on the game type being played
    if (denominator == GameOptions.GAME_OPTIONS_ETHEROLL_MODULO) {
      // it is an Etheroll bet; the bet wins if the mask value (which simply holds the number for Etheroll, see GameOption.sol)
      // does not exceed the gameOutcome number
      isWin = gameOutcome < mask;

      // append the actual number (+1 to make it start from 1 instead of 0)
      outcomeDescription = description.append(TinyStrings.SPACE).appendNumber(gameOutcome + 1);
    } else if (denominator == GameOptions.GAME_OPTIONS_TWO_DICE_MODULO) {
      // it is a TwoDice bet; the bet wins if the user has chosen a sum of two dice that we got
      // first off, compute the dice outcomes by splitting the game outcome into 2 parts, with 6 possible values in each one
      uint firstDice = gameOutcome / GameOptions.GAME_OPTIONS_DICE_MODULO;
      uint secondDice = gameOutcome % GameOptions.GAME_OPTIONS_DICE_MODULO;

      // compute the sum of two dice
      uint twoDiceSum = firstDice + secondDice;
      // check if the mask contains the bit set at that position
      isWin = (mask >> twoDiceSum) & 1 != 0;

      // append the value of the first dice to the human-readable description (+1 to make it start from 1 instead of 0)
      outcomeDescription = description.append(TinyStrings.SPACE).appendNumber(firstDice + 1);
      // append the value of the second dice to the human-readable description (+1 to make it start from 1 instead of 0)
      outcomeDescription = outcomeDescription.append(TinyStrings.PLUS).appendNumber(secondDice + 1);
    } else if (denominator == GameOptions.GAME_OPTIONS_DICE_MODULO) {
      // it is a dice game – all is very simple, to win the user should bet on a particular number, thus
      // check if the mask has the bit set at the position corresponding to the dice roll
      isWin = (mask >> gameOutcome) & 1 != 0;

      // append the value of the dice to the human-readable description (+1 to make it start from 1 instead of 0)
      outcomeDescription = description.append(TinyStrings.SPACE).appendNumber(gameOutcome + 1);
    } else if (denominator == GameOptions.GAME_OPTIONS_COIN_FLIP_MODULO) {
      // it is a CoinFlip game - similar to Dice, the player should bet on the correct side to win
      isWin = (mask >> gameOutcome) & 1 != 0;

      // append the space to the description of the outcome
      outcomeDescription = description.append(TinyStrings.SPACE);

      // append heads or tails to the description, based on the result
      if (gameOutcome == 0) {
        outcomeDescription = outcomeDescription.append(GameOptions.HEADS_STR);
      } else {
        outcomeDescription = outcomeDescription.append(GameOptions.TAILS_STR);
      }
    }

    // now, the payment amount would equal to the winAmount if bet wins, 0 otherwise
    payment = isWin.toUint() * winAmount;

    // the last bit to check for is the jackpot
    if (playsForJackpot) {
      // first of all, get a separate chunk of entropy and compute the Jackpot Outcome number, adding
      // +1 to convert from 0..999 range to 1..1000
      uint jackpotOutcome = (entropy / denominator) % JACKPOT_MODULO + 1;

      // append Jackpot Number to the human-readable description of the bet
      outcomeDescription = outcomeDescription.append(TinyStrings.SPACE).appendNumber(jackpotOutcome);

      // check the Jackpot Number matches the lucky outcome
      if (jackpotOutcome == JACKPOT_WINNING_OUTCOME) {
        // it does – compute the jackpot payment entropy chunk
        uint jackpotPaymentOutcome = entropy / denominator / JACKPOT_MODULO;
        // set the jackpotPayment to the value computed by a dedicated function
        jackpotPayment = computeJackpotAmount(jackpotPaymentOutcome, uint(jackpotMultiplier));
      }
    }
  }

  /**
   * Computes the amount a bet should pay to the user based on the odds the bet has and whether the bet plays for Jackpot.
   *
   * The logic of this method is based on the core principle of this smart contract: be fair. The bet ALWAYS pays the amount
   * decided by the odds (after the House fees are taken out). If the bet has 1 in 3 chances, the winning amount would be 3x;
   * if the bet has 1 in 16 chances of winning, the amount would be 16x.
   *
   * Running the contract is prety labour-intensive and requires constant investment of both labour and money (to settle the bets,
   * pay increased Jackpots and so on), that is why the contract always deducts the fees, calculated as follows:
   *  1. The House takes HOUSE_EDGE_PERCENT (1%) from every bet
   *  2. The House always takes at least HOUSE_EDGE_MINIMUM_AMOUNT (0.001 Ether) fee - roughly how much it costs to settle the bet.
   *  3. If the bet plays for Jackpot, a fixed Jackpot fee is taken to contribute towards the Jackpot payments.
   *
   * @param amount the amount being wagered in this bet.
   * @param numerator the number of possible outcomes chosen by the user.
   * @param denominator the total number of possible outcomes
   * @param isJackpot the flag indicating whether the bet takes part in Jackpot games.
   *
   * @return the amount the bet would pay as a win if it settles so.
   */
  function computeWinAmount(uint amount, uint numerator, uint denominator, bool isJackpot) internal pure returns (uint) {
    // range check
    if (numerator == 0 || numerator > denominator) {
      revert WinProbabilityOutOfRange(numerator, denominator);
    }

    // house edge clamping
    uint houseEdge = amount * HOUSE_EDGE_PERCENT / 100;

    if (houseEdge < HOUSE_EDGE_MINIMUM_AMOUNT) {
      houseEdge = HOUSE_EDGE_MINIMUM_AMOUNT;
    }

    // jackpot fee
    uint jackpotFee = isJackpot.toUint() * JACKPOT_FEE;

    return (amount - houseEdge - jackpotFee) * denominator / numerator;
  }

  /**
   * Computes the amount the bet would pay as the Jackpot if the user wins the Jackpot.
   *
   * The value is computed in the following way:
   *  1. Base Jackpot value is computed by multiplying Jackpot Odds by Jackpot fee.
   *  2. A random number is sampled from JACKPOT_PAYTABLE, providing a number between 0.25 and 4.
   *  3. A current jackpotMultiplier value is read, providing another mutlplier.
   *  4. Everything is multipled together, providing the final Jackpot value.
   *
   * If the House keeps jackpotMultiplier at a default value (1), the Jackpot would pay between 0.25x and 4x
   * of base Jackpot value. During the happy hours this number can go up significantly.
   *
   * @param jackpotPaymentOutcome the Jackpot payment entropy value to use to sample a slot from the paytable.
   * @param currentJackpotMultiplier the current Jackpot Multiplier as set by the house.
   *
   * @return the jackpot payment value decided by all the attributes.
   */
  function computeJackpotAmount(uint jackpotPaymentOutcome, uint currentJackpotMultiplier) internal pure returns (uint) {
    // compute base jackpot value that would be paid if the paytable was flat
    uint baseJackpotValue = JACKPOT_FEE * JACKPOT_MODULO * currentJackpotMultiplier;
    // get random slot from the paytable
    uint paytableSlotIndex = jackpotPaymentOutcome % JACKPOT_PAYTABLE_SLOTS_COUNT;
    // compute the paytable contribution based on the slot index
    uint paytableMultiplier = ((JACKPOT_PAYTABLE >> (paytableSlotIndex << 3)) & 0xFF);

    return baseJackpotValue * paytableMultiplier / JACKPOT_MULTIPLIER_BASE / JACKPOT_PAYTABLE_BASE;
  }

  /**
   * Computes the total value the contract currently owes to players in case all the pending bets resolve as winning ones.
   *
   * The value is composed primarily from the sum of possible wins from every bet and further increased by the current maximum
   * Jackpot payout value for every bet playing for Jackpot (capped at 5 since Jackpots are very rare).
   *
   * @param currentLockedInBets the current sum of potential winnings.
   * @param currentJackpotBetCount the current number of bets playing for jackpot and not yet settled.
   * @param currentJackpotMultiplier the current jackpot mutiplier value.
   *
   * @return the total number of funds required to cover the most extreme resolution of pending bets (everything wins everything).
   */
  function computeTotalLockedInBets(uint currentLockedInBets, uint currentJackpotBetCount, uint currentJackpotMultiplier) internal pure returns (uint) {
    return currentLockedInBets + Math.min(currentJackpotBetCount, JACKPOT_LOCK_COUNT_CAP) * JACKPOT_LOCK_MULTIPLIER * currentJackpotMultiplier / JACKPOT_MULTIPLIER_BASE;
  }

  /**
   * A House-controlled function used to modify maxProfit value – the maximum amount of winnings
   * a single bet can take from the contract.
   *
   * Bets potentially exceedign this value will not be allowed to be placed.
   *
   * @param newMaxProfit the updated maxProfit value to set.
   */
  function setMaxProfit(uint72 newMaxProfit) external onlyOwner {
    maxProfit = newMaxProfit;
    emit MaxProfitUpdated(maxProfit);
  }

  /**
   * A House-controlled function used to modify jackpotMultiplier value – the scale factor
   * of the Jackpot payment paid out on Jackpot wins.
   *
   * The House reserves the right to tweak this value for marketing purposes.
   *
   * @param newJackpotMultiplier the updated maxProfit value to set.
   */
  function setJackpotMultiplier(uint40 newJackpotMultiplier) external onlyOwner  {
    jackpotMultiplier = newJackpotMultiplier;
    emit JackpotMultiplierUpdated(jackpotMultiplier);
  }

  /**
   * A House-controlled function used to send a portion of contract's balance to an external
   * address – primarily used for bankroll management.
   *
   * The function DOES NOT allow withdrawing funds from the bets that are currently being
   * processed to make sure the house cannot do a bank run.
   *
   * @param to the address to withdraw the funds to.
   * @param amount the amount of funds to withdraw.
   */
  function withdrawFunds(address to, uint amount) external onlyOwner  {
    // make sure there will be at least lockedInBets funds after the withdrawal
    require((amount + lockedInBets) <= address(this).balance, "Cannot withdraw funds - pending bets might need the money.");
    // transfer the money out
    (bool transferSuccess,) = to.call{value: amount}("");
    require(transferSuccess, "Transfer failed!");
  }

  /**
   * A House-controlled function used block a specific address from placing any new bets.
   *
   * The already-placed bets can still be processed or refunded.
   *
   * The primary use of this function is to block addresses containing funds associated with
   * illegal activity (such as stolen or otherwise acquired in an illegal way).
   *
   * This is a legal requirement to have this function.
   *
   * @param player the address of the player to suspend.
   * @param suspend whether to suspend or un-suspend the player.
   */
  function suspendPlayer(address player, bool suspend) external onlyOwner {
    BetStorage.suspendPlayer(bets, player, suspend);
  }

  /**
   * A House-controlled function used to destroy the contract.
   *
   * It would only work if the value of lockedInBets is 0, meaning there are no pending bets
   * and the contract destruction would not take any player's money.
   */
  function destroy() external onlyOwner {
    require(lockedInBets == 0, "There are pending bets");
    selfdestruct(owner);
  }

  /**
   * A function used to add funds to the contract.
   */
  function topUpContract() external payable {
  }
}

/* RSA permutation NIZK:
 *    nizk:8:0:ow7gON+Eb+e/r5XB9aWrVMc6TVtdG9+39gWGrGw30Hd/Aj4z5Pj3dhh1QllYgOCFTiI0a23lwf/PFYWQ3+4rUYsUFtjkx82NkLQLLYC6WBzbg2kuhvdE1/0BL6teTjjFV1SNkU9OGbV5MVQX7aE6nWdO/fqyxWf+eh0abaNg6XM=
 *    nizk:8:1:QGSackZNonPtwVDhLWRjGhq3ROEJA1/ko7ZofC34eQGZ429glcIHlkN47HlCwhK1ewXDy1R6w566iW+xU1Kn8FOQG1sRA+r1op0xa4uXnsz+j9JVmiiNgz5407vq+lALjGWEr5Y0w8q0JBBHAwKoYRG8viDSsovmFcuNVGQVyEE=
 *    nizk:8:2:e23Td+FPMDKb2BQkaXrS2M66Enf2mUkHXa61gXv/d5bE9Tm7VtDEL2/Te/4oug6bGLSmmL3aY0qFY7eOLRCA87+sAqc3a+KRSd8vvvupqykbwpsLXFjbD1srrNl/q5FCnUILOqpTterSuyl/Di1cKZ1RJ94lIcmF9ld/yTw9fm8=
 *    nizk:8:3:BF6ScEDU+6dMknR45fxuwLxMIXDILb9WOzzOfc1bJLt9pq8HWfMGXzcL7RNEpZHpFsXL88J9dSygCzy/VliWGTF5MhlmAvn/8VV2fwut9WLKcDjTHspChS5MtT+sAF7kqTUgmF7aSWa+2v5Km4dOtyG6773LejJ3YxXSAB5yWgs=
 *    nizk:8:4:hZnWtDxEFFveo5nzs20FfcCrg4zCsDyHqmMhlY3SZGNVLgL9qvwnVRBMdSn4DyT7LH6SGVSLnABOe4mkI8yuaPS9jDAG/d1uxVKzd5p/bitGOryv1ePDHLYZHeJMMEMT+ZGG7P799tiFqZ1zbjij7MCqtI9qU4YqZ+Sup99ZP4Y=
 *    nizk:8:5:bkratF6TVVbLObR2h3xUfr/DkBw++1OhmuQ3XTbebpxVnna2od/hOWCA4B1Gz6+w+GWo9uSI0lVTT37dWnkT1BUILryhdqGE0H9vpBzbL/BJxIcSltM4N8Sm+np0VCw7yQGeYuyK20qJj392mI/zLmtJZX4Xiho7KWNcXjCEzvI=
 *    nizk:8:6:dvrldgSHZAUT6wn1IOLZsloht+nWhW70+mTTnH2sdz4lofZR9aX6wV9hGqLYgLwVN/EIrswc0gOzCmIAXnyrwRqkBEcGaYlSuiQ9v2DahXRJzBJjD2hs+7Ce8m700tdv8syKLvMgm1pFzTkY4CXfUihDivDNm59sH9a9+M7L3Ts=
 *    nizk:8:7:YazoLkX8tk63ehhbU/+kOpC2dI0LCM4m2gVIEMX9haGt8WHFFPOp762HbkQRLgkg8cFQfPihumPYnEmwPiQ57REM5Wq9zwU5+sNv2ncTi+maG79eDnJw22Q2hqkapEtPQSW/CV4CESiUncoNC7WLAv6dYUZIhZkdYQZLKMQpk3M=
 */

File 2 of 16 : BetStorage.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/BetStorage.sol";
import "../contracts/Math.sol";
import "../contracts/GameOptions.sol";
import "../contracts/PackedBets.sol";

contract $BetStorage {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    mapping(uint256 => BetStorage.Bets) internal $v_BetStorage_Bets;

    event return$storePackedBet(uint256 playerNonce);

    event return$ejectPackedBet(PackedBet ret0);

    constructor() payable {
    }

    function $PLAYER_NONCE_ACCOUNT_BANNED_BIT() external pure returns (uint256) {
        return BetStorage.PLAYER_NONCE_ACCOUNT_BANNED_BIT;
    }

    function $PLAYER_NONCE_MOD8_MASK() external pure returns (uint256) {
        return BetStorage.PLAYER_NONCE_MOD8_MASK;
    }

    function $MULTIPLY_BY_32_BIT_SHIFT() external pure returns (uint256) {
        return BetStorage.MULTIPLY_BY_32_BIT_SHIFT;
    }

    function $PACKED_BET_MASK() external pure returns (uint256) {
        return BetStorage.PACKED_BET_MASK;
    }

    function $PLAYER_NONCE_DIV8_MASK() external pure returns (uint256) {
        return BetStorage.PLAYER_NONCE_DIV8_MASK;
    }

    function $PACKED_BETS_PER_SLOT() external pure returns (uint256) {
        return BetStorage.PACKED_BETS_PER_SLOT;
    }

    function $ALL_QUANT_AMOUNTS_MASK() external pure returns (uint256) {
        return BetStorage.ALL_QUANT_AMOUNTS_MASK;
    }

    function $FULL_SLOT_THRESHOLD() external pure returns (uint256) {
        return BetStorage.FULL_SLOT_THRESHOLD;
    }

    function $storePackedBet(uint256 bets,address player,PackedBet packedBet) external payable returns (uint256 playerNonce) {
        (playerNonce) = BetStorage.storePackedBet($v_BetStorage_Bets[bets],player,packedBet);
        emit return$storePackedBet(playerNonce);
    }

    function $ejectPackedBet(uint256 bets,address player,uint256 playerNonce) external payable returns (PackedBet ret0) {
        (ret0) = BetStorage.ejectPackedBet($v_BetStorage_Bets[bets],player,playerNonce);
        emit return$ejectPackedBet(ret0);
    }

    function $suspendPlayer(uint256 bets,address player,bool suspend) external payable {
        BetStorage.suspendPlayer($v_BetStorage_Bets[bets],player,suspend);
    }

    receive() external payable {}
}

File 3 of 16 : Dice9.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/Dice9.sol";
import "../contracts/TinyStrings.sol";
import "../contracts/Math.sol";
import "../contracts/Options.sol";
import "../contracts/BetStorage.sol";
import "../contracts/GameOptions.sol";
import "../contracts/PackedBets.sol";
import "../contracts/VRF.sol";

contract $Dice9 is Dice9 {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor(uint256 m0, uint256 m1, uint256 m2, uint256 m3) Dice9(m0, m1, m2, m3) payable {
    }

    function $HOUSE_EDGE_PERCENT() external pure returns (uint256) {
        return HOUSE_EDGE_PERCENT;
    }

    function $HOUSE_EDGE_MINIMUM_AMOUNT() external pure returns (uint256) {
        return HOUSE_EDGE_MINIMUM_AMOUNT;
    }

    function $MIN_JACKPOT_BET() external pure returns (uint256) {
        return MIN_JACKPOT_BET;
    }

    function $JACKPOT_FEE() external pure returns (uint256) {
        return JACKPOT_FEE;
    }

    function $JACKPOT_MODULO() external pure returns (uint256) {
        return JACKPOT_MODULO;
    }

    function $JACKPOT_WINNING_OUTCOME() external pure returns (uint256) {
        return JACKPOT_WINNING_OUTCOME;
    }

    function $JACKPOT_MULTIPLIER_BASE() external pure returns (uint256) {
        return JACKPOT_MULTIPLIER_BASE;
    }

    function $JACKPOT_LOCK_COUNT_CAP() external pure returns (uint256) {
        return JACKPOT_LOCK_COUNT_CAP;
    }

    function $JACKPOT_LOCK_MULTIPLIER() external pure returns (uint256) {
        return JACKPOT_LOCK_MULTIPLIER;
    }

    function $JACKPOT_PAYTABLE() external pure returns (uint256) {
        return JACKPOT_PAYTABLE;
    }

    function $JACKPOT_PAYTABLE_SLOTS_COUNT() external pure returns (uint256) {
        return JACKPOT_PAYTABLE_SLOTS_COUNT;
    }

    function $JACKPOT_PAYTABLE_BASE() external pure returns (uint256) {
        return JACKPOT_PAYTABLE_BASE;
    }

    function $REFUNDABLE_BET_EPOCHS_DISTANCE() external pure returns (uint256) {
        return REFUNDABLE_BET_EPOCHS_DISTANCE;
    }

    function $owner() external view returns (address payable) {
        return owner;
    }

    function $placeBet(address player,uint256 amount,GameOption gameOptions) external {
        super.placeBet(player,amount,gameOptions);
    }

    function $describePackedBet(PackedBet packedBet) external pure returns (uint256 winAmount, bool playsForJackpot, uint256 betDenominator, uint256 betMask, TinyString betDescription) {
        (winAmount, playsForJackpot, betDenominator, betMask, betDescription) = super.describePackedBet(packedBet);
    }

    function $computeBetOutcomes(uint256 entropy,uint256 winAmount,bool playsForJackpot,uint256 denominator,uint256 mask,TinyString description) external view returns (bool isWin, uint256 payment, uint256 jackpotPayment, TinyString outcomeDescription) {
        (isWin, payment, jackpotPayment, outcomeDescription) = super.computeBetOutcomes(entropy,winAmount,playsForJackpot,denominator,mask,description);
    }

    function $computeWinAmount(uint256 amount,uint256 numerator,uint256 denominator,bool isJackpot) external pure returns (uint256 ret0) {
        (ret0) = super.computeWinAmount(amount,numerator,denominator,isJackpot);
    }

    function $computeJackpotAmount(uint256 jackpotPaymentOutcome,uint256 currentJackpotMultiplier) external pure returns (uint256 ret0) {
        (ret0) = super.computeJackpotAmount(jackpotPaymentOutcome,currentJackpotMultiplier);
    }

    function $computeTotalLockedInBets(uint256 currentLockedInBets,uint256 currentJackpotBetCount,uint256 currentJackpotMultiplier) external pure returns (uint256 ret0) {
        (ret0) = super.computeTotalLockedInBets(currentLockedInBets,currentJackpotBetCount,currentJackpotMultiplier);
    }

    receive() external payable {}
}

File 4 of 16 : GameOptions.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/GameOptions.sol";
import "../contracts/TinyStrings.sol";
import "../contracts/Math.sol";

contract $GameOptions {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor() payable {
    }

    function $HEADS_STR() external pure returns (TinyString5) {
        return GameOptions.HEADS_STR;
    }

    function $TAILS_STR() external pure returns (TinyString5) {
        return GameOptions.TAILS_STR;
    }

    function $JCKPT_STR() external pure returns (TinyString5) {
        return GameOptions.JCKPT_STR;
    }

    function $COINFLIP_STR() external pure returns (TinyString) {
        return GameOptions.COINFLIP_STR;
    }

    function $DICE_STR() external pure returns (TinyString) {
        return GameOptions.DICE_STR;
    }

    function $TWODICE_STR() external pure returns (TinyString) {
        return GameOptions.TWODICE_STR;
    }

    function $ETHEROLL_STR() external pure returns (TinyString) {
        return GameOptions.ETHEROLL_STR;
    }

    function $GAME_OPTIONS_COIN_FLIP_MASK_BITS() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_COIN_FLIP_MASK_BITS;
    }

    function $GAME_OPTIONS_DICE_MASK_BITS() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_DICE_MASK_BITS;
    }

    function $GAME_OPTIONS_TWO_DICE_MASK_BITS() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_TWO_DICE_MASK_BITS;
    }

    function $GAME_OPTIONS_ETHEROLL_MASK_BITS() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_ETHEROLL_MASK_BITS;
    }

    function $GAME_OPTIONS_ETHEROLL_MASK_MAX() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_ETHEROLL_MASK_MAX;
    }

    function $GAME_OPTIONS_DICE_FLAG() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_DICE_FLAG;
    }

    function $GAME_OPTIONS_TWO_DICE_FLAG() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_TWO_DICE_FLAG;
    }

    function $GAME_OPTIONS_ETHEROLL_FLAG() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_ETHEROLL_FLAG;
    }

    function $GAME_OPTIONS_THRESHOLD() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_THRESHOLD;
    }

    function $GAME_OPTIONS_COIN_FLIP_MODULO() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_COIN_FLIP_MODULO;
    }

    function $GAME_OPTIONS_DICE_MODULO() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_DICE_MODULO;
    }

    function $GAME_OPTIONS_TWO_DICE_MODULO() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_TWO_DICE_MODULO;
    }

    function $GAME_OPTIONS_ETHEROLL_MODULO() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_ETHEROLL_MODULO;
    }

    function $GAME_OPTIONS_TWO_DICE_SUMS() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_TWO_DICE_SUMS;
    }

    function $GAME_OPTIONS_DICE_SUMS() external pure returns (uint256) {
        return GameOptions.GAME_OPTIONS_DICE_SUMS;
    }

    function $toCoinFlipOptions(uint256 mask) external pure returns (GameOption ret0) {
        (ret0) = GameOptions.toCoinFlipOptions(mask);
    }

    function $toDiceOptions(uint256 mask) external pure returns (GameOption ret0) {
        (ret0) = GameOptions.toDiceOptions(mask);
    }

    function $toTwoDiceOptions(uint256 mask) external pure returns (GameOption ret0) {
        (ret0) = GameOptions.toTwoDiceOptions(mask);
    }

    function $toEtherollOptions(uint256 option) external pure returns (GameOption ret0) {
        (ret0) = GameOptions.toEtherollOptions(option);
    }

    function $describe(GameOption self) external pure returns (uint256 numerator, uint256 denominator, uint256 mask, TinyString betDescription) {
        (numerator, denominator, mask, betDescription) = GameOptions.describe(self);
    }

    receive() external payable {}
}

File 5 of 16 : Math.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/Math.sol";

contract $Math {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor() payable {
    }

    function $MAX128() external pure returns (uint256) {
        return Math.MAX128;
    }

    function $MAX64() external pure returns (uint256) {
        return Math.MAX64;
    }

    function $MAX32() external pure returns (uint256) {
        return Math.MAX32;
    }

    function $MAX16() external pure returns (uint256) {
        return Math.MAX16;
    }

    function $MAX8() external pure returns (uint256) {
        return Math.MAX8;
    }

    function $getBitLength32(uint256 number) external pure returns (uint256 length) {
        (length) = Math.getBitLength32(number);
    }

    function $getBitLength8(uint256 number) external pure returns (uint256 length) {
        (length) = Math.getBitLength8(number);
    }

    function $toUint(bool boolean) external pure returns (uint256 integer) {
        (integer) = Math.toUint(boolean);
    }

    function $isPowerOf2(uint256 number) external pure returns (bool ret0) {
        (ret0) = Math.isPowerOf2(number);
    }

    function $min(uint256 a,uint256 b) external pure returns (uint256 ret0) {
        (ret0) = Math.min(a,b);
    }

    function $max(uint256 a,uint256 b) external pure returns (uint256 ret0) {
        (ret0) = Math.max(a,b);
    }

    function $weightedPopCnt(uint256 mask,uint256 weightNibbles) external pure returns (uint256 result) {
        (result) = Math.weightedPopCnt(mask,weightNibbles);
    }

    receive() external payable {}
}

File 6 of 16 : Options.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/Options.sol";
import "../contracts/Math.sol";
import "../contracts/TinyStrings.sol";
import "../contracts/GameOptions.sol";

contract $Options {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor() payable {
    }

    function $parseOptions(TinyString tinyString,uint256 min,uint256 max) external pure returns (uint256 mask, uint256 lastOption) {
        (mask, lastOption) = Options.parseOptions(tinyString,min,max);
    }

    receive() external payable {}
}

File 7 of 16 : PackedBets.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/PackedBets.sol";
import "../contracts/Math.sol";
import "../contracts/GameOptions.sol";

contract $PackedBets {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor() payable {
    }

    function $PACKED_BET_LENGTH() external pure returns (uint256) {
        return PackedBets.PACKED_BET_LENGTH;
    }

    function $EPOCH_LENGTH() external pure returns (uint256) {
        return PackedBets.EPOCH_LENGTH;
    }

    function $QUANT_AMOUNT_MASK() external pure returns (uint256) {
        return PackedBets.QUANT_AMOUNT_MASK;
    }

    function $GAME_OPTIONS_MASK() external pure returns (uint256) {
        return PackedBets.GAME_OPTIONS_MASK;
    }

    function $QUANT_AMOUNT_THRESHOLD() external pure returns (uint256) {
        return PackedBets.QUANT_AMOUNT_THRESHOLD;
    }

    function $ALL_BUT_EPOCH_BITS() external pure returns (uint256) {
        return PackedBets.ALL_BUT_EPOCH_BITS;
    }

    function $QUANT_STEP() external pure returns (uint256) {
        return PackedBets.QUANT_STEP;
    }

    function $JACKPOT_BIT_OFFSET() external pure returns (uint256) {
        return PackedBets.JACKPOT_BIT_OFFSET;
    }

    function $JACKPOT_MASK() external pure returns (uint256) {
        return PackedBets.JACKPOT_MASK;
    }

    function $GAME_OPTIONS_BIT_OFFSET() external pure returns (uint256) {
        return PackedBets.GAME_OPTIONS_BIT_OFFSET;
    }

    function $EPOCH_BIT_OFFSET() external pure returns (uint256) {
        return PackedBets.EPOCH_BIT_OFFSET;
    }

    function $EPOCH_NUMBER_MODULO() external pure returns (uint256) {
        return PackedBets.EPOCH_NUMBER_MODULO;
    }

    function $EPOCH_NUMBER_MODULO_MASK() external pure returns (uint256) {
        return PackedBets.EPOCH_NUMBER_MODULO_MASK;
    }

    function $pack(uint256 amount,GameOption gameOptions,bool isJackpot) external view returns (PackedBet ret0) {
        (ret0) = PackedBets.pack(amount,gameOptions,isJackpot);
    }

    function $isEmpty(PackedBet self) external pure returns (bool ret0) {
        (ret0) = PackedBets.isEmpty(self);
    }

    function $toUint(PackedBet self) external pure returns (uint256 ret0) {
        (ret0) = PackedBets.toUint(self);
    }

    function $withoutEpoch(PackedBet self) external pure returns (PackedBet ret0) {
        (ret0) = PackedBets.withoutEpoch(self);
    }

    function $extractEpochs(PackedBet self) external view returns (uint256 betEpoch, uint256 currentEpoch) {
        (betEpoch, currentEpoch) = PackedBets.extractEpochs(self);
    }

    function $equals(PackedBet self,PackedBet other) external pure returns (bool ret0) {
        (ret0) = PackedBets.equals(self,other);
    }

    function $hasZeroAmount(PackedBet self) external pure returns (bool ret0) {
        (ret0) = PackedBets.hasZeroAmount(self);
    }

    function $unpack(PackedBet self) external pure returns (GameOption gameOptions, uint256 amount, bool isJackpot) {
        (gameOptions, amount, isJackpot) = PackedBets.unpack(self);
    }

    receive() external payable {}
}

File 8 of 16 : TinyStrings.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/TinyStrings.sol";
import "../contracts/Math.sol";

contract $TinyStrings {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor() payable {
    }

    function $COMMA() external pure returns (TinyString1) {
        return TinyStrings.COMMA;
    }

    function $PLUS() external pure returns (TinyString1) {
        return TinyStrings.PLUS;
    }

    function $SPACE() external pure returns (TinyString1) {
        return TinyStrings.SPACE;
    }

    function $MSB_BYTES() external pure returns (uint256) {
        return TinyStrings.MSB_BYTES;
    }

    function $ZERO_ASCII_CODE() external pure returns (uint256) {
        return TinyStrings.ZERO_ASCII_CODE;
    }

    function $BITS_PER_CHARACTER() external pure returns (uint256) {
        return TinyStrings.BITS_PER_CHARACTER;
    }

    function $BITS_PER_FIVE_CHARACTERS() external pure returns (uint256) {
        return TinyStrings.BITS_PER_FIVE_CHARACTERS;
    }

    function $TINY_STRING_MAX_LENGTH() external pure returns (uint256) {
        return TinyStrings.TINY_STRING_MAX_LENGTH;
    }

    function $TINY_STRING_MAX_LENGTH_BITS() external pure returns (uint256) {
        return TinyStrings.TINY_STRING_MAX_LENGTH_BITS;
    }

    function $LAST_CHARACTER_MASK_BITS() external pure returns (uint256) {
        return TinyStrings.LAST_CHARACTER_MASK_BITS;
    }

    function $toString(TinyString self) external pure returns (string memory result) {
        (result) = TinyStrings.toString(self);
    }

    function $toTinyString(string calldata self) external pure returns (TinyString tinyString) {
        (tinyString) = TinyStrings.toTinyString(self);
    }

    function $getLastChar(TinyString self) external pure returns (bool isDigit, uint256 digit) {
        (isDigit, digit) = TinyStrings.getLastChar(self);
    }

    function $isEmpty(TinyString self) external pure returns (bool ret0) {
        (ret0) = TinyStrings.isEmpty(self);
    }

    function $chop(TinyString self) external pure returns (TinyString ret0) {
        (ret0) = TinyStrings.chop(self);
    }

    function $append(TinyString self,TinyString1 chunk) external pure returns (TinyString ret0) {
        (ret0) = TinyStrings.append(self,chunk);
    }

    function $append(TinyString self,TinyString5 chunk) external pure returns (TinyString ret0) {
        (ret0) = TinyStrings.append(self,chunk);
    }

    function $equals(TinyString self,TinyString5 other) external pure returns (bool ret0) {
        (ret0) = TinyStrings.equals(self,other);
    }

    function $appendNumber(TinyString self,uint256 number) external pure returns (TinyString ret0) {
        (ret0) = TinyStrings.appendNumber(self,number);
    }

    function $appendBitNumbers(TinyString self,uint256 mask,uint256 startNumber) external pure returns (TinyString ret0) {
        (ret0) = TinyStrings.appendBitNumbers(self,mask,startNumber);
    }

    receive() external payable {}
}

File 9 of 16 : VRF.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/VRF.sol";
import "../contracts/Math.sol";
import "../contracts/PackedBets.sol";

contract $VRF {
    bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";

    constructor() payable {
    }

    function $RSA_MODULUS_BYTES() external pure returns (uint256) {
        return VRF.RSA_MODULUS_BYTES;
    }

    function $RSA_CIPHER_TEXT_BYTES() external pure returns (uint256) {
        return VRF.RSA_CIPHER_TEXT_BYTES;
    }

    function $RSA_EXPONENT_BYTES() external pure returns (uint256) {
        return VRF.RSA_EXPONENT_BYTES;
    }

    function $RSA_EXPONENT() external pure returns (uint256) {
        return VRF.RSA_EXPONENT;
    }

    function $EIP198_MODEXP() external pure returns (address) {
        return VRF.EIP198_MODEXP;
    }

    function $P0_PADDING_VALUE() external pure returns (uint256) {
        return VRF.P0_PADDING_VALUE;
    }

    function $P1_CONTRACT_ADDRESS_BIT_OFFSET() external pure returns (uint256) {
        return VRF.P1_CONTRACT_ADDRESS_BIT_OFFSET;
    }

    function $P1_PACKED_BET_BIT_OFFSET() external pure returns (uint256) {
        return VRF.P1_PACKED_BET_BIT_OFFSET;
    }

    function $P1_PLAYER_NONCE_BIT_OFFSET() external pure returns (uint256) {
        return VRF.P1_PLAYER_NONCE_BIT_OFFSET;
    }

    function $computeVrfInputHash(address player,uint256 playerNonce,PackedBet packedBet) external view returns (bytes32 ret0) {
        (ret0) = VRF.computeVrfInputHash(player,playerNonce,packedBet);
    }

    function $decrypt(uint256 modulus0,uint256 modulus1,uint256 modulus2,uint256 modulus3,bytes calldata cipherText) external view returns (bytes32 vrfHash, bytes32 vrfInputHash, address player, uint256 playerNonce, PackedBet packedBet) {
        (vrfHash, vrfInputHash, player, playerNonce, packedBet) = VRF.decrypt(modulus0,modulus1,modulus2,modulus3,cipherText);
    }

    receive() external payable {}
}

File 10 of 16 : BetStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { Math } from "./Math.sol";
import { GameOption, GameOptions } from "./GameOptions.sol";
import { PackedBet, PackedBets } from "./PackedBets.sol";

/**
 * The library provides abstractions to manage contract's bets storage in a code-friendly way.
 *
 * The main idea behind the routines is to abuse the fact that a single PackedBet occupies 32 bytes, meaining
 * that the contract can store 8 of those in a single storage slot. Squeezing multiple bets into a single slots
 * allows to save on gas tremendeously and requires just a handful of helper routines to make it transparent to the
 * outer contract.
 *
 * The library exports a struct called Bets that is designed to keep track of players' bets – an instance of this structre,
 * along with "storage" modifier, is required to invoke libraries' functions.
 */
library BetStorage {
  // Extension functions
  using PackedBets for PackedBet;

  /**
   * The structure defining mappings to keep track of players' bets.
   * it keeps two separate mappings, one for tracking player nonces (seq numbers of bets being placed),
   * the other holds the data itself.
   */
  struct Bets {
    mapping (address => uint) playerNoncesBy8;
    mapping (address => mapping (uint => uint)) playerBets;
  }

  // The bit to be set in playerNoncesBy8 mapping to disable accepting bets from an address
  uint constant internal PLAYER_NONCE_ACCOUNT_BANNED_BIT = 1;
  // The bit mask selecting the bits so that the number would turn into number mod 8
  uint constant internal PLAYER_NONCE_MOD8_MASK = uint(0x7);
  // The number of bits to shift the number to divide or multiply by 32 (log2(32))
  uint constant internal MULTIPLY_BY_32_BIT_SHIFT = 5;
  // The bit mask selecting exactly PackedBets.PACKED_BET_LENGTH bits
  uint constant internal PACKED_BET_MASK = 2 ** PackedBets.PACKED_BET_LENGTH - 1;
  // The bit mask selecting the bits so that the number would turn into number div 8
  uint constant internal PLAYER_NONCE_DIV8_MASK = ~PLAYER_NONCE_MOD8_MASK;
  // The number of packed bets stored in a single storage slot
  uint constant internal PACKED_BETS_PER_SLOT = 8;

  // The bit mask selecting bits from 8 possible PackedBets stored in a single slot. ANDing the slot value with
  // this constant allows for quick checks of whether there any PackedBets with non-zero amounts in this slot.
  uint constant internal ALL_QUANT_AMOUNTS_MASK =
    PackedBets.QUANT_AMOUNT_MASK |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 1) |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 2) |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 3) |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 4) |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 5) |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 6) |
    PackedBets.QUANT_AMOUNT_MASK << (PackedBets.PACKED_BET_LENGTH * 7);

  // The number indicating the slot is full with bets, i.e. all 8 spots are occupied by instances of PackedBet. The check is based
  // on the fact that we fill up the slot from left to right, meaning that placing the 8th PackedBet into a slot will set some bits higher than 224th one.
  uint constant internal FULL_SLOT_THRESHOLD = PackedBets.QUANT_AMOUNT_THRESHOLD << (PackedBets.PACKED_BET_LENGTH * 7);

  // An error indicating the player's address is not allowed to place the bets
  error AccountSuspended();

  /**
   * Being given the storage-located struct, the routine places PackedBet instance made by a player into a spare slot
   * and returns this bet's playerNonce - a seq number of the bet made by the player against this instance of the contract.
   *
   * @param bets the instance of Bets struct to manipulate.
   * @param player the address of the player placing the bet.
   * @param packedBet the instance of the PackedBet to place.
   *
   * @return playerNonce the seq number of the bet made by this player.
   */
  function storePackedBet(Bets storage bets, address player, PackedBet packedBet) internal returns (uint playerNonce) {
    // first off, read the current player's nonce. We are storing the nonces in 8 increments to avoid
    // unneccessary storage operations – in any case, each storage slot contains 8 bets, so we only need to know
    // the number / 8 to operate.
    uint playerNonceBy8 = bets.playerNoncesBy8[player];

    // if the PLAYER_NONCE_ACCOUNT_BANNED_BIT bit is set, it means we do not want to accept the bets from this player's address
    if (playerNonceBy8 & PLAYER_NONCE_ACCOUNT_BANNED_BIT != 0) {
      revert AccountSuspended();
    }

    // read the current slot being
    uint slot = bets.playerBets[player][playerNonceBy8];

    // identify how many 32 bit chunks (i.e. PackedBet) are already stored there
    uint betOffsetInSlot = Math.getBitLength32(slot);
    // divide this number by 32 (to get from bit offsets to actual number)
    uint playerNonceMod8 = betOffsetInSlot >> MULTIPLY_BY_32_BIT_SHIFT;

    // modify the slot by placing the current bet into the spare space – shift the data by freeShift value to achieve this
    slot |= (packedBet.toUint() << betOffsetInSlot);

    // update the slot in the storage
    bets.playerBets[player][playerNonceBy8] = slot;

    // IMPORTANT: did we just take the last avaiable spot in the slot?
    if (playerNonceMod8 == (PACKED_BETS_PER_SLOT - 1)) {
      // if we did, update the player's nonce so that next bets would write to the new slot
      bets.playerNoncesBy8[player] = playerNonceBy8 + PACKED_BETS_PER_SLOT;
    }

    // return full value of player's nonce
    playerNonce = playerNonceBy8 + playerNonceMod8;
  }

  /**
   * Being given the storage-located struct, the routine extracts a bet from the storage.
   *
   * Extracting the bet means the corresponding part of the storage slot is modified so that the amount kept in
   * corresponding PackedBet entry is reset to 0 to indicate the bet has been proccessed.
   *
   * Once ALL of the bets in a slot are marked as processed, the slot is cleared to become 0, allowing us to reclaim a
   * bit of gas.
   *
   * @param bets the instance of Bets struct to manipulate.
   * @param player the address of the player placing the bet.
   * @param playerNonce the playerNonce to read from the storage.
   *
   * @return the instance of the PackedBet found in the corresponding slot; might be 0x0 if missing.
   */
  function ejectPackedBet(Bets storage bets, address player, uint playerNonce) internal returns (PackedBet) {
    // compute the playerNonce div 8 – that's the nonce value we use in the store (see storePackedBet)
    uint playerNonceBy8 = playerNonce & PLAYER_NONCE_DIV8_MASK;
    // compute the position of the bet in the slot – it's offset by N PackedBet places, where N = playerNonce mod 8
    uint betOffsetInSlot = (playerNonce & PLAYER_NONCE_MOD8_MASK) << MULTIPLY_BY_32_BIT_SHIFT;

    // read the current slot's value
    uint slot = bets.playerBets[player][playerNonceBy8];

    // read the specific PackedBet, ANDing with PACKED_BET_MASK to avoid integer overflows
    uint data = (slot >> betOffsetInSlot) & PACKED_BET_MASK;

    // compute the positions of the bits where the amount value for the current bet is stored – it is simply
    // QUANT_AMOUNT_MASK shifted into the position of the PackedBet instance within the slot.
    uint amountZeroMask = ~(PackedBets.QUANT_AMOUNT_MASK << betOffsetInSlot);

    // clear up the bits corresponding to amount of our packed bet, essentially clearing the amount down to 0
    slot &= amountZeroMask;

    // check if all the spots in the slot contain 0s in amount AND if the slot is full...
    if (((slot & ALL_QUANT_AMOUNTS_MASK) == 0) && (slot >= FULL_SLOT_THRESHOLD)) {
      // delete the slot's data to get some gas refunded
      slot = 0;
    }

    // update the storage
    bets.playerBets[player][playerNonceBy8] = slot;

    // produce a PackedBet instance by wrapping the data. Since the data comes from the contract storage, and this library is the
    // only one that writes it, we do not need to perform additional validations here
    return PackedBet.wrap(data);
  }

  /**
   * Marks the entry in playerNonce with a flag indicating this player address should not be allowed to place new bets.
   *
   * @param bets the instance of Bets struct to manipulate.
   * @param player the address of the player placing the bet.
   * @param suspend whether to suspend or un-suspend the player.
   */
  function suspendPlayer(Bets storage bets, address player, bool suspend) internal {
    if (suspend) {
      // set 1st bit on the nonce counter
      bets.playerNoncesBy8[player] |= PLAYER_NONCE_ACCOUNT_BANNED_BIT;
    } else {
      // clear 1st bit from the nonce counter
      bets.playerNoncesBy8[player] &= ~PLAYER_NONCE_ACCOUNT_BANNED_BIT;
    }
  }
}

File 11 of 16 : GameOptions.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { TinyStrings, TinyString, TinyString5 } from "./TinyStrings.sol";
import { Math } from "./Math.sol";

/* GameOption is a custom type that represents a bit mask holding the outcome(s) the player has made a bet on.
 * The encoding scheme is using lower 12 bits of the integer to keep the flag indicating the
 * type of bet used along with the options chosen by the user.
 *
 * Having this as a separate type allows us to clearly bear the meaining of variables holding game options data
 * and also be less error-prone to things like implicit casts done by Solidity.
 *
 * The type's constructors defined in the library below also perform sanity checks on the values provided;
 * this way, if there is an instance of GameOptions somewhere, it is guaranteed to be valid and it is not neccessary to
 * re-validate it on the spot.
 *
 * Examples:
 *  1. Coin Flip bet on tails: 0b000000000010
 *  2. Dice bet on 1, 2 and 3: 0b010000000111
 *  3. TwoDice bet on 8 and 9: 0b100011000000
 *  4. Etheroll bet on <= 55:  0b001000110111
 */
type GameOption is uint256;

/**
 * This library provides a set of constants (like human-readable strings used for logging) along with
 * utility methods to abstract away the manipulation of GameOption custom type.
 */
library GameOptions {
  // Extension methods
  using TinyStrings for TinyString;

  // Human-readable representation of "heads" option for CoinFlip game
  TinyString5 constant internal HEADS_STR     = TinyString5.wrap(uint40(bytes5("heads")));
  // Human-readable representation of "tails" option for CoinFlip game
  TinyString5 constant internal TAILS_STR     = TinyString5.wrap(uint40(bytes5("tails")));
  // Human-readable representation of "jackpot" string
  TinyString5 constant internal JCKPT_STR     = TinyString5.wrap(uint40(bytes5("jckpt")));
  // Prefix for logging description of CoinFlip games
  TinyString constant internal COINFLIP_STR   = TinyString.wrap(uint72(bytes9("CoinFlip ")));
  // Prefix for logging description of Dice games
  TinyString constant internal DICE_STR       = TinyString.wrap(uint40(bytes5("Dice ")));
  // Prefix for logging description of TwoDice games
  TinyString constant internal TWODICE_STR    = TinyString.wrap(uint24(bytes3("2D ")));
  // Prefix for logging description of Etheroll games
  TinyString constant internal ETHEROLL_STR   = TinyString.wrap(uint88(bytes11("Etheroll <=")));

  // The mask selecting bits of GameOption containing CoinFlip choices – lower 2 bits
  uint constant internal GAME_OPTIONS_COIN_FLIP_MASK_BITS = (1 << 2) - 1;
  // The mask selecting bits of GameOption containing Dice choices – lower 6 bits
  uint constant internal GAME_OPTIONS_DICE_MASK_BITS      = (1 << 6) - 1;
  // The mask selecting bits of GameOption containing TwoDice choices – lower 11 bits
  uint constant internal GAME_OPTIONS_TWO_DICE_MASK_BITS  = (1 << 11) - 1;
  // The mask selecting bits of GameOption containing Etheroll number – lower 8 bits
  uint constant internal GAME_OPTIONS_ETHEROLL_MASK_BITS  = (1 << 8) - 1;
  // The maximum number allowed for an Etheroll game
  uint constant internal GAME_OPTIONS_ETHEROLL_MASK_MAX   = 99;

  // The flag indicating the GameOption describes a Dice game – 10th bit set
  uint constant internal GAME_OPTIONS_DICE_FLAG = (1 << 10);
  // The flag indicating the GameOption describes a TwoDice game – 11th bit set
  uint constant internal GAME_OPTIONS_TWO_DICE_FLAG = (1 << 11);
  // The flag indicating the GameOption describes an Etheroll game – 9th bit set
  uint constant internal GAME_OPTIONS_ETHEROLL_FLAG = (1 << 9);
  // The maximum value of GameOption as an integer – having higher bits set would mean there was on overflow
  uint constant internal GAME_OPTIONS_THRESHOLD = 2 ** 12;

  // The number of combinations in CoinFlip game
  uint constant internal GAME_OPTIONS_COIN_FLIP_MODULO = 2;
  // The number of combinations in Dice game
  uint constant internal GAME_OPTIONS_DICE_MODULO = 6;
  // The number of combinations in TwoDice game
  uint constant internal GAME_OPTIONS_TWO_DICE_MODULO = 36;
  // The number of combinations in Etheroll game
  uint constant internal GAME_OPTIONS_ETHEROLL_MODULO = 100;

  // The number where each hex digit represents the number of 2 dice combinations summing to a specific number
  uint constant internal GAME_OPTIONS_TWO_DICE_SUMS = 0x12345654321;
  // The number where each hex digit represents the number of dice outcomes representing a specific number (trivial)
  uint constant internal GAME_OPTIONS_DICE_SUMS = 0x111111;

  /**
   * Converts a given mask into a CoinFlip GameOption instance.
   *
   * @param mask CoinFlip choice(s) to encode.
   *
   * @return GameOption representing the passed mask.
   */
  function toCoinFlipOptions(uint mask) internal pure returns (GameOption) {
    require(mask > 0 && mask <= GAME_OPTIONS_COIN_FLIP_MASK_BITS, "CoinFlip mask is not valid");
    // CoinFlip does not have any dedicated flag set – thus simply wrap the mask
    return GameOption.wrap(mask);
  }

  /**
   * Converts a given mask into a Dice GameOption instance.
   *
   * @param mask Dice choice(s) to encode.
   *
   * @return GameOption representing the passed mask.
   */
  function toDiceOptions(uint mask) internal pure returns (GameOption) {
    require(mask > 0 && mask <= GAME_OPTIONS_DICE_MASK_BITS, "Dice mask is not valid");
    return GameOption.wrap(GAME_OPTIONS_DICE_FLAG | mask);
  }

  /**
   * Converts a given mask into a TwoDice GameOption instance.
   *
   * @param mask TwoDice choice(s) to encode.
   *
   * @return GameOption representing the passed mask.
   */
  function toTwoDiceOptions(uint mask) internal pure returns (GameOption) {
    require(mask > 0 && mask <= GAME_OPTIONS_TWO_DICE_MASK_BITS, "Dice mask is not valid");
    return GameOption.wrap(GAME_OPTIONS_TWO_DICE_FLAG | mask);
  }

  /**
   * Converts a given mask into a TwoDice Etheroll instance.
   *
   * @param option Etheroll choice to encode.
   *
   * @return GameOption representing the passed mask.
   */
  function toEtherollOptions(uint option) internal pure returns (GameOption) {
    require(option > 0 && option <= GAME_OPTIONS_ETHEROLL_MASK_MAX, "Etheroll mask is not valid");
    return GameOption.wrap(GAME_OPTIONS_ETHEROLL_FLAG | option);
  }

  /**
   * As the name suggests, the routine parses the instance of GameOption type and returns a description of what
   * kind of bet it represents.
   *
   * @param self GameOption instance to describe.
   *
   * @return numerator containing the number of choices selected in this GameOption
   *         denominator containing the total number of choices available in the game this GameOption describes
   *         bitMask containing bits set at positions where game options were selected by the player
   *         humanReadable containing an instance of TinyString describing the bet, e.g. "CoinFlip heads"
   */
  function describe(GameOption self) internal pure returns (uint numerator, uint denominator, uint mask, TinyString betDescription) {
    // we need bare underlying bits, so have to unwrap the GameOption
    uint gameOptions = GameOption.unwrap(self);

    // check if the game described in TwoDice
    if ((gameOptions & GAME_OPTIONS_TWO_DICE_FLAG) != 0) {
      // mask out the bit relevant for TwoDice game
      mask = gameOptions & GAME_OPTIONS_TWO_DICE_MASK_BITS;
      // each bit in the mask can correspond to different number of outcomes: e.g. you can 5 by rolling 1 and 4, or 4 and 1, or 3 and 2 etc.
      // to calculate the total number of rolls matching the mask, we simply multiply positions of bits set in the mask with a constant
      // containing how many combinations of 2 dice would yield a particular number
      numerator = Math.weightedPopCnt(mask, GAME_OPTIONS_TWO_DICE_SUMS);
      // denomination is always the same, 36
      denominator = GAME_OPTIONS_TWO_DICE_MODULO;
      // prepare human-readable string composed of a prefix and numbers of bits set up, with the lowest corresponding
      // to 2 (the minimum sum of 2 dice is 2), e.g. "2D 5,6,12"
      betDescription = TWODICE_STR.appendBitNumbers(mask, 2);

    // check if the game described in Dice
    } else if ((gameOptions & GAME_OPTIONS_DICE_FLAG) != 0) {
      // mask out the bit relevant for Dice game
      mask = gameOptions & GAME_OPTIONS_DICE_MASK_BITS;
      // similar to Two Dice game above, but every bit corresponding to a single option
      numerator = Math.weightedPopCnt(mask, GAME_OPTIONS_DICE_SUMS);
      // denomination is always the same, 6
      denominator = GAME_OPTIONS_DICE_MODULO;
      // prepare human-readable string composed of a prefix and numbers of bits set up, with the lowest corresponding
      // to 1 (the minimum sum of a single dice is 1), e.g. "Dice 1,2,3"
      betDescription = DICE_STR.appendBitNumbers(mask, 1);

    // check if the game described in Etheroll
    } else if ((gameOptions & GAME_OPTIONS_ETHEROLL_FLAG) != 0) {
      // mask out the bit relevant for Etheroll game
      mask = gameOptions & GAME_OPTIONS_ETHEROLL_MASK_BITS;
      // Etheroll lets players bet on a single number, stored "as in" in the mask
      numerator = mask;
      // denomination is always the same, 100
      denominator = GAME_OPTIONS_ETHEROLL_MODULO;
      // prepare human-readable string composed of a prefix and the number the player bets on, e.g. "Etheroll <=55"
      betDescription = ETHEROLL_STR.appendNumber(mask);

    // if none bits match, we are describing a CoinFlip game
    } else {
      // mask out the bit relevant for CoinFlip game
      mask = gameOptions & GAME_OPTIONS_COIN_FLIP_MASK_BITS;
      // we only let players bet on a single option in CoinFlip
      numerator = 1;
      // denomination is always the same, 2
      denominator = GAME_OPTIONS_COIN_FLIP_MODULO;
      // prepare human-readable string composed of a prefix and the side the player bets on, e.g. "CoinFlip tails"
      betDescription = COINFLIP_STR.append(mask == 1 ? HEADS_STR : TAILS_STR);
    }
  }
}

File 12 of 16 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * Tiny library containing bespoke mathematical functions allowing us to express contract's logic
 * in a more clear and gas efficient way.
 */
library Math {
  // Maximum number represented by 128 bit uint
  uint constant internal MAX128 = 2**128 - 1;
  // Maximum number represented by 64 bit uint
  uint constant internal MAX64  = 2**64  - 1;
  // Maximum number represented by 32 bit uint
  uint constant internal MAX32  = 2**32  - 1;
  // Maximum number represented by 16 bit uint
  uint constant internal MAX16  = 2**16  - 1;
  // Maximum number represented by 8 bit uint
  uint constant internal MAX8   = 2**8   - 1;

  /**
   * Returns the number of bits set rounded up to the nearest multiple of 32 – essentially,
   * how many whole 4 byte words are required to "fit" the number.
   *
   * @param number the number to compute the bit length for.
   *
   * @return length the bit length, rounded up to 32.
   */
  function getBitLength32(uint number) internal pure returns (uint length) {
    // if the number is greater than 2^128, then it is at least 128 bits long
    length  = toUint(number > MAX128) << 7;
    // if the left-most remaining part is greater than 2^64, then it's at least 64 bits longer
    length |= toUint((number >> length) > MAX64) << 6;
    // if the left-most remaining part is greater than 2^32, then it's at least 32 bits longer
    length |= toUint((number >> length) > MAX32) << 5;

    unchecked {
      // if there are more bits remaining, then it's at least another 32 bits longer (effectively, ceil())
      length += toUint((number >> length) > 0) << 5;
    }
  }

  /**
   * Returns the number of bits set rounded up to the nearest multiple of 8 – essentially,
   * how many whole 8-bit bytes are required to "fit" the number.
   *
   * @param number the number to compute the bit length for.
   *
   * @return length the bit length, rounded to 8.
   */
  function getBitLength8(uint number) internal pure returns (uint length) {
    // please refer to the explanation of getBitLength32() - the below is similar,
    // it just operates in 8 bit increments instead of 32, resulting in two extra steps.
    length  = toUint(number > MAX128) << 7;
    length |= toUint((number >> length) > MAX64) << 6;
    length |= toUint((number >> length) > MAX32) << 5;
    length |= toUint((number >> length) > MAX16) << 4;
    length |= toUint((number >> length) >  MAX8) << 3;

    unchecked {
      length += toUint((number >> length) > 0) << 3;
    }
  }

  /**
   * Returns 1 for true and 0 for false, as simle as that.
   *
   * @param boolean the bool to convert into an integer.
   * @return integer an integer of 0 or 1.
   */
  function toUint(bool boolean) internal pure returns (uint integer) {
    // As of Solidity 0.8.14, conditionals like (boolean ? 1 : 0) are not
    // optimized away, thus inline assembly forced cast is needed to save gas.
    assembly {
      integer := boolean
    }
  }

  /**
   * Returns true if a number is an exact power of 2.
   *
   * @param number the number to test for 2^N
   *
   * @return true if the number is an exact power of 2 and is not 0.
   */
  function isPowerOf2(uint number) internal pure returns (bool) {
    unchecked {
      return ((number & (number - 1)) == 0) && (number != 0);
    }
  }

  /**
   * Returns the minimum of 2 numbers.
   *
   * @param a the first number.
   * @param b the second number.
   *
   * @return a if it's not greater than b, b otherwise.
   */
  function min(uint a, uint b) internal pure returns (uint) {
    return a < b ? a : b;
  }

  /**
   * Returns the maximum of 2 numbers.
   *
   * @param a the first number.
   * @param b the second number.
   *
   * @return a if it's not greater than b, b otherwise.
   */
  function max(uint a, uint b) internal pure returns (uint) {
    return a < b ? b : a;
  }

  /**
   * Multiplies every set bit's number from mask with a hex digit of the second parameter.
   * This allows us to easily count the number of bits set (by providing weightNibbles of 0x1111...)
   * or to perform a "weighted" population count, where each bit has its own bespoke contribution.
   *
   * A real-life example would be to count how many combinations of 2 dice would yield one of
   * chosen in the mask. Consider the mask of 0b1011 (we bet on 2, 3 and 5) and the weightNibbles
   * set to be 0x12345654321 (2 is only 1+1, 3 is either 1+2 or 2+1 and so on). Calling this function
   * with the above arguments would return 7 - as there 7 combinations of 2 dice outcomes that would
   * yield either 1, 2 or 4.
   *
   * @param mask the number to get set bits from.
   * @param weightNibbles the number to get multiplier from.
   *
   * @return result the sum of bit position multiplied by custom weight.
   */
  function weightedPopCnt(uint mask, uint weightNibbles) internal pure returns (uint result) {
    result = 0;

    // we stop as soon as weightNibbles is zeroed out
    while (weightNibbles != 0) {
      // check if the lowest bit is set
      if ((mask & 1) != 0) {
        // it is – add the lowest hex octet from the nibbles
        result += weightNibbles & 0xF;
      }

      // shift the mask to consider the next bit
      mask >>= 1;
      // shift the nibbles to consider the next octet
      weightNibbles >>= 4;
    }
  }
}

File 13 of 16 : Options.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { Math } from "./Math.sol";
import { TinyStrings, TinyString } from "./TinyStrings.sol";
import { GameOptions } from "./GameOptions.sol";

/**
 * The library providing helper method to parse user input from a TinyString instance.
 */
library Options {
  // Extension functions
  using TinyStrings for TinyString;

  // The error indicating the option found within a string falls out of min...max range.
  error OptionNotInRange(uint option, uint min, uint max);

  /**
   * Being given an instance of TinyString and min/max constraints, parses the string returning
   * the last found option as an integer as well as bit mask with bits set at places corresponding
   * to the numbers found in the string.
   *
   * If the string is "heads" or "tails", it is instantly considered a CoinFlip option description,
   * returning hardcoded values for the mask and option parameters.
   *
   * The string is considered to consist of digits separated by non-digits characters. To save the gas,
   * the function does not distinguish between the types of separators; any non-digit character is considered
   * a separator.
   *
   * Examples:
   *  1. "heads" -> (1, 0)
   *  2. "tails" -> (2, 1)
   *  3. "1" -> (0b1, 1)
   *  4. "1,2,3" -> (0b111, 3)
   *
   * @param tinyString an instance of TinyString to parse.
   * @param min the minimum allowed number.
   * @param max the maximum allowed number.
   *
   * @return mask the bit mask where the bit is set if the string contains such a number
   *         lastOption the last found number.
   */
  function parseOptions(TinyString tinyString, uint min, uint max) internal pure returns (uint mask, uint lastOption) {
    // fast track: is the string "heads"?
    if (tinyString.equals(GameOptions.HEADS_STR)) {
      return (1, 0);
    }

    // fast track: is the string "tails"?
    if (tinyString.equals(GameOptions.TAILS_STR)) {
      return (2, 1);
    }

    // we parse the string left-to-right, meaning the first digit of a number has to be multipled by 1, the second by 10 etc
    uint digitMultiplier = 1;

    // we run the whole loop without arithmetic checks since we only use
    // functions operating on heavily constrained values
    unchecked {
      // repeat until stopped explicitly
      while (true) {
        // classify the last character.
        // IMPORTANT: empty string would return isDigit = false
        (bool isDigit, uint digit) = tinyString.getLastChar();

        // is the last character a digit?
        if (isDigit) {
          // is it the first digit of a new number? if so, reset the lastOption to 0
          lastOption = digitMultiplier == 1 ? 0 : lastOption;
          // add the digit multiplied by current multiplier to the lastOption value
          lastOption += digitMultiplier * digit;
          // the next digit would be 10x
          digitMultiplier *= 10;
        } else {
          // we stumbled upon a separator OR an empty string – let's validate the computed number
          if (lastOption < min || lastOption > max) {
            // the number falls out of min..max range, we have to crash
            revert OptionNotInRange(lastOption, min, max);
          }

          // set the bit corresponding to the last found number
          mask |= 1 << (lastOption - min);
          // reset the digit multiplier to 1 (since the next number will be a new number)
          digitMultiplier = 1;
        }

        // is the string empty?
        if (tinyString.isEmpty()) {
          // it is – stop the loop, we are done
          break;
        }

        // remove the last character from the string and repeat
        tinyString = tinyString.chop();
      }
    }
  }
}

File 14 of 16 : PackedBets.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { Math } from "./Math.sol";
import { GameOption, GameOptions } from "./GameOptions.sol";

/**
 * PackedBet is a custom type that represents a single bet placed by a player. It encodes the bet amount
 * (quantized down to a specific increment to save space), the GameOption instance (see ./GameOptions.sol) and
 * the bit indicating whether the bet was nominated to play for a jackpot.
 *
 * The memory layout of the type if pretty straightforward:
 * 1. Lowest 16 bits hold bet amount (divided by quant constant)
 * 2. Bit #17 is set to 1 if the bet plays for a jackpot
 * 3. Bits #18-29 hold GameOption data
 * 4. Bits #30-32 hold bet time (mod 4 hours).
 *
 * The type's constructors defined in the library below also perform sanity checks on the values provided;
 * this way, if there is an instance of PackedBet somewhere, it is guaranteed to be valid and it is not neccessary to
 * re-validate it on the spot.
 */
type PackedBet is uint256;

/**
 * The library containing conversion routines (to pack and unpack bet data into an instance of PackedBet),
 * as well as basic utilities to compute certain attributes of a PackedBet.
 */
library PackedBets {
  // Extension functions
  using Math for bool;

  // The byte length of the PackedBet type
  uint constant internal PACKED_BET_LENGTH = 32;
  // The length of the epoch for our contract (4 hours)
  uint constant internal EPOCH_LENGTH = 4 * 3600;
  // The bit mask selecting the bits allocated to hold quantified amount of the bet
  uint constant internal QUANT_AMOUNT_MASK = QUANT_AMOUNT_THRESHOLD - 1;
  // The bit mask selecting the bits allocated to hold GameOption data (not shifted)
  uint constant internal GAME_OPTIONS_MASK = GameOptions.GAME_OPTIONS_THRESHOLD - 1;
  // The maximum amount after quanitification (to avoid bit overflow)
  uint constant internal QUANT_AMOUNT_THRESHOLD = 2 ** 17;
  // The bit mask selecting all the data but epoch number
  uint constant internal ALL_BUT_EPOCH_BITS = 2 ** 30 - 1;
  // The value by which we quantify the bets
  uint constant internal QUANT_STEP = 0.001 ether;
  // The bit number where isJackpot flag is stored
  uint constant internal JACKPOT_BIT_OFFSET = 17;
  // The bit mask selecting bit representing isJackpot flag (shifted).
  uint constant internal JACKPOT_MASK = 1 << JACKPOT_BIT_OFFSET;
  // The bit number where GameOption data starts
  uint constant internal GAME_OPTIONS_BIT_OFFSET = 18;
  // The bit number where epoch number data starts
  uint constant internal EPOCH_BIT_OFFSET = 30;
  // The modulo of the epoch number – we only keep 3 bits of epoch number along with the bets
  uint constant internal EPOCH_NUMBER_MODULO = 4;
  // The bit mask selecting bits for epoch number
  uint constant internal EPOCH_NUMBER_MODULO_MASK = 3;

  /**
   * Packs the given amount (in wei), GameOption instance and a jackpot flag into a instance of a PackedBet.
   *
   * The routine assumes GameOption passed to it is valid and does not perform any additional checks.
   *
   * The routine checks that amount does not exceed maximum allowed one and is also an exact multiple of QUANT_STEP,
   * to avoid situations where 0.0011 Ethers are wagered and trimmed down to 0.001 in further calculations – it would
   * crash otherwise.
   *
   * @param amount the amount of wei being wagered.
   * @param gameOptions the instance of GameOption to encode.
   * @param isJackpot the flag inidicating the jackpot participation.
   *
   * @return an instance of PackedBet representing all the data passed.
   */
  function pack(uint amount, GameOption gameOptions, bool isJackpot) internal view returns (PackedBet) {
    // calculate quantified amount and the reminder
    uint quantAmount = amount / QUANT_STEP;
    uint quantReminder = amount % QUANT_STEP;

    // make sure the quantAmount does not overflow allowed size and that the reminder is 0
    require(quantAmount != 0 && quantAmount < QUANT_AMOUNT_THRESHOLD && quantReminder == 0, "Bet amount not quantifiable");

    // take 3 lowest of the current epoch number to keep it along with the PackedBet
    uint epochMod4 = getCurrentEpoch() & EPOCH_NUMBER_MODULO_MASK;

    // construct the packed bet by:
    // 1. Storing the epoch number in bits 30..32
    // 2. Storing GameOption in bits 18...29
    // 3. Storing isJackpot in bit 17
    // 4. Storing quantAmount in bits 0..16
    uint packedBet =  epochMod4 << EPOCH_BIT_OFFSET |
                      GameOption.unwrap(gameOptions) << GAME_OPTIONS_BIT_OFFSET |
                      isJackpot.toUint() << JACKPOT_BIT_OFFSET |
                      quantAmount;

    return PackedBet.wrap(packedBet);
  }

  /**
   * Checks if the PackedBet instance is empty (does not contain anything).
   *
   * @param self PackedBet instance to check.
   *
   * @return true if the PackedBet instance is empty.
   */
  function isEmpty(PackedBet self) internal pure returns (bool) {
    return PackedBet.unwrap(self) == 0;
  }

  /**
   * Converts the PackedBet to an integer representation.
   *
   * @param self PackedBet instance to convert into an integer.
   *
   * @return the number representing the PackedBet instance.
   */
  function toUint(PackedBet self) internal pure returns (uint256) {
    return PackedBet.unwrap(self);
  }

  /**
   * Removes all the bits encoding epoch number from the instance of the PackedBet.
   * This routine is helpful when two PackedBets need to be checked for equality.
   *
   * @param self the instance of the PackedBet to remove epoch number from.
   *
   * @return an instance of the PackedBet with epoch bits set to 0.
   */
  function withoutEpoch(PackedBet self) internal pure returns (PackedBet) {
    return PackedBet.wrap(PackedBet.unwrap(self) & ALL_BUT_EPOCH_BITS);
  }

  /**
   * Returns the number of the current and bet's epochs mod 4.
   *
   * @param self PackedBet instance to check.
   *
   * @return betEpoch the bet's epoch mod 4
   *         currentEpoch the current epoch mod 4
   */
  function extractEpochs(PackedBet self) internal view returns (uint betEpoch, uint currentEpoch) {
    // get current epoch % 4 value
    currentEpoch = getCurrentEpoch() & EPOCH_NUMBER_MODULO_MASK;
    // get bet's epoch % 4 value
    betEpoch = (PackedBet.unwrap(self) >> EPOCH_BIT_OFFSET) & EPOCH_NUMBER_MODULO_MASK;
  }

  /**
   * Checks if two PackedBet instances are exactly equal.
   *
   * @param self first instance of the PackedBet.
   * @param other second instance of the PackedBet.
   *
   * @return true if the instances are exactly the same.
   */
  function equals(PackedBet self, PackedBet other) internal pure returns (bool) {
    return PackedBet.unwrap(self) == PackedBet.unwrap(other);
  }

  /**
   * Checks if the PackedBet instance has 0 in the amount portion. This is needed to make
   * sure we don't settle the same bet twice (upon resolving a bet we clear the amount bits).
   *
   * @param self the instance of the PackedBet to check.
   *
   * @return true if the amount portion of the PackedBet is zero.
   */
  function hasZeroAmount(PackedBet self) internal pure returns (bool) {
    return (PackedBet.unwrap(self) & QUANT_AMOUNT_MASK) == 0;
  }

  /**
   * Unpacks the PackedBet instance into separate values of GameOption, amount used and isJackpot flag.
   *
   * @param self PackedBet to unpack.
   *
   * @return gameOptions the instance of GameOption encoded in this packed bet.
   *         amount the amount encoded, multiplied by quantification coefficient.
   *         isJackpot the flag indicating whether the bet plays for jackpot.
   */
  function unpack(PackedBet self) internal pure returns (GameOption gameOptions, uint amount, bool isJackpot) {
    // we need raw bits, so have to unwrap the bet into an integer
    uint data = PackedBet.unwrap(self);

    // the amount is essentially quantAmount times QUANT_STEP
    amount = (data & QUANT_AMOUNT_MASK) * QUANT_STEP;
    isJackpot = (data & JACKPOT_MASK) != 0;
    gameOptions = GameOption.wrap((data >> GAME_OPTIONS_BIT_OFFSET) & GAME_OPTIONS_MASK);
  }

  /**
   * Returns the number of the current epoch, expressed as the number of 4 hour interval since the beginning of times.
   *
   * @return the number of the curret epoch.
   */
  function getCurrentEpoch() private view returns (uint) {
    return block.timestamp / EPOCH_LENGTH;
  }
}

File 15 of 16 : TinyStrings.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { Math } from "./Math.sol";

/**
 * TinyString is a custom type providing an alternative representation of short (up to 32 chars) strings containing
 * ASCII characters.
 *
 * We heavily use this primitive in the contract to keep human-readable logs and input parameters yet avoid overspending
 * tremendeous amount on gas.
 */
type TinyString  is uint256;

/**
 * A special case of TinyString - a string that contains a single ASCII character.
 */
type TinyString1 is uint8;

/**
 * A special case of TinyString - a string that contains 5 ASCII characters.
 */
type TinyString5 is uint40;

/**
 * The library providing functions for manipulating TinyString instances in a nice and readable way.
 */
library TinyStrings {
  // Extension functions
  using TinyStrings for TinyString;

  // The constant holding "," character
  TinyString1 constant internal COMMA = TinyString1.wrap(uint8(bytes1(",")));
  // The constant holding "+" character
  TinyString1 constant internal PLUS  = TinyString1.wrap(uint8(bytes1("+")));
  // The constant holding " " character
  TinyString1 constant internal SPACE = TinyString1.wrap(uint8(bytes1(" ")));

  // The bit mask selecting 8th bit from every byte of 32 byte integer - used to check whether a string
  // contains any characters > 128
  uint constant internal MSB_BYTES = 0x8080808080808080808080808080808080808080808080808080808080808080;
  // The code of "0" in ASCII – allows us to do super-cheap parsing by subtracting this from the charCode (1 = 49, 2 = 50 etc)
  uint constant internal ZERO_ASCII_CODE = 48;
  // How many bit there are in a single ASCII character.
  uint constant internal BITS_PER_CHARACTER = 8;
  // How many bit there are in 5 ASCII characters.
  uint constant internal BITS_PER_FIVE_CHARACTERS = BITS_PER_CHARACTER * 5;
  // The maximum possible length in bits of TinyString
  uint constant internal TINY_STRING_MAX_LENGTH = 32;
  // The maximum possible length of TinyString in bits.
  uint constant internal TINY_STRING_MAX_LENGTH_BITS = TINY_STRING_MAX_LENGTH * BITS_PER_CHARACTER;
  // Contains the bit mask selecting bits of the very last character in the TinyString - simply the lowest byte
  uint constant internal LAST_CHARACTER_MASK_BITS = 0xFF;

  // The error indicating a native string passed to the library exceeds 32 characts and cannot be manipulated.
  error StringTooLong();
  // The error indicating a string contains non-ASCII charactes and cannot be manipulated by the library.
  error NotAscii();

  /**
   * Converts an instance of TinyString into a native string placed in memory so that it can be used in
   * logs and other places requiring native string instances.
   *
   * @param self the instance of TinyString to convert into a native one.
   *
   * @return result the native string instance containing all characters from the TinyString instance.
   */
  function toString(TinyString self) internal pure returns (string memory result) {
    // convert the string into an integer
    uint tinyString = TinyString.unwrap(self);
    // calculate the length of the string using the Math library: the length of the string would be
    // equivalent to the number of highest bit set divied by 8 (since every character occupy 8 bits) and
    // rounded up to nearest multiple of 8.
    uint length = Math.getBitLength8(tinyString);

    // Allocate a string in memory (divide the length by 8 since it is in bits and we need bytes)
    result = new string(length >> 3);

    // Copy over character data (situated right after a 32-bit length prefix)
    assembly {
      // we need to shift the bytes so that the characters reside in the higher bits, with lower set to 0
      length := sub(256, length)
      tinyString := shl(length, tinyString)
      // once we shifted the characters, simply copy the memory over
      mstore(add(result, 32), tinyString)
    }
  }

  /**
   * Converts a native string into a TinyString instance, performing all required validity checks
   * along the way.
   *
   * @param self a native string instance to convert into TinyString.
   *
   * @return tinyString an instance of the TinyString type.
   */
  function toTinyString(string calldata self) internal pure returns (TinyString tinyString) {
    // first off, make sure the length does not exceed 32 bytes, since it is the maximum length a
    // TinyString can store being backed by uint256
    uint length = bytes(self).length;
    if (length > TINY_STRING_MAX_LENGTH) {
      // the string is too long, we have to crash.
      revert StringTooLong();
    }

    // start unchecked block since we know that length is within [0..32] range
    unchecked {
      // copying the memory from native string would fill higher bits first, but we want
      // TinyString to contain characters in the lowest bits; thus, we need to compute the number
      // of bits to shift the data so that bytes like 0xa000 end up 0xa.
      uint shift = TINY_STRING_MAX_LENGTH_BITS - (length << 3);

      // Using inline assembly to efficiently fetch character data in one go.
      assembly {
        // simply copy the memory over (we have validated the length already, so all is good)
        tinyString := calldataload(self.offset)
        // shift the bytes to make sure the data sits in lower bits
        tinyString := shr(shift, tinyString)
      }
    }

    // Check that string contains ASCII characters only - i.e. there are no bytes with the value of 128+
    if (TinyString.unwrap(tinyString) & MSB_BYTES != 0) {
      // there are non-ascii characters – we have to crash
      revert NotAscii();
    }
  }

  /**
   * Reads the last character of the string and classifies it as a digit or a non-digit one.
   *
   * If the string is empty, it would return a tuple of (false, -48).
   *
   * @param self an instance of TinyString to get the last character from.
   *
   * @return isDigit flag set to true if the character is a digit (0..9).
   *         digit the actual digit value of the charact (valid only is isDigit is true).
   */
  function getLastChar(TinyString self) internal pure returns (bool isDigit, uint digit) {
    // we are operating on a single-byte level and thus do not need integer overflow checks
    unchecked {
      // get the lowest byte of the string
      uint charCode = TinyString.unwrap(self) & LAST_CHARACTER_MASK_BITS;

      // compute the digit value, which is simply charCode - 48
      digit = charCode - ZERO_ASCII_CODE;
      // indicate whether the character is a digit (falls into 0..9 range)
      isDigit = digit >= 0 && digit < 10;
    }
  }

  /**
   * Checks whether the string contains any characters.
   *
   * @param self an instance of TinyString to check for emptiness.
   *
   * @return true if the string is empty.
   */
  function isEmpty(TinyString self) internal pure returns (bool) {
    // as simple as it gets: if there are no characters, the string will be 0x0
    return TinyString.unwrap(self) == 0;
  }

  /**
   * Returns a copy of TinyString instance without the last character.
   *
   * @param self an instance of TinyString to remove the last character from.
   *
   * @return a new instance of TinyString.
   */
  function chop(TinyString self) internal pure returns (TinyString) {
    // we simply right-shift all the bytes by 8 bits – and that effectively deletes the last character.
    return TinyString.wrap(TinyString.unwrap(self) >> BITS_PER_CHARACTER);
  }

  /**
   * Returns a copy of TinyString instance with TinyString1 attached at the end.
   *
   * @param self an instance of TinyString to append the TinyString1 to.
   * @param chunk an instance of TinyString1 to append.
   *
   * @return a new instance of TinyString.
   */
  function append(TinyString self, TinyString1 chunk) internal pure returns (TinyString) {
    // we just left-shift the string and OR with the TinyString1 chunk to copy its character over into the lowest byte.
    return TinyString.wrap((TinyString.unwrap(self) << BITS_PER_CHARACTER) | TinyString1.unwrap(chunk));
  }

  /**
   * Returns a copy of TinyString instance with TinyString1 attached at the end.
   *
   * @param self an instance of TinyString to append the TinyString1 to.
   * @param chunk an instance of TinyString1 to append.
   *
   * @return a new instance of TinyString.
   */
  function append(TinyString self, TinyString5 chunk) internal pure returns (TinyString) {
    // we just left-shift the string and OR with the TinyString5 chunk to copy its characters over into the lowest bytes.
    return TinyString.wrap((TinyString.unwrap(self) << BITS_PER_FIVE_CHARACTERS) | TinyString5.unwrap(chunk));
  }

  /**
   * Checks whether TinyString contains the same characters as TinyString5.
   *
   * @param self an instance of TinyString to check.
   * @param other an instance of TinyString5 to check.
   *
   * @return true if the strings are the same.
   */
  function equals(TinyString self, TinyString5 other) internal pure returns (bool) {
    return TinyString.unwrap(self) == TinyString5.unwrap(other);
  }

  /**
   * Appends a number to an instance of a TinyString: "1 + 2 = ".toTinyString().append(3) => "1 + 2 = 3".
   *
   * @param self an instance of TinyString to append the number to.
   * @param number the number to append.
   *
   * @return a new instance of TinyString.
   */
  function appendNumber(TinyString self, uint number) internal pure returns (TinyString) {
    // since we work on character level, we don't need range checks.
    unchecked {
      uint str = TinyString.unwrap(self);

      if (number >= 100) {
        // if number is > 100, append number of hundreds
        str = (str << BITS_PER_CHARACTER) | (ZERO_ASCII_CODE + number / 100);
      }

      if (number >= 10) {
        // if number is > 100, append number of tens
        str = (str << BITS_PER_CHARACTER) | (ZERO_ASCII_CODE + number / 10 % 10);
      }

      // append any remainder (0..9) to the string
      return TinyString.wrap((str << BITS_PER_CHARACTER) | (ZERO_ASCII_CODE + number % 10));
    }
  }

  /**
   * Appends the numbers of bits set in the mask, naming them from startNumber.
   *
   * This is a very specific method that allows us to easily compose strings like "Dice 1,2,5", where 1,2,5 portion
   * is coming from a bit mask.
   *
   * startNumber parameter is required so that every bit of the mask is named properly (i.e. in Dice game the lowest
   * bit represents an outcome of 1, whereas in TwoDice game the lowest bit means 2).
   *
   * @param self an instance of TinyString to append the bit numbers to.
   * @param mask the mask to extract bits from to append to the string.
   * @param startNumber the value of the lowest bit in the mask.
   *
   * @return a new instance of TinyString.
   */
  function appendBitNumbers(TinyString self, uint mask, uint startNumber) internal pure returns (TinyString) {
    // repeat while the mask is not empty
    while (mask != 0) {
      // check if the lowest bit is set
      if (mask & 1 != 0) {
        // it is set – append current value of startNumber to the string and add a ","
        self = self.appendNumber(startNumber).append(TinyStrings.COMMA);
      }

      // right-shift the mask to start considering the next bit
      mask >>= 1;
      // increment the number since the next bit is one higher than the previous one. we don't check for overflows here
      // since the loop is guaranteed to end in at most 256 iterations anyway
      unchecked {
        startNumber++;
      }
    }

    // remove the last character since every bit appends "," after its number
    return self.chop();
  }
}

File 16 of 16 : VRF.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// Imports
import { Math } from "./Math.sol";
import { PackedBets, PackedBet } from "./PackedBets.sol";

/**
 * The library providing RSA-based Verifiable Random Function utilities.
 *
 * The main workhorse of our VRF generation is decrypt() function. Being given RSA modulus chunks and the memory slice
 * of 256 bytes containing encrypted VRF, it attemps to decrypt the data and expand it into a tuple of bet properties.
 *
 * The calling contract can then validate that the bet described by the returned tuple is active and proceed with settling it.
 *
 * The random number source is the hash of encrypted data chunk. The players cannot predict that value since they do not know
 * the secret key corresponding to the RSA modulus hardcoded in the contract; the house cannot tamper with player's bets since
 * the VRF value that passes all the checks is unique due to RSA being a permutation (see Dice9.sol for the proof).
 *
 * The distribution is uniform due to RSA ciphertext being hashed.
 *
 * The above properties allow Dice9 to perform robust and verifiable random number generation using a seed value consisting of bet
 * information such as player address, bet amount, bet options and bet nonce.
 */
library VRF {
  // Extension functions
  using PackedBets for PackedBet;

  // The byte length of the RSA modulus (1024 bits)
  uint constant internal RSA_MODULUS_BYTES = 128;
  // The byte length of the RSA ciphertext (1024 bits)
  uint constant internal RSA_CIPHER_TEXT_BYTES = 128;
  // The byte length of the RSA exponent (we use a hardcoded value of 65537)
  uint constant internal RSA_EXPONENT_BYTES = 32;
  // The RSA exponent value
  uint constant internal RSA_EXPONENT = 65537;
  // The address of EIP-198 modExp precompile contract, which makes RSA decryption gas cost feasible.
  address constant internal EIP198_MODEXP = 0x0000000000000000000000000000000000000005;
  // The value that the first 256 bits of the decrypted text must have (254 ones).
  uint constant internal P0_PADDING_VALUE = 2 ** 254 - 1;
  // The bit number where the contract address value starts in the first 256 bit decoded chunk
  uint constant internal P1_CONTRACT_ADDRESS_BIT_OFFSET = 96;
  // The bit number where the 30 bit (without 2 epoch bits) packed bet data starts in the first 256 bit decoded chunk
  uint constant internal P1_PACKED_BET_BIT_OFFSET = 160;
  // The bit number where the player nonce data starts in the first 256 bit decoded chunk
  uint constant internal P1_PLAYER_NONCE_BIT_OFFSET = 160 + 30;

  // The error indicating that the ciphertext was decrypted into something invalid (e.g. random bytes were submitted to reveal a bet)
  error InvalidSignature();

  /**
   * Being given all the bet attributes, computes a hash sum of the bet, allowing the contract to quickly verify the equivalence
   * of the bets being considered as well as providing a unique identifier for any bet ever placed.
   *
   * @param player the address of the player placing the bet.
   * @param playerNonce the seq number of the bet played by this player against this instance of the contract.
   * @param packedBet an instance of PackedBet representing a bet placed by the player.
   *
   * @return the keccak256 hash of all the parameters prefixed by the chain id and contract address to avoid replay and Bleichenbacher-like attacks.
   */
  function computeVrfInputHash(address player, uint playerNonce, PackedBet packedBet) internal view returns (bytes32) {
    return keccak256(abi.encode(block.chainid, address(this), player, playerNonce, packedBet.toUint()));
  }

  /**
   * Performs RSA "decryption" procedure using BigModExp (https://eips.ethereum.org/EIPS/eip-198) to remain gas-efficient.
   *
   * If the cipherText was produced by a legic signatory (i.e. a party possessing the secret key that corresponds to the hardcoded modulus and exponent),
   * the plaintext produced can further be decoded into a set of bet attributes and a number of checksum-like fields, which get validated as well
   * to make sure the bet attributes descibed are accurate, have not been taken from previous bets and so on.
   *
   * As mentioned above, cipherText gets hashed using keccak256 hash function to further be used as a source of entropy by the smart contract
   * to determine the outcome of the bet, the amount to be paid out, the jackpot roll value and so on.
   *
   * Assuming the hardcoded modulus corresponds to a set of valid RSA parameters (see Dice9.sol for the proof), every set of bet attributes would
   * produce a single cipherText decrypting into that same bet attributes, meaning that any bet a player places would get a single random number
   * associated.
   *
   * @param modulus0 first 32 bytes of the modulus
   * @param modulus1 second 32 bytes of the modulus
   * @param modulus2 third 32 bytes of the modulus
   * @param modulus3 fourth 32 bytes of the modulus
   * @param cipherText the ciphertext received from a Croupier to decrypt.
   *
   * @return vrfHash the hash of cipherText which can be used as the entropy source
   *         vrfInputHash the hash of bet attributes (see computeVrfInputHash() above)
   *         player the address of the player of the bet represented by given ciphertext
   *         playerNonce the player nonce of the bet represented by given ciphertext
   *         packedBet an instance of PackedBet of the bet represented by given ciphertext.
   */
  function decrypt(uint modulus0, uint modulus1, uint modulus2, uint modulus3, bytes calldata cipherText) internal view returns (bytes32 vrfHash, bytes32 vrfInputHash, address player, uint playerNonce, PackedBet packedBet)  {
    vrfHash = keccak256(cipherText);

    // RSA decryption
    bytes memory precompileParams = abi.encodePacked(
      // byte length of the ciphertext
      RSA_CIPHER_TEXT_BYTES,
      // byte length of the exponent value
      RSA_EXPONENT_BYTES,
      // byte length of the modulus
      RSA_MODULUS_BYTES,
      // the ciphertext to decrypt
      cipherText,
      // exponent value
      RSA_EXPONENT,
      // modulus values
      modulus0,
      modulus1,
      modulus2,
      modulus3
    );

    // EIP-198 places BigModExp precompile at 0x5
    (bool modExpSuccess, bytes memory plainText) = EIP198_MODEXP.staticcall(precompileParams);

    // make sure the decryption succeeds
    require(modExpSuccess, "EIP-198 precompile failed!");

    // unpack the bet attributes from the decrypted text
    (player, playerNonce, packedBet, vrfInputHash) = unpack(plainText);
  }

  /**
   * Unpacks the bet attributes from the plaintext (decrypted bytes) presented as an argument.
   *
   * The routine checks that the leading padding is set to a specific value - to make sure the ciphertext produced was actually created with
   * a valid secret key correponding to the contract's public key (RSA modulus).
   *
   * An additional check tests that the ciphertext was produced for this particular contract on this particular chain by checking decoded
   * data – otherwise it is considered a replay attack with data taken from another chain.
   * 
   * Last but not least, decoded parameters are equality tested against the corresponding vrfInputHash in the lowest 256 bits of the plaintext
   * to prevent Bleichenbacher style attacks (bruteforcing the plaintext to obtain a perfect power).
   *
   * @param plainText the decoded array of bytes as returned during the decryption stage.
   *
   * @return player the address of the player placing the bet.
   *         playerNonce the seq number of the player's bet against this instance of the contract.
   *         packedBet the instance of PackedBet describing the bet the player placed.
   *         vrfInputHash the hash of the input parameters of the bet prior to encoding.
   */
  function unpack(bytes memory plainText) private view returns (address player, uint playerNonce, PackedBet packedBet, bytes32 vrfInputHash) {
    uint p0;
    uint p1;
    uint p2;

    // decode the plaintext into 4 uint256 chunks
    (p0, p1, p2, vrfInputHash) = abi.decode(plainText, (uint, uint, uint, bytes32));

    // the first 32 bytes should be equal to a hardcoded value to guarantee the ciphertext was produced by a legit signatory
    if (p0 != P0_PADDING_VALUE) {
      revert InvalidSignature();
    }

    // the second 32 bytes should contain the contract's address (bits 96..256) and chain id (bits 0..96)
    if (p1 != uint(uint160(address(this))) << P1_CONTRACT_ADDRESS_BIT_OFFSET | block.chainid) {
      revert InvalidSignature();
    }

    // the player address is going to be kept in lowest 160 bits of the next 32 bytes
    player = address(uint160(p2));
    // the packed bet occupies 30 bits in positions 161..190, the remaining 2 bits are supposed to store epoch value which we simply discard
    // by masking the value against PackedBets.ALL_BUT_EPOCH_BITS
    packedBet = PackedBet.wrap((p2 >> P1_PACKED_BET_BIT_OFFSET) & PackedBets.ALL_BUT_EPOCH_BITS);
    // the player nonce is the easiest, it takes bites 191..256, so just transfer it over
    playerNonce = p2 >> P1_PLAYER_NONCE_BIT_OFFSET;

    // the last but not least: verify that the ciphertext contained an ecrypted bet attributes for the encoded
    // bet attributes – this disallows the signatory to craft multiple ciphertexts per bet attributes by tampering with vrfInputHash bytes
    if (vrfInputHash != VRF.computeVrfInputHash(player, playerNonce, packedBet)) {
      revert InvalidSignature();
    }
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "viaIR": true,
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"m0","type":"uint256"},{"internalType":"uint256","name":"m1","type":"uint256"},{"internalType":"uint256","name":"m2","type":"uint256"},{"internalType":"uint256","name":"m3","type":"uint256"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AccountSuspended","type":"error"},{"inputs":[],"name":"CannotAffordToLoseThisBet","type":"error"},{"inputs":[],"name":"CoinFlipSingleOption","type":"error"},{"inputs":[],"name":"EtherollSingleOption","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"MaxProfitExceeded","type":"error"},{"inputs":[],"name":"NonFullVRFs","type":"error"},{"inputs":[],"name":"NotAscii","type":"error"},{"inputs":[{"internalType":"uint256","name":"option","type":"uint256"},{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"OptionNotInRange","type":"error"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"playerNonce","type":"uint256"},{"internalType":"uint256","name":"betEpoch","type":"uint256"},{"internalType":"uint256","name":"currentEpoch","type":"uint256"}],"name":"RefundEpochMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"playerNonce","type":"uint256"}],"name":"StorageSignatureMismatch","type":"error"},{"inputs":[],"name":"StringTooLong","type":"error"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"WinProbabilityOutOfRange","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"playerNonce","type":"uint256"}],"name":"Duplicate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"JackpotMultiplierUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vrfInputHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"playerNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"jackpotPayment","type":"uint256"},{"indexed":false,"internalType":"string","name":"humanReadable","type":"string"}],"name":"Lost","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"MaxProfitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"playerNonce","type":"uint256"}],"name":"Nonexistent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vrfInputHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"playerNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"packedBet","type":"uint256"},{"indexed":false,"internalType":"string","name":"humanReadable","type":"string"}],"name":"Placed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vrfInputHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"playerNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"jackpotPayment","type":"uint256"},{"indexed":false,"internalType":"string","name":"humanReadable","type":"string"}],"name":"Won","type":"event"},{"inputs":[],"name":"destroy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"jackpotBetCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"jackpotMultiplier","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedInBets","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxProfit","outputs":[{"internalType":"uint72","name":"","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"modulus0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"modulus1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"modulus2","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"modulus3","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"options","type":"string"}],"name":"playCoinFlip","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"options","type":"string"}],"name":"playDice","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"options","type":"string"}],"name":"playEtheroll","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"options","type":"string"}],"name":"playTwoDice","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"playerNonce","type":"uint256"}],"name":"refundBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"newJackpotMultiplier","type":"uint40"}],"name":"setJackpotMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint72","name":"newMaxProfit","type":"uint72"}],"name":"setMaxProfit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"vrfs","type":"bytes"}],"name":"settleBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"bool","name":"suspend","type":"bool"}],"name":"suspendPlayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"topUpContract","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]

610100601f612a1d38819003918201601f19168301916001600160401b038311848410176100d9578084926080946040528339810103126100d4578051906020810151606060408301519201519260805260a05260c05260e0523360018060a01b03196001541617600155690800000000000000000060018060701b0319600054161760005560405161292d90816100f082396080518181816105510152610f92015260a0518181816101eb0152610530015260c05181818161018d0152610593015260e0518181816105720152610d920152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001257600080fd5b60003560e01c80635abb93ef1461015757806374a1be9b14610152578063820e320a1461014d57806383197ef01461014857806383595e9d14610143578063840e5bbd1461013e5780638afe7aea1461013957806392ad7734146101345780639b3b20d11461012f578063b539cd551461012a578063bff92bba14610125578063c107532914610120578063c60f70571461011b578063cd97323f14610116578063d1b7f0e814610111578063d5315c2a1461010c578063dc2e3ad814610107578063dc3e51d614610102578063df88126f146100fd5763e88a69a2146100f857600080fd5b610f5c565b610f11565b610ed1565b610dfb565b610db5565b610d5c565b610c89565b610b99565b610a79565b6109ba565b610973565b610947565b610902565b6104d6565b610495565b6103bf565b610276565b610231565b6101b5565b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b600080fd5b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101b057565b346101b05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05761027461026b61020e565b6024359061178f565b005b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05773ffffffffffffffffffffffffffffffffffffffff600154166102c9813314611ceb565b69ffffffffffffffffffff60005460901c166102e157ff5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f5468657265206172652070656e64696e672062657473000000000000000000006044820152fd5b9181601f840112156101b05782359167ffffffffffffffff83116101b057602083818601950101116101b057565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101b057600435916024359067ffffffffffffffff82116101b0576103bb9160040161033f565b9091565b6103da6103d56103ce3661036d565b91506124f8565b611f10565b506103e481611e5c565b1561046b5780151580610460575b15610402576102749034336110f7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f436f696e466c6970206d61736b206973206e6f742076616c69640000000000006044820152fd5b5060038111156103f2565b60046040517fcf3ec9a9000000000000000000000000000000000000000000000000000000008152fd5b6102746104ac6104a76103ce3661036d565b612015565b50801515806104ca575b6104bf90611d50565b6108001734336110f7565b506107ff8111156104b6565b346101b05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b0576004803567ffffffffffffffff81116101b057610525903690830161033f565b90607f82166108d9577f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060005b8681106105be57005b6105df6105d56105cd8361101a565b838a8a61147b565b9085858989612769565b6105ec828496959461161c565b801561087e576201ffff81161561081f57633fffffff829116036107c2576000610790969561077a61077261065661078b98859869ffffffffffffffffffff61067f889a6106cd6107296106e563ffffffff6106488e9f6119d8565b92849e94819e939293611a11565b99829c93979299919d16600061067a905469ffffffffffffffffffff9060901c1690565b611493565b7fffffffff00000000000000000000ffffffffffffffffffffffffffffffffffff7bffffffffffffffffffff0000000000000000000000000000000000006000549260901b16911617600055565b166106e08e5463ffffffff9060701c1690565b6114af565b7fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff71ffffffff00000000000000000000000000006000549260701b16911617600055565b8a146107955761076a61075c7f5e450238c651e103ae241f8b55cc5b30f9720edb1ef6fb0efbb207142a1a07d794612448565b876040519485948d8661105f565b0390a2611052565b801590611052565b905af1610785611574565b506115a4565b61101a565b6105b5565b61076a61075c7f325df7b841142d795406035b9236a2ad039223670c5b38c985e4d54a51191fe694612448565b848d61081b846040519384937f6fbed4b800000000000000000000000000000000000000000000000000000000855284016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff9095168552602085019190915261079094937fb6a11c8fd439c75283b878187bb665a6de83da1cd09c4d2784e1b05071d9022a93509150819081015b0390a161101a565b50506040805173ffffffffffffffffffffffffffffffffffffffff9095168552602085019190915261079094937f1631d7a0831826269077aebd7189b36b522e25369f150a3e6d215bda68da09a29350915081908101610876565b826040517f1a6356f9000000000000000000000000000000000000000000000000000000008152fd5b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602063ffffffff60005460701c16604051908152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057005b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602068ffffffffffffffffff60005416604051908152f35b346101b05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760043568ffffffffffffffffff81168091036101b05760207f3cd703d90ed49e5ecefd6ac22b0bcbcd33a50f94dad387fd8ebdfe5d897c8db991610a4673ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b807fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000006000541617600055604051908152a1005b346101b05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057610ab061020e565b60243590610ad773ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b60009169ffffffffffffffffffff835460901c168101808211610b94574710610b10578280808093610b0d955af1610785611574565b80f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f43616e6e6f742077697468647261772066756e6473202d2070656e64696e672060448201527f62657473206d69676874206e65656420746865206d6f6e65792e0000000000006064820152fd5b610fb5565b346101b05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057610bd061020e565b60243580151581036101b057610bff73ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b15610c3757610c2e9073ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b80546001179055005b610c619073ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b80547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe169055005b610ca7610ca0610c9b6103ce3661036d565b61212e565b9190611e5c565b15610d325780151580610d27575b15610cc957610274906102001734336110f7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45746865726f6c6c206d61736b206973206e6f742076616c69640000000000006044820152fd5b506063811115610cb5565b60046040517fea5478ca000000000000000000000000000000000000000000000000000000008152fd5b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602064ffffffffff60005460481c16604051908152f35b346101b05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05764ffffffffff60043581811681036101b0577f1fecbaa6256b253892dd2f7b1fca105910462fd3694e7365619cddb8d4a6d71291602091610e8473ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b7fffffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffff6dffffffffff0000000000000000006000549260481b1691161790816000556040519160481c168152a1005b610274610ee8610ee36103ce3661036d565b612247565b5080151580610f06575b610efb90611d50565b6104001734336110f7565b50603f811115610ef2565b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602069ffffffffffffffffffff60005460901c16604051908152f35b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91909169ffffffffffffffffffff80809416911601918211610b9457565b91909163ffffffff80809416911601918211610b9457565b9060808201809211610b9457565b9060088201809211610b9457565b9060048201809211610b9457565b9060018201809211610b9457565b91908201809211610b9457565b9391929073ffffffffffffffffffffffffffffffffffffffff90969596168452602092838501526040840152606083015260a060808301528351938460a084015260005b8581106110e3575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84600060c0809697860101520116010190565b81810183015184820160c0015282016110a3565b9061112e929167016345785d8a0000821015928261112061111a86848197612360565b92611db5565b809993989291509784611bb6565b916000549669ffffffffffffffffffff93611150858216868b60901c16610fe4565b9861118163ffffffff61116a818b16828560701c16611002565b9764ffffffffff8460481c16918916908d16611ca3565b471061129e5768ffffffffffffffffff61119c911685611052565b10611274577fe55f87571bef932b3114c119667e56e218d122a255db962d9fe32ad1b3dddb729661125594611248926111d585896112d5565b9861125a575b50611243906106e56111f4633fffffff87168b8b6126a8565b9b7fffffffff00000000000000000000ffffffffffffffffffffffffffffffffffff7bffffffffffffffffffff0000000000000000000000000000000000006000549260901b16911617600055565b612448565b916040519586958661105f565b0390a2565b60081b60201760281b646a636b70741791506112436111db565b60046040517f9fce9b98000000000000000000000000000000000000000000000000000000008152fd5b60046040517fe6c99569000000000000000000000000000000000000000000000000000000008152fd5b91908203918211610b9457565b906113008273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b546001811661141357826113376113d69473ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b826000526020526040600020546fffffffffffffffffffffffffffffffff811160071b67ffffffffffffffff82821c1160061b1763ffffffff82821c1160051b1781811c151560051b01938460051c941b176113c6836113b78473ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b90600052602052604060002090565b55600783146113d9575b50611052565b90565b61140c6113e583611028565b9173ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b55386113d0565b60046040517f905642ec000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115611476570690565b61143d565b909392938483116101b05784116101b0578101920390565b69ffffffffffffffffffff9182169082160391908211610b9457565b63ffffffff9182169082160391908211610b9457565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761153557604052565b6114c5565b67ffffffffffffffff811161153557601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b3d1561159f573d906115858261153a565b9161159360405193846114f4565b82523d6000602084013e565b606090565b156115ab57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5472616e73666572206661696c656421000000000000000000000000000000006044820152fd5b81810292918115918404141715610b9457565b9073ffffffffffffffffffffffffffffffffffffffff60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff883169260051b16921690600082815260036020526040812082825260205260408120546201ffff63ffffffff82871c16951b1916917e01ffff0001ffff0001ffff0001ffff0001ffff0001ffff0001ffff0001ffff831615806116db575b6116d1575b6040929382526003602052828220908252602052205590565b90915081906116b8565b507e020000000000000000000000000000000000000000000000000000000000008310156116b3565b1561170b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f6e6c792074686520706c61796572206f722074686520486f7573652063616e60448201527f20646f20746869732e00000000000000000000000000000000000000000000006064820152fd5b9073ffffffffffffffffffffffffffffffffffffffff821633148015611990575b6117b990611704565b6117c3818361161c565b90811561193d576201ffff8216156118e9576117ea8260038061384042041691601e1c1691565b9160026003611801846117fc87611036565b6112c8565b16106118875750505061067f69ffffffffffffffffffff63ffffffff61188594611877856118726106e561183761185f996119d8565b50505060009791979a8b9a8b9a8b9a1661067a8b5469ffffffffffffffffffff9060901c1690565b166106e0875463ffffffff9060701c1690565b612416565b509190505af1610785611574565b565b6040517f614ff28a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602481019190915260448101919091526003919091166064820152608490fd5b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401919091527fb6a11c8fd439c75283b878187bb665a6de83da1cd09c4d2784e1b05071d9022a929150819081015b0390a1565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401919091527f1631d7a0831826269077aebd7189b36b522e25369f150a3e6d215bda68da09a292915081908101611938565b506117b96119cf6119b660015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b331490506117b0565b6119f4916119e8611a0092612416565b94859492939193611db5565b94928297929196611bb6565b9493929190565b8115611476570490565b6000969295879592949286611a26858561146c565b9960648603611ace575050611a58916020611a46611a50938c109b611044565b9160081b176125ea565b945b88611609565b95611a61575050565b610378611a93602095611a87611a82611a7a8787611a07565b6103e8900690565b611044565b96879160081b176125ea565b9414611a9d575050565b611acb929450611ab891611ab091611a07565b6103e8900490565b60005460481c64ffffffffff1690611c5e565b91565b60248603611b2f5750506006808a0499069190611aeb838b611052565b1c600116151598611afb90611044565b9060081b60201790611b0c916125ea565b60081b602b1790611b1c90611044565b611b25916125ea565b93611a5890611a52565b60068603611b59575050611a58916020611a466001611b53948d1c1615159b611044565b94611a52565b9692916002869b929b14611b74575b505050611a5890611a52565b811c60011615159950919550611a589160089190911b60201790611ba65760281b64686561647317945b903880611b68565b60281b647461696c731794611b9e565b909182158015611c55575b611c1e5781800460011482151715610b94576064820466038d7ea4c6800094858210611c16575b858102958187041490151715610b94576113d6946117fc611c0c92611c11956112c8565b611609565b611a07565b859150611be8565b82604491604051917fb03159ea00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50808311611bc1565b90670de0b6b3a76400009080820291820403610b945760ff7304040404080808081010101010101010102020406014611c9d940660031b1c1690611609565b60071c90565b91906005811015611ce257905b673782dace9d90000091828102928184041490151715610b9457611cd391611609565b60031c8101809111610b945790565b50600590611cb0565b15611cf257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c79206f776e65722063616e20646f2074686973000000000000000000006044820152fd5b15611d5757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f44696365206d61736b206973206e6f742076616c6964000000000000000000006044820152fd5b610800811615611dda576107ff1690611dcd82611ed0565b916024916113d682612667565b610400811615611dfe57603f1690611df182611e93565b916006916113d682612622565b610200811615611e195760ff1690816064916113d682612588565b60019160029160031690838203611e4b5764ffffffffff6468656164735b166d436f696e466c69702000000000001790565b64ffffffffff647461696c73611e37565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018116159081611e8c575090565b9050151590565b906211111180926000935b611ea6575050565b6001808216611ebc575b1c9060041c9081611e9e565b93600f83168101809111610b945793611eb0565b906501234565432180926000935b611ee6575050565b6001808216611efc575b1c9060041c9081611ede565b93600f83168101809111610b945793611ef0565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b611f688460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b919015611fa457600a919060018403611f9e575086905b8302019102925b8015611f965760081c9290611f37565b509293509050565b90611f7f565b9491505060018411611fbe576001841b1792600192611f86565b6040517fb8e0bda8000000000000000000000000000000000000000000000000000000008152600481018590526000602482015260016044820152606490fd5b5092505050600290600190565b5060019350909150565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b61206d8460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b9190156120a157600a91906001840361209b575086905b8302019102925b8015611f965760081c929061203c565b90612084565b94915050600284108015612124575b6120e45760017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe85011b179260019261208b565b6040517fb8e0bda80000000000000000000000000000000000000000000000000000000081526004810185905260026024820152600c6044820152606490fd5b50600c84116120b0565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b6121868460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b9190156121ba57600a9190600184036121b4575086905b8302019102925b8015611f965760081c9290612155565b9061219d565b9491505060038410801561223d575b6121fd5760017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd85011b17926001926121a4565b6040517fb8e0bda8000000000000000000000000000000000000000000000000000000008152600481018590526003602482015260616044820152606490fd5b50606184116121c9565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b61229f8460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b9190156122d357600a9190600184036122cd575086905b8302019102925b8015611f965760081c929061226e565b906122b6565b94915050600184108015612356575b6123165760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85011b17926001926122bd565b6040517fb8e0bda8000000000000000000000000000000000000000000000000000000008152600481018590526001602482015260066044820152606490fd5b50600684116122e2565b9166038d7ea4c6800092838104938415159182612409575b826123fe575b5050156123a05760111b9060121b63c00000006138404204601e1b1617171790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f42657420616d6f756e74206e6f74207175616e7469666961626c6500000000006044820152fd5b06159050388061237e565b6202000086109250612378565b66038d7ea4c680006201ffff8216818102918115918304141715610b945790610fff62020000821615159160121c1692565b906fffffffffffffffffffffffffffffffff821160071b67ffffffffffffffff83821c1160061b1763ffffffff83821c1160051b1761ffff83821c1160041b1760ff83821c1160031b1782811c151560031b01918260031c906124c36124ad8361153a565b926124bb60405194856114f4565b80845261153a565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208201930136843793610100031b9052565b6020821161255e57359060031b610100031c907f8080808080808080808080808080808080808080808080808080808080808080821661253457565b60046040517f380b4665000000000000000000000000000000000000000000000000000000008152fd5b60046040517fb11b2ad8000000000000000000000000000000000000000000000000000000008152fd5b6a45746865726f6c6c203c3d9060648110156125ce575b600a8110156125b8575b600a90066030019060081b1790565b60089190911b6030600a808404060117906125a9565b6b45746865726f6c6c203c3d006030606483040117915061259f565b9081606482101561260f575b50600a8110156125b857600a90066030019060081b1790565b60081b60306064830401179150386125f6565b644469636520600180835b61263b575050905060081c90565b81841661264f575b92811c9281018361262d565b602c61265c8284956125ea565b60081b179250612643565b62324420906002815b61267c57505060081c90565b600191828116612691575b821c910181612670565b9261269e82602c926125ea565b60081b1792612687565b916040519173ffffffffffffffffffffffffffffffffffffffff6020840194468652306040860152166060840152608083015260a082015260a0815260c0810181811067ffffffffffffffff8211176115355760405251902090565b1561270b57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4549502d31393820707265636f6d70696c65206661696c6564210000000000006044820152fd5b9195949293906127788461153a565b61278560405191826114f4565b848152602081019036868501116101b05761282696600096879661280a94610120948389833789602085830101525190209b60405196838894602086019a60808c526020604088015260806060880152608087013784019262010001608085015260a084015260c083015260e0820152610100928382015203908101845201826114f4565b519060055afa61282161281b611574565b91612704565b61284b565b93565b91908260809103126101b0578151916020810151916060604083015192015190565b61285e9060208082518301019101612829565b9290917f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8492036128cd57463060601b17036128cd5773ffffffffffffffffffffffffffffffffffffffff821693633fffffff8360a01c169260be1c936128c68486886126a8565b036128cd57565b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fdfea264697066735822122019b8fafd51fabb6a7d8e2c93ba741bbc968f4bc3cc2c0b9d3f004da94b22866264736f6c63430008130033aa3c65b05cfa27b5c7ec477454e8f409396da57819cb3b10af22652d0cc9c7f3ba38bd6bd705e92ff46f22030e6045e72bb56a5ab62d11abdc4b508c7e2c21e6dbb12f1b78c4e328dc7b833a765d05ffa643cd780b81d0d62fc14a2a14cd4f8518b73d7dbf70c16cc9fdd64ccd897bd9002ffa5ac5fcb1b4252c0d20f7a77bdf

Deployed Bytecode

0x6080604052600436101561001257600080fd5b60003560e01c80635abb93ef1461015757806374a1be9b14610152578063820e320a1461014d57806383197ef01461014857806383595e9d14610143578063840e5bbd1461013e5780638afe7aea1461013957806392ad7734146101345780639b3b20d11461012f578063b539cd551461012a578063bff92bba14610125578063c107532914610120578063c60f70571461011b578063cd97323f14610116578063d1b7f0e814610111578063d5315c2a1461010c578063dc2e3ad814610107578063dc3e51d614610102578063df88126f146100fd5763e88a69a2146100f857600080fd5b610f5c565b610f11565b610ed1565b610dfb565b610db5565b610d5c565b610c89565b610b99565b610a79565b6109ba565b610973565b610947565b610902565b6104d6565b610495565b6103bf565b610276565b610231565b6101b5565b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517fdbb12f1b78c4e328dc7b833a765d05ffa643cd780b81d0d62fc14a2a14cd4f858152f35b600080fd5b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517fba38bd6bd705e92ff46f22030e6045e72bb56a5ab62d11abdc4b508c7e2c21e68152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101b057565b346101b05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05761027461026b61020e565b6024359061178f565b005b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05773ffffffffffffffffffffffffffffffffffffffff600154166102c9813314611ceb565b69ffffffffffffffffffff60005460901c166102e157ff5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f5468657265206172652070656e64696e672062657473000000000000000000006044820152fd5b9181601f840112156101b05782359167ffffffffffffffff83116101b057602083818601950101116101b057565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101b057600435916024359067ffffffffffffffff82116101b0576103bb9160040161033f565b9091565b6103da6103d56103ce3661036d565b91506124f8565b611f10565b506103e481611e5c565b1561046b5780151580610460575b15610402576102749034336110f7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f436f696e466c6970206d61736b206973206e6f742076616c69640000000000006044820152fd5b5060038111156103f2565b60046040517fcf3ec9a9000000000000000000000000000000000000000000000000000000008152fd5b6102746104ac6104a76103ce3661036d565b612015565b50801515806104ca575b6104bf90611d50565b6108001734336110f7565b506107ff8111156104b6565b346101b05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b0576004803567ffffffffffffffff81116101b057610525903690830161033f565b90607f82166108d9577fba38bd6bd705e92ff46f22030e6045e72bb56a5ab62d11abdc4b508c7e2c21e67faa3c65b05cfa27b5c7ec477454e8f409396da57819cb3b10af22652d0cc9c7f37f18b73d7dbf70c16cc9fdd64ccd897bd9002ffa5ac5fcb1b4252c0d20f7a77bdf7fdbb12f1b78c4e328dc7b833a765d05ffa643cd780b81d0d62fc14a2a14cd4f8560005b8681106105be57005b6105df6105d56105cd8361101a565b838a8a61147b565b9085858989612769565b6105ec828496959461161c565b801561087e576201ffff81161561081f57633fffffff829116036107c2576000610790969561077a61077261065661078b98859869ffffffffffffffffffff61067f889a6106cd6107296106e563ffffffff6106488e9f6119d8565b92849e94819e939293611a11565b99829c93979299919d16600061067a905469ffffffffffffffffffff9060901c1690565b611493565b7fffffffff00000000000000000000ffffffffffffffffffffffffffffffffffff7bffffffffffffffffffff0000000000000000000000000000000000006000549260901b16911617600055565b166106e08e5463ffffffff9060701c1690565b6114af565b7fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff71ffffffff00000000000000000000000000006000549260701b16911617600055565b8a146107955761076a61075c7f5e450238c651e103ae241f8b55cc5b30f9720edb1ef6fb0efbb207142a1a07d794612448565b876040519485948d8661105f565b0390a2611052565b801590611052565b905af1610785611574565b506115a4565b61101a565b6105b5565b61076a61075c7f325df7b841142d795406035b9236a2ad039223670c5b38c985e4d54a51191fe694612448565b848d61081b846040519384937f6fbed4b800000000000000000000000000000000000000000000000000000000855284016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff9095168552602085019190915261079094937fb6a11c8fd439c75283b878187bb665a6de83da1cd09c4d2784e1b05071d9022a93509150819081015b0390a161101a565b50506040805173ffffffffffffffffffffffffffffffffffffffff9095168552602085019190915261079094937f1631d7a0831826269077aebd7189b36b522e25369f150a3e6d215bda68da09a29350915081908101610876565b826040517f1a6356f9000000000000000000000000000000000000000000000000000000008152fd5b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602063ffffffff60005460701c16604051908152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057005b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602068ffffffffffffffffff60005416604051908152f35b346101b05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760043568ffffffffffffffffff81168091036101b05760207f3cd703d90ed49e5ecefd6ac22b0bcbcd33a50f94dad387fd8ebdfe5d897c8db991610a4673ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b807fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000006000541617600055604051908152a1005b346101b05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057610ab061020e565b60243590610ad773ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b60009169ffffffffffffffffffff835460901c168101808211610b94574710610b10578280808093610b0d955af1610785611574565b80f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f43616e6e6f742077697468647261772066756e6473202d2070656e64696e672060448201527f62657473206d69676874206e65656420746865206d6f6e65792e0000000000006064820152fd5b610fb5565b346101b05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057610bd061020e565b60243580151581036101b057610bff73ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b15610c3757610c2e9073ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b80546001179055005b610c619073ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b80547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe169055005b610ca7610ca0610c9b6103ce3661036d565b61212e565b9190611e5c565b15610d325780151580610d27575b15610cc957610274906102001734336110f7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45746865726f6c6c206d61736b206973206e6f742076616c69640000000000006044820152fd5b506063811115610cb5565b60046040517fea5478ca000000000000000000000000000000000000000000000000000000008152fd5b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517f18b73d7dbf70c16cc9fdd64ccd897bd9002ffa5ac5fcb1b4252c0d20f7a77bdf8152f35b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602064ffffffffff60005460481c16604051908152f35b346101b05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05764ffffffffff60043581811681036101b0577f1fecbaa6256b253892dd2f7b1fca105910462fd3694e7365619cddb8d4a6d71291602091610e8473ffffffffffffffffffffffffffffffffffffffff600154163314611ceb565b7fffffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffff6dffffffffff0000000000000000006000549260481b1691161790816000556040519160481c168152a1005b610274610ee8610ee36103ce3661036d565b612247565b5080151580610f06575b610efb90611d50565b6104001734336110f7565b50603f811115610ef2565b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b057602069ffffffffffffffffffff60005460901c16604051908152f35b346101b05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b05760206040517faa3c65b05cfa27b5c7ec477454e8f409396da57819cb3b10af22652d0cc9c7f38152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91909169ffffffffffffffffffff80809416911601918211610b9457565b91909163ffffffff80809416911601918211610b9457565b9060808201809211610b9457565b9060088201809211610b9457565b9060048201809211610b9457565b9060018201809211610b9457565b91908201809211610b9457565b9391929073ffffffffffffffffffffffffffffffffffffffff90969596168452602092838501526040840152606083015260a060808301528351938460a084015260005b8581106110e3575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84600060c0809697860101520116010190565b81810183015184820160c0015282016110a3565b9061112e929167016345785d8a0000821015928261112061111a86848197612360565b92611db5565b809993989291509784611bb6565b916000549669ffffffffffffffffffff93611150858216868b60901c16610fe4565b9861118163ffffffff61116a818b16828560701c16611002565b9764ffffffffff8460481c16918916908d16611ca3565b471061129e5768ffffffffffffffffff61119c911685611052565b10611274577fe55f87571bef932b3114c119667e56e218d122a255db962d9fe32ad1b3dddb729661125594611248926111d585896112d5565b9861125a575b50611243906106e56111f4633fffffff87168b8b6126a8565b9b7fffffffff00000000000000000000ffffffffffffffffffffffffffffffffffff7bffffffffffffffffffff0000000000000000000000000000000000006000549260901b16911617600055565b612448565b916040519586958661105f565b0390a2565b60081b60201760281b646a636b70741791506112436111db565b60046040517f9fce9b98000000000000000000000000000000000000000000000000000000008152fd5b60046040517fe6c99569000000000000000000000000000000000000000000000000000000008152fd5b91908203918211610b9457565b906113008273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b546001811661141357826113376113d69473ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b826000526020526040600020546fffffffffffffffffffffffffffffffff811160071b67ffffffffffffffff82821c1160061b1763ffffffff82821c1160051b1781811c151560051b01938460051c941b176113c6836113b78473ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b90600052602052604060002090565b55600783146113d9575b50611052565b90565b61140c6113e583611028565b9173ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b55386113d0565b60046040517f905642ec000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115611476570690565b61143d565b909392938483116101b05784116101b0578101920390565b69ffffffffffffffffffff9182169082160391908211610b9457565b63ffffffff9182169082160391908211610b9457565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761153557604052565b6114c5565b67ffffffffffffffff811161153557601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b3d1561159f573d906115858261153a565b9161159360405193846114f4565b82523d6000602084013e565b606090565b156115ab57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5472616e73666572206661696c656421000000000000000000000000000000006044820152fd5b81810292918115918404141715610b9457565b9073ffffffffffffffffffffffffffffffffffffffff60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff883169260051b16921690600082815260036020526040812082825260205260408120546201ffff63ffffffff82871c16951b1916917e01ffff0001ffff0001ffff0001ffff0001ffff0001ffff0001ffff0001ffff831615806116db575b6116d1575b6040929382526003602052828220908252602052205590565b90915081906116b8565b507e020000000000000000000000000000000000000000000000000000000000008310156116b3565b1561170b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f6e6c792074686520706c61796572206f722074686520486f7573652063616e60448201527f20646f20746869732e00000000000000000000000000000000000000000000006064820152fd5b9073ffffffffffffffffffffffffffffffffffffffff821633148015611990575b6117b990611704565b6117c3818361161c565b90811561193d576201ffff8216156118e9576117ea8260038061384042041691601e1c1691565b9160026003611801846117fc87611036565b6112c8565b16106118875750505061067f69ffffffffffffffffffff63ffffffff61188594611877856118726106e561183761185f996119d8565b50505060009791979a8b9a8b9a8b9a1661067a8b5469ffffffffffffffffffff9060901c1690565b166106e0875463ffffffff9060701c1690565b612416565b509190505af1610785611574565b565b6040517f614ff28a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602481019190915260448101919091526003919091166064820152608490fd5b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401919091527fb6a11c8fd439c75283b878187bb665a6de83da1cd09c4d2784e1b05071d9022a929150819081015b0390a1565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401919091527f1631d7a0831826269077aebd7189b36b522e25369f150a3e6d215bda68da09a292915081908101611938565b506117b96119cf6119b660015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b331490506117b0565b6119f4916119e8611a0092612416565b94859492939193611db5565b94928297929196611bb6565b9493929190565b8115611476570490565b6000969295879592949286611a26858561146c565b9960648603611ace575050611a58916020611a46611a50938c109b611044565b9160081b176125ea565b945b88611609565b95611a61575050565b610378611a93602095611a87611a82611a7a8787611a07565b6103e8900690565b611044565b96879160081b176125ea565b9414611a9d575050565b611acb929450611ab891611ab091611a07565b6103e8900490565b60005460481c64ffffffffff1690611c5e565b91565b60248603611b2f5750506006808a0499069190611aeb838b611052565b1c600116151598611afb90611044565b9060081b60201790611b0c916125ea565b60081b602b1790611b1c90611044565b611b25916125ea565b93611a5890611a52565b60068603611b59575050611a58916020611a466001611b53948d1c1615159b611044565b94611a52565b9692916002869b929b14611b74575b505050611a5890611a52565b811c60011615159950919550611a589160089190911b60201790611ba65760281b64686561647317945b903880611b68565b60281b647461696c731794611b9e565b909182158015611c55575b611c1e5781800460011482151715610b94576064820466038d7ea4c6800094858210611c16575b858102958187041490151715610b94576113d6946117fc611c0c92611c11956112c8565b611609565b611a07565b859150611be8565b82604491604051917fb03159ea00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50808311611bc1565b90670de0b6b3a76400009080820291820403610b945760ff7304040404080808081010101010101010102020406014611c9d940660031b1c1690611609565b60071c90565b91906005811015611ce257905b673782dace9d90000091828102928184041490151715610b9457611cd391611609565b60031c8101809111610b945790565b50600590611cb0565b15611cf257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c79206f776e65722063616e20646f2074686973000000000000000000006044820152fd5b15611d5757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f44696365206d61736b206973206e6f742076616c6964000000000000000000006044820152fd5b610800811615611dda576107ff1690611dcd82611ed0565b916024916113d682612667565b610400811615611dfe57603f1690611df182611e93565b916006916113d682612622565b610200811615611e195760ff1690816064916113d682612588565b60019160029160031690838203611e4b5764ffffffffff6468656164735b166d436f696e466c69702000000000001790565b64ffffffffff647461696c73611e37565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018116159081611e8c575090565b9050151590565b906211111180926000935b611ea6575050565b6001808216611ebc575b1c9060041c9081611e9e565b93600f83168101809111610b945793611eb0565b906501234565432180926000935b611ee6575050565b6001808216611efc575b1c9060041c9081611ede565b93600f83168101809111610b945793611ef0565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b611f688460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b919015611fa457600a919060018403611f9e575086905b8302019102925b8015611f965760081c9290611f37565b509293509050565b90611f7f565b9491505060018411611fbe576001841b1792600192611f86565b6040517fb8e0bda8000000000000000000000000000000000000000000000000000000008152600481018590526000602482015260016044820152606490fd5b5092505050600290600190565b5060019350909150565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b61206d8460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b9190156120a157600a91906001840361209b575086905b8302019102925b8015611f965760081c929061203c565b90612084565b94915050600284108015612124575b6120e45760017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe85011b179260019261208b565b6040517fb8e0bda80000000000000000000000000000000000000000000000000000000081526004810185905260026024820152600c6044820152606490fd5b50600c84116120b0565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b6121868460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b9190156121ba57600a9190600184036121b4575086905b8302019102925b8015611f965760081c9290612155565b9061219d565b9491505060038410801561223d575b6121fd5760017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd85011b17926001926121a4565b6040517fb8e0bda8000000000000000000000000000000000000000000000000000000008152600481018590526003602482015260616044820152606490fd5b50606184116121c9565b9060009060009081646865616473851461200b57647461696c738514611ffe576001949294905b61229f8460ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0911601600a811091565b9190156122d357600a9190600184036122cd575086905b8302019102925b8015611f965760081c929061226e565b906122b6565b94915050600184108015612356575b6123165760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85011b17926001926122bd565b6040517fb8e0bda8000000000000000000000000000000000000000000000000000000008152600481018590526001602482015260066044820152606490fd5b50600684116122e2565b9166038d7ea4c6800092838104938415159182612409575b826123fe575b5050156123a05760111b9060121b63c00000006138404204601e1b1617171790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f42657420616d6f756e74206e6f74207175616e7469666961626c6500000000006044820152fd5b06159050388061237e565b6202000086109250612378565b66038d7ea4c680006201ffff8216818102918115918304141715610b945790610fff62020000821615159160121c1692565b906fffffffffffffffffffffffffffffffff821160071b67ffffffffffffffff83821c1160061b1763ffffffff83821c1160051b1761ffff83821c1160041b1760ff83821c1160031b1782811c151560031b01918260031c906124c36124ad8361153a565b926124bb60405194856114f4565b80845261153a565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208201930136843793610100031b9052565b6020821161255e57359060031b610100031c907f8080808080808080808080808080808080808080808080808080808080808080821661253457565b60046040517f380b4665000000000000000000000000000000000000000000000000000000008152fd5b60046040517fb11b2ad8000000000000000000000000000000000000000000000000000000008152fd5b6a45746865726f6c6c203c3d9060648110156125ce575b600a8110156125b8575b600a90066030019060081b1790565b60089190911b6030600a808404060117906125a9565b6b45746865726f6c6c203c3d006030606483040117915061259f565b9081606482101561260f575b50600a8110156125b857600a90066030019060081b1790565b60081b60306064830401179150386125f6565b644469636520600180835b61263b575050905060081c90565b81841661264f575b92811c9281018361262d565b602c61265c8284956125ea565b60081b179250612643565b62324420906002815b61267c57505060081c90565b600191828116612691575b821c910181612670565b9261269e82602c926125ea565b60081b1792612687565b916040519173ffffffffffffffffffffffffffffffffffffffff6020840194468652306040860152166060840152608083015260a082015260a0815260c0810181811067ffffffffffffffff8211176115355760405251902090565b1561270b57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4549502d31393820707265636f6d70696c65206661696c6564210000000000006044820152fd5b9195949293906127788461153a565b61278560405191826114f4565b848152602081019036868501116101b05761282696600096879661280a94610120948389833789602085830101525190209b60405196838894602086019a60808c526020604088015260806060880152608087013784019262010001608085015260a084015260c083015260e0820152610100928382015203908101845201826114f4565b519060055afa61282161281b611574565b91612704565b61284b565b93565b91908260809103126101b0578151916020810151916060604083015192015190565b61285e9060208082518301019101612829565b9290917f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8492036128cd57463060601b17036128cd5773ffffffffffffffffffffffffffffffffffffffff821693633fffffff8360a01c169260be1c936128c68486886126a8565b036128cd57565b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fdfea264697066735822122019b8fafd51fabb6a7d8e2c93ba741bbc968f4bc3cc2c0b9d3f004da94b22866264736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

aa3c65b05cfa27b5c7ec477454e8f409396da57819cb3b10af22652d0cc9c7f3ba38bd6bd705e92ff46f22030e6045e72bb56a5ab62d11abdc4b508c7e2c21e6dbb12f1b78c4e328dc7b833a765d05ffa643cd780b81d0d62fc14a2a14cd4f8518b73d7dbf70c16cc9fdd64ccd897bd9002ffa5ac5fcb1b4252c0d20f7a77bdf

-----Decoded View---------------
Arg [0] : m0 (uint256): 76999896914165267028676313459183705025116293357335298508462381319570051418099
Arg [1] : m1 (uint256): 84230440609533548868587289213890279275254969824971719007945208661276642517478
Arg [2] : m2 (uint256): 99369570892916442004621450796573025208379874656710791136194221530320275853189
Arg [3] : m3 (uint256): 11179265775534357929716295400370188251439768473451997029099043269888656767967

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : aa3c65b05cfa27b5c7ec477454e8f409396da57819cb3b10af22652d0cc9c7f3
Arg [1] : ba38bd6bd705e92ff46f22030e6045e72bb56a5ab62d11abdc4b508c7e2c21e6
Arg [2] : dbb12f1b78c4e328dc7b833a765d05ffa643cd780b81d0d62fc14a2a14cd4f85
Arg [3] : 18b73d7dbf70c16cc9fdd64ccd897bd9002ffa5ac5fcb1b4252c0d20f7a77bdf


Deployed Bytecode Sourcemap

6006:39445:9:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;8672:30;6006:39445;;;;;;;;;;;;;;;;;;;;;8638:30;6006:39445;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;;;13079:5;6006:39445;;13057:54;13065:10;;:19;13057:54;:::i;:::-;6006:39445;;6600:9;;;6006:39445;;;45324:5;6006:39445;;;;;;;;;;;;;;;;1833:41:10;6006:39445:9;1833:41:10;;;6006:39445:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;14410:50;14431:22;6006:39445;;;:::i;:::-;14431:22;;;:::i;:::-;14410:50;:::i;:::-;14523:21;;;;:::i;:::-;14522:22;14518:72;;4886:8:10;;;:52;;;6006:39445:9;2605:12:10;;;14628:35:9;14617:9;;14605:10;14628:35;:::i;2605:12:10:-;;6006:39445:9;;2605:12:10;;;;6006:39445:9;2605:12:10;;;;;;;1833:41;2605:12;1833:41;;;2605:12;;4886:52;4898:40;2838:1:13;4898:40:10;;;4886:52;;14518:72:9;6006:39445;;;14561:22;;;;6006:39445;16205:34;16116:51;16137:22;6006:39445;;;:::i;16137:22::-;16116:51;:::i;:::-;5770:8:10;;;;:51;;;6006:39445:9;5762:86:10;;;:::i;:::-;3496:7;5877:33;16194:9:9;16182:10;16205:34;:::i;5770:51:10:-;-1:-1:-1;2925:13:10;5782:39;;;5770:51;;6006:39445:9;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1548:3:15;;;;22768:180:9;;23708:8;23698;23728;23718;-1:-1:-1;23050:19:9;;;;;;6006:39445;23071:34;23686:58;23204:43;23215:31;;;:::i;:::-;23204:43;;;;:::i;:::-;23686:58;;;;;;:::i;:::-;23909:52;;;;;;;:::i;:::-;4985:27:13;;24009:176:9;;2186:1:8;7169:42:13;;7168:49;24346:167:9;;2041:11:13;5766:43;;;6700:49;24816:366:9;;-1:-1:-1;23071:34:9;25459:28;;26664:45;26506:24;25644:102;26722:44;25459:28;;;6006:39445;25835:33;25459:28;;25835:33;25939:51;;6006:39445;25459:28;;;;:::i;:::-;25644:102;;;;;;;;;;:::i;:::-;6006:39445;;;;;;;;;;-1:-1:-1;6600:9:9;;;6006:39445;6600:9;;;6006:39445;6600:9;;;25835:33;:::i;:::-;1833:41:10;;22815:1:9;1833:41:10;;;;;;;;22815:1:9;1833:41:10;;25835:33:9;6006:39445;6600:9;;;6006:39445;6600:9;;;6006:39445;6600:9;;;25939:51;:::i;:::-;1833:41:10;;22815:1:9;1833:41:10;;;;;;;;22815:1:9;1833:41:10;;25939:51:9;26025:418;;;;26124:94;26188:29;26124:94;26188:29;;:::i;:::-;6006:39445;;;26124:94;;;;;;:::i;:::-;;;;26506:24;:::i;:::-;26691:17;;26664:45;;:::i;:::-;26645:69;;;;;:::i;:::-;;26722:44;:::i;:::-;23071:34;:::i;:::-;23034:14;;26025:418;26339:95;26404:29;26339:95;26404:29;;:::i;24816:366::-;6006:39445;;25128:45;6006:39445;;;25128:45;;;;;;;;1548:3:15;;;;;;6006:39445:9;1548:3:15;;;6006:39445:9;;1833:41:10;;1548:3:15;6006:39445:9;1548:3:15;25128:45:9;;;;24346:167;-1:-1:-1;;6006:39445:9;;;;;;;1833:41:10;;1548:3:15;;;6006:39445:9;;;;23071:34;;6006:39445;24456:30;;-1:-1:-1;6006:39445:9;-1:-1:-1;6006:39445:9;;1548:3:15;;24456:30:9;;;;23071:34;:::i;24009:176::-;-1:-1:-1;;6006:39445:9;;;;;;;1833:41:10;;1548:3:15;;;6006:39445:9;;;;23071:34;;6006:39445;24126:32;;-1:-1:-1;6006:39445:9;-1:-1:-1;6006:39445:9;;1548:3:15;;24126:32:9;1548:3:15;22768:180:9;6006:39445;;;22928:13;;;;6006:39445;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;42908:27;6006:39445;13057:54;6006:39445;13079:5;6006:39445;;13065:10;:19;13057:54;:::i;:::-;6006:39445;;-1:-1:-1;6006:39445:9;;;-1:-1:-1;6006:39445:9;;;;;;42908:27;6006:39445;;;;;;;;;;;;;;:::i;:::-;;;;13057:54;6006:39445;13079:5;6006:39445;;13065:10;:19;13057:54;:::i;:::-;-1:-1:-1;6600:9:9;6006:39445;6600:9;;;;6006:39445;6600:9;;;;;;;44064:21;-1:-1:-1;6006:39445:9;;44210:26;;;;;44242:44;44210:26;;;;;:::i;44242:44::-;6006:39445;;;;;;;;;;;;;;;;;;1833:41:10;6006:39445:9;1833:41:10;;;6006:39445:9;;;;;;;6600:9;;:::i;6006:39445::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;13057:54;6006:39445;13079:5;6006:39445;;13065:10;:19;13057:54;:::i;:::-;8666:264:8;;;8729:28;;6006:39445:9;;;;44936:4;6006:39445;;;;;;;8729:28:8;6006:39445:9;;13079:5;8729:63:8;1760:1;;6006:39445:9;8666:264:8;8859:28;;6006:39445:9;;;;44936:4;6006:39445;;;;;;;8859:28:8;6006:39445:9;;8891:32:8;8859:64;1760:1;;6006:39445:9;;17022:21;16908:51;16929:22;6006:39445;;;:::i;16929:22::-;16908:51;:::i;:::-;17022:21;;;:::i;:::-;17021:22;17017:72;;6192:10:10;;;:54;;;6006:39445:9;3214:2:10;;;17127:37:9;6306:35:10;3644:6;6306:35;17116:9:9;17104:10;17127:37;:::i;3214:2:10:-;;6006:39445:9;;3214:2:10;;;;6006:39445:9;3214:2:10;;;;;;;1833:41;3214:2;1833:41;;;3214:2;;6192:54;6206:40;3214:2;6206:40;;;6192:54;;17017:72:9;6006:39445;;;17060:22;;;;6006:39445;;;;;;;;;;;;;;8706:30;6006:39445;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;43396:43;6006:39445;;;13057:54;6006:39445;13079:5;6006:39445;;13065:10;:19;13057:54;:::i;:::-;6006:39445;;-1:-1:-1;6006:39445:9;;;;;;;;;;-1:-1:-1;6006:39445:9;;;6600:9;6006:39445;6600:9;6006:39445;;;43396:43;6006:39445;;15411:31;15323:50;15344:22;6006:39445;;;:::i;15344:22::-;15323:50;:::i;:::-;5357:8:10;;;;:47;;;6006:39445:9;5349:82:10;;;:::i;:::-;3349:7;5460:29;15400:9:9;15388:10;15411:31;:::i;5357:47:10:-;-1:-1:-1;2763:12:10;5369:35;;;5357:47;;6006:39445:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8604:30;6006:39445;;;6600:9;;;;;;;;;;;;;;6006:39445;;;;;;;6600:9;;;;;;:::o;:::-;;;;6006:39445;;;;;;;6600:9;;;;;;:::o;:::-;;1548:3:15;6600:9:9;;;;;;;:::o;:::-;;2186:1:8;6600:9:9;;;;;;;:::o;:::-;;2733:1:13;6600:9:9;;;;;;;:::o;:::-;;35866:1;6600:9;;;;;;;:::o;:::-;;;;;;;;;;:::o;1833:41:10:-;;;;;6006:39445:9;1833:41:10;;;;6006:39445:9;1833:41:10;;;;;;;6006:39445:9;1833:41:10;;;6006:39445:9;1833:41:10;;;6006:39445:9;1833:41:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;18853:2389:9;;19420:65;18853:2389;;6600:9;19004:25;;;19108:53;;19312:22;19108:53;;;;;;:::i;:::-;19312:22;;:::i;:::-;19248:86;;;;;;;19420:65;;;:::i;:::-;6600:9;19631:12;6600:9;6006:39445;;;19631:32;6006:39445;;;6600:9;;;;6006:39445;19631:32;:::i;:::-;6006:39445;19968:86;6006:39445;19782:50;6006:39445;;;6600:9;;;;6006:39445;19782:50;:::i;:::-;6600:9;6006:39445;6600:9;;;6006:39445;;;;;;;19968:86;:::i;:::-;20057:21;-1:-1:-1;19964:233:9;;6006:39445;20317:18;6006:39445;;20317:18;;:::i;:::-;-1:-1:-1;20301:131:9;;21142:95;20522:50;21142:95;20522:50;21212:24;20522:50;;;;;:::i;:::-;20637:121;;;18853:2389;5766:43:13;21049:36:9;5766:43:13;21013:30:9;20886:70;2041:11:13;5766:43;;20886:70:9;;;:::i;:::-;21013:30;1833:41:10;;22815:1:9;1833:41:10;;;;;;;;22815:1:9;1833:41:10;;21049:36:9;21212:24;:::i;:::-;6006:39445;;;21142:95;;;;;:::i;:::-;;;;18853:2389::o;20637:121::-;2186:1:8;1760;6600:9:9;8010:75:14;1610:26:13;1760:1:8;6006:39445:9;8608:81:14;;-1:-1:-1;21049:36:9;20637:121;;20301:131;20406:19;6006:39445;;20406:19;;;;19964:233;20163:27;6006:39445;;20163:27;;;;2186:1:8;;;;;;;;;;:::o;4046:1706::-;;4441:28;;6006:39445:9;;;;44936:4;6006:39445;;;;;;;4441:28:8;6006:39445:9;1476:1:8;4608:48;;4604:99;;4756:23;;5715:32;4756:23;6006:39445:9;;;;4756:15:8;6006:39445:9;;;;;;;4756:23:8;1476:1;-1:-1:-1;1476:1:8;;;;-1:-1:-1;1476:1:8;6006:39445:9;302:10:11;1160:15;;1180:1;1760::8;395:10:11;6006:39445:9;;;1299:26:11;1330:1;1760::8;1282:49:11;488:10;6006:39445:9;;;1449:26:11;1480:1;1760::8;1432:49:11;6006:39445:9;;;1630:22:11;;1480:1;1760::8;488:10:11;6006:39445:9;;1480:1:11;6006:39445:9;1760:1:8;;5210:47;5302:39;:23;;;6006:39445:9;;;;4756:15:8;6006:39445:9;;;;;;;5302:23:8;1476:1;;;;;;;;;;5302:39;1760:1;1180::11;5430:45:8;;5426:226;;4046:1706;5715:32;;:::i;:::-;4046:1706;:::o;5426:226::-;5577:28;5608:37;;;:::i;:::-;5577:28;6006:39445:9;;;;44936:4;6006:39445;;;;;;;5577:28:8;1760:1;5426:226;;;4604:99;4678:18;6006:39445:9;;4678:18:8;;;;1548:3:15;;;;;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;6006:39445:9;;;;;;;1548:3:15;;;;;;;:::o;:::-;6006:39445:9;;;;;;;1548:3:15;;;;;;;:::o;:::-;;;;;;;;;;;;1833:41:10;;1548:3:15;1833:41:10;;1548:3:15;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;1833:41:10;;;;1548:3:15;;;:::o;:::-;;;;;;;;;;:::i;:::-;6006:39445:9;1548:3:15;6006:39445:9;;1548:3:15;;;:::i;:::-;;;;-1:-1:-1;1548:3:15;;;;:::o;:::-;;;:::o;:::-;;;;:::o;:::-;;6006:39445:9;;1548:3:15;;;;;;;;;;;;1833:41:10;1548:3:15;1833:41:10;;;1548:3:15;;1610:26:13;;;;;;;;;;;;;;;;:::o;6505:1746:8:-;;6006:39445:9;1760:1:8;2050:23;6744:36;;1760:1;;;;6006:39445:9;;-1:-1:-1;;6006:39445:9;;;7049:15:8;6006:39445:9;;;;;1476:1:8;;;6006:39445:9;1476:1:8;6006:39445:9;1476:1:8;;6006:39445:9;2186:1:8;;6006:39445:9;;;7198:43:8;1760:1;;7475:50;7642:22;7767:29;2467:539;7767:29;;7766:36;7765:71;;;6505:1746;7761:157;;6505:1746;6006:39445:9;;;;;7049:15:8;6006:39445:9;;;;;1476:1:8;;;6006:39445:9;1476:1:8;;1760;6505:1746;:::o;7761:157::-;7903:8;;-1:-1:-1;7903:8:8;;7761:157;;7765:71;7808:27;1760:1;7808:27;;;7765:71;;6006:39445:9;;;;:::o;:::-;;;;;;;;;;;;;;;;1833:41:10;6006:39445:9;1833:41:10;;;6006:39445:9;;;;;;;28302:2060;;6006:39445;;;28434:10;:20;28433:47;;;;28302:2060;28424:103;;;:::i;:::-;28603:52;;;;:::i;:::-;4985:27:13;;;28699:166:9;;2186:1:8;7169:42:13;;7168:49;29020:157:9;;29381:32;;2838:1:13;8354:15;1471:8;8354:15;1525:41:10;6196:44:13;6006:39445:9;2586:2:13;6006:39445:9;6290:71:13;6044:322;;29381:32:9;29494:45;28629:4;2838:1:13;29494:56:9;:45;;;;:::i;:::-;:56;:::i;:::-;29493:96;29647:46;29643:286;;30037:35;;;30078:33;6006:39445;;30313:44;30037:35;30220:25;30037:35;30117:51;;30037:35;30078:33;30037:35;;:::i;:::-;-1:-1:-1;;;;;;;6006:39445:9;;;;;;;;6600:9;;;6006:39445;6600:9;;;6006:39445;6600:9;;30078:33;6006:39445;6600:9;;;6006:39445;6600:9;;;6006:39445;6600:9;;30117:51;30220:25;:::i;:::-;30277:30;;;;;;;;:::i;30313:44::-;28302:2060::o;29643:286::-;6006:39445;;29820:102;;;6006:39445;;;2733:1:13;29820:102:9;;1833:41:10;7913:1:9;;;6006:39445;;;;7913:1;;;6006:39445;;;;2838:1:13;29871:50:9;;;;7913:1;;;6006:39445;7913:1;;25128:45;29020:157;6006:39445;;;;;;;1833:41:10;;1548:3:15;;;6006:39445:9;;;;29126:30;;6006:39445;-1:-1:-1;6006:39445:9;;1548:3:15;;29126:30:9;;;;29164:7::o;28699:166::-;6006:39445;;;;;;;1833:41:10;;1548:3:15;;;6006:39445:9;;;;28812:32;;6006:39445;-1:-1:-1;6006:39445:9;;1548:3:15;;28812:32:9;1548:3:15;28433:47:9;6006:39445;28424:103;28460:19;6006:39445;28474:5;6006:39445;;;6600:9;;6006:39445;;;6600:9;;28460:19;28434:10;28460:19;28433:47;;;;31325:950;31883:22;31325:950;31668:18;31995:59;31325:950;31668:18;:::i;:::-;31883:22;;;;;;;;:::i;:::-;31995:59;;;;;;;;:::i;:::-;32108:27;32188:28;32222:14;32242:28;31325:950;:::o;1525:41:10:-;;;;;;;:::o;33668:4114:9:-;6006:39445;;33668:4114;;6006:39445;;33668:4114;;;6006:39445;33964:21;33668:4114;;33964:21;:::i;:::-;34040:55;4246:3:10;34040:55:9;;4246:3:10;;34289:18:9;;36821:26;34289:18;6600:9;34464:15;34413:67;34289:18;;;34464:15;;:::i;:::-;1760:1:8;2186;1760;8010:75:14;34413:67:9;:::i;:::-;34036:2684;;36821:26;;:::i;:::-;36902:876;;;33668:4114;;:::o;36902:876::-;6946:3;37257:73;6600:9;37108:21;37107:44;:40;37108:21;;;;:::i;:::-;6834:4;1548:3:15;;;;37107:40:9;:44;:::i;:::-;1760:1:8;;;2186;1760;8010:75:14;37257:73:9;:::i;:::-;37403:41;;37399:373;;33668:4114;;:::o;37399:373::-;37695:68;37550:21;;;:38;:21;;;;:::i;:::-;6834:4;1525:41:10;;;;37550:38:9;6006:39445;6600:9;;;6006:39445;;37695:68;;:::i;:::-;37399:373;33668:4114::o;34036:2684::-;4137:2:10;34497:55:9;;4137:2:10;;-1:-1:-1;;4030:1:10;1525:41;;;;1548:3:15;;1525:41:10;34990:22:9;1548:3:15;1525:41:10;34990:22:9;:::i;:::-;6006:39445;35116:1;35093:24;:29;;35323:13;;;;:::i;:::-;1760:1:8;2186;1760;6600:9:9;8010:75:14;35272:65:9;;;;:::i;:::-;2186:1:8;1760;6600:9:9;8010:75:14;35544:14:9;;;;:::i;:::-;35487:72;;;:::i;:::-;34493:2227;36821:26;34493:2227;34036:2684;;34493:2227;4030:1:10;35576:51:9;;4030:1:10;;6006:39445:9;;36821:26;6006:39445;6600:9;36067:15;35866:1;36016:67;6006:39445;;;35842:25;:30;;36067:15;;:::i;36016:67::-;35572:1148;34036:2684;;35572:1148;36100:56;;;7913:1;36100:56;;;;;36096:624;;35572:1148;;;;36821:26;35572:1148;34036:2684;;36096:624;6006:39445;;36297:1;36273:25;:30;;;-1:-1:-1;6006:39445:9;;-1:-1:-1;36821:26:9;;2186:1:8;1760;;;;6600:9:9;8010:75:14;;36514:16:9;;1610:26:13;1760:1:8;6006:39445:9;8608:81:14;;36510:204:9;36096:624;;;;;36510:204;1610:26:13;1760:1:8;6006:39445:9;8608:81:14;;36510:204:9;;39166:598;;;39313:14;;:41;;;;39166:598;39309:117;;1610:26:13;;;1476:1:8;1610:26:13;;;;;;;39506:3:9;1525:41:10;;6476:11:9;39520:37;;;;39516:95;;39166:598;1610:26:13;;;;;;;;;;;;;;39700:59:9;39701:18;;:31;:18;39700:47;39701:18;;:::i;:31::-;39700:47;:::i;:::-;:59;:::i;39516:95::-;39567:37;;-1:-1:-1;39516:95:9;;39309:117;6006:39445;;;;;39371:48;;;;;;;6006:39445;;;;;39371:48;39313:41;39331:23;;;;39313:41;;40745:663;;1610:26:13;;;;;;;;;;;6600:9:9;7499:66;7673:2;41316:37;1548:3:15;;41291:1:9;1760::8;6006:39445:9;41248:53;41316:37;;:::i;:::-;1525:41:10;;40745:663:9;:::o;42172:324::-;;;1760:1:8;3608:5:11;;1760:1:8;;;3608:13:11;;1610:26:13;;;;;;;;;;;;;;;;42356:109:9;;;:::i;:::-;1525:41:10;;6600:9:9;;;;;;;42172:324;:::o;3608:13:11:-;;1760:1:8;3608:13:11;;;6006:39445:9;;;;:::o;:::-;;;;;;;;;;;;;;;;1833:41:10;6006:39445:9;1833:41:10;;;6006:39445:9;;2763:12:10;;;;:::o;:::-;;6006:39445:9;;2763:12:10;;;;;;;;;;;;1833:41;2763:12;1833:41;;;2763:12;;6970:3123;3496:7;7276:40;;7275:47;3496:7;;2925:13;7391:45;7817:53;;;;:::i;:::-;7923:42;4137:2;8173:37;;;;:::i;7271:2818::-;3349:7;8272:36;;8271:43;3349:7;;2763:12;8380:41;8529:49;;;;:::i;:::-;8630:38;4030:1;8884:34;;;;:::i;8267:1822::-;3644:6;8984:40;;8983:47;3644:6;;3087:12;9100:45;9235:16;;4246:3;9488:31;;;;:::i;8979:1110::-;9792:1;;7913::9;;2838::13;9662:46:10;;10048:9;;;9792:1;;6006:39445:9;1175:36:14;10048:33:10;6006:39445:9;1760:1:8;8608:81:14;8979:1110:10;28302:2060:9:o;10048:33:10:-;6006:39445:9;1175:36:14;10048:33:10;;3200:151:11;6006:39445:9;;;3295:21:11;;3294:28;3293:47;;;;3286:54;3200:151;:::o;3293:47::-;3328:11;;;;3200:151;:::o;4831:534::-;;4599:8:10;4928:10:11;;4937:1;4999:362;5006:18;;;4831:534;;:::o;4999:362::-;5086:1;5079:8;;;5074:132;;4999:362;6006:39445:9;;5353:1:11;6006:39445:9;4999:362:11;;;;5074:132;5178:19;5194:3;5178:19;;6600:9:9;;;;;;;5074:132:11;;;4831:534;;4417:13:10;4928:10:11;;4937:1;4999:362;5006:18;;;4831:534;;:::o;4999:362::-;5086:1;5079:8;;;5074:132;;4999:362;6006:39445:9;;5353:1:11;6006:39445:9;4999:362:11;;;;5074:132;5178:19;5194:3;5178:19;;6600:9:9;;;;;;;5074:132:11;;;1621:2134:12;;14455:1:9;1709:9:12;14455:1:9;1720:15:12;;6006:39445:9;9046:52:14;;1785:74:12;;6006:39445:9;9046:52:14;;1907:74:12;;14458:1:9;2112:24:12;;;2332:1413;;2488:24;;3087:12:10;6006:39445:9;6098:547:14;6360:50;6006:39445:9;6632:2:14;6624:10;;6098:547;;2488:24:12;2565:921;;;;;2930:2;;2688:37;14458:1:9;2688:20:12;;14458:1:9;;2688:37:12;;;;6006:39445:9;;488:10:11;6006:39445:9;;2565:921:12;;4985:27:13;;3528:104:12;;2186:1:8;6006:39445:9;2332:1413:12;;;;3528:104;3616:5;;;;;;1621:2134::o;2688:37::-;;;;2565:921;3085:16;;-1:-1:-1;;14458:1:9;3085:16:12;;3061:186;;14458:1:9;1760::8;;3323:31:12;3456:19;14458:1:9;2565:921:12;;;3061:186;6006:39445:9;;3196:38:12;;;;;;6006:39445:9;;;-1:-1:-1;6006:39445:9;;;;14458:1;6006:39445;;;;;;25128:45;1907:74:12;1961:13;;;;;1969:1;1961:13;14458:1:9;1961:13:12;:::o;1785:74::-;-1:-1:-1;14458:1:9;;-1:-1:-1;1839:13:12;;-1:-1:-1;1839:13:12:o;1621:2134::-;;6006:39445:9;1709:9:12;6006:39445:9;1720:15:12;;6006:39445:9;9046:52:14;;1785:74:12;;6006:39445:9;9046:52:14;;1907:74:12;;2135:1;2112:24;;;2332:1413;;2488:24;;3087:12:10;6006:39445:9;6098:547:14;6360:50;6006:39445:9;6632:2:14;6624:10;;6098:547;;2488:24:12;2565:921;;;;;2930:2;;2688:37;2135:1;2688:20;;2135:1;;2688:37;;;;6006:39445:9;;488:10:11;6006:39445:9;;2565:921:12;;4985:27:13;;3528:104:12;;2186:1:8;6006:39445:9;2332:1413:12;;;;2688:37;;;;2565:921;3065:16;;-1:-1:-1;;16161:1:9;3065:16:12;;:36;;;;2565:921;3061:186;;2135:1;6006:39445:9;;;1760:1:8;3323:31:12;3456:19;2135:1;2565:921;;;3061:186;6006:39445:9;;3196:38:12;;;;;;6006:39445:9;;;16161:1;6006:39445;;;;16164:2;6006:39445;;;;;;25128:45;3065:36:12;3085:16;16164:2:9;3085:16:12;;3065:36;;1621:2134;;6006:39445:9;1709:9:12;6006:39445:9;1720:15:12;;6006:39445:9;9046:52:14;;1785:74:12;;6006:39445:9;9046:52:14;;1907:74:12;;2135:1;2112:24;;;2332:1413;;2488:24;;3087:12:10;6006:39445:9;6098:547:14;6360:50;6006:39445:9;6632:2:14;6624:10;;6098:547;;2488:24:12;2565:921;;;;;2930:2;;2688:37;2135:1;2688:20;;2135:1;;2688:37;;;;6006:39445:9;;488:10:11;6006:39445:9;;2565:921:12;;4985:27:13;;3528:104:12;;2186:1:8;6006:39445:9;2332:1413:12;;;;2688:37;;;;2565:921;3065:16;;-1:-1:-1;;16953:1:9;3065:16:12;;:36;;;;2565:921;3061:186;;2135:1;6006:39445:9;;;1760:1:8;3323:31:12;3456:19;2135:1;2565:921;;;3061:186;6006:39445:9;;3196:38:12;;;;;;6006:39445:9;;;16953:1;6006:39445;;;;16956:2;6006:39445;;;;;;25128:45;3065:36:12;3085:16;16956:2:9;3085:16:12;;3065:36;;1621:2134;;6006:39445:9;1709:9:12;6006:39445:9;1720:15:12;;6006:39445:9;9046:52:14;;1785:74:12;;6006:39445:9;9046:52:14;;1907:74:12;;15368:1:9;2112:24:12;;;2332:1413;;2488:24;;3087:12:10;6006:39445:9;6098:547:14;6360:50;6006:39445:9;6632:2:14;6624:10;;6098:547;;2488:24:12;2565:921;;;;;2930:2;;2688:37;15368:1:9;2688:20:12;;15368:1:9;;2688:37:12;;;;6006:39445:9;;488:10:11;6006:39445:9;;2565:921:12;;4985:27:13;;3528:104:12;;2186:1:8;6006:39445:9;2332:1413:12;;;;2688:37;;;;2565:921;3065:16;;-1:-1:-1;;15368:1:9;3065:16:12;;:36;;;;2565:921;3061:186;;15368:1:9;6006:39445;;;1760:1:8;3323:31:12;3456:19;15368:1:9;2565:921:12;;;3061:186;6006:39445:9;;3196:38:12;;;;;;6006:39445:9;;;15368:1;6006:39445;;;;15371:1;6006:39445;;;;;;25128:45;3065:36:12;3085:16;15371:1:9;3085:16:12;;3065:36;;3602:1106:13;;6476:11:9;1525:41:10;;;;3951:16:13;;;;:56;;;;3602:1106;3951:78;;;3602:1106;2137:11;;;;;2247:2;1760:1:8;;2487:2:13;1760:1:8;;1471:8:13;8354:15;1525:41:10;2586:2:13;1760:1:8;;4452:111:13;:176;:212;3602:1106;:::o;2137:11::-;;6006:39445:9;;2137:11:13;;;;;;;;;;;;1833:41:10;2137:11:13;1833:41:10;;;2137:11:13;;3951:78;1548:3:15;4011:18:13;;-1:-1:-1;3951:78:13;;;;:56;1928:7;3971:36;;;-1:-1:-1;3951:56:13;;7630:471;6476:11:9;2186:1:8;7924:24:13;;1610:26;;;;;;;;;;;;;;7981:19;2186:1:8;1760;7981:19:13;;7980:26;;6006:39445:9;2487:2:13;6006:39445:9;8042:53:13;7630:471;:::o;2933:1004:14:-;;302:10:11;2232:15;;2252:1;1760::8;395:10:11;6006:39445:9;;;2276:26:11;2307:1;1760::8;2259:49:11;488:10;6006:39445:9;;;2331:26:11;2362:1;1760::8;2314:49:11;581:10;6006:39445:9;;;2386:26:11;6600:9:9;1760:1:8;2369:49:11;6600:9:9;6006:39445;;;2441:26:11;6600:9:9;1760:1:8;2424:49:11;6006:39445:9;;;2515:22:11;;6600:9:9;1760:1:8;488:10:11;6006:39445:9;;6600:9;6006:39445;1548:3:15;6006:39445:9;1548:3:15;;;:::i;:::-;6006:39445:9;1548:3:15;6006:39445:9;;1548:3:15;;;:::i;:::-;;;;6006:39445:9;:::i;:::-;;;;;;;;;;;3623:310:14;;;;;;2933:1004::o;4211:1454::-;1371:2:13;4499:31:14;;4495:125;;5126:273;1760:1:8;5037::14;1760::8;6006:39445:9;;5126:273:14;5520:41;1390:66;5520:41;;5516:145;;4211:1454::o;5516:145::-;5644:10;6006:39445:9;;5644:10:14;;;;4495:125;4598:15;6006:39445:9;;4598:15:14;;;;9387:711;3087:12:10;9614:13:14;9624:3;9614:13;;;9610:163;;9387:711;9795:2;9785:12;;;9781:162;;9387:711;9795:2;1548:3:15;;1630:2:14;488:10:11;1760:1:8;2186;1760;10025:61:14;9387:711;:::o;9781:162::-;2186:1:8;1760;;;;1630:2:14;9795;1525:41:10;;;1548:3:15;488:10:11;9868:66:14;;9781:162;;9610:163;1760:1:8;1630:2:14;9624:3;1525:41:10;;488:10:11;9702:62:14;;-1:-1:-1;9610:163:14;;9387:711;;9567:34;9624:3;9614:13;;;9610:163;;9387:711;9785:12;9795:2;9785:12;;;9781:162;;9795:2;1548:3:15;;1630:2:14;488:10:11;1760:1:8;2186;1760;10025:61:14;9387:711;:::o;9610:163::-;2186:1:8;1760;1630:2:14;9624:3;1525:41:10;;488:10:11;9702:62:14;;-1:-1:-1;9610:163:14;;;10818:854;6006:39445:9;8916:1:10;;10818:854:14;10980:9;;;6006:39445:9;;;;2186:1:8;6006:39445:9;10818:854:14;:::o;10973:592::-;11043:8;;;11039:190;;10973:592;6006:39445:9;;;;939:36:14;;6006:39445:9;10973:592:14;;11039:190;6600:9:9;11164:30:14;;;;;:::i;:::-;2186:1:8;1760;8010:75:14;11039:190;;;;10818:854;4417:13:10;;8208:1;10818:854:14;10980:9;;;6006:39445:9;;2186:1:8;6006:39445:9;10818:854:14;:::o;10973:592::-;11050:1;11043:8;;;;11039:190;;10973:592;6006:39445:9;;;939:36:14;6006:39445:9;10973:592:14;;11039:190;11164:30;;;6600:9:9;11164:30:14;;:::i;:::-;2186:1:8;1760;8010:75:14;11039:190;;;3399:226:15;;6006:39445:9;;3538:81:15;6006:39445:9;3538:81:15;;;3549:13;;6006:39445:9;;3572:4:15;6006:39445:9;;;1833:41:10;6006:39445:9;;;;1833:41:10;6006:39445:9;;;;;;;;;3538:81:15;;6006:39445:9;1548:3:15;;;;;;;;;;;6006:39445:9;1548:3:15;1833:41:10;3528:92:15;;3399:226;:::o;1901:42::-;;;;:::o;:::-;;6006:39445:9;;1901:42:15;;;;;;;;;;;;1833:41:10;1901:42:15;1833:41:10;;;1901:42:15;;5470:1106;;;;;;;6006:39445:9;;;:::i;:::-;1548:3:15;6006:39445:9;;1548:3:15;;;:::i;:::-;6006:39445:9;;;;;;;;;;;;;;6554:17:15;;-1:-1:-1;;;;5790:405:15;;;;6006:39445:9;;;;;;;;;;;1833:41:10;5708:21:15;;6006:39445:9;;;5790:405:15;;;;6006:39445:9;5790:405:15;;6006:39445:9;1548:3:15;6006:39445:9;;;;1748:5:15;;6006:39445:9;1548:3:15;1748:5;;;6006:39445:9;1548:3:15;1748:5;;6006:39445:9;;;;1748:5:15;1548:3;6006:39445:9;;;1748:5:15;;;6006:39445:9;1748:5:15;;;6006:39445:9;1748:5:15;;;6006:39445:9;1748:5:15;;;;;6006:39445:9;5790:405:15;;;;;;;;;:::i;:::-;6299:42;;1901;6299;;6389:52;6299:42;;:::i;:::-;6389:52;;:::i;:::-;6554:17;:::i;:::-;6505:66;5470:1106::o;6006:39445:9:-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;7838:1704:15:-;8102:50;7838:1704;8102:50;1833:41:10;;;8102:50:15;;;;;;:::i;:::-;8073:79;;;2072:12;8073:79;8288:22;;8284:68;;8539:13;8495:4;2239:2;1760:1:8;8474:78:15;8468:84;8464:130;;6006:39445:9;;;;2041:11:13;6006:39445:9;2413:3:15;6006:39445:9;8955:64:15;6006:39445:9;2562:8:15;6006:39445:9;9441:55:15;;;;;;:::i;:::-;9425:71;9421:117;;7838:1704::o;9421:117::-;9513:18;6006:39445:9;;9513:18:15;;;

Swarm Source

ipfs://19b8fafd51fabb6a7d8e2c93ba741bbc968f4bc3cc2c0b9d3f004da94b228662

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.