Hello!...
any example to apply some pattern to divide logic and data structures of a contract type erc721?
I have a contract that I am building that is at the limit, (a marketplace with auction)... I would like to have 3 contracts, one for the logic of the marketplace, another for the auction, and the main one on top of these...
question, I've been researching but I don't understand which is the best way to do it.
Whether to use library, or to make 3 contracts and import the 2 in the main one and instantiate them....
Which would be the best way?
this is my contract
pragma solidity >=0.6.0 <0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
contract Woonkly is ERC721BurnableUpgradeable, AccessControlUpgradeable {
using SafeMathUpgradeable for uint256;
mapping (uint256 => uint256) private _tokens_price;
mapping (uint256 => address payable) private _auction_vendor;
mapping (uint256 => address payable) private _offers_marketplace;
mapping (uint256 => uint256) private _auction_platformCommissionPercent;
mapping (uint256 => uint256) private _offers_closeTimestamp;
mapping (uint256 => uint256) private _auction_closeTimestamp;
mapping (uint256 => address payable) private _auction_bidder;
mapping (uint256 => uint256) private _auction_amount;
mapping (uint256 => bool) private _auction_claimed;
mapping (uint256 => bool) private _auction_exist;
event AuctionCreated(uint256 indexed tokenId, uint256 closeTimestamp);
event AuctionBid(uint256 indexed tokenId, address indexed bidder, uint256 amount);
event AuctionClaimed(uint256 indexed tokenId, address indexed bidder, uint256 amount, uint256 platformCommision);
event OfferReceived(address indexed payer, uint256 tokenId, uint256 amount);
event OfferAccepted(address indexed payer, uint256 tokenId);
event OfferRejected(address indexed payer, uint256 tokenId);
event OfferExpired(address indexed payer, uint256 tokenId);
event OfferCancelled(address indexed payer, uint256 tokenId);
uint256 private constant COMMISSION_EXPONENT = 4;
address payable private _platformAddress;
uint256 private _platformCommissionPercent;
uint256 private _extensionTimePeriod;
uint256 private _offerExpirationTime;
modifier tokenExists(uint256 _tokenId) {
require(_exists(_tokenId), "Woonkly: Id for this token does not exist.");
_;
}
modifier onlyOwner(uint256 _tokenId) {
require(ownerOf(_tokenId) == msg.sender, "Woonkly: You are not the owner of this token.");
_;
}
modifier offerExist(uint256 _tokenId) {
require(_offers_marketplace[_tokenId] != address(0), "Kargain: Does not exist any offer for this token.");
_;
}
modifier onlyAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Woonkly: Caller is not a admin");
_;
}
function initialize(address payable _platformAddress_, uint256 _platformCommissionPercent_) public initializer {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
__AccessControl_init();
_offerExpirationTime = 1 days;
_platformAddress = _platformAddress_;
_platformCommissionPercent = _platformCommissionPercent_;
__ERC721Burnable_init();
__ERC721_init("Woonkly", "WKY");
}
function offerExpirationTime() external view returns (uint256) {
return _offerExpirationTime;
}
function setOfferExpirationTime(uint256 offerExpirationTime_) external onlyAdmin {
_offerExpirationTime = offerExpirationTime_;
}
function platformCommissionPercent() external view returns (uint256) {
return _platformCommissionPercent;
}
function setPlatformCommissionPercent(uint256 platformCommissionPercent_) external onlyAdmin {
_platformCommissionPercent = platformCommissionPercent_;
}
function tokenPrice(uint256 _tokenId) external view tokenExists(_tokenId) returns (uint256) {
return _tokens_price[_tokenId];
}
function setExtensionTimePeriod(uint256 extensionTimePeriod_) external onlyAdmin {
_extensionTimePeriod = extensionTimePeriod_;
}
function extensionTimePeriod() public view returns(uint256){
return _extensionTimePeriod;
}
function setTokenPrice(uint256 _tokenId, uint256 _price) external tokenExists(_tokenId) onlyOwner(_tokenId)
{
require(
_offers_marketplace[_tokenId] == address(0) ||
_offers_closeTimestamp[_tokenId] < now,
"Woonkly: An offer is already submitted, token price cannot be changed until it expires."
);
_tokens_price[_tokenId] = _price;
}
function _refundOffer(uint256 _tokenId) private tokenExists(_tokenId) offerExist(_tokenId) {
address payable buyer = _offers_marketplace[_tokenId];
delete _offers_marketplace[_tokenId];
delete _offers_closeTimestamp[_tokenId];
buyer.transfer(_tokens_price[_tokenId]);
}
function createOffer(uint256 _tokenId) external payable tokenExists(_tokenId)
{
require(ownerOf(_tokenId) != msg.sender, "Woonkly: You cannot buy your own token.");
require(_offers_marketplace[_tokenId] == address(0) && _offers_closeTimestamp[_tokenId] < now, "Woonkly: An offer is already submitted.");
require(msg.value == _tokens_price[_tokenId], "Woonkly: The offer amount is invalid.");
_offers_marketplace[_tokenId] = payable(msg.sender);
_offers_closeTimestamp[_tokenId] = now.add(_offerExpirationTime);
emit OfferReceived(msg.sender, _tokenId, msg.value);
}
function acceptOffer(uint256 _tokenId) external tokenExists(_tokenId) offerExist(_tokenId) onlyOwner(_tokenId) {
if (_offers_closeTimestamp[_tokenId] < now) {
delete _offers_marketplace[_tokenId];
delete _offers_closeTimestamp[_tokenId];
emit OfferExpired(msg.sender, _tokenId);
return;
}
address payable buyer = _offers_marketplace[_tokenId];
delete _offers_marketplace[_tokenId];
delete _offers_closeTimestamp[_tokenId];
uint256 platformCommission = _tokens_price[_tokenId].mul(_platformCommissionPercent).div(10**COMMISSION_EXPONENT);
_platformAddress.transfer(platformCommission);
msg.sender.transfer(_tokens_price[_tokenId].sub(platformCommission));
safeTransferFrom(msg.sender, buyer, _tokenId);
emit OfferAccepted(msg.sender, _tokenId);
}
function rejectOffer(uint256 _tokenId) external tokenExists(_tokenId) onlyOwner(_tokenId) offerExist(_tokenId) {
_refundOffer(_tokenId);
emit OfferRejected(msg.sender, _tokenId);
}
function mintFixedPrice(uint256 _tokenId, uint256 _price) external {
require(!_exists(_tokenId), "Woonkly: Id for this token already exists.");
require(_price > 0, "Woonkly: Prices must be greater than zero.");
_mint(msg.sender, _tokenId);
_tokens_price[_tokenId] = _price;
}
function mintAuction(uint256 tokenId, uint256 secondsToClose) external {
require(!_exists(tokenId), "Woonkly: Id for this token already exists.");
uint256 closeTimestamp = block.timestamp + secondsToClose;
_mint(msg.sender, tokenId);
_auction_vendor[tokenId] = msg.sender;
_auction_platformCommissionPercent[tokenId] = _platformCommissionPercent;
_auction_closeTimestamp[tokenId] = closeTimestamp;
_auction_bidder[tokenId] = payable(address(0));
_auction_amount[tokenId] = 0;
_auction_claimed[tokenId] = false;
_auction_exist[tokenId] = true;
emit AuctionCreated(tokenId,closeTimestamp);
}
function auction(uint256 tokenId, uint256 secondsToClose) external {
require(!_auction_exist[tokenId], "Woonkly: Auction for this token already exist");
uint256 closeTimestamp = block.timestamp + secondsToClose;
_auction_closeTimestamp[tokenId] = closeTimestamp;
_auction_vendor[tokenId] = msg.sender;
_auction_platformCommissionPercent[tokenId] = _platformCommissionPercent;
_auction_bidder[tokenId] = payable(address(0));
_auction_amount[tokenId] = 0;
_auction_claimed[tokenId] = false;
_auction_exist[tokenId] = true;
emit AuctionCreated(tokenId,closeTimestamp);
}
function placeBid(uint256 tokenId) external payable {
require(_auction_exist[tokenId], "Woonkly: Auction for this token not exist");
require(!_auction_claimed[tokenId], "Woonkly: Auction was claimed");
require(_auction_closeTimestamp[tokenId] >= block.timestamp,
"Woonkly: Auction for this token is closed");
require(msg.value > _auction_amount[tokenId],
"Woonkly: Bid should be higher than current");
uint256 refundAmount = _auction_amount[tokenId];
address payable refundAddress = _auction_bidder[tokenId];
_auction_bidder[tokenId] = payable(address(msg.sender));
_auction_amount[tokenId] = msg.value;
// Extend close time if it's required
uint256 bidTimeOffset = _auction_closeTimestamp[tokenId].sub(block.timestamp);
if (bidTimeOffset < _extensionTimePeriod) {
_auction_closeTimestamp[tokenId] = _auction_closeTimestamp[tokenId].add(
_extensionTimePeriod.sub(bidTimeOffset)
);
}
refundAddress.transfer(refundAmount);
emit AuctionBid(tokenId, msg.sender, msg.value);
}
function claimAuction(uint256 tokenId) external onlyOwner(tokenId) {
require(_auction_exist[tokenId], "Woonkly: Auction not exist");
require(!_auction_claimed[tokenId], "Woonkly: Auction was claimed");
require(_auction_closeTimestamp[tokenId] < block.timestamp,
"Woonkly: Auction is not closed");
_auction_claimed[tokenId] = true;
uint256 platformCommission;
if (_auction_bidder[tokenId] != address(0)){
platformCommission = _auction_amount[tokenId]
.mul(_auction_platformCommissionPercent[tokenId])
.div(10** COMMISSION_EXPONENT);
safeTransferFrom(address(this), _auction_bidder[tokenId], tokenId);
_platformAddress.transfer(platformCommission);
_auction_vendor[tokenId].transfer(
_auction_amount[tokenId].sub(platformCommission)
);
}
else {
burn(tokenId);
}
emit AuctionClaimed(tokenId, _auction_bidder[tokenId],
_auction_amount[tokenId], platformCommission);
}
}
Thanks!