[Ethereum] Implementing ERC721 Marketplace

blockchainerc-721solidity

I'm working on a project where I need to implement an ERC721 marketplace. It doesn't work for some reason and wanted to know if I'm doing everything correctly.

The way I approached this was:

The seller approves the contract address to act on their behalf.
The token is listed as available for sale in the marketplace.
Then, when the buyer wants to buy the token, the contract transfers the token from the seller to the buyer, and transfers the payment from buyer to seller.

I couldn't find many resources but found these as references:

https://github.com/status-im/embark-tutorial-erc721/tree/tutorial-series/contracts

Proper way to implement "buyable" ERC721 tokens

So here's the code I implemented:

The code is here:

 struct Token {
        uint id;
        uint price;
        uint prevPrice;
        bool forSale;
    }


function buyMarketplaceToken(uint _tokenId) public payable whenNotPaused() isForSale(_tokenId) {
        address ticketOwner = ownerOf(_tokenId);
        require(msg.value >= tokens[_tokenId].price, "It costs more!");
        ticketOwner.transfer(msg.value);
        safeTransferFrom(ticketOwner, msg.sender, _ticketId);
        clearApproval(this, _ticketId);
        tokens[_tokenId].forSale = false;
    }

    function addTokenForSale(uint _tokenId, uint newPrice) public onlyOwner(_tokenId) whenNotPaused() {
        tokens[_tokenId].forSale = true;
        tokens[_tokenId].prevPrice = tokens[_tokenId].price;
        tokens[_tokenId].price = newPrice;
        approve(address(this), _tokenId);
        emit TokenOnSale(_tokenId, newPrice);
    }

        function removeTokenForSale(uint _tokenId) public onlyOwner(_tokenId) whenNotPaused() {
        tokens[_tokenId].forSale = false;
        tokens[_tokenId].price = tokens[_tokenId].prevPrice;
        clearApproval(address(this), _tokenId);
        emit TokenOffSale(_tokenId, tokens[_tokenId].prevPrice);
    }

So my logic is that seller calls addTokenForSale with tokenId and a price for sale. This function approves the contract to transfer token on owners behalf and sets the forSale boolean for this token to true.

The buyer calls buyMarketplaceToken with tokenId and pays the token price which is transferred to original token owner. The token is transferred to msg.sender by the contract and approval is cleared. Then the token forSale boolean is set to false meaning its not for sale anymore.

The code stops working at safeTransferFrom because even though the contract has approval to transfer token, contract is not msg.sender.

How do I make sure that safeTransferFrom caller is the contract and not msg.sender?

What am I missing here? Any help would be appreciated.

Best Answer

ERC721 is similar to ERC20 in the approve/transferFrom. It has to be the user whom approves your market contract, the market contract cannot request for approval.

For example to approve the market the user will execute

token721.approve(marketAddress, id, { from: userAddress })

Now the market can transfer user tokens to the recipient (or you can use safeTransferFrom)

token721.transferFrom(userAddress, recipient, id)
Related Topic