// SPDX-License-Identifier: Proprietary pragma solidity ^0.7.2; library LibAuctionable { bytes32 constant AUCTIONABLE_STORAGE_POSITION = keccak256("gallery.token.nft.LibAuctionable"); enum PaymentType { Coin, Token } struct Auction { address payable beneficiary; // person who gets paid when auction wins (owner usually) uint256[] itemIds; // NFT ids up for auction uint256 endTime; // absolute time in seconds address payable highestBidder; uint256 highestBid; PaymentType paymentType; bool ended; bool finalized; // ownership of nfts transferred } struct AuctionableStore { uint256 auctionId; //nonce for auction ids mapping (uint256 => Auction) auctions; mapping (uint256 => mapping(address => uint256)) pendingReturns; mapping (uint256 => uint256) nftId2AuctionId; } function store() internal pure returns (AuctionableStore storage aus) { bytes32 position = AUCTIONABLE_STORAGE_POSITION; assembly { aus.slot := position } } event AuctionStarted(uint256 indexed id, address from); event HighBid(uint256 indexed id, address indexed bidder, uint256 amount); event AuctionEnded(uint256 indexed id, address indexed winner, uint256 amount); function getNewAuctionId() internal returns(uint256) { uint256 newAuctionId = store().auctionId; store().auctionId++; return newAuctionId; } /** This just creates the auction, it's not responsible for changing the NFT's state. */ function createNewAuction(PaymentType _paymentType, uint256 _endTime, uint256[] memory _nftIds) internal returns (uint256) { uint256 newAuctionId = getNewAuctionId(); Auction storage newAuction = store().auctions[newAuctionId]; newAuction.beneficiary = msg.sender; newAuction.itemIds = _nftIds; newAuction.endTime = _endTime; newAuction.paymentType = _paymentType; for (uint i = 0; i < _nftIds.length; ++i) { store().nftId2AuctionId[_nftIds[i]] = newAuctionId; } emit AuctionStarted(newAuctionId, msg.sender); return newAuctionId; } /** This just ends the auction, it doesn't transfer the NFTs or pay the winner. */ function endAuction(uint256 _auctionId) internal { Auction storage auction = store().auctions[_auctionId]; require(!auction.ended, "Auction already ended"); require(block.timestamp >= auction.endTime, "Not time to end auction"); auction.ended = true; emit AuctionEnded(_auctionId, auction.highestBidder, auction.highestBid); } /** Dissociates NFTs from auction. This MUST be called, and ONLY AFTER NFTs are transferred */ function postAuctionCleanup(uint256 _auctionId) internal { Auction storage auction = store().auctions[_auctionId]; require(auction.ended, "Auction must be ended"); require(!auction.finalized, "Auction must not be finalized"); auction.finalized = true; for (uint i = 0; i < auction.itemIds.length; ++i) { delete store().nftId2AuctionId[ auction.itemIds[i] ]; } } /** This is needed to facilitate NFT transfer after auction conclusion. */ function getAuctionForNft(uint256 _nftId) internal view returns (uint256) { return store().nftId2AuctionId[_nftId]; } /** Used to approve transfer of NFT when auction successfully concludes. */ function validAuctionTransfer(address _from, address _to, uint256 _nftId) internal view returns (bool) { uint256 auctionId = getAuctionForNft(_nftId); if (auctionId == 0) return false; Auction storage auction = store().auctions[auctionId]; return auction.ended && !auction.finalized && _from == auction.beneficiary && _to == auction.highestBidder ; } }