【问题标题】:Called function suddenly needs to be payable调用函数突然需要支付
【发布时间】:2021-07-24 18:06:45
【问题描述】:

我有一个公共的 uint 变量,表示它是“哪一轮”,以及一个推进轮次并在轮次推进的同时进行处理的函数:

       uint public round;

       function completeRound() public inPaused() inRound() {
            if (round == 6) {
              // win
          } else {
            reduceByHalf();
            round.add(1);
          }
          
       }

如果我在 remix 中运行它,它运行了 4 次,然后在第 5 次始终失败,表明一个函数突然需要支付:

交易到 Playingwithsmartcontracts.completeRound 错误:VM 错误:还原。 revert 事务已恢复到初始状态。注意:如果您发送值并且您发送的值应该小于您当前的余额,则调用的函数应该是应付的。调试事务以获取更多信息。

如果我在调用 reduceByHalf 的地方注释掉 round.add(1),代码就会整天工作。我可以在 Remix 中无限期地单击它而不会出错。

奇怪的是,这开始是一个 Enum 来跟踪回合,并且有同样的问题。在推进枚举的同时,我可以在上述失败之前执行 5 次并将其注释掉,一切正常。

reduceByHalf 代码似乎不是问题,但如果它与问题有关,则显示如下:


    struct Foo {
        address owner;
        uint mintedRound;
        uint winningRound;
    }
    struct FooOwner {
        uint[] foos;
        uint totalWinningFoos;
    }

    uint[][5] roundFoos;
    uint[][5] roundWinners;

    mapping(uint => Foo) public winningFoos;
    mapping(address => FooOwner) public fooOwners;

    uint totalWinningFoos;

    function shuffleFoos (uint256[] memory _array) internal view returns(uint[] memory){
        uint[] memory clone = cloneArray(_array, _array.length);
    
        for (uint256 i = 0; i < clone.length; i++) {
            uint256 n = i + uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp))) % (clone.length - i);
            uint256 temp = clone[n];
            clone[n] = clone[i];
            clone[i] = temp;
        }
        
        return clone;
    }
    
    function cloneArray(uint256[] memory _array, uint256 _length) internal pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](_length);
        for (uint256 i = 0; i < _length; i++) {
            array[i] = _array[i];
        }
        return array;
    }

    function reduceByHalf() internal  {
        uint[] memory clone = shuffleFoos(roundFoos[round]);
      
        uint halfLength = 0;

        halfLength = roundFoos[round].length.div(2);
        for (uint w = 0; w < halfLength; w++) {
           
           uint fooId = clone[w];
           
           roundWinners[round].push(fooId); 
           winningFoos[round].winningRound = round;
           
           address fooOwner = winningFoos[fooId].owner;
           
           fooOwners[fooOwner].totalWinningFoos = fooOwners[fooOwner].totalWinningFoos.add(1);    
        }
        
        totalWinningFoos = totalWinningFoos.add(halfLength);
    }

据我所知,我没有发送价值,也不知道为什么它只认为我在交易执行 5 时发送价值。

有人能帮我理解 Remix/Solidity 对什么感到疯狂吗?

我完全不能理解某些东西,但它看起来像是关于数字 5 的东西......我可以将回合推进到 6,但是一旦我将 uint 值设置为 5,我就会开始看到这些问题。 ……好诡异……

【问题讨论】:

    标签: blockchain ethereum solidity remix


    【解决方案1】:

    交易已恢复到初始状态。

    这是您案例中错误消息的重要部分。

    注意:如果你发送值,调用的函数应该是支付的

    这只是一个注释,可能是因为这种组合经常发生。但由于您的函数和交易不会发送任何价值,因此不适用于您的情况。


    round.add(1);
    

    这个(失败的)sn-p 表明,应该有一个库used foruint,但它没有定义。我将使用SameMath 库,因为.add() 函数名称和uint 上的使用。但理论上,它可以是任何库,SafeMath 只是这种情况下最可能的选择。

    请注意,round.add(1);(使用 SafeMath)返回的 round 的值增加了 1,但它不会将(增加的)值存储在任何地方。这看起来像是一个错字,真正的用法应该是round = round.add(1);

    您的代码没有显示 SafeMath 库的任何用法,也没有显示 Solidity 版本,所以我将把答案分成 3 个部分。

    1. 您使用的是 Solidity 0.8+。

      不需要SameMath,因为整数溢出是在较低级别处理的,您可以放心替换

      // even with correctly imported SafeMath, it doesn't update the stored value
      round.add(1);
      

      // updates the stored value
      round++;
      
    2. 您使用的是 Solidity 0.7 或更早版本,以及 uint256 的 SafeMath(不是 uint

      改变定义

      uint public round;
      

      uint256 public round;
      

      这样一来,round 将使用 SafeMath,并允许使用函数 .add()

      请注意,您可能还想存储增量值,请参见上面示例的粗体段落。

    3. 您使用的是 Solidity 0.7 或更早版本,而根本没有使用 SafeMath。

      您需要import the SafeMath library,然后进行第 2 点所述的更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-14
      • 2012-04-22
      • 2014-11-18
      • 1970-01-01
      • 2018-03-05
      相关资源
      最近更新 更多