msg.sender.transfer(purchaseExcess); falla en la prueba de solidez

Obtuve una excepción en la línea msg.sender.transfer(purchaseExcess);en mi prueba de unidad de solidez de trufa. ¿Alguna idea del motivo?

mi código de contrato:

function purchase(uint256 _tokenId) public payable {
    address oldOwner = tokenOwner[_tokenId];
    uint256 sellingPrice = emojiIndexToPrice[_tokenId];
    address newOwner = msg.sender;

    require(oldOwner != newOwner);

    require(newOwner != address(0));

    require(msg.value >= sellingPrice);

    uint256 percentage = SafeMath.sub(100, ownerCut);
    uint256 payment = uint256(SafeMath.div(SafeMath.mul(sellingPrice, percentage), 100));
    uint256 purchaseExcess = SafeMath.sub(msg.value, sellingPrice);

    emojiIndexToPrice[_tokenId] = SafeMath.div(SafeMath.mul(sellingPrice, 150), percentage);

    _transfer(oldOwner, newOwner, _tokenId);

    if (oldOwner != address(this)) {
      oldOwner.transfer(payment);
    }

    msg.sender.transfer(purchaseExcess);
}

código de pruebas unitarias:

contract TestEmojiCoin {
    uint public initialBalance = 1 ether;

    function testPurchase() public {
        address contractAddress = DeployedAddresses.EmojiCoin();
        EmojiCoin emojiCoin = EmojiCoin(contractAddress);

        // Emoji 0 is created in previous test.

        emojiCoin.purchase.value(1 ether).gas(30000000000)(0);      
    }
}

mensaje de error

     Error: VM Exception while processing transaction: revert
  at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)
  at /usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:86:1
  at /usr/local/lib/node_modules/truffle/build/webpack:/~/truffle-provider/wrapper.js:134:1
  at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
  at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:64:1)
  at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:354:1)
  at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:509:1)
  at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:469:1)
  at endReadableNT (_stream_readable.js:1101:12)
  at process._tickCallback (internal/process/next_tick.js:114:19)
¿Qué error estás recibiendo?
No estoy completamente seguro, pero es posible que deba aumentar el saldo del contrato en caso de que la transacción requiera que se ejecute gas. uint public initialBalance = 2 ether;podría resolver el problema.
@mirg desafortunadamente, no resolvió el problema :-(

Respuestas (1)

La transacción está fallando porque el contrato de prueba no tiene una función de reserva pagadera para aceptar la "transferencia" de fondos en exceso .

pragma solidity ^0.4.17;

import "../contracts/EmojiCoin.sol"; 
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";

contract TestEmojiCoin {
    uint public initialBalance = 1 ether;

    function testPurchase() public {
        address contractAddress = DeployedAddresses.EmojiCoin();
        EmojiCoin emojiCoin = EmojiCoin(contractAddress);

        // Emoji 0 is created in previous test.

        address owner_0 = emojiCoin.tokenOwner(0);
        Assert.notEqual( owner_0, this, "owner for coin 0 is incorrect" );

        emojiCoin.purchase.value(1 ether).gas(30000000000)(0);

        owner_0 = emojiCoin.tokenOwner(0);
        Assert.equal( owner_0, this, "owner for coin 0 is incorrect" );
    }

    // this new function IS REQUIRED for the test to work
    function() public payable { }

}

Se agregaron dos pruebas de afirmación (algunos podrían argumentar que esta no es la mejor práctica al tener más de una afirmación en la misma prueba)

Además, se tuvo que implementar un contrato de EmojiCoin muy básico para comprobar que este era el problema:

pragma solidity ^0.4.17;

import "zeppelin-solidity/contracts/math/SafeMath.sol";

contract EmojiCoin {

    mapping(uint256 => address) public tokenOwner;
    mapping(uint256 => uint256) public emojiIndexToPrice;
    uint256 public ownerCut  = 5;


    function EmojiCoin() public {

        // very simple constructor for the purpose of testing only
        tokenOwner[0] = msg.sender;
        emojiIndexToPrice[0] = 1 ether;

    }

    // simple _transfer implementation for the purpose of testing only
    function _transfer(address oldOwner, address newOwner, uint256 _tokenId) internal {
        require(tokenOwner[_tokenId] == oldOwner);
        tokenOwner[_tokenId] = newOwner;
    }

    function purchase(uint256 _tokenId) public payable {
        address oldOwner = tokenOwner[_tokenId];
        uint256 sellingPrice = emojiIndexToPrice[_tokenId];
        address newOwner = msg.sender;

        require(oldOwner != newOwner);

        require(newOwner != address(0));

        require(msg.value >= sellingPrice);

        uint256 percentage = SafeMath.sub(100, ownerCut);
        uint256 payment = uint256(SafeMath.div(SafeMath.mul(sellingPrice, percentage), 100));
        uint256 purchaseExcess = SafeMath.sub(msg.value, sellingPrice);

        emojiIndexToPrice[_tokenId] = SafeMath.div(SafeMath.mul(sellingPrice, 150), percentage);

        _transfer(oldOwner, newOwner, _tokenId);

        if (oldOwner != address(this)) {
           oldOwner.transfer(payment);
        }

        msg.sender.transfer(purchaseExcess);
    }


}
¡Muchas gracias! ¿Puede elaborar un poco más sobre "cualquiera de las transferencias podría fallar y quedar atrapado en el contrato"?
Mi comentario final fue infundado y ha sido eliminado; era tarde y estaba pensando en una ruta de código imposible.