we are building a staking smart contract on the ethereum mainnet. All the stakes will need to have a timestamp attached to it, so our logic relies on time. The logic is at the end of the month each stakeholders will have credits, and based on that the contract owner will attribute rewards to each stakeholders based on their creditsAmount. But in order to get the total creditsAmount we need to iterate through the list of stakeHolders, which is super expensive. Here is a very short example of our staking contract:

pragma solidity 0.8.6;
import “@openzeppelin/contracts/utils/structs/EnumerableSet.sol”;

contract Test {
  using EnumerableSet for EnumerableSet.AddressSet;

  struct Stake {
    uint256 lockedToken;
    uint256 creditsEarned; // numberOfDays * lockedToken = 15days * 1000 = 15000
  // Rewards = RatioOfCreditsEarnedByStakeholder * MonthlyRewards

  EnumerableSet.AddressSet private stakeholders;
  mapping(address => Stake) private stakeholderToStake;

  function createStake(
   address stakeholder,
   uint256 lockedToken,
   uint256 creditsEarned
  ) public {
   stakeholderToStake[stakeholder] = Stake({
     lockedToken: lockedToken,
     creditsEarned: creditsEarned
function distributeRewards() public {
  uint256 totalCredits = 0;
  for (uint256 i = 0; i < stakeholders.length(); i++) {
    totalCredits += stakeholderToStake[stakeholders.at(i)].creditsEarned;

So as you can imagine the very last loop is extremely costly but we did not find another way to do so for now. Do you have any idea proposition on how to avoid such loop? or other staking contract which relies on time like us?


1 Answer 1


I'm not 100% sure on the use case of your distributeRewards function. It looks like the function isn't complete yet and that you're missing some logic at the end since you don't do anything with the totalCredits variable.

I also don't see any functionality for how the stakers gets their creditsEarned to increase.

But I will go along with what I know so far. If you need a sum of all credits that have been earned for a specific period. You should have a state variable called maybe sumOfCredits that starts off at 0, and that get's increased every time Stake.creditsEarned is increased for an individual staker. So the total sum of credits keeps accumulating in the state variable as more and more people get's their individual reward.

Then once you want to distribute the reward in distributeRewards, you can instead just use the total which now is saved already in the state varaible sumOfCredits. And if you after that point want to reset it back to 0, you can do it in that same function also.

But it is a little weird implementation of staking. What most projects will do is to let each stakeholder distribute or mint their own reward. They pay for the gas required to get their own reward. But I hope this helped.

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.