Environment
Truffle v5.2.6 (core: 5.2.6)
Solidity - 0.5.5 (solc-js)
Node v14.16.0
Web3.js v1.2.9
Details
I developing a TimedCrowdsale contract, but I’m open the Crowdsale for the test
Code to reproduce
//Crowdsale Test
import ether from './helpers/ether';
import EVMRevert from './helpers/EVMRevert';
import { latest, advanceBlock, increase, duration } from '@openzeppelin/test-helpers/src/time';
const BN = web3.utils.BN;
const PFBTokenCrowdsale = artifacts.require('PFBTokenCrowdsale');
const PFBToken = artifacts.require('PFBToken');
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bn')(BN))
.should();
contract('PFBTokenCrowdsale', function([_ , wallet, investor1, investor2]) {
before(async function () {
// Advance to the next block to correctly read time in the solidity "now" function interpreted by ganache
await advanceBlock();
// Advance time to crowdsale start
await increase(duration.weeks(2));
});
beforeEach(async function () {
// Token config
this.name = "PFB Test Token";
this.symbol = 'PFBT';
this.decimals = new BN(18);
// Deploy Token
this.token = await PFBToken.new(
this.name,
this.symbol,
this.decimals
);
// Crowdsale config
this.rate = new BN(1500000);
this.wallet = wallet;
this.cap = ether(14001);
this.openingTime = new BN(latest() + duration.weeks(1));
this.closingTime = this.openingTime + duration.years(1);
this.goal = ether(14000);
// Investor caps
this.investorMinCap = ether(0.01);
this.inestorHardCap = ether(350);
this.crowdsale = await PFBTokenCrowdsale.new(
this.rate,
this.wallet,
this.token.address,
this.cap,
this.openingTime,
this.closingTime,
this.goal
);
// Add Mintor Role to crowdsale
await this.token.addMinter(this.crowdsale.address);
// Add investors to whitelist
await this.crowdsale.addWhitelisted(investor1);
await this.crowdsale.addWhitelisted(investor2);
});
describe('crowdsale', function() {
it('tracks the rate', async function() {
const rate = await this.crowdsale.rate();
rate.should.be.bignumber.equal(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);
});
});
describe('minted crowdsale', function() {
it('mints tokens after purchase', async function() {
const originalTotalSupply = await this.token.totalSupply();
await this.crowdsale.sendTransaction({ value: ether(1), from: investor1 });
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.bignumber.equal(this.cap);
});
});
describe('timed crowdsale', function() {
it('is open', async function() {
const isOpen = await this.crowdsale.isOpen();
isOpen.should.be.true;
});
it('is not closed yest', async function() {
const isClosed = await this.crowdsale.hasClosed();
isClosed.should.be.false;
});
});
describe('whitelisted crowdsale', function() {
it('rejects contributions from non-whitelisted investors', async function() {
const notWhitelisted = _;
await this.crowdsale.buyTokens(notWhitelisted, { value: ether(1), from: notWhitelisted }).should.be.rejectedWith(EVMRevert);
});
});
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 contribution is less than the minimum cap', function() {
it('rejects the transaction', async function() {
const value = ether(0.0001);
await this.crowdsale.buyTokens(investor2, { value: value, from: investor2 }).should.be.rejectedWith(EVMRevert);
});
});
describe('when the investor has already met the minimum cap', function() {
it('allows the investor to contribute below the minimum cap', async function() {
// First contribution is valid
const value1 = ether(1);
await this.crowdsale.buyTokens(investor1, { value: value1, from: investor1 });
// Second contribution is less than investor cap
const value2 = ether(0.001);
await this.crowdsale.buyTokens(investor1, { value: value2, from: investor1 }).should.be.fulfilled;
});
});
});
describe('when the contribution is within the valid range', function () {
const value = ether(2);
it('succeeds & updates the contribution 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.equal(value);
});
});
});
//end of code
the results
Compiling your contracts...
===========================
> Compiling .\contracts\PFBTokenCrowdsale.sol
> Artifacts written to C:\Users\Ahmed\AppData\Local\Temp\test--10724-ZPTnk01x4uXs
> Compiled successfully using:
- solc: 0.5.5+commit.47a71e8f.Linux.g++
web3-shh package will be deprecated in version 1.3.5 and will no longer be supported.
web3-bzz package will be deprecated in version 1.3.5 and will no longer be supported.
Contract: PFBTokenCrowdsale
crowdsale
√ tracks the rate (218ms)
√ tracks the wallet (104ms)
√ tracks the token (151ms)
minted crowdsale
1) mints tokens after purchase
Events emitted during test:
---------------------------
PFBToken.MinterAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBToken.PauserAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBTokenCrowdsale.WhitelistAdminAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
Warning: Could not decode event!
PFBToken.MinterAdded(
account: <indexed> 0x00344a1debD5F462a88325e923f1e75966b74206 (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0x743c38F8d164706dF82Ab25EC8a45cCBaCfF486e (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0xA02495B4577421D23F98565e2636c84253Cf022d (type: address)
)
---------------------------
capped crowdsale
√ has the correct hard cap (135ms)
timed crowdsale
2) is open
Events emitted during test:
---------------------------
PFBToken.MinterAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBToken.PauserAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBTokenCrowdsale.WhitelistAdminAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
Warning: Could not decode event!
PFBToken.MinterAdded(
account: <indexed> 0x52e3D517B63aB5A97CF8363A4E619A725ae38612 (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0x743c38F8d164706dF82Ab25EC8a45cCBaCfF486e (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0xA02495B4577421D23F98565e2636c84253Cf022d (type: address)
)
---------------------------
√ is not closed yest (157ms)
whitelisted crowdsale
√ rejects contributions from non-whitelisted investors (507ms)
accepting payments
3) should accept payments
Events emitted during test:
---------------------------
PFBToken.MinterAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBToken.PauserAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBTokenCrowdsale.WhitelistAdminAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
Warning: Could not decode event!
PFBToken.MinterAdded(
account: <indexed> 0x1319Fa7A68870E11dE058A6cB8b64B1f581eE428 (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0x743c38F8d164706dF82Ab25EC8a45cCBaCfF486e (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0xA02495B4577421D23F98565e2636c84253Cf022d (type: address)
)
---------------------------
buyTokens()
when the contribution is less than the minimum cap
√ rejects the transaction (325ms)
when the investor has already met the minimum cap
4) allows the investor to contribute below the minimum cap
Events emitted during test:
---------------------------
PFBToken.MinterAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBToken.PauserAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBTokenCrowdsale.WhitelistAdminAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
Warning: Could not decode event!
PFBToken.MinterAdded(
account: <indexed> 0x4f6b8385ef4c1F1C9E2bdbaea833532bD3A2e311 (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0x743c38F8d164706dF82Ab25EC8a45cCBaCfF486e (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0xA02495B4577421D23F98565e2636c84253Cf022d (type: address)
)
---------------------------
when the contribution is within the valid range
5) succeeds & updates the contribution amount
Events emitted during test:
---------------------------
PFBToken.MinterAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBToken.PauserAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
PFBTokenCrowdsale.WhitelistAdminAdded(
account: <indexed> 0xB4D759068d9057822baE22b77b38139b96ae2BE8 (type: address)
)
Warning: Could not decode event!
PFBToken.MinterAdded(
account: <indexed> 0x8eD879B552ebDC112239B82C79387eCF06B670F8 (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0x743c38F8d164706dF82Ab25EC8a45cCBaCfF486e (type: address)
)
PFBTokenCrowdsale.WhitelistedAdded(
account: <indexed> 0xA02495B4577421D23F98565e2636c84253Cf022d (type: address)
)
---------------------------
7 passing (29s)
5 failing
1) Contract: PFBTokenCrowdsale
minted crowdsale
mints tokens after purchase:
Error: Returned error: VM Exception while processing transaction: revert TimedCrowdsale: not open -- Reason given: TimedCrowdsale: not open.
at Context._callee6$ (test\PFBTokenCrowdsale.test.js:188:37)
at tryCatch (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:65:40)
at Generator.invoke [as _invoke] (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:303:22)
at Generator.prototype.<computed> [as next] (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:117:21)
at step (test\PFBTokenCrowdsale.test.js:17:191)
at D:\Google Drive\PFB\Test\PFB_Token_Sale\test\PFBTokenCrowdsale.test.js:17:361
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
2) Contract: PFBTokenCrowdsale
timed crowdsale
is open:
AssertionError: expected false to be true
+ expected - actual
-false
+true
at Context._callee9$ (test\PFBTokenCrowdsale.test.js:256:32)
at tryCatch (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:65:40)
at Generator.invoke [as _invoke] (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:303:22)
at Generator.prototype.<computed> [as next] (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:117:21)
at step (test\PFBTokenCrowdsale.test.js:17:191)
at D:\Google Drive\PFB\Test\PFB_Token_Sale\test\PFBTokenCrowdsale.test.js:17:361
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
3) Contract: PFBTokenCrowdsale
accepting payments
should accept payments:
AssertionError: expected promise to be fulfilled but it was rejected with 'Error: Returned error: VM Exception while processing transaction: revert TimedCrowdsale: not open -- Reason given: TimedCrowdsale: not open.'
4) Contract: PFBTokenCrowdsale
buyTokens()
when the investor has already met the minimum cap
allows the investor to contribute below the minimum cap:
Error: Returned error: VM Exception while processing transaction: revert TimedCrowdsale: not open -- Reason given: TimedCrowdsale: not open.
at Context._callee14$ (test\PFBTokenCrowdsale.test.js:365:39)
at tryCatch (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:65:40)
at Generator.invoke [as _invoke] (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:303:22)
at Generator.prototype.<computed> [as next] (node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:117:21)
at step (test\PFBTokenCrowdsale.test.js:17:191)
at D:\Google Drive\PFB\Test\PFB_Token_Sale\test\PFBTokenCrowdsale.test.js:17:437
at new Promise (<anonymous>)
at Context.<anonymous> (test\PFBTokenCrowdsale.test.js:17:99)
at processImmediate (internal/timers.js:461:21)
5) Contract: PFBTokenCrowdsale
when the contribution is within the valid range
succeeds & updates the contribution amount:
AssertionError: expected promise to be fulfilled but it was rejected with 'Error: Returned error: VM Exception while processing transaction: revert TimedCrowdsale: not open -- Reason given: TimedCrowdsale: not open.'
The block time, noting that I’m running the code on Apr 02, 2021
Transaction: 0xcbfc10f19e0b7a6f996f6c8f9d3bc1314de67092bafc426860be11c4d3032c86
Gas usage: 27245
Block Number: 151
Block Time: Fri Apr 30 2021 18:51:20 GMT+0300 (Eastern European Summer Time)