/* Crafted with love by Fueled on Bacon https://fueledonbacon.com */ //SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./ERC721AOperator.sol"; import "./interfaces/IERC721AMarketplace.sol"; abstract contract ERC721AMarketplace is IERC721AMarketplace, ERC721AOperator, ReentrancyGuard { //in the form of offerId => Offer; mapping(uint256 => Offer) public offers; //in the form of buyer => offerIds mapping(address => uint256[]) public offersByBuyer; //in the forom of tokenId => offerIds mapping(uint256 => uint256[]) public offersByTokenId; uint256 offersCount; /// @inheritdoc IERC721AMarketplace function versionERC721AMarketplace() external pure virtual override returns (string memory) { return "1.0.0-beta.0+fob.rsv.iERC721AMarketplace"; } /** Buyer */ function buyBatch(uint256[] memory tokenIds, uint256[] memory sellPrices) external virtual override { if(tokenIds.length != sellPrices.length) revert ERC721AMKInvalidBatchLengths(); for (uint256 i; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; uint256 buyPrice = priceByToken[tokenId]; address buyer = _msgSender(); address seller = ownerOf(tokenId); _transferTokens(tokenId, seller, buyer, buyPrice); //sets new selling price uint256 sellPrice = sellPrices[i]; if(sellPrice == 0) revert ERC721AMKInvalidSellPrice(); priceByToken[tokenId] = sellPrice; emit Sell(buyer, seller, tokenId, buyPrice); } } /** Buyer sets Offers for batches*/ function setOffers( uint256[] memory tokenIds, uint256[] memory offerPrices, uint64 deadline ) external virtual override { if(tokenIds.length != offerPrices.length) revert ERC721AMKInvalidBatchLengths(); if(deadline <= block.timestamp) revert ERC721AMKWrongDeadline(); for (uint256 i; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; if(!_exists(tokenId)) revert ERC721AMKUnexistentToken(); uint256 offerPrice = offerPrices[i]; if(offerPrice == 0) revert ERC721AMKInvalidOfferPrice(); address buyer = _msgSender(); uint256 offerId = offersCount += 1; Offer memory offer = Offer(tokenId, offerPrice, buyer, deadline); offers[offerId] = offer; offersByBuyer[buyer].push(offerId); offersByTokenId[tokenId].push(offerId); emit NewOffer(buyer, tokenId, offerId); } } /** Seller */ function acceptOffers(uint256[] memory offerIds) external virtual override nonReentrant { for (uint256 i; i < offerIds.length; i++) { uint256 offerId = offerIds[i]; Offer storage offer = offers[offerId]; address buyer = offer.buyer; if(buyer == address(0)) revert ERC721AMKUnexistentOffer(); uint256 tokenId = offer.tokenId; address seller = _msgSender(); if(ownerOf(tokenId) != seller) revert ERC721AMKNotTokenOwner(); if(offer.offerDeadline < block.timestamp) revert ERC721AMKOfferDeadlineOver(); offer.offerDeadline = 0; _transferTokens(tokenId, seller, buyer, offer.offerPrice); emit AcceptOffer(offerId); } } /** Buyer */ function cancelOffers(uint256[] memory offerIds) external virtual override { for (uint256 i; i < offerIds.length; i++) { uint256 offerId = offerIds[i]; Offer memory offer = offers[offerId]; address buyer = offer.buyer; address sender = _msgSender(); if(buyer != sender) revert ERC721AMKNotOfferOwner(); delete offers[offerId]; emit CancelOffer(offerId); } } /** When theres a sell */ function _transferTokens( uint256 tokenId, address seller, address buyer, uint256 sellPrice ) private { (address receiver, uint256 amount) = royaltyInfo(tokenId, sellPrice); IERC20 token = IERC20(paymentToken); token.transferFrom(buyer, receiver, amount); token.transferFrom(buyer, ownerOf(tokenId), sellPrice - amount); ERC721A._approve(buyer, tokenId, seller); ERC721A.transferFrom(seller, buyer, tokenId); } /** Getters */ function getOffersByToken( uint256 tokenId, bool active, bool expired ) external view virtual override returns (uint256[] memory) { uint256[] memory offersByToken = offersByTokenId[tokenId]; return _activeOffers(offersByToken, active, expired); } function getOffersByBuyer( address buyer, bool active, bool expired ) external view virtual override returns (uint256[] memory) { uint256[] memory offersByBuyerCount = offersByBuyer[buyer]; return _activeOffers(offersByBuyerCount, active, expired); } function getOffer(uint256 offerId) external view virtual override returns (Offer memory) { return offers[offerId]; } function _activeOffers( uint256[] memory offers_, bool active, bool expired ) private view returns (uint256[] memory tempOffers) { tempOffers = new uint256[](offers_.length); for (uint256 i; i < offers_.length; i++) { Offer memory offer = offers[offers_[i]]; if ( (offer.offerDeadline > block.timestamp && active) || (offer.offerDeadline < block.timestamp && expired) ) { tempOffers[i] = offers_[i]; } } } function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721AOperator) returns (bool) { return interfaceId == type(IERC721AMarketplace).interfaceId || super.supportsInterface(interfaceId); } }