The Zethr Slot odds are on display on the Zethr Slots guide. To back them up I wanted to dive more into the odds from a contract point of view so you can see how the odds were created and how you can find them yourself. As well as this I wanted to first touch on how the pseudorandom number generator works in the Zethr Slots contract. The below points will also show how Zethr is provably fair, a term used in casinos that describes an algorithm which can be analyzed and verified for fairness on the part of the service operator.

Digging into the code

The current Zethr Slots contract can be found here.

The General Rundown – How The Contract Works

Step 1 – Request random number (submit tx)
Step 2 – Block is mined and random number created
Step 3 – Query the contract to find out the value of the random number
Step 4 – Collect winnings / swallow sadness

Pseudorandom Number Generator

The below section of code shows the use of the keccak hash function which is used to generate evenly distributed random numbers. Keccak is the most widely used hash function in the Solidity coding language and is thoroughly documented. In order for a hash to be created it first needs to be supplied with inputs. The inputs into the Zethr hash function are the blockhash of the block the bet is mined into as well as the users address (This way not everyone gets the same result in the same block). At the time of placing the bet it’s not possible to know the blockhash of the bet transaction (if you did, you could submit it to the Ethereum network and claim the block reward!).

// Returns a random number using a specified block number
// Always use a FUTURE block number.
function maxRandom(uint blockn, address entropy, uint index) 
  private 
  view
  returns (uint256 randomNumber) 
{
  return uint256(keccak256(
      abi.encodePacked(
        blockhash(blockn),
        entropy,
        index
  )));
}

// Random helper
function random(uint256 upper, uint256 blockn, address entropy, uint index)
  internal 
  view 
  returns (uint256 randomNumber)
{
  return maxRandom(blockn, entropy, index) % upper;
}

Zethr uses this pseudorandom number generator to create a number between 1 – 1,000,000. This below section of code shows the generator using the random function above and setting the limit to 1,000,000.

