0

I'm getting a revert when trying to transfer a token from another contract, I'm calling the function from an Auctions contract, for example, when the max bidder claims the reward they get the token but I'm getting a revert because of the msg.sender since it changes to the address of the Auctions contract.

This is the current function of the Auctions contract:

function withdraw(uint256 _auctionId) public {
    AuctionData storage auction = auctions[_auctionId];
    require(!auction.isOpen);

    uint256 payout;

    if(msg.sender == auction.owner) {
        payout = auction.maxBid;
        auction.maxBid = 0;
    }

    // We ensure the msg.sender isn't the max bidder nor the owner.
    // If the address is the owner that would evaluate to true two times (above and this one)
    // and 'payout' wouldn't be correct.
    // If msg.sender didn't bid then the payout will be 0 anyhow.
    if(msg.sender != auction.maxBidder && msg.sender != auction.owner) {
        payout = auction.bids[msg.sender];
        auction.bids[msg.sender] = 0;
    }

    if(msg.sender == auction.maxBidder) {
        // Manually sends the token from owner to maxBidder.
        // CToken(ctoken).transferTokenTo(auction.owner, msg.sender, auction.horse);
        delete auction.maxBidder;
    }

    msg.sender.transfer(payout);

    emit Withdraw(msg.sender, payout);
}

The relevant part is the if(msg.sender == auction.maxBidder) {}

The Token contract only calls approve / safeTransferFrom from OpenZeppelin's contract.

So, what are my options here to avoid this revert / maintain the msg.sender to the address that sent the transaction and not the contract?

Edit: Token contract

pragma solidity ^0.4.2;

import "./ERC721Token.sol";
import "./CryptofieldBase.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

contract CToken is ERC721Token {
    using SafeMath for uint256;

    uint256 stallionsAvailable = 168;
    uint256 maresAvailable = 379;
    uint256 coltsAvailable = 230;
    uint256 filliesAvailable =  334;

    // Variable for enumeration.
    address[] addresses;
    address cryptofieldBase;

    constructor(address _cryptofieldBase) ERC721Token("CToken", "CT") public {
        cryptofieldBase = _cryptofieldBase;
    }

    /* @dev Returns an array of ids of horses owned by '_from' */
    function getOwnedTokens(address _from) public view returns(uint256[]) {
        return super.getOwnedTokens(_from);
    }

    function createHorse(address _owner, string _hash) public payable {
        require(stallionsAvailable > 0);

        uint256 tokenId = addresses.push(_owner) - 1;

        _mint(_owner, tokenId);
        CryptofieldBase(cryptofieldBase).buyHorse(_owner, _hash);

        stallionsAvailable = stallionsAvailable.sub(1);
    }

    function transferTokenTo(address _from, address _to, uint256 _tokenId) public {
        safeTransferFrom(_from, _to, _tokenId);
    }

    function ownerOfToken(uint256 _tokenId) public view returns(address) {
        return ownerOf(_tokenId);
    }

    function approveAddress(address _to, uint256 _tokenId) public {
        approve(_to, _tokenId);
    }

    /*
    @dev Transfer a token of '_from' to '_to'
    */
    function tokenSold(address _from, address _to, uint256 _tokenId) public {
        safeTransferFrom(_from, _to, _tokenId);
        CryptofieldBase(cryptofieldBase).horseSold(_tokenId);
    }
}
  • Could you add CToken source code? – Luiz Soares Jul 27 '18 at 19:57
  • @LuizSoares added – Aguxez Jul 27 '18 at 23:08
  • It is not cleat what is the relationship between Auction and CToken, who is calling who? and in what order they are invoked. The pair approve+transferFrom should work in most cases. – Ismael Jul 30 '18 at 2:33
  • @Ismael the Auctions contract is calling the CToken contract. Auctions needs to call Token and Token calls the ERC721Token contract by OpenZeppelin. The solution I’m using right now is that An address has to give permissions to the Auctions contract to transfer the given token, the downside is that the user has to sign another transaction. – Aguxez Jul 30 '18 at 6:09
  • ERC721 allows to use safeTransferFrom to pass a bytes parameter that will be passed to the receiving contract function onERC721Received. That should allow the user to create a single transaction with safeTransferFrom and if your auction as recipient and has a method onERC721Received to complete the process. – Ismael Jul 30 '18 at 6:24
2

Here is an example contract which can deposit tokens and then auction them off. All the while it records which person originally deposited it.

Here is the setup. First we have to import the ERC-721 headers. I am using the reference implementation for ERC-721 for this:

pragma solidity 0.5.1;
import "https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/erc721.sol";
import "https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/erc721-token-receiver.sol";

Here is the contract and the main data structure:

// This implements an ERC-721 auction by taking ownership of tokens
contract CollectableAuction is ERC721TokenReceiver {
    mapping (uint256 => AuctionDetails) auctionDetails;

    struct AuctionDetails {
        ERC721 nftContract;
        bool bidIsComplete;
        address seller;
        address winningBidder;
        uint256 tokenId;
    }
}

We add in the deposit mechanism. This works by allowing people to send tokens directly to the auction contract. You may implement a different mechanism to start auctions, but this one works just as well.

    // Deposit an asset and start an auction
    function onERC721Received(
        address,
        address from,
        uint256 tokenId,
        bytes calldata
    )
        external
        returns(bytes4)
    {
        uint256 auctionId = uint256(keccak256(abi.encode(uint256(msg.sender), tokenId)));
        auctionDetails[auctionId] = AuctionDetails({
            nftContract: ERC721(msg.sender),
            bidIsComplete: false,
            seller: from,
            winningBidder: address(0),
            tokenId: tokenId
        });
        return 0x150b7a02;
    }

Here is a mock implementation for your auction process. Your actual auction will certainly be more complicated.

    function completeAuction(uint256 auctionId) external {
        auctionDetails[auctionId].bidIsComplete = true;
    }

Lastly, when the auction is done then the winning bidder needs to take the token.

    function withdraw(uint256 auctionId) external {
        AuctionDetails storage details = auctionDetails[auctionId];

        require(details.bidIsComplete);
        require(msg.sender == details.winningBidder);
        // Collect money from winning bidder

        details.nftContract.safeTransferFrom(address(this), details.winningBidder, details.tokenId);
        // Send money to seller
        // Do event logging
        delete auctionDetails[auctionId];
    }

The above is a fully-functioning starting point for this project.

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.