Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Protocol3] Staking design (part1) - user LRC staking and reward claim #208

Closed
dong77 opened this issue Jun 6, 2019 · 2 comments

Comments

@dong77
Copy link
Contributor

commented Jun 6, 2019

Staking

1. Protocol pool staking (Global)

Everyone can stake LRC to get a part of 70% of the protocol fees of all exchanges.

  • Who: Anyone
  • How much: Any amount, user receives an amount proportionally to the amount staked and how long it is staked
  • How long: people can stake for 3, 6, 12, and 24 months.

Protocol fee pool distribution

  • 70% to LRC stakers (either allow people to withdraw the tokens or sell for LRC and add this extra LRC to the stake of the user)
  • 20% development fund (all tokens withdrawable)
  • 10% burned (sell to LRC using Oedex/..., burn LRC)

2. Exchange owner staking (Per exchange)

The exchange owner needs to stake LRC for reputation. We will enforce a minimum amount of LRC for creating an exchange and a minimum amount for still being able to commit new blocks. Part of this stake, until the minimum stake amount is reached, can be burned when blocks are reverted (~50,000 LRC) or fines are paid to for token distribution.

  • Who: The exchange owner
  • How much: ~250,000 LRC minimum for an exchange with data-availability; ~1,000,000 LRC minimum for an exchange without data-availability, exchange owner can add to this stake any time he wants
  • How long: stake is only withdrawable when the exchange is shutdown correctly, otherwise it will be burned

3. Exchange protocol fee reduction staking (Per exchange)

All orders pay the protocol fee, the taker and the maker protocol fees can be lowered.
Taker: 0.050% -> 0.025%
Maker: 0.025% -> 0.0% or close to 0.0%

  • Who: The exchange owner, but the exchange owner will let anyone stake for this using a custom contract: ring-matchers/wallets/market-makers/...
  • How much: A lot of LRC
    • Lowering the taker protocol fee to 0.025%: 2,500,000 LRC
    • Lowering the maker protocol fee to 0.01%: 1,000,000 LRC (I would make this less costly then lowering the taker protocol fee)
  • How long: No duration imposed by the protocol, can be done in the Exchange owner contract

The amount staked by the Exchange owner (see 2.) is added to the amount staked here for the protocol fee reduction calculation with a 2x factor! We want to incentivize the exchange owner to lock up as much LRC as possible in the exchange to ensure a correct shutdown for its users. While this is much more important for exchanges without data-availability I would not make this factor any bigger for exchanges without data-availability, otherwise people may create an exchange without data-availability just so they can lower the protocol fee cheaper.

@dong77 dong77 added the improvement label Jun 6, 2019

@dong77 dong77 added this to To do in Loopring Protocol v3 via automation Jun 6, 2019

@dong77 dong77 changed the title [Protocol3] Staking reward distributuion [Protocol3] Staking design and reward distribution Jun 6, 2019

@dong77 dong77 added the core_feature label Jun 6, 2019

@dong77

This comment has been minimized.

Copy link
Contributor Author

commented Jun 8, 2019

Design Option#1

Here is my idea regarding how users claim their rewards.

What can user claim

All non-LRC tokens and ETH will be auctioned off for LRC. So we end up only providing staking reward in LRC. Therefore, users will only claim LRC, not Ether or other tokens.

Distribution vs Claiming

Assuming many unique addresses will stake LRC to receive rewards, proactively distributing LRC reward to each address will be very expensive. No one would be willing to be the one triggers the distribution, including DEX operators.

Draft Implementation and its properties

The interface:

contract IUserStakingPool {
  address  public lrcAddress;
  function getNumberOfAddresses() public view returns(uint);
  function getTotalStake() public view returns(uint);
  function getTotalReward() public view returns(uint);
  function getUserStake(address user) public view returns(uint);
  function getUserReward(address user) public view returns(uint); 
  function deposit(uint amount) public;
  function withdraw(uint amount) public ;
  function claimReward();
  function getUserWithdrawalWaitTime(address user) public view returns(uint minutes);
  function getUserClaimWaitTime(address user) public view returns(uint minutes);
}

First, let's define a structure to capture each user's staking information.

struct Stake {
    address user;
    uint64  stake;
    uint64  depositedAt;
    uint64  claimedAt; // timestamp from which more points will be accumulated
}