for(uint i=0; i<spin.spins; i++) {
    
  // If the block is more than 255 blocks old, we can't get the result
  // Also, if the result has already happened, fail as well
  uint result;
  if (block.number - spin.blockn > 255) {
    result = 1000000; // Can't win: default to largest number
    output[i] = 1000000;
  } else {
    // Generate a result - random based ONLY on a past block (future when submitted).
    // Case statement barrier numbers defined by the current payment schema at the top of the contract.
    result = random(1000000, spin.blockn, target, i) + 1;
    output[i] = result;
  }

Querying The Contract

Once the block has been mined and the random number generated, Zethr is then able to query the contract and determine the outcome of the bet, displayed on your UI. This is completed in the multiple steps outlined below:

Weed out the losers

Since our maximum number is 1,000,000, all our following results will be a number that equals a percentage of that. The first thing the contract looks for once receiving results is if you’ve won or lost. To do this it simply checks if your number is over 506,856 (1,000,000 – 506,856 = 100% – 50.69% = 49.31% loss chance), if your number is over 506,856 then you’ve lost. Not to worry though, 1% of your loss will go directly into the Jackpot pool.

if (result > 506856) {
  // Player has lost. Womp womp.

  // Add one percent of player loss to the jackpot
  // (do this by requesting a payout to the jackpot)
  outcomeTrack.loss += spin.tokenValue/100;
Check if you’ve won the Jackpot!

Now that the contract has removed the losers it will check to see if you’ve won the big one! Doing this is simple. since the random number generator is 1,000,000 and the Jackpot is simply 1 it checks to see if the random number is less than 2. This gives the Jackpot a literal 1 in 1,000,000 chance, or 0.0001%.

else if (result < 2) {
        // Player has won the three-moon mega jackpot!
      
        // Get profit amount via jackpot
        profit = ZlotsJackpotHoldingContract(zlotsJackpot).getJackpot();
        category = 1;
Check to see what you’ve won.

Now that contract knows that a) you didn’t lose, and b) you didn’t win the Jackpot, it needs to determine what you did win.

To do this the contract goes over each possible win scenario, starting from the lowest number to the highest. The first example in the code below is two moons. The contract is looking to see if your random number is less than 299 (but obviously can’t be 1 because that condition would have been filled previously by the Jackpot statement).

To work out the odds some simple maths is required. Below you’ll find the results for the first two win results:

Two Moons: < 299 is equal to 298 – 1 (the previous condition) = 297/1,000,000 = 0.0003 x 100 = 0.03%.

Z T H: < 3128 is equal to 3127 – 298 (the previous condition) = 2,828/1,000,000 = 0.0028 x 100 = 0.28%

          if (result < 299) {
          // Player has won a two-moon prize!
          profit = SafeMath.mul(spin.tokenValue, 50);
          category = 2;
          emit TwoMoonPrize(target, spin.blockn);
        } else if (result < 3128) {
          // Player has won the Z T H prize!
          profit = SafeMath.mul(spin.tokenValue, 20);
          category = 3;
          emit ZTHPrize(target, spin.blockn);
        } else if (result < 16961) {
          // Player has won a three Z symbol prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 30), 10);
          category = 4;
          emit ThreeZSymbols(target, spin.blockn);
        } else if (result < 30794) {
          // Player has won a three T symbol prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 30), 10);
          category = 5;
          emit ThreeTSymbols(target, spin.blockn);
        } else if (result < 44627) {
          // Player has won a three H symbol prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 30), 10);
          category = 6;
          emit ThreeHSymbols(target, spin.blockn);
        } else if (result < 46627) {
          // Player has won a three Ether icon prize!
          profit = SafeMath.mul(spin.tokenValue, 11);
          category = 7;
          emit ThreeEtherIcons(target, spin.blockn);
        } else if (result < 49127) {
          // Player has won a three purple pyramid prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 75), 10);
          category = 8;
          emit ThreePurplePyramids(target, spin.blockn);
        } else if (result < 51627) {
          // Player has won a three gold pyramid prize!
          profit = SafeMath.mul(spin.tokenValue, 9);
          category = 9;
          emit ThreeGoldPyramids(target, spin.blockn);
        } else if (result < 53127) {
          // Player has won a three rocket prize!
          profit = SafeMath.mul(spin.tokenValue, 13);
          category = 10;
          emit ThreeRockets(target, spin.blockn);
        } else if (result < 82530) {
          // Player has won a one moon prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 25),10);
          category = 11;
          emit OneMoonPrize(target, spin.blockn);
        } else if (result < 150423) {
          // Player has won a each-coloured-pyramid prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 15),10);
          category = 12;
          emit OneOfEachPyramidPrize(target, spin.blockn);
        } else if (result < 203888) {
          // Player has won a two Z symbol prize!
          profit = spin.tokenValue;
          category = 13;
          emit TwoZSymbols(target, spin.blockn);
        } else if (result < 257353) {
          // Player has won a two T symbol prize!
          profit = spin.tokenValue;
          category = 14;
          emit TwoTSymbols(target, spin.blockn);
        } else if (result < 310818) {
          // Player has won a two H symbol prize!
          profit = spin.tokenValue;
          category = 15;
          emit TwoHSymbols(target, spin.blockn);
        } else if (result < 364283) {
          // Player has won a two Ether icon prize!
          profit = SafeMath.mul(spin.tokenValue, 2);
          category = 16;
          emit TwoEtherIcons(target, spin.blockn);
        } else if (result < 417748) {
          // Player has won a two purple pyramid prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 125), 100);
          category = 17;
          emit TwoPurplePyramids(target, spin.blockn);
        } else if (result < 471213) {
          // Player has won a two gold pyramid prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 133), 100);
          category = 18;
          emit TwoGoldPyramids(target, spin.blockn);
        } else {
          // Player has won a two rocket prize!
          profit = SafeMath.div(SafeMath.mul(spin.tokenValue, 25), 10);
          category = 19;
          emit TwoRockets(target, spin.blockn);
        }

Finally the two rockets finish it off by catching anything that hasn’t had it’s condition met.

Thank you for reading the guide, if you have any questions please don’t hesitate to contact myself or the Zethr team in discord.