即使我使用以太坊官方网页中的示例也无法发送交易

Posted

技术标签:

【中文标题】即使我使用以太坊官方网页中的示例也无法发送交易【英文标题】:Can't send transaction even if I use an example from official Ethereum webpage 【发布时间】:2018-07-28 07:45:17 【问题描述】:

我使用 this example 编写 Crowdsale。但我无法发送交易,我的测试失败并出现错误:

 Contract: Crowdsale should accept payments after start:
     AssertionError: expected promise to be fulfilled but it was rejected with 'Error: VM Exception while processing the transaction: revert'

我尝试像 crowdsale.sendTransaction(value, from: buyer, gas: 4712388) 这样为交易设置汽油价格,但这对我没有帮助。

我的众筹:

pragma solidity ^0.4.16;

interface token 
  function transfer(address receiver, uint amount) external;


contract Crowdsale 
  address public beneficiary;
  uint public fundingGoal;
  uint public amountRaised;
  uint public deadline;
  uint public price;
  token public tokenReward;
  mapping(address => uint256) public balanceOf;

  event FundTransfer(address backer, uint amount, bool isContribution);

  function Crowdsale(
    address ifSuccessfulSendTo,
    uint fundingGoalInEthers,
    uint durationInMinutes,
    uint etherCostOfEachToken,
    address addressOfTokenUsedAsReward
  ) public 
    beneficiary = ifSuccessfulSendTo;
    fundingGoal = fundingGoalInEthers * 1 ether;
    deadline = now + durationInMinutes * 1 minutes;
    price = etherCostOfEachToken * 1 ether;
    tokenReward = token(addressOfTokenUsedAsReward);
  

  function () public payable 
    uint amount = msg.value;
    balanceOf[msg.sender] += amount;
    amountRaised += amount;
    tokenReward.transfer(msg.sender, amount / price);
    FundTransfer(msg.sender, amount, true);
  

我的测试(我以these tests 为例):

// You can find all those helpers here: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers
import ether from '../helpers/ether';
import  advanceBlock  from '../helpers/advanceToBlock';
import  increaseTimeTo, duration  from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';

const BigNumber = web3.BigNumber;

const should = require('chai')
  .use(require('chai-as-promised'))
  .use(require('chai-bignumber')(BigNumber))
  .should();

const Crowdsale = artifacts.require('Crowdsale');
const Coin = artifacts.require('Coin');

contract('Crowdsale', accounts => 
  let startTime;

  let crowdsale;
  let token;

  const value = ether(8);
  const buyer = accounts[1];
  const tokenReward = accounts[2];
  const beneficiary = accounts[2];

  before(async () => 
    // Advance to the next block to correctly read time in the solidity "now" function interpreted by testrpc
    await advanceBlock();
  );

  beforeEach(async () => 
    token = await Coin.new();

    // Transfer an amount to address of token used as reward
    const tokenRewardAmount = 1000000;
    token.transfer(tokenReward, tokenRewardAmount);

    startTime = latestTime() + duration.minutes(1);

    crowdsale = await Crowdsale.new(beneficiary, 200, 30, 1, tokenReward);
  );

  it('should accept payments after start', async () => 
    await increaseTimeTo(startTime);
    await crowdsale.sendTransaction(value, from: buyer, gas: 4712388).should.be.fulfilled;
  );
);

我的硬币:

pragma solidity ^0.4.19;

import 'zeppelin-solidity/contracts/token/ERC20/MintableToken.sol';

contract Coin is MintableToken 
  uint8 public decimals = 0;
  uint public initialSupply = 1000000 * 10 ** uint(decimals); // 1,000,000;
  string public name = "My Coin";
  string public symbol = "XMY";

  function Coin() public 
    totalSupply_ = initialSupply;
    balances[msg.sender] = totalSupply_;
  

我正在开发一个“博彩公司”,众筹代表一个事件。这意味着我将创建数十个众筹,但我不能使用 Zeppelin 的众筹,因为它会铸造一个代币。 那么,有没有其他方法可以在不铸造代币的情况下创建众筹?我认为应该有其他方法,因为来自以太坊网页的示例无需铸造,但由于某种原因,它对我不起作用。我的错误在哪里?我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

据我了解,您正在尝试向您的众筹合约发送一些资金。

改变这一行:

await crowdsale.sendTransaction(value, from: buyer, gas: 4712388).should.be.fulfilled;

到这里:

await web3.eth.sendTransaction(
  from: buyer, 
  to: crowdsale,
  value: value,  
  gas: 4712388
).should.be.fulfilled;

【讨论】:

嗨罗曼,感谢您的回答!我试过了,但失败并出现错误:AssertionError: expected promise to be fulfilled but it was rejected with 'Error: VM Exception while processing transaction: revert'。然后我将to: crowdsale 更改为to: crowdsale.address 也失败了。【参考方案2】:

首先,您的测试用例有一个错误。您发送的是受益人账户的地址,而不是代币合约的地址。

crowdsale = await Crowdsale.new(beneficiary, 200, 30, 1, tokenReward);

应该是

crowdsale = await Crowdsale.new(beneficiary, 200, 30, 1, token.address);

其次,正如所写的,由于几个原因,您将在事务本身中失败。您的后备功能正在尝试使用tokenReward.transfer(msg.sender, amount / price); 转移代币,然后硬币合约的transfer 方法使用require(_value <= balances[msg.sender]); 检查余额。 msg.sender 的两个值不同。在第一次使用中,msg.sender 将是buyer 的值。但是,在第二次使用中,msg.sender 是代币合约的地址。 msg.sender 是消息的发送者,而不是交易的原始发送者。

来自 Solidity 文档:

msg 的所有成员的值,包括 msg.sender 和 msg.value 可以随着每个外部函数调用而改变。这包括对库函数的调用。

例如,地址A调用合约B,合约B再调用合约C。msg.sender的值将是合约B内部的A,但它是合约B在合约C中的地址。

transfer 只能由令牌所有者直接调用。对于您的用例,您可以先调用approve,然后再调用transferFrom,或者由于您使用的是MintableToken,因此您可以铸造代币。您已经在使用 Zeppelin 库,因此请查看其 CrowdSale 合约中的 buyTokens 函数。

最后一点,在后备函数中放置太多逻辑绝不是一个好主意。它们被限制为 2300 gas,当您尝试做的不仅仅是记录事件时,它们经常会失败。

【讨论】:

嗨,亚当!谢谢,很有帮助!唯一的问题是我正在开发一个“博彩公司”,其中定制的 Crowdsales 代表事件。因此,我将创建数十个 Crowdsales。在 Zeppelin 的众筹中,它创建了一个代币合约,这意味着我只能创建一个“事件”。但是在以太坊网页上,我找到了一个适合我的例子。它使用来自tokenReward 地址的令牌。

以上是关于即使我使用以太坊官方网页中的示例也无法发送交易的主要内容,如果未能解决你的问题,请参考以下文章

以太坊中的nonce是什么

以太坊合约常见问题

以太坊交易在 750 秒内未被挖掘

以太坊交易中的nonce和confirmation

智能合约重构社会契约以太坊总结

以太坊交易:为啥交易后余额没有变化?