Truffle test 和 Truffle dev 中的“VM Exception while processing transaction: revert”
這是我的問題:)
問題
“錯誤:返回錯誤:處理事務時的 VM 異常:還原”時到達傳輸函式。我認為這與我的 Crowdsale 智能合約沒有任何我的代幣 TotalSupply 的事實有關。
重現步驟
代幣智能合約:
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_); } }
眾售智能合約:
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; } }
測試 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; }); }); */ });
部署腳本:
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; };
出現錯誤的第一種方法:
通過 cmd 啟動 ganache-cli:
ganache-cli
編譯:
truffle compile
啟動測試:
truffle test ./test/TokenCrowdsale.test.js
第二種方法有錯誤:
通過 cmd 啟動 ganache-cli:
ganache-cli
編譯:
truffle compile
遷移:
truffle migrate
使用控制台:
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 })
預期行為
Crowdsale 智能合約有一個固定的總供應量,我不想要任何鑄造的代幣。所以我希望眾籌進行交易以購買代幣並將其轉移給使用者。
實際結果
環境
- 作業系統:Windows 10
- 乙太坊客戶端:Ganache-cli => v6.9.1, Ganache-core => v2.10.2
- 松露版 (
truffle version
): v5.1.20- 節點版本(
node --version
):v12.16.1- npm 版本(
npm --version
):v6.13.4更多資訊
我正在學習 DappUniversity 的教程:Real World Ico 來幫助我建構我的智能合約功能。我對遇到的許多錯誤進行了大量研究,幾乎每次都能找到解決方案。但是虛擬機錯誤是廢話,並沒有說明任何事情。另外,我沒有發現任何與我類似的問題。只有一個話題指出了這樣一個事實,即大多數情況下,如果不是用氣問題,那是因為我們沒有任何代幣可以購買,這對我來說是正確的。我真的認為這是因為我的眾籌中沒有任何 totalSupply,所以他無法購買/轉讓/等…我被困住了,因為我找不到解釋如何獲得固定總數的教程或主題供應。實際上有一個真正堅持我的情況,那就是那個話題:
但是他在眾籌合約的幫助下創建了代幣合約,我不想這樣做,我也不想這樣做。我需要找到一種方法將totalSupply 從代幣合約轉移到眾籌合約,並用松露測試對其進行測試。
非常感謝您的幫助窺視!
你的眾籌合約沒有代幣可以轉移給買家。
您在測試文件中轉移了代幣合約的所有權,但總供應量仍由代幣合約部署者地址控制。從外觀上看應該是您的帳戶
$$ 0 $$. 在你的兩個合約都被實例化後,你需要做一些類似的事情:
await this.token.transfer(desiredAmountOfTokens, this.crowdsale.address)
是的,我很確定這是因為你說我的 Crowdsale 契約沒有供應。我期待從 Token.sol 智能合約進行轉賬,但實際上,我需要在測試腳本中進行嗎?所以我不能讓它成為一個自動化?我需要在部署後手動將供應從代幣轉移到眾籌嗎?
非常感謝您的寶貴時間!