# The Ethlizards Protocol

{% content-ref url="/pages/BmFO0CR0B3acatEQp4nT" %}
[Staking](/staking/how-does-staking-work)
{% endcontent-ref %}


# How does staking work?

This page provides an overview of how users can stake their Ethlizards.

Staking allows a user to lock their Ethlizards Genesis V1 and V2 NFTs in exchange for rewards on the investments made via the Ethlizards investment DAO. The official staking site can be found here: <https://staking.ethlizards.io/>

## Staking & Un-Staking

Stakers can deposit their Ethlizard NFT and receive a Locked Lizard (LLZ) to represent their deposit. Once deposited, an Ethlizard cannot be withdrawn for 90 days, and the user can claim rewards from council deposits.&#x20;

After the lockup period has ended, a user will be able to un-stake their deposited Ethlizard at any time. To initiate an un-stake, they must use their original staker's address and hold their corresponding LLZ. **When un-staked, a user will lose access to all their pending rewards** and lose access to future rewards (unless they re-stake). It's essential for users to claim their rewards before un-staking, which is reflected in the staking interfaces.&#x20;

We encourage users to deposit their NFTs via a cold wallet address, as claims and withdrawals will all be made via the depositor's address. The received LLZ is retractable and allows the original depositor to always recall the NFT back to their address. This means, as long as you have access to the wallet you originally staked with, you will be able to re-claim your LLZ back.

**Ethlizards is not responsible for phished wallets.**&#x20;

## Rewards Distribution

Revenue distributions will occur according to the Investment Council’s decisions based on token liquidations. Once over 50,000 USDC has been deposited into the contract, a new "pool" will be created, allowing users to claim their rewards.

## Claiming

A user can only claim rewards when staked, and will only receive a share of rewards deposited after they have been staked. In order to claim rewards, the user will also need to engage in at least one on-chain governance vote/action with their Ethlizards, to ensure active participation.&#x20;

Claims are done via a dynamic pool system. Each time a reset occurs and a sufficient amount of rewards have been deposited into the contract, a pool will be created and a user will be able to claim rewards assuming they've been staked prior. Users will receive different rewards per pool and will have to claim rewards separately per pool.

