2

Thanks for your time.

I'm working with a Non-fungible token. I am trying to call the non-fungible token contract from a second contract called agContract.

I can create and track the owners of the tokens I create in agContract using createNFT(), but I cannot call transfer() within the nonfungibletoken contract from my agcontract. I get reverted during the _clearApprovalAndTransfer(_from, _to, _tokenId); line after using batchLeavesFacility() function to try to call my nonfungibletoken

Code is below:

pragma solidity ^0.4.18;
contract ERC721 {
    // Function
    function totalSupply() public returns (uint256 _totalSupply);
    function balanceOf(address _owner) public view returns (uint256 _balance);
    function ownerOf(uint _tokenId) public view returns (address _owner);
    function approve(address _to, uint _tokenId) public;
    function getApproved(uint _tokenId) public view returns (address _approved);
    function transferFrom(address _from, address _to, uint _tokenId) public;
    function transfer(address _to, uint _tokenId) public;
    function implementsERC721() public view returns (bool _implementsERC721);

    // Events
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}

/**
 * Interface for optional functionality in the ERC721 standard
 * for non-fungible tokens.
 *
 * Author: Nadav Hollander (nadav at dharma.io)
 */
contract DetailedERC721 is ERC721 {
    function name() public view returns (string _name);
    function symbol() public view returns (string _symbol);
    function tokenMetadata(uint _tokenId) public view returns (string _infoUrl);
    function tokenOfOwnerByIndex(address _owner, uint _index) public view returns (uint _tokenId);
}


/**
 * @title NonFungibleToken
 *
 * Generic implementation for both required and optional functionality in
 * the ERC721 standard for non-fungible tokens.
 *
 * Heavily inspired by Decentraland's generic implementation:
 * https://github.com/decentraland/land/blob/master/contracts/BasicNFT.sol
 *
 * Standard Author: dete
 * Implementation Author: Nadav Hollander <nadav at dharma.io>
 */
