2

I need to develop an Staking contract which allows users to:

  • Stake their tokens
  • Claim rewards
  • Unstake their tokens (no more rewards afterwards)

I have been researching about different examples like this or this but all of them are based on a fixed reward rate. This is not my case. I have an Smart Contract (Main Contract) which is generating stablecoin tokens (let's say USDT), earned as fees. These USDT tokens, earned as fees (by Main Contract), must go to another contract (Staking Contract), in order to distribute the USDT tokens between de stakers.

The USDT tokens are transferred from the Main Contract to the Staking Contract several times a day (depending on the Main Contract activity earning fees).

I need to evolve from the fixed rate (based on a uint256 state variable) Staking Contract to a dynamic approach based on:

  • Total USDT Tokens received by Staking Contract (since user staked)
  • Proportional reward (in USDT tokens) based on the staking time by user. It is **something similar to the fees shared with Liquidity Provider Token stakers (Farming) **.

The main challenge is that every day the USDT input amount on Staking Contract is totally different, and I'm not sure how to properly track how much USDT tokens must go to each user, based on their staking time and the total USDT amount earned as fees.

UPDATE 1: ERC-1726 Dividend Paying Token

I've been researching about ERC-1726 proposal and I've customized the code of the standard to pay dividends in an ERC20 token (USDT in my case), rather than in ETH. I can share the code, if anyone is interested. My biggest concern is, if this is the best option (magnitude approach sounds great to avoid loops, and save gas), why isn't it more widespread?

UPDATE 1.1: I see that ERC-1726 (Dividend Paying Token) does not take into account elapse time and treats every token holder the same, that could make sense for some scenarios. The main issue is that a new "whale" appears (someone who puts tons of money and gets lots of new tokens, will dilute the rest of investors taking their previously earned rewards. If token total supply remains the same, this problem will not exist.

UPDATE 2: elapsedTime variable

In this post there is an example of how to achieve something like this, but I'm not sure if the formula will keep the rewards very precise over the time.

The formula they propose is this:

rewards = stakedAmount * rewardRate * elapsedTime;

Which is implemented in this function:

function calculateReward(address user) public view returns (uint256) {
    StakerData storage staker = stakers[user];
    uint256 stakingDuration = block.timestamp.sub(staker.lastStakedTimestamp);
    return (staker.totalStaked * rewardRate * stakingDuration)/100;
}

My main concern is if this will be an optimum solution to take into account the elapsed time (more rewards earned) in a variable token supply system (new investors with new tokens entering).

I think that LP-Tokens approach (with cumulative rewards), without address loops, is the best option, but I do not find how they do it in Uniswap or PancakeSwap.

Any help is welcome, thanks.

1 Answer 1

1

So one thing you should consider here, you don't want users to be able to frontrun the stablecoin deposits from the main contract. Ie. stake right before the rewards are added and unstake right after.

Another aspect of building a good system here is minimizing storage, and therefore gas. You can record a lot of information every time a user stakes, or the stablecoins come in, calculate rates and add a lot of complex logic to solve this in theory, but it would be very gas heavy.

This problem kind of reminds me of how uniswap v3 handles its tickpoints. Maybe you could use a similar approach?

What you could do is develop a time * capital index for all funds in the contract. Every time someone stakes or unstakes, this value is updated. Every time capital is deposited from the main contract, a tickpoint is created, and the total time * capital that occurred between that tickpoint and the current is calculated. Downside of this approach is that the claim operation would need to loop through multiple tickpoints and add up how much rewards the user is owed. The operation would be purely arithmetical, but could require looping through a lot of tickpoints if the user has not claimed in a long time. Also each tickpoint will use some storage to store the time*captial index, the USDT amount deposited, and a timestamp. This would technically split users rewards exactly fairly based on their staking time however. (More info on the similar system uniswap v3 uses https://uniswap.org/whitepaper-v3.pdf)

I think a time*capital index might be useful, but it will be different between every deposit that happens from the main contract, since deposits differ.

Maybe you could also put a limit of users not being able to unstake for a day or something after they stake, and do a simpler setup of splitting the rewards based purely on the capital in the contract or something.

I think you definitely have some different options and tradeoffs here. It might be worth exploring how much gas the tickpoint uniswap v3 model would use here though, since it is a pretty well engineered algorithmic system already used in smart contracts.

2
  • Thanks too much for your suggestions, and the Uniswap's tickpoint approach. Without doubts, very interesting but maybe too complex and potentially gas expensive for a process that should be a simple flow. Sep 6, 2023 at 6:34
  • Yeah. Maybe you could use that index of time*capital though. I'm not sure exactly how it would work, but it might be a useful pattern in your case.
    – Bruce
    Sep 7, 2023 at 3:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.