Skip to content

Instantly share code, notes, and snippets.

@M4cs
Created September 6, 2021 05:55
Show Gist options
  • Save M4cs/aee4eff4058c91c9d17e6012a8df885f to your computer and use it in GitHub Desktop.
Save M4cs/aee4eff4058c91c9d17e6012a8df885f to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./LittleShartsAdmins.sol";
contract LittleShartMarketplace is LittleShartsAdmins {
using SafeMath for uint256;
uint256 public _listingFee = 1;
address _nft;
address _token;
struct Listing {
address seller;
bool isActive;
uint price;
}
struct Bid {
address bidder;
bool isActive;
uint value;
}
// Treasury for sellers/bidders
mapping (uint256 => Listing) _listings;
mapping (uint256 => Bid) _bids;
mapping (address => uint256[]) _tokensListed;
// Locks up until listing is bought or refunds lister if cancelled
mapping (uint256 => uint256) _pendingFeeTreasury;
// Tracks tokenIds for sale for easy call access
uint256[] public _tokensForSale;
constructor (
address _nftAddress,
address _tokenAddress,
address _masterAdmin
) {
_nft = _nftAddress;
_token = _tokenAddress;
masterAdmin = _masterAdmin;
admins = [_masterAdmin];
}
function buyNow(uint256 tokenId) public payable {
Listing memory listing = _listings[tokenId];
require(listing.isActive, "Listing is inactive!");
require(listing.seller != msg.sender, "Cannot purchase your own listing!");
require(msg.value == listing.price, "Value of TXN is not the same as listing price!");
Bid memory bid = _bids[tokenId];
uint256 bidValue = bid.value;
address bidder = bid.bidder;
if (bid.isActive && (bid.bidder == msg.sender)) {
// Buyer is current bidder, refund them their bid
payable(msg.sender).transfer(bidValue);
_bids[tokenId] = Bid(address(0), false, 0);
}
ERC721(_nft).approve(bidder, tokenId);
ERC721(_nft).transferFrom(address(this), bidder, tokenId);
require(ERC721(_nft).balanceOf(bid.bidder) == 1, "Transfer did not complete!");
payable(listing.seller).transfer(msg.value);
payable(masterAdmin).transfer(_pendingFeeTreasury[tokenId]);
_removeFromSale(tokenId);
}
function acceptBid(uint256 tokenId) public {
Listing memory listing = _listings[tokenId];
require(listing.seller == msg.sender, "Cannot accept bid on listing you do not own!");
Bid memory bid = _bids[tokenId];
require(bid.isActive, "No active bid to accept!");
require(bid.value > 0, "Bid value is zero.");
ERC721(_nft).approve(bid.bidder, tokenId);
ERC721(_nft).transferFrom(address(this), bid.bidder, tokenId);
_bids[tokenId] = Bid(address(0), false, 0);
require(ERC721(_nft).balanceOf(bid.bidder) == 1, "Transfer did not complete!");
payable(masterAdmin).transfer(_pendingFeeTreasury[tokenId]);
_removeFromSale(tokenId);
}
function cancelBid(uint256 tokenId) public {
Bid memory currentBid = _bids[tokenId];
require(currentBid.isActive, "Bid is not active!");
require(currentBid.bidder == msg.sender, "You are not owner of this bid!");
uint refundAmnt = currentBid.value;
_bids[tokenId] = Bid(address(0), false, currentBid.value);
payable(msg.sender).transfer(refundAmnt);
}
function createBid(uint256 tokenId) public payable {
require(msg.value > 0, "Bid Price Cannot Be Negative!");
Listing memory listing = _listings[tokenId];
require(listing.isActive, "Listing Must Be Active To Bid!");
require(listing.seller != msg.sender, "Cannot bid on your own listing!");
Bid memory currentBid = _bids[tokenId];
// Check if there is already a Bid
if (currentBid.isActive) {
// Make sure new bid is greater than old bid
require(msg.value > currentBid.value, "Too low to outbid current bid!");
// Refund old bidder
payable(currentBid.bidder).transfer(currentBid.value);
}
_bids[tokenId] = Bid(msg.sender, true, msg.value);
}
function updateListing(uint256 tokenId, uint256 value) public payable {
require(value > 0, "Listing price cannot be negative!"); // Listing Cannot be Negatively Priced
Listing memory listing = _listings[tokenId];
require(listing.seller==msg.sender, "Must be owner of token!"); // Must own the token trying to be updated
require(_listings[tokenId].isActive, "Listing is not active!"); // Listing must be active to update. List it first
uint256[2] memory amounts = _getFeeAmounts(_listingFee, value);
require(msg.value == amounts[1], "Value does not cover listing fee!");
_pendingFeeTreasury[tokenId] += amounts[1];
_listings[tokenId] = Listing(msg.sender, true, value);
}
function cancelListing(uint256 tokenId) public {
Listing memory listing = _listings[tokenId];
require(listing.seller == msg.sender, "Must be owner of listing!");
require(listing.isActive, "Listing must be active!");
// At this point we know the sender is the seller.
// Lets refund and remove their Listing
payable(listing.seller).transfer(_pendingFeeTreasury[tokenId]);
_removeFromSale(tokenId);
}
function createListing(uint256 tokenId, uint256 value) public payable {
require(value > 0, "Listing price cannot be negative!"); // Listing Cannot be Negatively Priced
require(ERC721(_nft).ownerOf(tokenId)==msg.sender, "Must be owner of token!"); // Must own the token trying to be listed
require(!_listings[tokenId].isActive, "Listing is already active!"); // Listing must be inactive to start. Cancel or Update Otherwise
uint256[2] memory amounts = _getFeeAmounts(_listingFee, value);
require(msg.value == amounts[1], "Value does not cover listing fee!");
// Contract Should Be Approved, Transfer to Contract
ERC721(_nft).transferFrom(msg.sender, address(this), tokenId);
require(ERC721(_nft).ownerOf(tokenId)==address(this), "Token Transfer Failed!");
_pendingFeeTreasury[tokenId] = amounts[1];
_tokensListed[msg.sender].push(tokenId);
_listings[tokenId] = Listing(msg.sender, true, value);
_tokensForSale.push(tokenId);
}
function getTokensForSale() public view returns (uint256[] memory) {
return _tokensForSale;
}
function getTokensListed() public view returns (uint256[] memory) {
return _tokensListed[msg.sender];
}
function getTokensListed(address _owner) public view returns (uint256[] memory) {
return _tokensListed[_owner];
}
function getFeeTreasuryAmountForListing(uint256 tokenId) public view onlyAdmin returns (uint256) {
return _pendingFeeTreasury[tokenId];
}
function _removeFromSale(uint256 tokenId) internal {
uint256 tokenIndex;
for (uint i=0;i<_tokensForSale.length;i++) {
if (_tokensForSale[i] == tokenId) {
tokenIndex = i;
}
}
_tokensForSale[tokenIndex] = _tokensForSale[_tokensForSale.length-1];
_tokensForSale.pop();
_listings[tokenId] = Listing(msg.sender, false, 0);
_pendingFeeTreasury[tokenId] = 0;
uint256 listedIndex;
for (uint i=0;i<_tokensListed[msg.sender].length;i++) {
if (_tokensListed[msg.sender][i] == tokenId) {
listedIndex = i;
}
}
_tokensListed[msg.sender][listedIndex] = _tokensListed[msg.sender][_tokensListed[msg.sender].length-1];
_tokensListed[msg.sender].pop();
}
function _getFeeAmounts(uint256 fee, uint256 amount) internal pure returns (uint256[2] memory outAmounts) {
uint256 feeAmount = (amount / 100) * fee;
uint256 tOut = amount - feeAmount;
outAmounts[0] = tOut;
outAmounts[1] = feeAmount;
return outAmounts;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment