0

Got an exception at the line msg.sender.transfer(purchaseExcess); in my truffle solidity unit test. Any idea the reason?

my contract code:

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);
}

unit tests code:

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);      
    }
}

error msg

     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)
  • what error are you getting? – mirg Apr 12 '18 at 3:08
  • I'm not completely sure but you might need to increase the contract balance in case the transaction requires gas to be executed. uint public initialBalance = 2 ether; might solve the issue. – mirg Apr 12 '18 at 6:51
  • @mirg unfortunately, it didn't solve the issue :-( – Wenjing Apr 12 '18 at 7:24
0

The transaction is failing because the test contract does not have a payable fallback function to accept the excess funds "transfer".

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 { }

}

Two assertion tests have been added (some might argue this is not best practice having more than one assertion in the same test)

Also, a very basic EmojiCoin contract had to be implemented to check that this was the issue:

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);
    }


}
  • Thanks a lot! can you elaborate a little bit more on "either of the transfers might fail and strand ether in the contract" – Wenjing Apr 12 '18 at 8:37
  • My final comment was unfounded and has been removed; it was late, and I was thinking about an impossible code path. – Drew Apr 12 '18 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.