All about Ethervista
  • ⚡An intro to Ethervista
  • 📈The Euler Model
  • ⚙️Pool Configuration
  • 🔓Hardstake and Hardlock
  • 🤪Etherfun
  • 🔗Official links
Powered by GitBook
On this page
  • Introduction
  • Implementing the Euler model

The Euler Model

This page is for anyone interested in understanding how the Euler model can be implemented to efficiently distribute rewards paid in ETH to millions of users with negligble gas costs

Introduction

We explain the Euler model using the Ethervista LP pool as an example, but the model can be applied to any contract that implements staking or locking of assets and distributes ETH rewards based on the user's share of those assets

The Ethervista pair smart contract maintains a sequence of ascending numbers known as Euler amounts. These values are updated each time native ETH is transferred to the pair contract. Each Euler amount is determined by adding the previous Euler amount to the ratio of the fee to the current total supply of liquidity provider tokens (LP). The initial Euler amount is set to zero.

Mathematically, this update can be represented as:

Eulern=Eulern-1+feeLP supply \text{Euler}_n = \text{Euler}_\text{n-1} + \frac{\text{fee}}{\text{LP supply}} Eulern​=Eulern-1​+LP supplyfee​

With the corresponding sequence of rising numbers:

 Euler1, Euler2, Euler3, Euler4… Eulern{\text{ Euler}_1, \text{ Euler}_2, \text{ Euler}_3, \text{ Euler}_4 \ldots \text{ Euler}_n }  Euler1​, Euler2​, Euler3​, Euler4​… Eulern​

This sequence is particularly of interest to liquidity providers. Each provider is represented by a struct which stores the LP holdings of each user and a variable called euler0 which is suggestively named after the Euler amounts in our sequence.

struct Provider {
    uint256 lp;
    uint256 euler0;
};

This uint256 number represents the latest Euler amount in our sequence at the time the user adds liquidity, in which case we would have

 euler0= Eulern\text{ euler0} = \text{ Euler}_n euler0= Eulern​

Suppose the user decides to claim rewards a thousand swaps later. In that moment, the latest Euler amount is

Eulern+1000\text{Euler}_\text{n+1000}Eulern+1000​

The exact amount of rewards that this provider accumulated during these thousand swaps is:

Reward=lp∗(Eulern+1000−euler0)\text{Reward} = \text{lp} * (\text{Euler}_\text{n+1000}-\text{euler0}) Reward=lp∗(Eulern+1000​−euler0)

This approach operates under the assumption that the LP balance remains constant throughout the period. Therefore, whenever a provider takes any action, such as adding/removing/transferring liquidity, the variable euler0 will be refreshed to reflect the latest Euler amount in our sequence. This measure prevents a liquidity provider from manipulating their own share of rewards.

As such, it is advisable for a liquidity provider to always claim rewards before adjusting their LP balance.

The Ethervista DEX uses the Euler model for:

  • Rewarding liquidity providers

  • Hardstaking: Users can stake their tokens and receive rewards paid in ETH from the protocol fees every transaction

  • Hardlocking: Creators/users can lock their LP tokens and still benefit from rewards, something which was not possible in other AMM standards

Implementing the Euler model

Three key components are needed to implement the Euler model in a smart contract

  • An updateStaker function which update the user baseline Euler0 when there is a balance change (i.e a user stakes/un-stakes/transfers) or a reward claim

  • A updateEuler function which updates the Euler-array when ETH is received by the smart contract

  • A payable function which calls updateEuler when receiving ETH

  • A viewShare function which returns the reward share of each user

struct Staker {
    uint256 amountStaked;
    uint256 euler0;
}

uint256[] public euler; 
mapping(address => Staker) public stakers;

function updateEuler(uint256 Fee) internal { 
    if (euler.length == 0){
        euler.push((Fee*bigNumber)/totalSupply);
    }else{
        euler.push(euler[euler.length - 1] + (Fee*bigNumber)/totalSupply); 
    }
}

//alternatively can be the receive() function if ETH is sent directly
//to this contract
function contributeETH() external payable nonReentrant {
    updateEuler(msg.value);
}

function updateStaker(address user) external  { 
    if (euler.length == 0){
        stakers[user] = Staker(balanceOf[user], 0); 
    }else{
        stakers[user] = Staker(balanceOf[user], euler[euler.length - 1]);
    }
}

//+ stake/unstake/claim functions

function viewShare() public view returns (uint256 share) {
    if (euler.length == 0){
        return 0;
    }else{
        return stakers[msg.sender].amountStaked * (euler[euler.length - 1] - stakers[msg.sender].euler0)/bigNumber;
    }
}

PreviousAn intro to EthervistaNextPool Configuration

Last updated 6 months ago

📈