Tengo un contrato simbólico y un contrato de crowdsale. Cuando trato de enviar ether al contrato de crowdsale desde accounts[3]
(o cualquier otro realmente), aparece un error de código de operación:
Error: Excepción de máquina virtual al procesar la transacción: código de operación no válido.
Estoy probando con Truffle. He enviado Testtokens a la dirección de crowdsalecontract. He enviado Ether a la dirección del contrato del token. Todo en el contrato de token funciona cuando interactúo con él directamente. Intenté muchas cosas, pero sigo atascado en este punto. Cualquier ayuda muy apreciada. Ejecutar estos contratos en testrpc
código de migración de trufas:
module.exports = function(deployer,accounts) {
var sendaccounts = web3.eth.accounts;
var sendTo = sendaccounts[0];
deployer.deploy(TestToken).then(function() {
return deployer.deploy(TestCrowdSale, sendTo, TestToken.address);
});
código web3 que genera el error:
var sender = accounts[3];
var receiver = crowdsaleAddress;
var amount = web3.toWei(0.5, 'ether');
web3.eth.sendTransaction({from:sender, to:receiver, value: amount});
en el código de solidez de crowdsale, si esta línea está comentada, el error no ocurre:
tokenReward.transfer(msg.sender, amount);
código de solidez del token:
pragma solidity ^0.4.13;
contract TestToken {
/* Public variables of the token */
string public constant name = 'TestToken';
string public constant symbol = 'TEST';
uint256 public constant decimals = 6;
address owner;
address contractaddress;
//Swap value in % of ETH
uint256 public swapValue = 50;
// One hundred million coins, each divided to up to 10^decimals units.
uint256 private constant totalSupply = 100000000 * (10 ** decimals);
uint256 public constant TestWei = 100000000000000;
bool crowdsaleClosed = false;
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
/* This generates a public event on the blockchain that will notify clients */
event Transfer(address indexed from, address indexed to, uint256 value);
//event LogWithdrawal(address receiver, uint amount);
modifier onlyOwner() {
// Only owner is allowed to do this action.
if (msg.sender != owner) {
revert();
}
_;
}
/* Initializes contract with initial supply tokens to the creator of the contract */
function TestToken() {
contractaddress = address(this);
balanceOf[msg.sender] = totalSupply / 2; // Give the creator half of all tokens
balanceOf[contractaddress] = totalSupply / 2; // Give the contract half of all tokens
owner = msg.sender;
}
/*ERC20*/
/* Internal transfer, only can be called by this contract */
function _transfer(address _from, address _to, uint256 _value) internal {
//function _transfer(address _from, address _to, uint _value) public {
require (_to != 0x0); // Prevent transfer to 0x0 address. Use burn() instead
require (balanceOf[_from] > _value); // Check if the sender has enough
require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
Transfer(_from, _to, _value);
}
/// @notice Send `_value` tokens to `_to` from your account
/// @param _to The address of the recipient
/// @param _value the amount to send
function transfer(address _to, uint256 _value) public returns (bool success) {
if(_to == contractaddress ){
swap(msg.sender, _value);
}
else{
_transfer(msg.sender, _to, _value);
return true;
}
}
//deposit collateral ETH
function depositFunds() public payable onlyOwner
{
}
/*fallback function*/
function () public payable onlyOwner{}
//raise the minimum eth payout, minimum will always be 50% ETH
function raiseSwap(uint _newSwap)
onlyOwner
{
//swap can only be more than 50%
//to guarantee there will always be at least a 50% payout
if(_newSwap > 50){
swapValue = _newSwap;
}
}
//swap for swapvalue % ether (minimum 50)
function swap(address _sender,uint256 _value) internal returns(bool success){
//must be even integer
uint even = _value / 2;
require((2 * even) == _value);
uint TestRefund = (_value / 100) * swapValue;
uint ethRefund = TestRefund * TestWei;
// Test is tranferred to contract balance
uint256 originalBalance = balanceOf[_sender];
// all necessary checks happen inside _transfer function
_transfer(_sender, owner, _value);
// check if tokens are returned succesfully
if(originalBalance == balanceOf[_sender] + _value){
//transfer swapvalue % of eth per Test back to sender
_sender.transfer(ethRefund);
return true;
}
}
function getBalance(address addr) public returns(uint256) {
return balanceOf[addr];
}
function getEtherBalance() public returns(uint256) {
//return contract ether balance;
return this.balance;
}
function getOwner() public returns(address) {
return owner;
}
/* exchange ETH for Test with the contract address */
function buyTest() public payable returns (bool){
require(msg.value > 0);
uint even = msg.value / 2;
require((2 * even) == msg.value);
uint _value;
if(swapValue > 100){
uint amount = (msg.value / swapValue) * 100;
_value = amount / TestWei;
}
else{
//Receive 10000 Test per 1 ETH
_value = msg.value / TestWei;
}
_transfer(contractaddress, msg.sender, _value);
return true;
}
function closeCrowdSale() public
onlyOwner{
crowdsaleClosed = true;
}
}
código de venta masiva:
pragma solidity ^0.4.13;
import './TestToken.sol';
contract TestCrowdSale {
address public beneficiary;
address public crowdsaleAddress;
//debugging
address public tokenAddress;
uint public fundingGoal;
uint public amountRaised;
uint public deadline;
uint public initiation;
uint public price;
uint256 public constant TestWei = 100000000000000;
TestToken public tokenReward;
mapping(address => uint256) public balanceOf;
bool fundingGoalReached = false;
bool crowdsaleClosed = false;
event GoalReached(address beneficiary, uint amountRaised);
event FundTransfer(address backer, uint amount, bool isContribution);
/**
* Constructor function
*
* Setup the owner
*/
function TestCrowdSale(
address ifSuccessfulSendTo,
address addressOfTokenUsedAsReward
) {
beneficiary = ifSuccessfulSendTo;
fundingGoal = 10 * 1 ether;
deadline = now + 100 * 1 minutes;
initiation = now;
price = TestWei;
crowdsaleAddress = address(this);
tokenAddress = addressOfTokenUsedAsReward;
tokenReward = TestToken(addressOfTokenUsedAsReward);
}
/**
* Fallback function
*
* The function without name is the default function that is called whenever anyone sends funds to a contract
*/
function () public payable {
//require(!crowdsaleClosed);
uint256 amount = msg.value / price;
balanceOf[msg.sender] += amount;
amountRaised += amount;
tokenReward.transfer(msg.sender, amount);
FundTransfer(msg.sender, amount, true);
}
modifier afterDeadline() { if (now >= deadline) _; }
/**
* Check if goal was reached
*
* Checks if the goal or time limit has been reached and ends the campaign
*/
function checkGoalReached() afterDeadline {
if (amountRaised >= fundingGoal){
fundingGoalReached = true;
GoalReached(beneficiary, amountRaised);
}
crowdsaleClosed = true;
}
/**
* Withdraw the funds
*
* Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached,
* sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw
* the amount they contributed.
*/
function safeWithdrawal() afterDeadline {
if (!fundingGoalReached) {
uint amount = balanceOf[msg.sender];
balanceOf[msg.sender] = 0;
if (amount > 0) {
if (msg.sender.send(amount)) {
FundTransfer(msg.sender, amount, false);
} else {
balanceOf[msg.sender] = amount;
}
}
}
if (fundingGoalReached && beneficiary == msg.sender) {
if (beneficiary.send(amountRaised)) {
FundTransfer(beneficiary, amountRaised, false);
} else {
//If we fail to send the funds to beneficiary, unlock funders balance
fundingGoalReached = false;
}
}
}
}
Estoy un 90% seguro de que está fallando en esta línea:
require (saldoDe[_desde] > _valor); // Comprobar si el remitente tiene suficiente
La función alternativa de TestCrowdSale está llamando
tokenReward.transfer(msg.sender, cantidad);
Entonces TestToken .transfer(address _to, uint256 _value) está llamando
_transfer(mensaje.remitente, _a, _valor);
Esto significa que balanceOf[_from]
siempre va a ser balanceOf[TestCrowdSale]
. Si TestCrowdSale
no tiene un saldo mayor que msg.value / price
, su transacción fallará.
Recomiendo crear una nueva función en TestToken , por ejemplo subscribe
. transfer
está diseñado solo para eso, transferir, por lo que esto no es apropiado para un nuevo suscriptor de su crowdsale.
hdries