3
votes

I'm trying to develop a set of contracts in where an ERC721 token is auctioned off, then the winner gets the token placed into their wallet. I'm not entirely sure how to structure this. These are the contracts I was thinking would be needed.

WALLET

  1. Contains the mapping of addresses to tokens owned
  2. Upon deployment, creates deploys an AuctionFactory contract and saves the address

AUCTIONFACTORY

  1. Deploys an Auction contract upon command, with the item being the ERC721 token specified

AUCTION

  1. Contains all the logic for an auction
  2. Inherits from Wallet, allowing manipulation of the mapping state variable found in the contract, placing ERC721 tokens won into the winners wallet

The problem is that Auction can't inherit from Wallet. The compiler will throw an error when AuctionFactory tries to deploy an auction - cannot create instance of derived or same contract. And that makes sense to me, since Wallet deploys the factory, and if the factory deploys an Auction that inherits from Wallet, it's technically deploying its parent contract.

So my question is, how can I structure this set of contracts? How can I allow an instance of an auction contract to communicate and manipulate with a store on another contract?

2

2 Answers

2
votes

Here is an example contract which can deposit tokens and then auction them off. This is a basic auction model which shows the control flow for transferring possession of the token.

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.

0
votes

I'm not sure why you need to inherit from a Wallet contract. The Auction structure should be something like:

  • Owner of token X calls a function createAuction from your contract to create an auction
  • createAuction(tokenId, minPrice, duration) will:
    1. Store the owner address, token id and some other information (minimum price, duration, etc.) into a mapping. You'll probably use your token id as the mapping key.
    2. Use the transferFrom function from your ERC721 contract to transfer the ownership from current owner to the Auction contract itself, something like: erc721.transferFrom(owner, this, tokenId). This is required later when you have to transfer the ownership to the buyer.
  • A buyer joins the game, and bid on this auction by calling a bidOnAuction(tokenId). Check the parameters (msg.value > minPrice, duration, etc.). And if these are bid winning conditions, then you transfer the money to the seller (owner) and you transfer the token owrnership from your contract to the buyer, again by calling the erc721 method: erc721.transferFrom(this, msg.sender, tokenId).

The Auction contract works as an escrow, it keeps the ERC721 token ownership until the auction is finished (bought or cancelled). You don't have to "manipulate ownership", you only have to let the owner transfer the token to your contract and then, your contract transfer the ownership back to the previous owner if the auction was cancelled or to the buyer if the auction was completed.