0

I have a function, shown in its entirety below, that is getting the dreaded "stack too deep" compiler error. I have read several of the other posts on this forum about this error, but all of them attribute it to passing too many parameters to the function. As you can see below, I only have 5 method parameters. I have marked the line that the compiler via Remix flags with the "stack too deep" error.

Why am I getting this error and how can I fix it?:

/**
 * @notice Make payments to the bands and players by calculating and then storing
 *  the amount due them in the claims map.  For bands that we don't find an
 *  address for in the band addresses array, put the amount in escrow 
 *  for them, using the owner ID associated with a given ID as the escrow 
 *  key.
 * 
 * NOTE: All the array must be of the same length and are to be greated as 
 *  "columns" of a table where each array element at the same index is
 *   onefield value in the row.
 * 
 * @param _aryVideoIds - An array of the video IDs that were used in the 
 *  game.
 * @param _aryOwnerIds - An array of the entities that own each of the 
 *  videos.  For example, a channel ID for a YouTube video if this game was 
 *  played using YouTube videos, where the channel belongs to the band that 
 *  owns or made the video.
 * @param _aryWinnerAddr - an array of the winners from each round of 
 *  game play.  Each winner in a row won the round the video ID for the video 
 *  in the same row of the table.
 * @param _aryBandAddr - an array of the addresses for each band that 
 *  was involved in the game.  Each address corresponds to the band who the 
 *  video belongs to.
 */
function makePayments(
        uint256 _gameId, 
        bytes32[] _aryVideoIds, 
        bytes32[] _aryOwnerIds,
        address[] _aryWinnerAddr,
        address[] _aryBandAddr
    )
        public payable 
        onlyIfValidGameId(_gameId) onlyIfHouseAddr(_gameId) onlyIfPlayingState(_gameId) {
    // The arrays all must have the same length and that length must be non-zero.
    require(_aryVideoIds.length > 0, "(makePayments) The video ID array is empty.");
    require(_aryVideoIds.length == _aryOwnerIds.length && _aryOwnerIds.length == _aryWinnerAddr.length && _aryWinnerAddr.length == _aryBandAddr.length,
        "(makePayments) One or more of the given arrays is not the same length.");

    // Change the game to the GAME OVER state or several of the payment function 
    //  calls below will fail because they require the game to be in the game over 
    //  state.
    // --------->>>>>>>> STACK TOO DEEP ERROR OCCURS HERE.
    s_mapGameIdToGame[_gameId].gameState = enGameState.GAME_OVER;

    uint256 numVideos = _aryVideoIds.length;

    // Calculate the amount due the house.
    uint256 housePayment = s_mapGameIdToGame[_gameId].gameBalance.div(100).mul(pct_house);

    // Calculate the remaining funds after removing the house percentage.
    uint256 remainder = s_mapGameIdToGame[_gameId].gameBalance - housePayment;

    require(remainder > 0, "(makePayments) The game balance after removing the house percentage is 0 or less.");

    uint256 bandPaymentPool = 0;
    uint256 perBandPayment = 0;
    uint256 playerPaymentPool = 0;
    uint256 perVideoPayment = 0;

    // Calculate the amount of money to be distributed to the bands and the players.
    if (s_mapGameIdToGame[_gameId].bandDonationPercent == 0)
    {
        // Everything goes to the players.
        playerPaymentPool = remainder;
        perVideoPayment = playerPaymentPool.div(numVideos);
    } 
    else if (s_mapGameIdToGame[_gameId].bandDonationPercent == 100)
    {
        // Everything goes to the bands.
        bandPaymentPool = remainder;
        perBandPayment = bandPaymentPool.div(numVideos);
    }
    else
    {
        // Split the remainder between the players and the bands according to the 
        //  band donation percentage.
        bandPaymentPool = remainder.div(100).mul(s_mapGameIdToGame[_gameId].bandDonationPercent);

        // The band payment pool is distributed evenly across the number of participating bands.
        perBandPayment = bandPaymentPool.div(numVideos);

        // Calculate the amount of funds to distributed across the players.
        playerPaymentPool = remainder.sub(bandPaymentPool);

        // The per player payment is based on the number of games each player won.  However,
        //  the amount paid for each video is based on the number of videos in the game.
        perVideoPayment = playerPaymentPool.div(numVideos);
    }

    // Watch out for having no payments at all to make.
    require(perBandPayment == 0 && perVideoPayment == 0, "(makePayments) There are no payments to be made for this game, given the current amounts.");

    // Process the band payments if we have any.
    if (perBandPayment > 0)
    {
        processBandPayments(_gameId, _aryOwnerIds, _aryBandAddr, perBandPayment);
    }

    if (perVideoPayment > 0)
    {
        // Process the player payments.
        processPlayerPayments(_gameId, _aryWinnerAddr, perVideoPayment);
    }
}
1

Well you got 12 variables in function since parameters and return values are added into stack too. I believe modifier parameters are included too, as you can see here. So depending on what is in modifiers it may exceed the limit.

  • take a look at my self-answer as to what got rid of the error. I'd like your comments if you are so inclined. – Robert Oschler Jul 4 '18 at 3:43
1

I'm posting an answer to this in case it helps someone else. Be forewarned I have not done extensive careful testing to see the real details and ramifications of this fix.

It turns out it was some strange problem with the chaining of SafeMath operations in a single line. I am not saying it's a problem with SafeMath (a fantastic library). I'm just pointing out the line that I changed and by doing so, got rid of the problem. My uneducated guess at this point as to what the problem is, is that it may be a Solidity compiler issue.

This is the original line as per my original post:

// Calculate the amount due the house.
uint256 housePayment = s_mapGameIdToGame[_gameId].gameBalance.div(100).mul(pct_house);

Here is my refactoring of that line where all I did was break the arithmetic operations out to separate lines of code:

uint256 gameBalance = s_mapGameIdToGame[_gameId].gameBalance;
uint256 gameBalanceTemp = gameBalance.mul(pct_house);
uint256 housePayment = gameBalanceTemp.div(100);

After I did this, the "stack too deep" error went away. Your mileage may vary.

0

It may be related to the fact that you are passing variable sized arrays. Since the compiler doesn't know what size is expected, it can only assign an arbitrary size for that variable thus resulting in the same error that you see when too many parameters are passed. Edit (added some suggestions): You could work around this issue by:

  • Splitting the problematic function into multiple functions
  • take a look at my self-answer as to what got rid of the error. I'd like your comments if you are so inclined. – Robert Oschler Jul 4 '18 at 3:43
  • Arrays are passed by reference not by value. – Ismael Jul 30 '18 at 2:14

Your Answer

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

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