contract NonFungibleToken is DetailedERC721 {
    string public name;
    string public symbol;
    address public owner;
    address public agServiceContract;
    uint256 public numTokensTotal;

    mapping(uint => address) public tokenIdToOwner;
    mapping(uint => address) public tokenIdToApprovedAddress;
    mapping(uint => string) public tokenIdToMetadata;
    mapping(address => uint[])  public ownerToTokensOwned;
    mapping(uint => uint)public  tokenIdToOwnerArrayIndex;

    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId
    );

    event Approval( address indexed _owner, address indexed _approved, uint256 _tokenId
    );

    modifier onlyExtantToken(uint _tokenId) {
        require(ownerOf(_tokenId) != address(0));
        _;
    }
    function NonFungibleToken(){
        owner = msg.sender;
    }

    function name()
        public
        view
        returns (string _name)
    {
        return name;
    }

    function symbol()
        public
        view
        returns (string _symbol)
    {
        return symbol;
    }

    function totalSupply()
        public

        returns (uint256 _totalSupply)
    {
        return numTokensTotal;
    }

    function balanceOf(address _owner)
        public
        view
        returns (uint _balance)
    {
        return ownerToTokensOwned[_owner].length;
    }
    function setAgContract(address addr) {
        require(msg.sender == owner);
        agServiceContract = addr;
    }

    function ownerOf(uint _tokenId)
        public
        view
        returns (address _owner)
    {
        return _ownerOf(_tokenId);
    }

    function tokenMetadata(uint _tokenId)
        public
        view
        returns (string _infoUrl)
    {
        return tokenIdToMetadata[_tokenId];
    }

    function approve(address _to, uint _tokenId)
        public
        onlyExtantToken(_tokenId)
    {
        require(msg.sender == ownerOf(_tokenId));
        require(msg.sender != _to);

        if (_getApproved(_tokenId) != address(0) ||
                _to != address(0)) {
            _approve(_to, _tokenId);
            Approval(msg.sender, _to, _tokenId);
        }
    }

    function transferFrom(address _from, address _to, uint _tokenId)
        public
        onlyExtantToken(_tokenId)
    {
        require(getApproved(_tokenId) == msg.sender);
        require(ownerOf(_tokenId) == _from);
        require(_to != address(0));

        _clearApprovalAndTransfer(_from, _to, _tokenId);

        Approval(_from, 0, _tokenId);
        Transfer(_from, _to, _tokenId);
    }

    function transfer(address _to, uint _tokenId)
        public
        onlyExtantToken(_tokenId)
    {
        /* used to be require(ownerOf(_tokenId) == msg.sender) but still reverted */
        require(ownerOf(_tokenId) == msg.sender);
        require(_to != address(0));
        _clearApprovalAndTransfer(msg.sender, _to, _tokenId);

        Approval(msg.sender, 0, _tokenId);
        Transfer(msg.sender, _to, _tokenId);
    }
    function transferAgContract(address _to, address _from, uint _tokenId)
        public
        onlyExtantToken(_tokenId)
    {
        /* used to be require(ownerOf(_tokenId) == msg.sender) but still reverted */
        require(ownerOf(_tokenId) == msg.sender || agServiceContract == msg.sender);
        require(_to != address(0));
        _clearApprovalAndTransfer(_from, _to, _tokenId);

        Approval(msg.sender, 0, _tokenId);
        Transfer(msg.sender, _to, _tokenId);
    }

    function tokenOfOwnerByIndex(address _owner, uint _index)
        public
        view
        returns (uint _tokenId)
    {
        return _getOwnerTokenByIndex(_owner, _index);
    }

    function getOwnerTokens(address _owner)
        public
        view
        returns (uint[] _tokenIds)
    {
        return _getOwnerTokens(_owner);
    }

    function implementsERC721()
        public
        view
        returns (bool _implementsERC721)
    {
        return true;
    }

    function getApproved(uint _tokenId)
        public
        view
        returns (address _approved)
    {
        return _getApproved(_tokenId);
    }

    function _clearApprovalAndTransfer(address _from, address _to, uint _tokenId)
        internal
    {
        _clearTokenApproval(_tokenId);
        _removeTokenFromOwnersList(_from, _tokenId);
        _setTokenOwner(_tokenId, _to);
        _addTokenToOwnersList(_to, _tokenId);
    }

    function _ownerOf(uint _tokenId)
        internal
        view
        returns (address _owner)
    {
        return tokenIdToOwner[_tokenId];
    }

    function _approve(address _to, uint _tokenId)
        internal
    {
        tokenIdToApprovedAddress[_tokenId] = _to;
    }

    function _getApproved(uint _tokenId)
        internal
        view
        returns (address _approved)
    {
        return tokenIdToApprovedAddress[_tokenId];
    }

    function _getOwnerTokens(address _owner)
        internal
        view
        returns (uint[] _tokens)
    {
        return ownerToTokensOwned[_owner];
    }

    function _getOwnerTokenByIndex(address _owner, uint _index)
        internal
        view
        returns (uint _tokens)
    {
        return ownerToTokensOwned[_owner][_index];
    }

    function _clearTokenApproval(uint _tokenId)
        internal
    {
        tokenIdToApprovedAddress[_tokenId] = address(0);
    }

    function _setTokenOwner(uint _tokenId, address _owner)
        internal
    {
        tokenIdToOwner[_tokenId] = _owner;
    }

    function _addTokenToOwnersList(address _owner, uint _tokenId)
        internal
    {
        ownerToTokensOwned[_owner].push(_tokenId);
        tokenIdToOwnerArrayIndex[_tokenId] =
            ownerToTokensOwned[_owner].length - 1;
    }

    function _removeTokenFromOwnersList(address _owner, uint _tokenId)
        internal
    {
        uint length = ownerToTokensOwned[_owner].length;
        uint index = tokenIdToOwnerArrayIndex[_tokenId];

        uint swapToken = ownerToTokensOwned[_owner][length - 1];




        ownerToTokensOwned[_owner][index] = swapToken;
        tokenIdToOwnerArrayIndex[swapToken] = index;

        delete ownerToTokensOwned[_owner][length - 1];
        ownerToTokensOwned[_owner].length--;
    }

    function _insertTokenMetadata(uint _tokenId, string _metadata)
        internal
    {
        tokenIdToMetadata[_tokenId] = _metadata;
    }
}

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  /**
  * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}
/**
 * @title MintableNonFungibleToken
 *
 * Superset of the ERC721 standard that allows for the minting
 * of non-fungible tokens.
 */
contract MintableNonFungibleToken is NonFungibleToken {
    using SafeMath for uint;

    event Mint(address indexed _to, uint256 indexed _tokenId);

    modifier onlyNonexistentToken(uint _tokenId) {
        require(tokenIdToOwner[_tokenId] == address(0));
        _;
    }

    function mint(address _owner, uint256 _tokenId, address _approvedAddress, string _metadata)
        public
        onlyNonexistentToken(_tokenId)
    {
        _setTokenOwner(_tokenId, _owner);
        _addTokenToOwnersList(_owner, _tokenId);
        _approve(_approvedAddress, _tokenId);
        _insertTokenMetadata(_tokenId, _metadata);

        numTokensTotal = numTokensTotal.add(1);

        Mint(_owner, _tokenId);
    }
}
contract
agContract {
 using SafeMath for uint;
    address public owner;
    MintableNonFungibleToken nft;
    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }
   function agContract(address _nftContract){
        owner = msg.sender;
        nft = MintableNonFungibleToken(_nftContract);
    }
