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--;
}
createNFT()
, what address are you passing in? Only the owner of the token (the address you pass when callingcreateNFT()
) cantransfer()
that token, unless they'veapprove()
d some other address first. If you callbatchLeavesFactory()
and pass in a token that theagContract
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.)agContract
has to be the owner of the token, period. It doesn't have code to calltransferFrom()
, so an approval wouldn't help.CreateNFT()
is called by account 1 and sends the NFT to account2, that is the address passed in. So Account 1 callsCreateNFT(50, "Account2")
, then Account 2, callsbatchLeavesFacility()
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 newTransfer()
for the agontract that passes in the original token owner, into the_removeTokenFromOwnersList
but it still doesn't work.