Генерация токенов в токене MiniMe с помощью контракта на продажу по умолчанию вызывает недопустимую ошибку кода операции.

Я создаю краудсейл на основе MiniMeToken. Во время тестирования я столкнулся с очень странной проблемой.

Полный репозиторий исполняемого кода можно найти по адресу https://github.com/roderik/truffle-issue. Запустите testrpcи запустите truffle test, чтобы воспроизвести.

Эти тесты создают новый экземпляр токена и продажи для каждого теста. Генерация токенов непосредственно на токене работает отлично. Однако отправка средств в контракт на продажу завершается ошибкой с недопустимым кодом операции при создании токенов.

const MiniMeTokenFactory = artifacts.require('MiniMeTokenFactory');
const Campaign = artifacts.require('Campaign');
const MultiSigWallet = artifacts.require('MultiSigWallet');
const MyToken = artifacts.require('MyToken');

const timetravel = s => {
  return new Promise((resolve, reject) => {
    web3.currentProvider.sendAsync(
      {
        jsonrpc: '2.0',
        method: 'evm_increaseTime',
        params: [s],
        id: new Date().getTime(),
      },
      function(err) {
        if (err) return reject(err);
        resolve();
      }
    );
  });
};

contract('Campaign', function(accounts) {
  let factory;
  let token;
  let wallet;
  let sale;

  const startTime = 1505750400; // 09/18/2017 @ 4:00pm (UTC) = 5:00pm (CET)
  const endTime = 1508169600; // 10/16/2017 @ 4:00pm (UTC) = 5:00pm (CET)

  beforeEach(async () => {
    factory = await MiniMeTokenFactory.new();
    wallet = await MultiSigWallet.new(
      [
        accounts[7], // account_index: 7
        accounts[8], // account_index: 8
        accounts[9], // account_index: 9
      ],
      2
    );
    token = await MyToken.new(factory.address);
    sale = await Campaign.new(
      startTime,
      endTime,
      28125000000000000000000,
      wallet.address,
      token.address
    );
  });

  it('should return correct balances after generation', async function() {
    await token.generateTokens(accounts[1], 100);
    const totalSupply = await token.totalSupply();
    assert.equal(totalSupply.toNumber(), 100);
  });

  it('should work when trying to send ether during the sale', async function() {
    await token.changeController(sale.address);
    const { timestamp } = web3.eth.getBlock('latest');
    const travelTime = startTime - timestamp + 60; // 60 seconds after the start of the sale
    await timetravel(travelTime);
    web3.eth.sendTransaction({
      from: accounts[0],
      to: sale.address,
      value: web3.toWei(1, 'ether'),
    });
    const totalSupply = await token.totalSupply();
    assert.equal(totalSupply.toNumber(), 1200);
    const totalCollected = await sale.totalCollected;
    assert.equal(totalCollected.toNumber(), 1200);
    const balance0 = await token.balanceOf(accounts[0]);
    assert.equal(balance0.toNumber(), 1200);
  });
});

Отладка в течение нескольких часов привела меня к конкретным строкам, которые терпят неудачу, вызовы updateValueAtNowсбоя в generateTokens

/// @notice Generates `_amount` tokens that are assigned to `_owner`
/// @param _owner The address that will be assigned the new tokens
/// @param _amount The quantity of tokens generated
/// @return True if the tokens are generated correctly
function generateTokens(address _owner, uint _amount
) onlyController returns (bool) {
    uint curTotalSupply = totalSupply();
    require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
    uint previousBalanceTo = balanceOf(_owner);
    require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
    updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
    updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
    Transfer(0, _owner, _amount);
    return true;
}

Фактические строки, которые прерываются, - это два вызова установщика для newCheckpoint.

function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value
) internal  {
    if ((checkpoints.length == 0)
    || (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
           Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
           newCheckPoint.fromBlock =  uint128(block.number); // THIS ONE
           newCheckPoint.value = uint128(_value); // AND THIS ONE
       } else {
           Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
           oldCheckPoint.value = uint128(_value);
       }
}

Добавление ключевого storageслова довольно новое, но его удаление не помогло.

Так как прямой звонок работает, а через контракт нет, то тут видимо есть тонкая разница.

Есть предположения?

Ответы (1)

Что ж, благодаря Антону с https://mothership.cx я смог разобраться, добавление gas: 300000к вызову sendTransaction в тесте заставляет его работать.

Хотя это удивительно, так как я ожидал, что он выдаст ошибку об отсутствии газа.