0

Why setting array.length to 0 triggers an error ?

I have a function that deletes an element from a dynamic array of structs within a mapping as follows:

mapping(address => MyStruct[]) public structs;

My problem is that if I try to set the length to 0 it triggers this error: VM Exception while processing transaction: invalid opcode

structs[_user].length = 0; // will trigger an error !

But: structs[_user].length = 1; will work fine.

It is inside a function like this one:

function _burn(uint index) internal {

  require(index < array.length);

  array[index] = array[array.length-1];
  delete array[array.length-1];

  // when array.length is set to 0 it doesn't work:

  array.length = array.length - 1; // doesn't work if array.length = 1

  array.length = 0;  // doesn't work

  // when array.length is set to a value > 0 it works:

  array.length = 1;  // works

  array.length = 2; //  works

}

I tried at least for 45 hours without findnig any idea why it happens! If you have encontered this issue or know how to handle it, help would be really appreciated.

P.S: I am using solidity 0.5.2

Here is the whole code:

// ----  Redeem a Stake ------  //

function stakeclmidx (uint _stakeidx) public {
require(_stakeidx >= 0);

Stake storage _stake = stakes[msg.sender][_stakeidx];

// See if the stake is over
require(block.number > _stake.endBlock);

require(_stake.amount > 0);

// transfer back token from contract to staker:
balances[address(this)] = balances[address(this)].sub(_stake.amount);

balances[msg.sender] = balances[msg.sender].add(_stake.amount);

emit Transfer(address(this), msg.sender, _stake.amount);
emit StakeClaimed(_stakeidx);

// deletes the stake
_deletestake(msg.sender, _stakeidx);

}

function _deletestake(address _staker,uint _index) internal {
require(_index < stakes[_staker].length);
stakes[_staker][_index] = stakes[_staker][stakes[_staker].length-1];
delete stakes[_staker][stakes[_staker].length-1];
stakes[_staker].length = stakes[_staker].length - 1;
// this last line is the one triggering the error
// it seems it triggers the error only when length value is set to 0
// stakes[_staker].length = 1; works !
// stakes[_staker].length = 2; works !
// stakes[_staker].length = 0; doesn't work !
}
// ----  Redeem a Stake ------  //

Best Regards

11
  • Get rid of the delete array[array.length-1], it's redundant here. Commented May 20, 2019 at 14:32
  • Thanks for reply @goodvibration. Yeah I tried removing this line but array.length = 0; still triggers an error!
    – Kevin Wad
    Commented May 20, 2019 at 14:38
  • 1. Did you recompile and redeploy the contract? 2. What do you mean by "triggers an error"? What sort of error do you get? If it's a VM revert, are you sure it is not due to the require statement at the beginning of the function? Commented May 20, 2019 at 15:01
  • @goodvibration Yes I recompiled and redeployed. I get this error: Error: Returned error: VM Exception while processing transaction: revert. I tried to remove the require but I still get this error...
    – Kevin Wad
    Commented May 20, 2019 at 15:22
  • 1
    After writing ~30 lines of code myself to get things to compile, everything worked fine as-is. So the rest of your code is probably more relevant than you think. As Rob asked for, please share code that actually compiles and runs.
    – user19510
    Commented May 21, 2019 at 1:48

1 Answer 1

0

Issue in your code is bellow line :

array[index] = array[array.length-1];

You don't assign values to dynamic array. You need to push . Change it to

array.push(array[array.length-1]);

and then this should work. Just call reduce length twice.

Keep in mind that minus is very risky. After length is zero, it will not give error if you decrement again and it will cause underflow and a new array will be initialized with underflown length.

pragma solidity ^0.5.0;

contract Arraytest{

    uint8[] public array ;

    constructor() public{
        for(uint8 counter=0; counter<2; counter++)
        {
            array.push(counter);
        }
    }

    event MESSAGE(uint _message);

    function reduceLength() public returns (uint8[] memory) {


            array.length--;
            emit MESSAGE(array.length);
            return array;



        //     array[index] = array[array.length-1];
        //     delete array[array.length-1];

        //   // when array.length is set to 0 it doesn't work:

        //   array.length = array.length - 1; // doesn't work if array.length = 1

        //   array.length = 0;  // doesn't work

        //   // when array.length is set to a value > 0 it works:

        //   array.length = 1;  // works

        //   array.length = 2; //  works

}

}
3
  • Thank you very much ! The problem with this method is that I can't delete a specific element from array unless I use loops. Are you sure about assignemnt to dynamic arrays? Because the method I try to implement seems to be used widely. Best reagards
    – Kevin Wad
    Commented May 22, 2019 at 18:58
  • Yes I am sure. I could not find anything related to this in documentation but I was able to replicate it with error of invalid op Code. Also, this only refers to dynamic sized arrays...Fixed size arrays may allow, will have to check. And yes, you can delete only last element
    – logeekal
    Commented May 23, 2019 at 2:37
  • Ok thank you very much for information ! It's reassuring to know you were able to replicate as I suspected I had an issue from my local environment since other people said it was working... I will probably use a mapping instead as arrays in solidity have too much constraints. Best regards
    – Kevin Wad
    Commented May 23, 2019 at 8:25

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.