0

I am working on a dapp with a pyramid structure. when a user stake/withdraw tokens to/from this contract a % of the token amount goes to the existing staking holder in the pool.

I am currently approaching it by looping through each round and re-write the information.

for example, every time user stake/withdraw, the round count will +1. I make variables to store all information in each round like below:

    mapping(uint256 => uint256) public rewardOnRound; // the reward amount in certain round
    mapping(address => userDetail) public users; // each user info has their hold on certain round
    mapping(uint256 => uint256) public poolSizeOnRound; // the poolSize in certain round

    struct userDetail {
        uint256 hold;
        mapping (uint256 => uint256) holdOnRound;
    }

Every time user withdraws tokens, I will need to loop through all the round and update this information. And I found that when the round depth increment by 1, the transaction fee in bnb will increase 0.005. It means if users keep staking and withdrawing more than 100 times. and the transaction fee will become 0.5BNB. it is horrible.

Does anybody have the suggestion to get me out of this situation?

The most costly function is withdraw. the code is like below:


    function withdraw(uint256 _amount) public nonReentrant onlyUnlock {
        require(_amount <= users[msg.sender].hold, "Token balance is not enough");

        uint256 rewardClaim = getRewardUntilLastRound(msg.sender);
        uint256 feeForClaim = calcClaimFeeAmt(rewardClaim);
        uint256 redistroForClaim = calcRedistroAmt(rewardClaim);

        // uint256 feeForWithdraw = _amount.mul(withdrawFee).div(100);
        uint256 feeForWithdraw = calcWithdrawFeeAmt(_amount);
        uint256 redistroForWithdraw = calcRedistroAmt(_amount);


        token.transfer( msg.sender, rewardClaim - feeForClaim - redistroForClaim + _amount - feeForWithdraw - redistroForWithdraw);
        token.transfer( owner(), feeForClaim.add(feeForWithdraw));
        redistro(redistroForClaim.add(redistroForWithdraw));
        
        // restructure RewardOnRound
        updateRewardOnRound(msg.sender);

        // restructure poolsizeOnRound
        updatePoolSizeOnRound(msg.sender);

        // Left amount will be on currentRound
        users[msg.sender].holdOnRound[currentRound] =  users[msg.sender].hold.sub(_amount);
        poolSizeOnRound[currentRound] = poolSizeOnRound[currentRound] + users[msg.sender].hold.sub(_amount);

        // restructure user info
        users[msg.sender].hold = users[msg.sender].hold.sub(_amount);  
        updateHoldOnRound(msg.sender);

        // update pool size
        poolSize = poolSize.sub(_amount);
        totalReward = totalReward.sub(rewardClaim);

        if (users[msg.sender].hold == 0) {
            participatorCount = participatorCount.sub(1);
        }

        feeOnRound[currentRound] = feeOnRound[currentRound].add(feeForClaim).add(feeForWithdraw);
        emit withdrawn(msg.sender, _amount);
    }

    // Internal utils
    function getTotUserHoldOnRound(address _user, uint256 _round) internal view returns(uint256){
        uint256 holdUntilround;
        for( uint i = 1; i <= _round; i++){
            if (users[_user].holdOnRound[i] > 0) {
                holdUntilround = holdUntilround.add(users[_user].holdOnRound[i]);
            }
        }
        return holdUntilround;
    }

    function getTotPoolSizeOnRound(uint256 _round) internal view returns(uint256){
        uint256 poolSizeUntilround;
        for( uint i = 1; i <= _round; i++){
            poolSizeUntilround = poolSizeUntilround.add(poolSizeOnRound[i]);
        }
        return poolSizeUntilround;
    }

    function updateHoldOnRound(address _user) private {
        for (uint i = 1; i<= currentRound.sub(1); i++) {
            if (users[_user].holdOnRound[i] > 0) {
                users[_user].holdOnRound[i] = 0;
            }
        }
    }

    function updateRewardOnRound(address _user) private {
        for (uint i = 1; i <= currentRound.sub(1); i++) {
            uint256 totUserHoldOnRound = getTotUserHoldOnRound(_user, i);
            uint256 totPoolSizeOnRound = getTotPoolSizeOnRound(i);
            if ((totPoolSizeOnRound > 0) && (totUserHoldOnRound > 0)) {
                rewardOnRound[i] = rewardOnRound[i] - rewardOnRound[i].mul(totUserHoldOnRound).div(totPoolSizeOnRound);
            }
        }
    }

    function updatePoolSizeOnRound(address _user) private {
        
        for( uint i = 1; i <= currentRound.sub(1); i++){
            if ((poolSizeOnRound[i] > 0) && (users[_user].holdOnRound[i] > 0)) {
                poolSizeOnRound[i] = poolSizeOnRound[i] - users[_user].holdOnRound[i];
            }
        }
    }

    function redistro(uint256 _amount) private {
        require(rewardOnRound[currentRound] == 0, "Reward needs to be == 0 before input");
        rewardOnRound[currentRound] = _amount;
        totalReward = totalReward.add(_amount);
        currentRound += 1;
        emit redistrobuted(block.timestamp, _amount);
    }
``
Really appreciate your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.