Я создаю краудсейл на основе 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
слова довольно новое, но его удаление не помогло.
Так как прямой звонок работает, а через контракт нет, то тут видимо есть тонкая разница.
Есть предположения?
Что ж, благодаря Антону с https://mothership.cx я смог разобраться, добавление gas: 300000
к вызову sendTransaction в тесте заставляет его работать.
Хотя это удивительно, так как я ожидал, что он выдаст ошибку об отсутствии газа.