基于OpenZeppelin的Crowdsale合约执行truffle test触发VM revert错误求助
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:
Run tests with verbose RPC flag
Execute your test command with the--verbose-rpcflag to print full execution details, including which function is reverting and its reason (OpenZeppelin contracts often include descriptive revert messages):truffle test --verbose-rpcUpdate Truffle config for permanent verbose logging
AddverboseRpc: trueto your development network intruffle-config.js(ortruffle.jsfor 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 (usuallyaccounts[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 parentCrowdsalecontract, 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




