Diving in the Immortal Lottery

Dexaran
6 min readApr 29, 2023

The Immortal Lottery is an autonomous smart-contract that implements a fully decentralized gambling APP on-chain. Without any third parties involved, without any off-chain non-verifiable sources of entropy.

You can read more about the Immortal Lottery details here.

The lottery is deployed on Callisto Network mainnet:

How it works?

The lottery operates in “rounds” and it can be in one of the four possible states:

  • Idle (Infinite, until a new round is started)
  • Deposit phase (1 hour)
  • Reveal phase (1 hour)
  • Round finished, winner awaits reward (Infinite, until reward is paid)

When the lottery is in Idle state anyone can start a new round. In order to do so a user must simply send CLO to the lottery contract address and the round will start automatically.

After the round is started the lottery transits to Deposit phase. Other users can deposit CLO to the Lottery contract address during this phase. The funds are accumulated in reward pool. Each user has a chance to win the reward pool which is proportional to the deposit share i.e. if Alice deposited 20 CLO and Bob deposited 30 CLO then Alice has 40% chance to win and Bob has 60% chance to win the round. During the deposit phase entropy providers must submit their entropy hashes to Entropy contract.

After a specified period of time the deposit phase is finished and lottery automatically transits to Reveal phase. No deposits accepted during this phase. Entropy providers must reveal their entropy.

After a specified period of time the deposit phase is finished and lottery automatically transits to Pending state awaiting for winners reward to be paid. It is not possible to start a new round before the reward for the previous one is paid.

Interacting with the contracts

Starting a new round

To start a new round I need to call the start_new_round() function in the contract.

I have deposited 223 CLO alongside the round starting transaction. This amount will be the first deposit of the round.

It is not possible to start a round without initial deposit. Minimal deposit amount is 10 CLO.

After the initialization transaction is submitted to the blockchain the lottery will change it’s state to Deposit phase.

Accessing states of the lottery contract

The current state of the lottery can be browsed via get_phase() function of the contract.

  • Phase 1 — deposits and entropy are accepted.
  • Phase 2 — deposits and entropy are not accepted. Entropy hashes must be revealed.
  • Phase 3 — entropy hashes are revealed and winner must be paid.
  • Phase 0 — winner is paid and the previous round is finished. It is possible to start a new round.

Deposits

In order to make a deposit a user can simply send funds to the address of the Lottery contract during the Deposit Phase (get_phase = 1)

For demonstration purposes I sent a number of deposits from different addresses.

Entropy

During the Deposit Phase (get_phase = 1) when deposits are accepted users can also submit their “entropy” to become an entropy provider.

Entropy providers deposit a collateral and submit entropy hashes during the deposit phase. Entropy providers must reveal the entropy during the reveal phase or the collateral will be lost!

When the entropy is revealed the entropy provider will get collateral back and an additional reward. Currently entropy reward is 4% of the reward pool. Entropy reward is equally split between all entropy providers who revealed their entropy hashes successfully during the reveal phase.

In order to get a proper entropy hash the entropy provider can invoke the test_hash() function of the Entropy contract.

_entropy_payload and _salt are two numbers that must be hashed. The resulting bytes32 hash must be submitted to the submit_entropy() function of the Entropy contract.

A collateral must be deposited alongside submit_entropy() transaction. Currently the collateral is set to 1000 CLO.

At least one entropy provider must submit and reveal entropy in order for a round to be successful. If the entropy threshold is not met then the round will fail and users will be allowed to claim back their deposits.

Revealing Entropy

At the specified time the Lottery will enter the Reveal Phase (get_phase=2). During the reveal phase entropy providers must submit the numbers (entropy payload and salt) that they provided before. If the numbers will not match the previously provided then the transaction will fail.

In order to reveal the entropy, the entropy provider must invoke the reveal_entropy() function of the Entropy contract.

If the entropy is successfully revealed then the entropy provider will instantly get their collateral and a fraction of the entropy reward.

Calculating the winner

After the Reveal Phase is finished the lottery will automatically enter the pending state. The winner must be calculated and the reward must be paid.

Winner is calculated by the following formula:

  • Each user is assigned a “winning interval” upon making a deposit. The winning interval length depends on the deposited amount
  • Upon completion of the Reveal Phase the entropy is accumulated and can be fed into Random Number Generator. The RNG will then generate a single random number
  • The user in boundaries of whose “winning interval” the generated random number lays is considered a winner.

Unfortunately it is not possible to calculate who is the winner in the contract logic.

Example

I have deposited 223 CLO from 0x222E674FB1a7910cCF228f8aECF760508426b482 address. This address was assigned a winning interval [0;223000000000000000000]

After that I have deposited 100 CLO from 0x01000b5fe61411c466b70631d7ff070187179bbf address. This address was assigned [223000000000000000000;323000000000000000000] interval.

Another deposit of 700 CLO was made from 0x222E674FB1a7910cCF228f8aECF760508426b482 address. This address got another interval [323000000000000000000;1023000000000000000000].

So, if the RNG output will lay within boundaries of [0;223000000000000000000] or [323000000000000000000;1023000000000000000000] intervals then the address 0x222E674FB1a7910cCF228f8aECF760508426b482 will be the winner of the round. If the RNG output will lay within [223000000000000000000;323000000000000000000] interval then 0x01000b5fe61411c466b70631d7ff070187179bbf will be the winner.

The generated random number 354584007913129639936 lays within [323000000000000000000 ; 1023000000000000000000] interval.

Finishing the round

In order to finish the round anyone can call the finish_round() function of the Lottery contract. The function must be provided with the address of the winner.

NOTE: The contract can not calculate who is the winner of the round. The contract can only VERIFY if the provided address is the winner. If the winners address is provided then the round is finished and the reward is paid.

Here is an example of the transaction

After the finish_round() function is called the winner is automatically paid. The winner will receive total_amount_of_deposits — (entropy_reward + token_reward).

entropy_reward is a percentage of the reward pool allocated for the entropy providers.

token_reward is a share of the reward which is redistributed between the token holdes (currently set to 0 as no Lottery token is implemented).

It is planned to introduce CLOE staking which will allow CLOE token holders to receive rewards from each lottery round.

--

--