public Stake global;
public mapping(address, Stake) users

uint64 MIN_CLAIM_DELAY = 90 days;
uint64 MIN_WITHDRAW_DELAY = 90 days;
function deposit(uint64 amount) {
    require(amount > 0);
    // lets trandfer LRC first.
    lrc.safeTransfer(msg.sender, address(this), amount);
    
    State storage user = users[msg.sender];
    // we update the user's stake
    user.depositedAt = (user.stake * user.depositedAt + amount * now) / (user.stake + amount);
    if (user.claimedAt == 0) {
      user.claimedAt = user.depositedAt;
    } else {
      user.claimedAt = (user.stake * user.claimedAt + amount * now) / (user.stake + amount);
    }
    user.stake += amount
    
    // update global stake the same way (create an internal function for this)
    global.depositedAt = (global.stake * global.depositedAt + amount * now) / (global.stake + amount);
    if (global.claimedAt == 0) {
      global.claimedAt = global.depositedAt;
    } else {
      global.claimedAt = (global.stake * global.claimedAt + amount * now) / (global.stake + amount);
    }
    global.stake += amount
}
function getUserWithdrawalWaitTime(address user) view returns (uint minutes) {
   if (users[user].depositedAt + MIN_WITHDRAW_DELAY <= now) return 0;
   else return users[user].depositedAt + MIN_WITHDRAW_DELAY - now;
}
function withdraw(uint64 amount) {
    claimReward();  // always claim reward first
    
    require(amount > 0);
    require(getUserWithdrawalWaitTime(msg.sender) == 0);
    
    State storage user = users[msg.sender];
    require(user.stake >= amount);
    
    user.stake -= amount;
    global.stake -= amount;
    
    // transfer LRC to user
    lrc.safeTransfer(address(this), msg.sender, amount);
}
function getTotalReward() view returns (uint64) {
    return lrc.balanceOf(address(this)) - global.stake;
}
function getUserClaimWaitTime(address user) view returns (uint minutes) {
   if (users[user].claimedAt + MIN_CLAIM_DELAY <= now) return 0;
   else return users[user].claimedAt + MIN_CLAIM_DELAY - now
}
function claimReward() returns (uint64 claimed) {
    require(getUserClaimWaitTime(msg.sender) == 0);
    
    State storage user = users[msg.sender];
    
    uint globalPoints = global.stake * (now - global.claimedAt);
    uint userPoints = user.stake * (now - user.claimedAt);
    require(globalPoints > 0 && userPoints > 0);

    uint claimed =  getTotalReward() * userPoints / globalPoints;
    
    global.stake += claimed;
    global.claimedAt = (globalPoints - userPoints) / global.stake; 
    
    user.stake += claimed;
    user.claimedAt = now;
}

Properties

  1. We allow each user to deposit/withdraw/claim based on its own timestamps. This prevents users from acting the same way in a certain time window which will affect the circulation supply and price.
  2. LRC will be claimed proportionally to the points a user earn overtime. However, the earlier a user claim his/her reward, the more LRC reward he/she will get in total. Therefore, we must restrict the frequency of reward claiming. Based on the proposed implementation, it's still a user's best interest to claim reward ASAP. We set the MIN_CLAIM_DELAY to 180 days so that users don't have to frequently monitor their account. We can also discourage users from making a timely claim by delaying its withdrawal.
  3. Claimed LRC reward will be automatically staked, no token transfers are required.
  4. We can handle as many users as possible. No limitation.

@dong77 dong77 added this to the 3.0beta3 milestone Jun 8, 2019

@dong77 dong77 changed the title [Protocol3] Staking design and reward distribution [Protocol3] Staking design: part1 - user LRC staking and reward claim Jun 9, 2019

@dong77 dong77 added the discussion label Jun 9, 2019

@dong77 dong77 changed the title [Protocol3] Staking design: part1 - user LRC staking and reward claim [Protocol3] Staking design (part1) - user LRC staking and reward claim Jun 9, 2019

@dong77

This comment has been minimized.

Copy link
Contributor Author

commented Jun 19, 2019

Implementated by #216 #218

@dong77 dong77 moved this from To do to In progress in Loopring Protocol v3 Jun 26, 2019

@dong77 dong77 closed this Jun 29, 2019

Loopring Protocol v3 automation moved this from In progress to Done Jun 29, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.