Hey guys, here is my issue !
Issue
“Error: Returned error: VM Exception while processing transaction: revert” when reaching the Transfer function. I think it is related to the fact that my Crowdsale smart contrat hasn’t any TotalSupply of my tokens.
Steps to Reproduce
Token Smart contract :
pragma solidity 0.5.16;
import "openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/PausableToken.sol";
contract TokenSPC is PausableToken, DetailedERC20
{
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _amount)
DetailedERC20(_name, _symbol, _decimals)
public
{
require(_amount > 0, "amount has to be greater than 0");
totalSupply_ = _amount.mul(10 ** uint256(_decimals));
balances[msg.sender] = totalSupply_;
emit Transfer(address(0), msg.sender, totalSupply_);
}
}
Crowdsale Smart Contract :
pragma solidity 0.5.16;
import './TokenSPC.sol';
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/PausableToken.sol";
import "openzeppelin-solidity/contracts/crowdsale/Crowdsale.sol";
import "openzeppelin-solidity/contracts/crowdsale/validation/CappedCrowdsale.sol";
contract TokenCrowdsale is Crowdsale, CappedCrowdsale {
//Minim invest contrib
//Max invest contrib
uint256 public investorMinCap = 2000000000000000;
uint256 public investorHardCap = 50000000000000000000;
mapping(address => uint256) public contributions;
constructor(
uint256 _rate,
address payable _wallet,
ERC20 _token,
uint256 _cap
)
Crowdsale(_rate, _wallet, _token)
CappedCrowdsale(_cap)
public
{
}
function getUserContribution(address _beneficiary)
public view returns(uint256)
{
return contributions[_beneficiary];
}
function _preValidatePurchase(
address _beneficiary,
uint256 _weiAmount
)
internal
{
super._preValidatePurchase(_beneficiary,_weiAmount);
uint256 _existingContribution = contributions[_beneficiary];
uint256 _newContribution = _existingContribution.add(_weiAmount);
require(_newContribution >= investorMinCap && _newContribution <= investorHardCap);
contributions[_beneficiary] = _newContribution;
}
}
Test Crowdsale.test :
import ether from './helpers/ether';
import sendTransaction from './helpers/sendTransaction';
import EVMRevert from './helpers/EVMRevert';
const BN = web3.utils.BN;
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bn')(BN))
.should();
const Token = artifacts.require('TokenSPC');
const TokenCrowdsale = artifacts.require('TokenCrowdsale');
contract('TokenCrowdsale', function([_, wallet, investor1, investor2]) {
/*before(async function() {
// Transfer extra ether to investor1's account for testing
await web3.eth.sendTransaction({ from: _, to: investor1, value: ether(60) })
await web3.eth.sendTransaction({ from: _, to: investor2, value: ether(20) })
});*/
beforeEach(async function () {
// Token config
this.name = "Seed Project Coin";
this.symbol = "SPC";
this.decimals = 18;
this.amount = 380000000;
// Deploy Token
this.token = await Token.new(
this.name,
this.symbol,
this.decimals,
this.amount
);
// Crowdsale config
this.rate = new BN(500);
this.wallet = wallet;
this.cap = ether(100);
//Invest cap
this.investorMinCap = ether(0.002);
this.investorHardCap = ether(50);
this.crowdsale = await TokenCrowdsale.new(
this.rate,
this.wallet,
this.token.address,
this.cap
);
// Transfer token owern to crowdsale
await this.token.transferOwnership(this.crowdsale.address);
});
describe('token', function() {
it("should check totalSupply", async function() {
const _supp = await this.token.totalSupply();
console.log( " ", "totalSupply =", _supp.toString());
});
});
describe('crowdsale', function() {
it('tracks the rate', async function() {
const _rate = await this.crowdsale.rate();
//console.log( " ", "Rate =", _rate );
//console.log( " ", "this.rate =", this.rate );
_rate.should.be.a.bignumber.that.equals(this.rate);
});
it('tracks the wallet', async function() {
const wallet = await this.crowdsale.wallet();
wallet.should.equal(this.wallet);
});
it('tracks the token', async function() {
const token = await this.crowdsale.token();
token.should.equal(this.token.address);
});
});
//A revoir---------------------------------------------
/* describe('actualization crowdsale', function() {
it('actualize total supply of crowdsale after purchase', async function() {
const originalTotalSupply = await this.token.totalSupply();
this.token.totalSupply_ -= 1;
const newTotalSupply = await this.token.totalSupply();
assert.isTrue(newTotalSupply < originalTotalSupply)
});
});*/
describe('capped crowdsale', async function() {
it('has the correct hard cap', async function() {
const _cap = await this.crowdsale.cap();
_cap.should.be.a.bignumber.that.equals(this.cap);
});
});
//A revoir ---------------------------------------------
/*describe('accepting payments', function() {
it('should accept payments', async function() {
const value = ether(1);
const purchaser = investor2;
await this.crowdsale.sendTransaction({ value : value, from : investor1}).should.be.fulfilled;
await this.crowdsale.buyTokens(investor1, { value: value, from: purchaser }).should.be.fulfilled;
});
});*/
describe('buyTokens()', function() {
describe('when the contrib is less than min cap', function(){
it('rejects the transaction', async function() {
const value = this.investorMinCap - 1;
await this.crowdsale.buyTokens(investor2, { value: value, from: investor2 }).should.be.rejectedWith(EVMRevert);
});
});
describe('when the invest has already met the min cap', function(){
it('allows the invest to contrib below the min cap', async function() {
//isvalid
const value1 = ether(1);
await this.crowdsale.buyTokens(investor1, { value: value1, from: investor1 });
console.log( " ", "inv =", investor1 );
console.log( " ", "value =", value1 );
console.log( " ", "inv.value =", await this.crowdsale.buyTokens(investor1, { value: value1, from: investor1 }) );
//is less then invest cap
const value2 = 1; //wei
await this.crowdsale.buyTokens(investor1, { value: value2, from: investor1 }).should.be.fulfilled;
});
});
});
/*---------------A revoir
describe('when the total contrib exceed the invest hardcap', function(){
it('reject the transaction', async function() {
//first contrib in valid range
const value1 = ether(2);
await this.crowdsale.buyTokens(investor1, { value: value1, from: investor1});
//second is over hardcap
const value2 = ether(49);
await this.crowdsale.buyTokens(investor1, { value: value2, from: investor1}).should.be.rejectedWith(EVMRevert);
});
});
describe('when the contrib is within the valid range', function() {
const value = ether(2);
it('succeeds & updates the contrib amount', async function() {
await this.crowdsale.buyTokens(investor2, { value: value, from: investor2 }).should.be.fulfilled;
const contribution = await this.crowdsale.getUserContribution(investor2);
contribution.should.be.bignumber.equals;
});
});
*/
});
Deployement script :
const Token = artifacts.require("./TokenSPC.sol");
const TokenCrowdsale = artifacts.require("./TokenCrowdsale.sol");
const ether = (n) => new web3.utils.BN(web3.utils.toWei(n.toString(), 'ether'));
const duration = {
seconds: function (val) { return val; },
minutes: function (val) { return val * this.seconds(60); },
hours: function (val) { return val * this.minutes(60); },
days: function (val) { return val * this.hours(24); },
weeks: function (val) { return val * this.days(7); },
years: function (val) { return val * this.days(365); },
};
module.exports = async function(deployer, network, accounts) {
const _name = "Seed Project Coin";
const _symbol = "SPC";
const _decimals = 18;
const _amount = 380000000;
await deployer.deploy(Token , _name, _symbol, _decimals, _amount );
const deployedToken = await Token.deployed();
const _rate = 1;
const _wallet = accounts[0]; // TODO: Replace me
const _token = deployedToken.address;
const _cap = ether(100);
await deployer.deploy(
TokenCrowdsale,
_rate,
_wallet,
_token,
_cap
);
return true;
};
First method to have the error :
Launch ganache-cli via cmd : ganache-cli
Compile : truffle compile
Launch test : truffle test ./test/TokenCrowdsale.test.js
Second method to have the error :
Launch ganache-cli via cmd : ganache-cli
Compile : truffle compile
Migrate : truffle migrate
Use console : truffle console
Commands in truffle console :
- TokenSPC.deployed().then(instance => token = instance)
- TokenCrowdsale.deployed().then(instance => crowdsale = instance)
- web3.eth.getAccounts().then(function(acc){ accounts = acc })
- var tokenamount = 100 * 10**18
- token.transfer(accounts[1], tokenamount.toString())
- crowdsale.buyTokens(accounts[1], { from: accounts[1], value: 10000000000000000000 })
Expected Behavior
The Crowdsale smart contract have a fixed totalSupply, i don’t want any minted token. So i expect the crowdsale to do the transactions to buy the token and transfer it to the user.
Environment
- Operating System: Windows 10
- Ethereum client: Ganache-cli => v6.9.1, Ganache-core => v2.10.2
- Truffle version (
truffle version
): v5.1.20 - node version (
node --version
): v12.16.1 - npm version (
npm --version
): v6.13.4
More Informations
I’m following a tutorial from DappUniversity : Real World Ico to help me building my smart contract functionnalities. I’ve done a lot of research about a lot of errors that i’ve encountered and almost find everytime a solution. But the VM Error is crap and doesn’t tell anything. Also, i didn’t find any issue similar to mine. There’s only one topic pointing out the fact that most of the time if it’s not out-of-gas issue, it’s because we don’t have any token to buy, and that’s true for my part. I really think it’s because i don’t have any totalSupply in my crowdsale so he can’t buy / transfer / ect… and i’m stuck because i can’t find a tutorial or topic explaing how to have a fixed total supply. Actually there is one really sticking to my situation which is that topic :
But he is creating the token contract with the help of the crowdsale contract, and i’m not doing this neither i want to. I need to find a way to transfer the totalSupply from the token contract to the crowdsale contract, and to test it with truffle test.
Thanks a lot for your help peeps !