See this [link](https://snapshot.org/#/ethlizards.eth/proposal/0xbd00c68323dc8b41621d21af6eb41b098a7ac1d075e3cba21343eabf23220cf9) for the original LIP passed the staking proposal.&#x20;


# Staking Instructions

How to stake your Ethlizards Genesis V1 and V2 NFTs

1. Navigate your web browser to <https://staking.ethlizards.io/>
2. Connect your wallet

<figure><img src="https://2102088694-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwrl3kgVDJvzMPlXuApYB%2Fuploads%2FCdJpiF1TqohVqWcvgied%2Fimage.png?alt=media&amp;token=09d5c14a-be13-449d-8d99-993568cdc3ed" alt=""><figcaption></figcaption></figure>

3. Navigate to the Dashboard and scroll down

<figure><img src="https://2102088694-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwrl3kgVDJvzMPlXuApYB%2Fuploads%2FdbgW3dJAwGN64zGDLscb%2Fimage.png?alt=media&amp;token=78471c05-c548-4270-ba0c-fc8d3c893bbc" alt=""><figcaption></figcaption></figure>

4. Approve your NFTs to be staked - (Requires an approval for Genesis V1 and/or V2 collections)

<figure><img src="https://2102088694-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwrl3kgVDJvzMPlXuApYB%2Fuploads%2FQ9xh1ensyCS2ZQ1PGtwA%2Fimage.png?alt=media&amp;token=0de71dcd-e089-4b00-998b-b0b1b2a762ca" alt=""><figcaption></figcaption></figure>

5. To stake your Ethlizards NFT(s): \
   A. If you want to stake all of your Ethlizards NFTs in the connected wallet, select the "Stake All" button.\
   \
   B. To stake a single Ethlizards NFT, select the "Stake" button below the specific Ethlizards NFT you would like to stake. \
   \
   *Important Note: Staking a single Ethlizards NFT incurs a transaction with gas fee each time if staking individually. If you want to stake multiple Ethlizards NFTs, to save on transaction gas fee costs, it is recommended to use the "Stake All" option instead.*

<figure><img src="https://2102088694-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fwrl3kgVDJvzMPlXuApYB%2Fuploads%2F8BKQ57wLaO8mXCbgFfiY%2Fimage.png?alt=media&amp;token=8aeafa72-0fa4-4ee3-8245-d0039ce5f5a5" alt=""><figcaption></figcaption></figure>

6. To see your staked Ethlizards NFTs, select the "Staked" tab.


# Audits

This page provides details on the audits that the Ethlizards staking protocol has undergone

Pashov - <https://github.com/pashov/audits/blob/master/solo/LizardStarking-security-review.md>

All critical, high and medium bugs that where found were fixed here.&#x20;


# Technical Documentation


# Maths

The staking mechanics incorporate different components in order to fulfil the following purposes:

* Reward long-term stakers without hard locking the assets for longer periods of time than the mandatory 90 days lock.
* Discourage gaming the distributions and reduce volatility (both in pool share and floor price) created by actors trying to do so.
* Converge all long-term stakers shares toward the same nominal value per lizard, over successive distributions, to guarantee a fair distribution process.&#x20;

*Note: This section describes the basic mathematics behind the staking solution. In order to be programmed in Solidity, adjustments will be applied, keeping the same principles.* &#x20;

### Rebase&#x20;

In order to both reward long-term stakers and discourage gaming distribution, staking is calculated through compounding rewards.

When a lizard is staked, a base weight of 1 is assigned. This weight will then compound at a rate of 0.5% per day.&#x20;

$$
W(t)=(1+r)^t
$$

*W(t) = lizard weight*\
*t = time staked* \
*r = compounding rate*

### Distribution

A lizard's share of the pool at a fixed distribution date is then calculated by dividing its weight by the sum of all weights in the pool.&#x20;

$$
S=W/P
$$

*S = the lizard's share of the pool*\
*W = the lizard's weight at the distribution time*\
*P = the sum of all weights in the pool at the distribution time*

Since every lizard's staked follow the same rule, if no other lizards are added to the pool, each lizard's share of the pool remains constant in time. \
A lizard being staked at a later date will have a smaller share of the pool than the others, hence providing an advantage to long-term stakers. &#x20;

### Reset

Since the pool size inflates over time due to the rebasing, if no other mechanism were introduced, a late staker would never be able to catch up to long-term stakers and reach their nominal share of the pool per lizard (1/number of lizards in the pool).&#x20;

To reward later participants for staking long-term, the inflation of the pool is partially reset every time a distribution has occurred, hence reducing the difference in share per lizard between the early stakers and late stakers that have been through one or more distributions.

### Examples &#x20;

This compound inflation will reset every rewards distribution cycle.

Each user is assigned a weight of 100 "shares", rebased at an additional 0.5% daily.

A global total of all shares is maintained and updated each time the stake or un-stake function is called, and whenever a new rewards "pool" is opened up.

A pool is created to represent a reset of inflated shares and additional rewards being claimable. A reset occurring means a user's inflated shares are slashed by 80%. User's inflated shares are calculated as:

$$
(currentShares - 100)
$$

However, a user's rewards will still be calculated off their pre-reset values for the corresponding pool created. Only future pools will take into account their reset shares. Users will also have different rewards based on which pool's rewards are being claimed.&#x20;

Here is an example of this mechanism in action:

1. User A stakes 10 lizards on day 3 of the staking pool.
2. The existing staking pool had 1000 deposits on the start day, then another 1000 on day 2.&#x20;
   1. The existing global shares of day 1 would be 100,500.
   2. The existing global shares of day 2 would be 201,502.5.
3. Day 3 ended and with User A, 490 more lizards were staked.&#x20;
   1. User A has a share of 1,005, with the global shares being 252,760.012.
   2. User A has a weight of 0.3976%.
4. Before day 4 ends, 100,000 USDC is deposited into the pool. An additional 200 lizards were also deposited.&#x20;
   1. The current global shares are 272,760.012.
   2. User A has a weight of 0.3685%.
   3. User A's reward for pool 1 would be 368.5 USDC.
   4. A reset occurs. The current inflated shares are 2760.012, and after the reset, will be 552.0024. Prior to the reset, the global shares will be 270,552.0024.


# Contracts

Deployed Contracts:&#x20;

LizardLounge: [https://etherscan.io/address/0x5e50e684ed294d0010b9353effe70b85e558dcfe<br>](<https://etherscan.io/address/0x5e50e684ed294d0010b9353effe70b85e558dcfe&#xA;>)


# System Architecture

Staking is comprised of a singular, non-upgradeable contract that handles deposits, withdrawals, as well as reward calculations and claims.&#x20;

**Contracts**

LizardLounge

* The main smart contract which handles deposits, withdraws, and calculations

ABDKMath64x64

* The library is used in order for LizardLounge to successfully handle 64.64 bit fixed point numbers, which is used in rewards calculations,


# Actor Functions

## User Functions

**depositStake**

Allows a user to deposit either their Regular or Genesis Ethlizards.

**withdrawStake**

Allows a user to withdraw either their Regular or Genesis Ethlizards, provided 90 days have passed.&#x20;

**claimRewards**

Allows a user to claim rewards passed off a pool.&#x20;

**retractLockedLizard**

Allows a user to send their Locked Lizard NFT (LLZ) back to the original depositors address.

## Council Functions

**depositRewards**

Allows the council address to deposit USDC rewards. Once the rewards held are over the minResetValue, a new pool is created and a reset of inflation occurs.

## DAO Functions

**withdrawalToDAO**

Allows the DAO to withdraw USDC from the contract. This function will be only used in emergency scenarios or in case of contract changes/upgrades.

## Owner Functions

**setDepositsActive**

Allows the contract owner to enable deposits

**setAllowedContracts**

Allows the contract owner to modify what contracts the LLZ tokens are allowed to interact with.

**setResetShareValue**

Sets the reset values when pools are created.

**whitelistCouncil**

Allows the contract owner to whitelist the council address.

**setMinResetValue**

Allows the contract owner to change the minimum USDC value required for a reset and a pool to be created.

**setMinLockedTime**

Sets the minimum time a user needs to be locked up before withdrawals are enabled

**setBaseURI**

Updates the metadata of the LLZ tokens.


# Contract Maths

## Introduction&#x20;

Ethlizard's staking contract is required to calculate the current share of a user and their weight amongst the total staked pool in order to distribute rewards fairly.&#x20;

In order to keep track of inflation, a user's weight as well as the global total, LizardLounge assigns each regular Ethlizards a value of 100 shares, and each genesis Ethlizards a value of 200 shares, both in big number form. In order to accurately calculate the weight of the user's pools, we also need to keep track of the global shares. We only store the value of the global shares in the contract, as storing all the user's individual shares, which update dynamically would be extremely expensive.&#x20;

When rewards are distributed, a pool is created and inflation will be reset. Users will have different weights and rewards based on different pools, and to calculate this, we create a pool structure that contains the value, timestamp and current global shares when created. Inflation being reset results in a reduction of 80% of the inflation accumulated prior to the pool's creation.

**For the rest of these sections, we will refer to a user's percentage ownership as their weight and their stored values as shares.**

## Functions

**getCurrentShareRaw**

This function will return the current raw share of a user. There are 4 cases that can occur:

* **Case A:** If the no pools have been created after the user is staked, this means we do not factor in resets.&#x20;
  * We call `calculateShareFromTime`and return the result.
* **Case B:** One or more pools were created, but the user was staked before the creation of all of them.&#x20;
  * We call `calculateShareFromTime`and `resetShareRaw` and then loop between the pools to calculate the shares between them, and then once again calculate the shares between the current time and the last pool.
* **Case C**: The user was staked between 2 pools.&#x20;
  * We iterate through the pools to find out which 2 pools the user is staked between, and calculate the shares between the first pool created after the token was staked, and the time the token was staked, via `calculateShareFromTime`. We then iterate over the remaining pools to calculate the shares between them as well as the last pool, and then once again calculate the shares between the current time and the last pool.

**createPool**

This will call `updateGlobalShares`, create a new pool structure and reset the current pending rewards and then call `resetGlobalShares.`

**resetGlobalShares**

This will reset the existing global shares by slashing the current inflated shares by 80%.&#x20;

**updateGlobalShares**

This will update the current global shares by calculating the days that have passed since the last update to shares, and then multiplying the result of`calculateRebasePercentage`and the prior global shares to compute the new global shares.

**claimCalculation**

This will calculate the owed rewards a staked tokenId based on an inputted pool. There are 3 cases that can occur:

* **Case A:** There is only 1 pool, which means we do not need to factor in any resets.&#x20;
  * We call `calculateShareFromTime`and calculate the owed rewards and return it
* **Case B:** There are one or more pools created, but the user was staked before all of them.
  * We call `calculateShareFromTime`and `resetShareRaw` and then loop between the pools to calculate the shares between them, resetting whenever iterating over a new loop. We then compute and return the rewards.
* **Case C:** The user was staked between 2 pools.
  * We iterate through the pools to find out which 2 pools the user is staked between, and calculate the shares between the first pool created after the token was staked, and the time the token was staked, via `calculateShareFromTime`. We then iterate over the remaining pools to calculate the shares between them and then calculate and return the amount of the owed rewards.

**calculateShareFromTime**

This takes in 2 different UNIX timestamps and returns the inflation-applied raw share of it. A rebase will only be valid if the current timestamp is a full day from the start of staking. For example, if staking started at 7 pm, a user deposited their rewards at 6 pm, they would rebase and have inflation applied to their shares. We call calculateRebasePercentage here to calculate the inflation, as our rebasing factor is stored in 64.64-bit binary fixed-point form.&#x20;

**calculateRebasePercentage**

This function calculates the power of the rebasing factor to the inputted required rebases, and then returns the number in big number form. See the implementation section for a detailed explanation.

**resetShareRaw**

This will reset the inputted user's shares by slashing the current inflated shares by 80%.&#x20;


# Implementation of Compound Interest

Ethlizard's staking contract uses a unique method to calculate compound interest. Since it is impossible to store decimals in solidity, and there are overflow requirements, we are unable to calculate compound interest via a regular, simple means.

Instead, we use this formula to calculate continuous compound interest, using the ABDKMath64x64 library:

$$
(1+r)^t = 2^{t,\cdot, \log\_2(1+r)}
$$

This is the result of converting a basic compound interest formula into the above via log laws.

$$
(1+r)^t = 2^{\log\_2((1+r)^t)}  = 2^{(t × \log\_2(1+r))}
$$

The value of $$(1 + r)$$ is stored in our contract in 64.64-bit binary fixed-point quadruple-precision binary floating-point numbers. This was an implementation allowed by the ABDKMaths64x64 library and allows the contract to calculate compound interest via calling the logarithm and exponent functions. We use an approximation of $$1.005$$ (the rebasing factor) in our contract.

The value of $$(1 + r)$$ in our contract is:

$$
1.85389777940780994
$$

And this means our rebasing factor is:

$$
1.0050000000000000014051260155412137464736588299274444580078125
$$

The implementation of the above formulas is found within the function `calculateRebasePercentage`.&#x20;


