Given the contracts below (unnecessary code omitted), my tests fail when trying to call the ERC20 transfer() or ERC223 transfer() functions. Why is this happening?

Both give Error: VM Exception while processing transaction: revert calls. I have deployed this on Ropsten and ERC20 transfer() works, but ERC223 transfer() fails.

What am I doing wrong with writing the tests for this?


describe.only('tokenFallback()', () => {
    it('calls setResult correctly', async () => {

      // ERC20 transfer
      const contract = new web3.eth.Contract(Abi.MyToken, token.address);
      const tx = contract.methods["transfer(address,uint256)"](
        Utils.getBigNumberWithDecimals(100, 8),
      await tx.send({ from: OWNER });

      // ERC223 transfer
      const contract = new web3.eth.Contract(Abi.MyToken, token.address);
      const tx = contract.methods["transfer(address,uint256,bytes)"](
        Utils.getBigNumberWithDecimals(100, 8),
      await tx.send({ from: OWNER });

ERC223 Token

pragma solidity ^0.4.24;

import "./ERC223.sol";
import "./ERC223ReceivingContract.sol";
import "../lib/SafeMath.sol";

contract StandardToken is ERC223 {
    using SafeMath for uint256;

    mapping(address => uint256) balances;
    mapping (address => mapping (address => uint256)) allowed;

    // Modifiers
    modifier validAddress(address _address) {
        require(_address != address(0));

    event TransferStep(
        uint256 indexed _step

    function transfer(address _to, uint256 _value) public returns (bool) {
        emit TransferStep(0);
        bytes memory empty;
        transfer(_to, _value, empty);

    function transfer(address _to, uint256 _value, bytes _data) public validAddress(_to) returns (bool success) {
        uint codeLength;

        emit TransferStep(1);

        assembly {
            // Retrieve the size of the code on target address, this needs assembly
            codeLength := extcodesize(_to)

        emit TransferStep(2);

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);

        emit TransferStep(3);

        // Call token fallback function if _to is a contract. Rejects if not implemented.
        if (codeLength > 0) {
            emit TransferStep(4);
            ERC223ReceivingContract(_to).tokenFallback(msg.sender, _value, _data);
            emit TransferStep(5);

        emit Transfer(msg.sender, _to, _value);
        emit Transfer(msg.sender, _to, _value, _data);
        return true;

    function approve(address _spender, uint256 _value) public returns (bool) {
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((_value == 0) || (allowed[msg.sender][_spender] == 0));

        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;

    function transferFrom(address _from, address _to, uint256 _value) public validAddress(_to) returns (bool) {
        uint256 _allowance = allowed[_from][msg.sender];

        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
        // require (_value <= _allowance);

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = _allowance.sub(_value);
        emit Transfer(_from, _to, _value);
        return true;

    function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];

    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
        return allowed[_owner][_spender];

ERC223 Receiver Abstract

pragma solidity ^0.4.24;

contract ERC223ReceivingContract { 
    function tokenFallback(address _from, uint _value, bytes _data) external;

My Contract

contract StandardEvent is ERC223ReceivingContract, BaseContract, Ownable {
    using BytesLib for bytes;
    using ByteUtils for bytes32;
    using SafeMath for uint256;

    event TokenFallbackHit(
        uint256 indexed _number
    event TokenFallbackParsed(
        bool indexed _success,
        address indexed _centralizedOracle,
        address indexed _resultSetter,
        uint8 _resultIndex

    function tokenFallback(address _from, uint _value, bytes _data) external {
        emit TokenFallbackHit(1);

        bytes memory functionId = _data.slice(0, 4);
        bytes memory setResultFunc = hex"65f4ced1";
        bytes memory voteFunc = hex"6f02d1fb";

        emit TokenFallbackHit(2);

        address centralizedOracle = _data.toAddress(4);
        address resultSetter = _data.toAddress(24);
        uint8 resultIndex = uint8(_data.toUint(44));

        emit TokenFallbackHit(3);

        if (functionId.equal(setResultFunc)) {
            setResult(centralizedOracle, resultSetter, resultIndex, _value);
            emit TokenFallbackParsed(true, centralizedOracle, resultSetter, resultIndex);
        } else if (functionId.equal(voteFunc)) {
            vote(centralizedOracle, resultSetter, resultIndex, _value);
        } else {
            revert("Unhandled function in tokenFallback");
  • posting a transaction hash would be helpful – Nulik Jul 29 '18 at 12:25
  • I see you are emitting transfer event twice, blockchain explorers are going to show incorrect balance of your tokens, they might duplicate the amounts – Nulik Jul 29 '18 at 12:26
  • @Nulik ropsten.etherscan.io/tx/… – The Nomad Jul 29 '18 at 15:32
  • It is different transfer events. Etherscan for instance does not listen specifically for the ERC223 transfer event. It's a different signature. – The Nomad Jul 29 '18 at 15:38

Your Answer

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

Browse other questions tagged or ask your own question.