function createNFT(uint256 _weight, address _owner, string _name){
        nft.mint(_owner, readNFT(), _owner, _name); 
    }
 function batchLeavesFacility(uint _tokenId, address _to, uint256 _weight){

        nft.transferAgContract(_to, msg.sender, _tokenId);
    }
 function readNFT() internal returns(uint)  {
        return nft.totalSupply();
    }


}

So once I create the mintabletoken contract and the agcontract, I set the addresses of both. Then I createNFT() and transfer it to Account 1. Account 1 then tries to transfer token 0 using batchLeavesFacility(), to account 2, but gets reverted from nft.transfer().

Debug stops here, but I'm not sure why, it should have an updated array of tokenOwners when the token is minted. Can someone see what I'm missing here? Thanks!

   function _ownerOf(uint _tokenId)
        internal
        view
        returns (address _owner)
    {
        return tokenIdToOwner[_tokenId];
    }

Also here

  function transfer(address _to, uint _tokenId)
        public
        onlyExtantToken(_tokenId)
    {

        require((ownerOf(_tokenId) == msg.sender));
        require(_to != address(0));

        _clearApprovalAndTransfer(msg.sender, _to, _tokenId);

        Approval(msg.sender, 0, _tokenId);
        Transfer(msg.sender, _to, _tokenId);
    }

edit: So it looks like the function _removeTokenFromOwnersList() is the problem but I'm not sure whats going on. when calling ownerToTokensOwned[_owner][length -1] throws since the owernToTokensOwned[] is only 1 element long. Can someone explain further? I included a if/else statement to remove the length-1 but it still throws.

_removeTokenFromOwnersList(address _owner, uint _tokenId)
        internal
    {
        uint length = ownerToTokensOwned[_owner].length;
        uint index = tokenIdToOwnerArrayIndex[_tokenId];
        if(length >= 1){
    uint swapToken = ownerToTokensOwned[_owner][length - 1];
    }else{
       swapToken = ownerToTokensOwned[_owner][length];
    }


        ownerToTokensOwned[_owner][index] = swapToken;
        tokenIdToOwnerArrayIndex[swapToken] = index;

        delete ownerToTokensOwned[_owner][length - 1];
        ownerToTokensOwned[_owner].length--;
    }
  • I'm a little unclear on what you're doing. When you call createNFT(), what address are you passing in? Only the owner of the token (the address you pass when calling createNFT()) can transfer() that token, unless they've approve()d some other address first. If you call batchLeavesFactory() and pass in a token that the agContract doesn't own or have approval for, then it will fail. If that's not the issue, please explain step by step how to reproduce the issue you're seeing. (What functions are called in what order with what parameters.) – smarx Feb 16 '18 at 9:24
  • Sorry, actually, at the moment, agContract has to be the owner of the token, period. It doesn't have code to call transferFrom(), so an approval wouldn't help. – smarx Feb 16 '18 at 9:33
  • CreateNFT() is called by account 1 and sends the NFT to account2, that is the address passed in. So Account 1 calls CreateNFT(50, "Account2"), then Account 2, calls batchLeavesFacility() to send its new NFT to another user. In this case account 2 would call, BatchLeavesFaciity(0, "Account3", 50). But it gets reverted. I added a check to require msg.sender be the agContract, but its the _removeTokenFromOwnersList that throws an issue. So I made a new Transfer() for the agontract that passes in the original token owner, into the _removeTokenFromOwnersList but it still doesn't work. – Zach Feb 16 '18 at 17:05
  • I can only help with the code you shared. If you've changed the code, edit the question. – smarx Feb 16 '18 at 22:32
  • The code body in the post is the code I'm asking my question about. I'm sorry @smarx I'm not sure what you mean? My edits reflect slight changes in the code as I try to troubleshoot myself. The current code posted is the same as in my remix. Try to compile it, set both contracts and then createNFT from user 1 to user 2, then when trying to call batchLeavesFactility by user2, to send the token to user 3, it reverts. If I'm not being clear, I apologize, please let me know if there is more information I can provide. Thank you again, your really great help – Zach Feb 16 '18 at 22:43
0

When calling BatchLeavesFacility() the msg.sender was the AgContract, not the token owner. So I had to create agTransfer() to pass in the msg.sender and allow for the transfer to go through.

 function transferAgContract(address _to, address _from, uint _tokenId)
            public
            onlyExtantToken(_tokenId)
        {   require(agServiceContract == msg.sender);
            /* used to be require(ownerOf(_tokenId) == msg.sender) but still reverted */
            require(ownerOf(_tokenId) == _from);
            require(_to != address(0));
            _clearApprovalAndTransfer(_from, _to, _tokenId);

            Approval(msg.sender, 0, _tokenId);
            Transfer(msg.sender, _to, _tokenId);
        }

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.