Contract Source Code:
File 1 of 1 : BrokerV2
pragma solidity ^0.5.17;
pragma experimental ABIEncoderV2;
// librray for TokenDets
library TokenDetArrayLib {
// Using for array of strcutres for storing mintable address and token id
using TokenDetArrayLib for TokenDets;
struct TokenDet {
address NFTAddress;
uint256 tokenID;
}
// custom type array TokenDets
struct TokenDets {
TokenDet[] array;
}
function addTokenDet(
TokenDets storage self,
TokenDet memory _tokenDet
// address _mintableAddress,
// uint256 _tokenID
) public {
if (!self.exists(_tokenDet)) {
self.array.push(_tokenDet);
}
}
function getIndexByTokenDet(
TokenDets storage self,
TokenDet memory _tokenDet
) internal view returns (uint256, bool) {
uint256 index;
bool tokenExists = false;
for (uint256 i = 0; i < self.array.length; i++) {
if (
self.array[i].NFTAddress == _tokenDet.NFTAddress &&
self.array[i].tokenID == _tokenDet.tokenID
) {
index = i;
tokenExists = true;
break;
}
}
return (index, tokenExists);
}
function removeTokenDet(
TokenDets storage self,
TokenDet memory _tokenDet
) internal returns (bool) {
(uint256 i, bool tokenExists) = self.getIndexByTokenDet(_tokenDet);
if (tokenExists == true) {
self.array[i] = self.array[self.array.length - 1];
self.array.pop();
return true;
}
return false;
}
function exists(
TokenDets storage self,
TokenDet memory _tokenDet
) internal view returns (bool) {
for (uint256 i = 0; i < self.array.length; i++) {
if (
self.array[i].NFTAddress == _tokenDet.NFTAddress &&
self.array[i].tokenID == _tokenDet.tokenID
) {
return true;
}
}
return false;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
// event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
// address msgSender = _msgSender();
_owner = msg.sender;
// emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
// emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// library for erc20address array
library ERC20Addresses {
using ERC20Addresses for erc20Addresses;
struct erc20Addresses {
address[] array;
}
function addERC20Tokens(erc20Addresses storage self, address erc20address)
external
{
self.array.push(erc20address);
}
function getIndexByERC20Token(
erc20Addresses storage self,
address _ercTokenAddress
) internal view returns (uint256, bool) {
uint256 index;
bool exists;
for (uint256 i = 0; i < self.array.length; i++) {
if (self.array[i] == _ercTokenAddress) {
index = i;
exists = true;
break;
}
}
return (index, exists);
}
function removeERC20Token(
erc20Addresses storage self,
address _ercTokenAddress
) internal {
if (self.array.length > 1){
for (uint256 i = 0; i < self.array.length; i++) {
if (
self.array[i] == _ercTokenAddress
) {
delete self.array[i];
}
}
}
else{
self.array.length = 0;
}
}
function exists(
erc20Addresses storage self,
address _ercTokenAddress
) internal view returns (bool) {
for (uint256 i = 0; i < self.array.length; i++) {
if (
self.array[i] == _ercTokenAddress
) {
return true;
}
}
return false;
}
}
interface IERC20 {
function transfer(address recipient, uint256 amount)
external
returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
contract IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes memory data
) public returns (bytes4);
}
contract ERC721Holder is IERC721Receiver {
function onERC721Received(
address,
address,
uint256,
bytes memory
) public returns (bytes4) {
return this.onERC721Received.selector;
}
}
contract IMintableToken {
// Required methods
function ownerOf(uint256 _tokenId) external view returns (address owner);
function royalities(uint256 _tokenId) public view returns (uint256);
function creators(uint256 _tokenId) public view returns (address payable);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public;
function getApproved(uint256 tokenId)
public
view
returns (address operator);
function isApprovedForAll(address owner, address operator)
public
view
returns (bool);
// Events
event Transfer(address from, address to, uint256 tokenId);
event Approval(address owner, address approved, uint256 tokenId);
// ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165)
function supportsInterface(bytes4 _interfaceID)
external
view
returns (bool);
}
contract Storage is Ownable {
using TokenDetArrayLib for TokenDetArrayLib.TokenDets;
using ERC20Addresses for ERC20Addresses.erc20Addresses;
// address owner;
// address owner;
uint16 public rewardDistributionPercentage;
uint16 public platFormFeePercentage;
uint16 public lpStakefeepercentage;
// uint16 public brokerage;
uint256 public updateClosingTime;
mapping(address => mapping(uint256 => bool)) tokenOpenForSale;
mapping(address => TokenDetArrayLib.TokenDets) tokensForSalePerUser;
TokenDetArrayLib.TokenDets fixedPriceTokens;
TokenDetArrayLib.TokenDets auctionTokens;
//auction type :
// 1 : only direct buy
// 2 : only bid
// 3 : both buy and bid
struct auction {
address payable lastOwner;
uint256 currentBid;
address payable highestBidder;
uint256 auctionType;
uint256 startingPrice;
uint256 buyPrice;
bool buyer;
uint256 startingTime;
uint256 closingTime;
address erc20Token;
}
struct OfferDetails {
address offerer;
uint256 amount;
}
/** Offer mapping
* {
* ERC721Address:{
* tokenId:{
* ERC20Address{
* offerer: Address of offerer,
* amount: Offer in this currency
* }
* }
* }
* }
*/
mapping(
address => mapping(
uint256 => mapping(
address => OfferDetails
)
)
) public offerprice;
mapping(address => mapping(uint256 => auction)) public auctions;
TokenDetArrayLib.TokenDets tokensForSale;
ERC20Addresses.erc20Addresses erc20TokensArray;
address public WETHAddress;
address public StakeAddress;
address public LPStakeAddress;
function getErc20Tokens()
public
view
returns (ERC20Addresses.erc20Addresses memory)
{
return erc20TokensArray;
}
function getTokensForSale()
public
view
returns (TokenDetArrayLib.TokenDet[] memory)
{
return tokensForSale.array;
}
function getFixedPriceTokensForSale()
public
view
returns (TokenDetArrayLib.TokenDet[] memory)
{
return fixedPriceTokens.array;
}
function getAuctionTokensForSale()
public
view
returns (TokenDetArrayLib.TokenDet[] memory)
{
return auctionTokens.array;
}
function getTokensForSalePerUser(address _user)
public
view
returns (TokenDetArrayLib.TokenDet[] memory)
{
return tokensForSalePerUser[_user].array;
}
// function setBrokerage(uint16 _brokerage) public onlyOwner {
// brokerage = _brokerage;
// }
function setBrokerage(address _rewardDistributionAddress,address _lpStakeAddress ,uint16 _lpStakefeepercentage, uint16 _rewardDistributionPercentage, uint16 _platFormFeePercentage) public onlyOwner {
require(_rewardDistributionAddress != address(0) &&_lpStakeAddress!=address(0), "Address is Zero");
require(_rewardDistributionPercentage >= 0 && _platFormFeePercentage >= 0 &&_lpStakefeepercentage>=0, "should be greater than zero");
require(_rewardDistributionPercentage <= 1000 && _platFormFeePercentage <= 1000 && _lpStakefeepercentage<=1000, "should be greater than zero");
rewardDistributionPercentage = _rewardDistributionPercentage;
platFormFeePercentage = _platFormFeePercentage;
lpStakefeepercentage=_lpStakefeepercentage;
StakeAddress = _rewardDistributionAddress;
LPStakeAddress = _lpStakeAddress;
}
function setUpdatedClosingTime(uint256 _updateTime) public onlyOwner {
updateClosingTime = _updateTime;
}
function setAddress(address _weth, address _rewardDistributionAddress) external onlyOwner {
WETHAddress =_weth;
StakeAddress = _rewardDistributionAddress;
}
}
contract BrokerModifiers is Storage {
modifier erc20Allowed(address _erc20Token) {
if (_erc20Token != address(0)) {
require(
erc20TokensArray.exists(_erc20Token),
"ERC20 not allowed"
);
}
_;
}
modifier onSaleOnly(uint256 tokenID, address _mintableToken) {
require(
tokenOpenForSale[_mintableToken][tokenID] == true,
"Token Not For Sale"
);
_;
}
modifier activeAuction(uint256 tokenID, address _mintableToken) {
require(
block.timestamp < auctions[_mintableToken][tokenID].closingTime,
"Auction Time Over!"
);
require(
block.timestamp > auctions[_mintableToken][tokenID].startingTime,
"Auction Not Started yet!"
);
_;
}
modifier auctionOnly(uint256 tokenID, address _mintableToken) {
require(
auctions[_mintableToken][tokenID].auctionType != 1,
"Auction Not For Bid"
);
_;
}
modifier flatSaleOnly(uint256 tokenID, address _mintableToken) {
require(
auctions[_mintableToken][tokenID].auctionType != 2,
"For flat sale only!"
);
_;
}
modifier tokenOwnerOnlly(uint256 tokenID, address _mintableToken) {
// Sender will be owner only if no have bidded on auction.
require(
IMintableToken(_mintableToken).ownerOf(tokenID) == msg.sender,
"You must be owner and Token should not have any bid"
);
_;
}
}
pragma solidity ^0.5.17;
interface IWETH {
function deposit() external payable;
function approve(address spender, uint value) external returns (bool);
function withdraw(uint) external;
function transfer(address to, uint value) external returns (bool);
}
interface IStake{
function receiveWETHFee(uint256 amount) external;
}
interface ILPStake{
function receiveWETHFee(uint256 amount) external;
}
contract BrokerV2 is ERC721Holder, BrokerModifiers {
// events
event Bid(
address indexed collection,
uint256 indexed tokenId,
address indexed seller,
address bidder,
uint256 amouont,
uint256 time,
address ERC20Address
);
event Buy(
address indexed collection,
uint256 tokenId,
address indexed seller,
address indexed buyer,
uint256 amount,
uint256 time,
address ERC20Address,
bool isOffer
);
event Collect(
address indexed collection,
uint256 indexed tokenId,
address indexed seller,
address buyer,
address collector,
uint256 time,
address ERC20Address
);
event OnSale(
address indexed collection,
uint256 indexed tokenId,
address indexed seller,
uint256 auctionType,
uint256 amount,
uint256 time,
address ERC20Address
);
event PriceUpdated(
address indexed collection,
uint256 indexed tokenId,
address indexed seller,
uint256 auctionType,
uint256 oldAmount,
uint256 amount,
uint256 time,
address ERC20Address
);
event OffSale(
address indexed collection,
uint256 indexed tokenId,
address indexed seller,
uint256 time,
address ERC20Address
);
event MakeAnOffer(
address indexed collection,
uint256 indexed tokenId,
address indexed offerer,
address erc20Token,
uint256 offerAmount
);
mapping(address => uint256) public brokerageBalance;
//Struct of Asset
struct Asset {
uint256 _tokenID;
uint256 _startingPrice;
uint256 _auctionType;
uint256 _buyPrice;
uint256 _startingTime;
uint256 _closingTime;
address _mintableToken;
address _erc20Token;
}
//Struct of Pair
struct Pair {
uint256 tokenID;
address _mintableToken;
}
constructor(
uint16 _rewardDistributionPercentage,
uint16 _platFormFeePercentage,
uint16 _lpStakefeepercentage,
uint256 _updatedTime,
address _weth,
address _rewardDistributionAddress,
address _lpStakeAddress
) public {
rewardDistributionPercentage = _rewardDistributionPercentage;
platFormFeePercentage = _platFormFeePercentage;
setUpdatedClosingTime(_updatedTime);
transferOwnership(msg.sender);
WETHAddress =_weth;
StakeAddress = _rewardDistributionAddress;
LPStakeAddress = _lpStakeAddress;
lpStakefeepercentage = _lpStakefeepercentage;
}
//Update contract parameter
function updateparams(
uint16 _rewardDistributionPercentage,
uint16 _platFormFeePercentage,
uint16 _lpStakefeepercentage,
uint256 _updatedTime,
address _weth,
address _rewardDistributionAddress,
address _lpStakeAddress
) external onlyOwner {
require(_rewardDistributionAddress != address(0) &&_lpStakeAddress!=address(0), "Address is Zero");
require(_rewardDistributionPercentage >= 0 && _platFormFeePercentage >= 0 &&_lpStakefeepercentage>=0, "should be greater than zero");
require(_rewardDistributionPercentage <= 1000 && _platFormFeePercentage <= 1000 && _lpStakefeepercentage<=1000, "should be greater than zero");
rewardDistributionPercentage = _rewardDistributionPercentage;
platFormFeePercentage = _platFormFeePercentage;
setUpdatedClosingTime(_updatedTime);
WETHAddress =_weth;
StakeAddress = _rewardDistributionAddress;
LPStakeAddress = _lpStakeAddress;
lpStakefeepercentage = _lpStakefeepercentage;
}
// Method to create any offer for any NFT.
function makeAnOffer(
uint256 tokenID,
address _mintableToken,
address _erc20Token,
uint256 amount
) public payable erc20Allowed(_erc20Token) {
// Allow offer only if token is not auction or auction have been ended withoud any bids.
if (auctions[_mintableToken][tokenID].auctionType == 2) {
require(
block.timestamp <
auctions[_mintableToken][tokenID].closingTime &&
!auctions[_mintableToken][tokenID].buyer,
"Can not make an offer on an incomplete auction"
);
}
// Check if the amount offered is better than previous
require(
amount >= offerprice[_mintableToken][tokenID][_erc20Token].amount,
"amount is not less than msg value"
);
// Revert previous offer
_revertOffer(_mintableToken, tokenID, _erc20Token);
// If the offer is not native currency
if (_erc20Token == address(0)) {
require(msg.value >= amount, "Value sent less than amount");
} else {
IERC20 erc20Token = IERC20(_erc20Token);
require(
erc20Token.allowance(msg.sender, address(this)) >= amount,
"Insufficient spent allowance "
);
erc20Token.transferFrom(msg.sender, address(this), amount);
}
// Update the offer
offerprice[_mintableToken][tokenID][_erc20Token].offerer = msg.sender;
offerprice[_mintableToken][tokenID][_erc20Token].amount = amount;
// Trigger the event
emit MakeAnOffer(
_mintableToken,
tokenID,
msg.sender,
_erc20Token,
amount
);
}
// Method to accept an offer.
function accpetOffer(
uint256 tokenID,
address _mintableToken,
address _erc20Token,
bool isNotClubare
)
public
payable
flatSaleOnly(tokenID, _mintableToken)
tokenOwnerOnlly(tokenID, _mintableToken)
erc20Allowed(_erc20Token)
{
// Chekc offer details and offer exists.
OfferDetails memory _offer = offerprice[_mintableToken][tokenID][
_erc20Token
];
require(
_offer.offerer != address(0),
"selected candidate amount not match "
);
IMintableToken Token = IMintableToken(_mintableToken);
_calculateFees(tokenID, _mintableToken, msg.sender, _erc20Token, _offer.amount, isNotClubare, false);
tokenOpenForSale[_mintableToken][tokenID] = false;
// Transfer the NFT
Token.safeTransferFrom(Token.ownerOf(tokenID), _offer.offerer, tokenID);
// Buy event
emit Buy(
_mintableToken,
tokenID,
msg.sender,
_offer.offerer,
_offer.amount,
block.timestamp,
_erc20Token,
true
);
// delete all auctin details.
delete offerprice[_mintableToken][tokenID][_erc20Token];
_revertAll(_mintableToken, tokenID);
TokenDetArrayLib.TokenDet memory _tokenDet = TokenDetArrayLib.TokenDet(
_mintableToken,
tokenID
);
tokensForSale.removeTokenDet(_tokenDet);
tokensForSalePerUser[msg.sender].removeTokenDet(_tokenDet);
fixedPriceTokens.removeTokenDet(_tokenDet);
delete auctions[_tokenDet.NFTAddress][_tokenDet.tokenID];
}
function _calculateFees(uint256 tokenID, address _collectionAddress, address payable _lastOwner2, address _erc20Token, uint256 amount, bool isNotClubare, bool isBuymethod) internal {
IMintableToken Token = IMintableToken(_collectionAddress);
{
uint256 royalities;
address payable creator;
uint256 royality;
if (!isNotClubare) {
royalities = Token.royalities(tokenID);
creator = Token.creators(tokenID);
royality = (royalities * amount) / 10000;
}
uint256 stakingAmt = ((rewardDistributionPercentage *
amount) / 10000);
uint256 lpStakingAmt = ((lpStakefeepercentage *
amount) / 10000);
uint256 brokerage = ((platFormFeePercentage *
amount) / 10000);
uint256 lastOwner_funds = amount -
royality -
stakingAmt -
lpStakingAmt-
brokerage;
address payable user = msg.sender;
address payable lastOwner2 = _lastOwner2;
IWETH weth = IWETH(WETHAddress);
if (_erc20Token == address(0)) {
if (isBuymethod) {
require(msg.value >= amount, "Insufficient Payment");
}
if (!isNotClubare) {
creator.transfer(royality);
}
lastOwner2.transfer(lastOwner_funds);
weth.deposit.value(stakingAmt+lpStakingAmt)();
} else {
IERC20 erc20Token = IERC20(_erc20Token);
if (isBuymethod) {
require(
erc20Token.allowance(user, address(this)) >=
amount,
"Insufficient spent allowance "
);
erc20Token.transferFrom(user, address(this), brokerage + stakingAmt + lpStakingAmt);
// transfer royalitiy to creator
if (!isNotClubare) {
erc20Token.transferFrom(user, creator, royality);
}
erc20Token.transferFrom(user, lastOwner2, lastOwner_funds);
} else {
if (!isNotClubare) {
erc20Token.transfer(creator, royality);
}
erc20Token.transfer(lastOwner2, lastOwner_funds);
}
}
if(_erc20Token==address(0)|| _erc20Token==WETHAddress){
if(stakingAmt > 0) {
weth.approve(address(StakeAddress), stakingAmt);
IStake stake = IStake(StakeAddress);
stake.receiveWETHFee(stakingAmt);
}
if(lpStakingAmt > 0) {
weth.approve(address(LPStakeAddress), lpStakingAmt);
ILPStake lpStake = ILPStake(LPStakeAddress);
lpStake.receiveWETHFee(lpStakingAmt);
}
}
// Update the brokerage and auction state of NFT
address _stackDeep_erc20Token = _erc20Token;
brokerageBalance[_stackDeep_erc20Token] += brokerage + stakingAmt + lpStakingAmt;
}
}
// Method to update revert the current offer.
function _revertOffer(
address _mintableToken,
uint256 tokenID,
address _erc20Token
) internal {
// If there is any amount offered for this currency
if (
offerprice[_mintableToken][tokenID][_erc20Token].amount > 0 &&
offerprice[_mintableToken][tokenID][_erc20Token].offerer !=
address(0)
) {
// Revert amount for native currency
if (_erc20Token == address(0)) {
address(
uint160(
offerprice[_mintableToken][tokenID][_erc20Token].offerer
)
).transfer(
offerprice[_mintableToken][tokenID][_erc20Token].amount
);
} else {
// Revert other currency
IERC20 erc20 = IERC20(_erc20Token);
erc20.transfer(
offerprice[_mintableToken][tokenID][_erc20Token].offerer,
offerprice[_mintableToken][tokenID][_erc20Token].amount
);
}
// Delete the mapping to gas reward.
delete offerprice[_mintableToken][tokenID][_erc20Token];
}
}
// Method to revert all offers on current tokenId.
function _revertAll(address _mintableToken, uint256 tokenID) internal {
for (uint256 i = 0; i < erc20TokensArray.array.length; i++) {
_revertOffer(_mintableToken, tokenID, erc20TokensArray.array[i]);
}
}
// Public method to revert offer
function revertOffer(
address _mintableToken,
uint256 tokenID,
address _erc20Token
) public payable {
// only allowed to token owner of offerer
require(
msg.sender ==
offerprice[_mintableToken][tokenID][_erc20Token].offerer ||
IMintableToken(_mintableToken).ownerOf(tokenID) == msg.sender,
"You must be offerer or owner to remove the offer."
);
// Must be valid offer
require(
offerprice[_mintableToken][tokenID][_erc20Token].amount > 0 &&
offerprice[_mintableToken][tokenID][_erc20Token].offerer !=
address(0),
" Offer doesn't exist. "
);
// revert offer
_revertOffer(_mintableToken, tokenID, _erc20Token);
}
// Public method to allow token owner to discard all offers
function revertAll(address _mintableToken, uint256 tokenID)
public
tokenOwnerOnlly(tokenID, _mintableToken)
{
_revertAll(_mintableToken, tokenID);
}
function addERC20TokenPayment(address _erc20Token) public onlyOwner {
erc20TokensArray.addERC20Tokens(_erc20Token);
}
function removeERC20TokenPayment(address _erc20Token)
public
erc20Allowed(_erc20Token)
onlyOwner
{
erc20TokensArray.removeERC20Token(_erc20Token);
}
function bid(
uint256 tokenID,
address _mintableToken,
uint256 amount
)
public
payable
onSaleOnly(tokenID, _mintableToken)
activeAuction(tokenID, _mintableToken)
{
IMintableToken Token = IMintableToken(_mintableToken);
auction memory _auction = auctions[_mintableToken][tokenID];
if (_auction.erc20Token == address(0)) {
require(
msg.value > _auction.currentBid,
"Insufficient bidding amount."
);
if (_auction.buyer == true) {
_auction.highestBidder.transfer(_auction.currentBid);
}
} else {
IERC20 erc20Token = IERC20(_auction.erc20Token);
require(
erc20Token.allowance(msg.sender, address(this)) >= amount,
"Allowance is less than amount sent for bidding."
);
require(
amount > _auction.currentBid,
"Insufficient bidding amount."
);
erc20Token.transferFrom(msg.sender, address(this), amount);
if (_auction.buyer == true) {
erc20Token.transfer(
_auction.highestBidder,
_auction.currentBid
);
}
}
_auction.currentBid = _auction.erc20Token == address(0)
? msg.value
: amount;
Token.safeTransferFrom(Token.ownerOf(tokenID), address(this), tokenID);
_auction.buyer = true;
_auction.highestBidder = msg.sender;
_auction.closingTime += updateClosingTime;
auctions[_mintableToken][tokenID] = _auction;
// Bid event
emit Bid(
_mintableToken,
tokenID,
_auction.lastOwner,
_auction.highestBidder,
_auction.currentBid,
block.timestamp,
_auction.erc20Token
);
}
struct TokenDetails {
uint256 tokenID;
address _mintableToken;
bool isNotClubare;
}
// Collect Function are use to collect funds and NFT from Broker
function collect(
uint256 tokenID,
address _mintableToken,
bool isNotClubare
) public {
IMintableToken Token = IMintableToken(_mintableToken);
auction memory _auction = auctions[_mintableToken][tokenID];
TokenDetArrayLib.TokenDet memory _tokenDet = TokenDetArrayLib.TokenDet(
_mintableToken,
tokenID
);
require(
block.timestamp > _auction.closingTime && _auction.auctionType == 2,
"Auction Not Over!"
);
if (_auction.buyer == true) {
address payable lastOwner2 = _auction.lastOwner;
_calculateFees(tokenID, _mintableToken, lastOwner2, _auction.erc20Token, _auction.currentBid, isNotClubare, false);
{
//Scope added for stack too deep error
uint id = tokenID;
auction memory auction = _auction;
Token.safeTransferFrom(
Token.ownerOf(id),
auction.highestBidder,
id
);
// Buy event
emit Buy(
_tokenDet.NFTAddress,
_tokenDet.tokenID,
lastOwner2,
auction.highestBidder,
auction.currentBid,
block.timestamp,
auction.erc20Token,
false
);
// Revert all the offers.
_revertAll(_mintableToken, id);
}
// Collect event
emit Collect(
_tokenDet.NFTAddress,
_tokenDet.tokenID,
lastOwner2,
_auction.highestBidder,
msg.sender,
block.timestamp,
_auction.erc20Token
);
tokenOpenForSale[_mintableToken][tokenID] = false;
tokensForSale.removeTokenDet(_tokenDet);
tokensForSalePerUser[lastOwner2].removeTokenDet(_tokenDet);
auctionTokens.removeTokenDet(_tokenDet);
delete auctions[_mintableToken][tokenID];
}
}
function buy(
uint256 tokenID,
address _mintableToken,
bool isNotClubare
)
public
payable
onSaleOnly(tokenID, _mintableToken)
flatSaleOnly(tokenID, _mintableToken)
{
IMintableToken Token = IMintableToken(_mintableToken);
auction memory _auction = auctions[_mintableToken][tokenID];
TokenDetArrayLib.TokenDet memory _tokenDet = TokenDetArrayLib.TokenDet(
_mintableToken,
tokenID
);
address payable lastOwner2 = _auction.lastOwner;
_calculateFees(tokenID, _mintableToken, lastOwner2, _auction.erc20Token, _auction.buyPrice, isNotClubare, true);
tokenOpenForSale[_tokenDet.NFTAddress][_tokenDet.tokenID] = false;
Token.safeTransferFrom(
Token.ownerOf(_tokenDet.tokenID),
msg.sender,
_tokenDet.tokenID
);
// Buy event
emit Buy(
_tokenDet.NFTAddress,
_tokenDet.tokenID,
lastOwner2,
msg.sender,
_auction.buyPrice,
block.timestamp,
_auction.erc20Token,
false
);
tokensForSale.removeTokenDet(_tokenDet);
tokensForSalePerUser[lastOwner2].removeTokenDet(_tokenDet);
fixedPriceTokens.removeTokenDet(_tokenDet);
delete auctions[_tokenDet.NFTAddress][_tokenDet.tokenID];
_revertAll(_tokenDet.NFTAddress, _tokenDet.tokenID);
}
function withdraw() public onlyOwner {
msg.sender.transfer(brokerageBalance[address(0)]);
brokerageBalance[address(0)] = 0;
}
function withdrawERC20(address _erc20Token) public onlyOwner {
require(
erc20TokensArray.exists(_erc20Token),
"This erc20token payment not allowed"
);
IERC20 erc20Token = IERC20(_erc20Token);
erc20Token.transfer(msg.sender, brokerageBalance[_erc20Token]);
brokerageBalance[_erc20Token] = 0;
}
function putOnSale(
uint256 _tokenID,
uint256 _startingPrice,
uint256 _auctionType,
uint256 _buyPrice,
uint256 _startingTime,
uint256 _closingTime,
address _mintableToken,
address _erc20Token
)
public
erc20Allowed(_erc20Token)
tokenOwnerOnlly(_tokenID, _mintableToken)
{
IMintableToken Token = IMintableToken(_mintableToken);
auction memory _auction = auctions[_mintableToken][_tokenID];
// Allow to put on sale to already on sale NFT \
// only if it was on auction and have 0 bids and auction is over
if (tokenOpenForSale[_mintableToken][_tokenID] == true) {
require(
_auction.auctionType == 2 &&
_auction.buyer == false &&
block.timestamp > _auction.closingTime,
"This NFT is already on sale."
);
}
TokenDetArrayLib.TokenDet memory _tokenDet = TokenDetArrayLib.TokenDet(
_mintableToken,
_tokenID
);
auction memory newAuction = auction(
msg.sender,
_startingPrice,
address(0),
_auctionType,
_startingPrice,
_buyPrice,
false,
_startingTime,
_closingTime,
_erc20Token
);
require(
(Token.isApprovedForAll(msg.sender, address(this)) ||
Token.getApproved(_tokenDet.tokenID) == address(this)),
"Broker Not approved"
);
require(
_closingTime > _startingTime,
"Closing time should be greater than starting time!"
);
auctions[_tokenDet.NFTAddress][_tokenDet.tokenID] = newAuction;
// Store data in all mappings if adding fresh token on sale
if (
tokenOpenForSale[_tokenDet.NFTAddress][_tokenDet.tokenID] == false
) {
tokenOpenForSale[_tokenDet.NFTAddress][_tokenDet.tokenID] = true;
tokensForSale.addTokenDet(_tokenDet);
tokensForSalePerUser[msg.sender].addTokenDet(_tokenDet);
// Add token to fixedPrice on Timed list
if (_auctionType == 1) {
fixedPriceTokens.addTokenDet(_tokenDet);
} else if (_auctionType == 2) {
auctionTokens.addTokenDet(_tokenDet);
}
}
// OnSale event
emit OnSale(
_tokenDet.NFTAddress,
_tokenDet.tokenID,
msg.sender,
newAuction.auctionType,
newAuction.auctionType == 1
? newAuction.buyPrice
: newAuction.startingPrice,
block.timestamp,
newAuction.erc20Token
);
}
/**
* @notice Bulk De listing from marketplace
* @param _assets array of struct Asset[]
**/
function batchListing(Asset[] calldata _assets) external {
for (uint i = 0; i < _assets.length; i++) {
Asset memory a = _assets[i];
putOnSale(
a._tokenID,
a._startingPrice,
a._auctionType,
a._buyPrice,
a._startingTime,
a._closingTime,
a._mintableToken,
a._erc20Token
);
}
}
function updatePrice(
uint256 tokenID,
address _mintableToken,
uint256 _newPrice,
address _erc20Token
)
public
onSaleOnly(tokenID, _mintableToken)
erc20Allowed(_erc20Token)
tokenOwnerOnlly(tokenID, _mintableToken)
{
// IMintableToken Token = IMintableToken(_mintableToken);
auction memory _auction = auctions[_mintableToken][tokenID];
if (_auction.auctionType == 2) {
require(
block.timestamp < _auction.closingTime,
"Auction Time Over!"
);
}
emit PriceUpdated(
_mintableToken,
tokenID,
_auction.lastOwner,
_auction.auctionType,
_auction.auctionType == 1
? _auction.buyPrice
: _auction.startingPrice,
_newPrice,
block.timestamp,
_auction.erc20Token
);
// Update Price
if (_auction.auctionType == 1) {
_auction.buyPrice = _newPrice;
} else {
_auction.startingPrice = _newPrice;
_auction.currentBid = _newPrice;
}
_auction.erc20Token = _erc20Token;
auctions[_mintableToken][tokenID] = _auction;
}
function putSaleOff(uint256 tokenID, address _mintableToken)
public
tokenOwnerOnlly(tokenID, _mintableToken)
{
// IMintableToken Token = IMintableToken(_mintableToken);
auction memory _auction = auctions[_mintableToken][tokenID];
TokenDetArrayLib.TokenDet memory _tokenDet = TokenDetArrayLib.TokenDet(
_mintableToken,
tokenID
);
tokenOpenForSale[_mintableToken][tokenID] = false;
// OffSale event
emit OffSale(
_mintableToken,
tokenID,
msg.sender,
block.timestamp,
_auction.erc20Token
);
tokensForSale.removeTokenDet(_tokenDet);
tokensForSalePerUser[msg.sender].removeTokenDet(_tokenDet);
// Remove token from list
if (_auction.auctionType == 1) {
fixedPriceTokens.removeTokenDet(_tokenDet);
} else if (_auction.auctionType == 2) {
auctionTokens.removeTokenDet(_tokenDet);
}
delete auctions[_mintableToken][tokenID];
}
/**
* @notice Bulk De listing from marketplace
* @param _pairs array of struct Pair[]
**/
function batchDelisting(Pair[] calldata _pairs) external {
for (uint i = 0; i < _pairs.length; i++) {
Pair memory p = _pairs[i];
putSaleOff(p.tokenID, p._mintableToken);
}
}
function getOnSaleStatus(address _mintableToken, uint256 tokenID)
public
view
returns (bool)
{
return tokenOpenForSale[_mintableToken][tokenID];
}
}