Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transactions Sent
Latest:
N/A
First:
N/A
Latest 25 from a total of 226 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Destroy | 18105867 | 420 days ago | IN | 0 ETH | 0.00028083 | ||||
Settle Bet | 18047833 | 428 days ago | IN | 0 ETH | 0.00062521 | ||||
Play Coin Flip | 18047832 | 428 days ago | IN | 0.01 ETH | 0.00044483 | ||||
Settle Bet | 18007275 | 433 days ago | IN | 0 ETH | 0.00093665 | ||||
Play Etheroll | 18007274 | 433 days ago | IN | 0.01 ETH | 0.00066288 | ||||
Settle Bet | 18007273 | 433 days ago | IN | 0 ETH | 0.00093069 | ||||
Play Etheroll | 18007272 | 433 days ago | IN | 0.01 ETH | 0.00066358 | ||||
Settle Bet | 18007270 | 433 days ago | IN | 0 ETH | 0.00107273 | ||||
Play Two Dice | 18007269 | 433 days ago | IN | 0.01 ETH | 0.00104703 | ||||
Settle Bet | 18007268 | 433 days ago | IN | 0 ETH | 0.00097203 | ||||
Play Two Dice | 18007267 | 433 days ago | IN | 0.01 ETH | 0.00083811 | ||||
Settle Bet | 18007266 | 433 days ago | IN | 0 ETH | 0.00110631 | ||||
Play Two Dice | 18007265 | 433 days ago | IN | 0.01 ETH | 0.00079453 | ||||
Settle Bet | 18007263 | 433 days ago | IN | 0 ETH | 0.00105165 | ||||
Play Dice | 18007262 | 433 days ago | IN | 0.02 ETH | 0.00078723 | ||||
Settle Bet | 18007261 | 433 days ago | IN | 0 ETH | 0.00099011 | ||||
Play Dice | 18007260 | 433 days ago | IN | 0.01 ETH | 0.00074894 | ||||
Settle Bet | 18007258 | 433 days ago | IN | 0 ETH | 0.00101502 | ||||
Play Coin Flip | 18007257 | 433 days ago | IN | 0.01 ETH | 0.00072049 | ||||
Settle Bet | 18007257 | 433 days ago | IN | 0 ETH | 0.00117814 | ||||
Play Coin Flip | 18007256 | 433 days ago | IN | 0.01 ETH | 0.00069893 | ||||
Settle Bet | 17999450 | 434 days ago | IN | 0 ETH | 0.00088779 | ||||
Play Etheroll | 17999449 | 434 days ago | IN | 0.01 ETH | 0.00062547 | ||||
Settle Bet | 17999446 | 434 days ago | IN | 0 ETH | 0.00097085 | ||||
Play Two Dice | 17999445 | 434 days ago | IN | 0.01 ETH | 0.00065234 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18105867 | 420 days ago | 1.32463311 ETH | ||||
18047833 | 428 days ago | 0.018 ETH | ||||
18007275 | 433 days ago | 1 wei | ||||
18007273 | 433 days ago | 1 wei | ||||
18007270 | 433 days ago | 1 wei | ||||
18007268 | 433 days ago | 0.0162 ETH | ||||
18007266 | 433 days ago | 0.0162 ETH | ||||
18007263 | 433 days ago | 1 wei | ||||
18007261 | 433 days ago | 0.027 ETH | ||||
18007258 | 433 days ago | 0.018 ETH | ||||
18007257 | 433 days ago | 1 wei | ||||
17999450 | 434 days ago | 1 wei | ||||
17999446 | 434 days ago | 1 wei | ||||
17999445 | 434 days ago | 1 wei | ||||
17999442 | 434 days ago | 1 wei | ||||
17999441 | 434 days ago | 0.0228 ETH | ||||
17999439 | 434 days ago | 0.0228 ETH | ||||
17999436 | 434 days ago | 1 wei | ||||
17999434 | 434 days ago | 0.018 ETH | ||||
17999433 | 434 days ago | 1 wei | ||||
17992550 | 435 days ago | 1 wei | ||||
17991858 | 435 days ago | 0.01125 ETH | ||||
17991856 | 435 days ago | 1 wei | ||||
17991804 | 435 days ago | 0.02025 ETH | ||||
17991693 | 435 days ago | 0.027 ETH |
Loading...
Loading
Contract Self Destruct called at Txn Hash 0x0c397bb60a376488919a6025ad779ef2795c8580338cb1a35f99ec375919c735
Contract Name:
Dice9
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity Standard Json-Input format)
// 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= */
// 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 {} }
// 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 {} }
// 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 {} }
// 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 {} }
// 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 {} }
// 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 {} }
// 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 {} }
// 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 {} }
// 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; } } }
// 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); } } }
// 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; } } }
// 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(); } } } }
// 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; } }
// 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(); } }
// 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(); } } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.