You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于OpenZeppelin的Crowdsale合约执行truffle test触发VM revert错误求助

Troubleshooting VM Revert Error in Truffle Crowdsale Test

Let's break down your problem step by step: first we'll clarify your code setup, then fix the logging issue to see detailed errors, and finally resolve the root cause of the revert.

Your Contract & Deployment Code

FloatFlowerTokenCrowdsale.sol

pragma solidity 0.4.23;
import "openzeppelin-solidity/contracts/crowdsale/Crowdsale.sol";
contract FloatFlowerTokenCrowdsale is Crowdsale{
 constructor(ERC20 _token) public Crowdsale(1000, msg.sender, _token) { }
}

FloatFlowerToken.sol

pragma solidity 0.4.23;
import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
contract FloatFlowerToken is StandardToken {
 string public name = "FLOATFLOWER TOKEN";
 string public symbol = "FFT";
 uint8 public decimals = 18;
 constructor() public {
 totalSupply_ = 36000000;
 balances[msg.sender] = totalSupply_;
 }
}

2_deploy_contract.js

const FloatFlowerToken = artifacts.require('./FloatFlowerToken.sol');
const FloatFlowerTokenCrowdsale = artifacts.require('./FloatFlowerTokenCrowdsale.sol');
module.exports = function(deployer, network, accounts) {
 return deployer
 .then(() => {
 return deployer.deploy(FloatFlowerToken);
 })
 .then(() => {
 return deployer.deploy(FloatFlowerTokenCrowdsale, FloatFlowerToken.address);
 })
};

Your Test Case & Error

You're testing the 1 ETH → 1000 FFT exchange rate, but running truffle test throws a vague Error: VM Exception while processing transaction: revert error with no details on where or why it's failing. Here's your test code:

it('one ETH should buy 1000 FLOATFLOWER TOKEN in Crowdsale', function(done) {
 FloatFlowerTokenCrowdsale.deployed().then(async function(instance) {
 const data = await instance.sendTransaction({from: accounts[7], value: web3.toWei(1, "ether")}, function(error, txhash) {
 console.log(error);
 });
 const tokenAddress = await instance.token.call();
 const FloatFlowerToken = FloatFlowerToken.at(tokenAddress);
 const tokenAmount = await FloatFlowerToken.balanceOf(accounts[7]);
 assert.equal(tokenAmount.toNumber(), 1000000000000000000000, 'The sender didn\'t receive the tokens as crowdsale rate.');
 })
})

Step 1: Get Detailed Error Logs

First, let's fix the logging issue so you can see exactly why the revert is happening. There are two simple ways to get verbose VM execution logs:

  1. Run tests with verbose RPC flag
    Execute your test command with the --verbose-rpc flag to print full execution details, including which function is reverting and its reason (OpenZeppelin contracts often include descriptive revert messages):

    truffle test --verbose-rpc
    
  2. Update Truffle config for permanent verbose logging
    Add verboseRpc: true to your development network in truffle-config.js (or truffle.js for older versions) to make verbose logs default:

    module.exports = {
      networks: {
        development: {
          host: "127.0.0.1",
          port: 7545,
          network_id: "*",
          verboseRpc: true // Add this line
        }
      }
    };
    

Step 2: Fix the Root Cause

Based on your code, the most likely reason for the revert is missing token access permissions for the crowdsale contract:

  • When you deploy FloatFlowerToken, all 36M tokens are assigned to the deployer (usually accounts[0]).
  • Your crowdsale contract tries to transfer tokens to the buyer when ETH is sent, but it has no tokens in its balance and no approval to spend the deployer's tokens.

Update Deployment Script

Modify 2_deploy_contract.js to either transfer tokens directly to the crowdsale contract or approve it to spend the deployer's tokens:

const FloatFlowerToken = artifacts.require('./FloatFlowerToken.sol');
const FloatFlowerTokenCrowdsale = artifacts.require('./FloatFlowerTokenCrowdsale.sol');
module.exports = async function(deployer, network, accounts) {
  // Deploy token first
  await deployer.deploy(FloatFlowerToken);
  const tokenInstance = await FloatFlowerToken.deployed();

  // Deploy crowdsale
  await deployer.deploy(FloatFlowerTokenCrowdsale, tokenInstance.address);
  const crowdsaleInstance = await FloatFlowerTokenCrowdsale.deployed();

  // Option 1: Approve crowdsale to spend all deployer's tokens
  await tokenInstance.approve(crowdsaleInstance.address, web3.toWei(36000000, 'ether'));

  // OR Option 2: Transfer all tokens directly to the crowdsale contract
  // await tokenInstance.transfer(crowdsaleInstance.address, web3.toWei(36000000, 'ether'));
};

Fix Test Code Async Handling

Your test mixes then() and async/await and doesn't properly call done(), which can lead to silent failures or incorrect execution timing. Rewrite it to use pure async/await:

it('one ETH should buy 1000 FLOATFLOWER TOKEN in Crowdsale', async function() {
  const crowdsaleInstance = await FloatFlowerTokenCrowdsale.deployed();
  
  // Send 1 ETH to crowdsale
  await crowdsaleInstance.sendTransaction({
    from: accounts[7],
    value: web3.toWei(1, "ether")
  });

  // Get token instance and check buyer's balance
  const tokenAddress = await crowdsaleInstance.token();
  const tokenInstance = await FloatFlowerToken.at(tokenAddress);
  const tokenAmount = await tokenInstance.balanceOf(accounts[7]);

  // Assert the correct amount (1000 * 10^18 decimals)
  assert.equal(tokenAmount.toString(), '1000000000000000000000', 'Incorrect token amount received');
});

Step 3: Verify Additional Checks

  • Solidity Version: You're using Solidity 0.4.23 which matches OpenZeppelin 2.x (the version you're importing), so this isn't an issue.
  • Crowdsale Constructor: You correctly pass the rate (1000), wallet (msg.sender = deployer), and token to the parent Crowdsale contract, so that configuration is valid.

After making these changes, run your tests again—they should pass, and you'll have clear logs to debug any future issues that pop up.

内容的提问来源于stack exchange,提问作者FloatFlower.Huang

火山引擎 最新活动