Skip to content

Instantly share code, notes, and snippets.

@mwaqasaslam
Created May 27, 2021 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwaqasaslam/61601c40ac56ac01889b080a6b1e2c2f to your computer and use it in GitHub Desktop.
Save mwaqasaslam/61601c40ac56ac01889b080a6b1e2c2f 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.6.12+commit.27d51765.js&optimize=true&runs=200&gist=
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/token/ERC721/IERC721Upgradeable.sol";
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/math/SafeMathUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/OwnableUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/AccessControlUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
import "./IERC721CreatorRoyalty.sol";
import "./IMarketplaceSettings.sol";
import "./Payments.sol";
contract ChimeraMarketAuctionV2 is Initializable, OwnableUpgradeable, AccessControlUpgradeable ,Payments {
using SafeMathUpgradeable for uint256;
/////////////////////////////////////////////////////////////////////////
// Structs
/////////////////////////////////////////////////////////////////////////
// The active bid for a given token, contains the bidder, the marketplace fee at the time of the bid, and the amount of wei placed on the token
struct ActiveBid {
address payable bidder;
uint8 marketplaceFee;
uint256 amount;
}
// The sale price for a given token containing the seller and the amount of wei to be sold for
struct SalePrice {
address payable seller;
uint256 amount;
}
/////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////
bytes32 public constant TOKEN_COLLECTOR_ROLE = "TOKEN_COLLECTOR_ROLE";
/////////////////////////////////////////////////////////////////////////
// State Variables
/////////////////////////////////////////////////////////////////////////
// Marketplace Settings Interface
IMarketplaceSettings public iMarketplaceSettings;
// Creator Royalty Interface
IERC721CreatorRoyalty public iERC721CreatorRoyalty;
// Mapping from ERC721 contract to mapping of tokenId to sale price.
mapping(address => mapping(uint256 => SalePrice)) private tokenPrices;
// Mapping of ERC721 contract to mapping of token ID to the current bid amount.
mapping(address => mapping(uint256 => ActiveBid)) private tokenCurrentBids;
// A minimum increase in bid amount when out bidding someone.
uint8 public minimumBidIncreasePercentage; // 10 = 10%
mapping(uint256 => bool) private TokenConfirmedByAdmin;
function confirmTokenByAdmin(uint256 _tokenId, address _originContract) onlyOwner external{
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
address tokenOwner = erc721.ownerOf(_tokenId);
require(tokenOwner != address(0), "confirmTokenByAdmin: token not exists");
TokenConfirmedByAdmin[_tokenId] = true;
}
function isTokenConfirmedByAdmin(uint256 _tokenId) public view returns(bool){
return TokenConfirmedByAdmin[_tokenId];
}
/////////////////////////////////////////////////////////////////////////////
// Events
/////////////////////////////////////////////////////////////////////////////
event Sold(
address indexed _originContract,
address indexed _buyer,
address indexed _seller,
uint256 _amount,
uint256 _tokenId
);
event SetSalePrice(
address indexed _originContract,
uint256 _amount,
uint256 _tokenId
);
event Bid(
address indexed _originContract,
address indexed _bidder,
uint256 _amount,
uint256 _tokenId
);
event AcceptBid(
address indexed _originContract,
address indexed _bidder,
address indexed _seller,
uint256 _amount,
uint256 _tokenId
);
event CancelBid(
address indexed _originContract,
address indexed _bidder,
uint256 _amount,
uint256 _tokenId
);
event collectorRoleGrantedTo(
address _collector
);
event collectorRoleRevokedTo(
address _collector
);
/////////////////////////////////////////////////////////////////////////
// initialize
/////////////////////////////////////////////////////////////////////////
/**
* @dev Initializes the contract setting the market settings and creator royalty interfaces.
* @param _iMarketSettings address to set as iMarketplaceSettings.
* @param _iERC721CreatorRoyalty address to set as iERC721CreatorRoyalty.
*/
function initialize(address _iMarketSettings, address _iERC721CreatorRoyalty) public initializer {
require(
_iMarketSettings != address(0),
"constructor::Cannot have null address for _iMarketSettings"
);
require(
_iERC721CreatorRoyalty != address(0),
"constructor::Cannot have null address for _iERC721CreatorRoyalty"
);
__Ownable_init();
__AccessControl_init();
Payments.initializePayment();
// Grant the contract deployer the default admin role: it will be able
// to grant and revoke any roles
_setupRole(AccessControlUpgradeable.DEFAULT_ADMIN_ROLE, owner());
// Set iMarketSettings
iMarketplaceSettings = IMarketplaceSettings(_iMarketSettings);
// Set iERC721CreatorRoyalty
iERC721CreatorRoyalty = IERC721CreatorRoyalty(_iERC721CreatorRoyalty);
minimumBidIncreasePercentage = 10;
}
//Grant Role to Collectors
function grantCollectorRole(address _collector) onlyOwner private {
grantRole(TOKEN_COLLECTOR_ROLE, _collector);
emit collectorRoleGrantedTo(_collector);
}
//revoke roles
function revokeCollectorRole(address _collector) onlyOwner private{
revokeRole(TOKEN_COLLECTOR_ROLE, _collector);
emit collectorRoleRevokedTo(_collector);
}
/////////////////////////////////////////////////////////////////////////
// setIMarketplaceSettings
/////////////////////////////////////////////////////////////////////////
/**
* @dev Admin function to set the marketplace settings.
* Rules:
* - only owner
* - _address != address(0)
* @param _address address of the IMarketplaceSettings.
*/
function setMarketplaceSettings(address _address) public onlyOwner {
require(
_address != address(0),
"setMarketplaceSettings::Cannot have null address for _iMarketSettings"
);
iMarketplaceSettings = IMarketplaceSettings(_address);
}
/////////////////////////////////////////////////////////////////////////
// setIERC721CreatorRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Admin function to set the IERC721CreatorRoyalty.
* Rules:
* - only owner
* - _address != address(0)
* @param _address address of the IERC721CreatorRoyalty.
*/
function setIERC721CreatorRoyalty(address _address) public onlyOwner {
require(
_address != address(0),
"setIERC721CreatorRoyalty::Cannot have null address for _iERC721CreatorRoyalty"
);
iERC721CreatorRoyalty = IERC721CreatorRoyalty(_address);
}
/////////////////////////////////////////////////////////////////////////
// setMinimumBidIncreasePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Admin function to set the minimum bid increase percentage.
* Rules:
* - only owner
* @param _percentage uint8 to set as the new percentage.
*/
function setMinimumBidIncreasePercentage(uint8 _percentage)
public
onlyOwner
{
minimumBidIncreasePercentage = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// Modifiers (as functions)
/////////////////////////////////////////////////////////////////////////
/**
* @dev Checks that the token owner is approved for the ERC721Market
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
*/
function ownerMustHaveMarketplaceApproved(
address _originContract,
uint256 _tokenId
) internal view {
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
address owner = erc721.ownerOf(_tokenId);
require(
erc721.isApprovedForAll(owner, address(this)),
"owner must have approved contract"
);
}
/**
* @dev Checks that the token is owned by the sender
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
*/
function senderMustBeTokenOwner(address _originContract, uint256 _tokenId)
internal
view
{
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
require(
erc721.ownerOf(_tokenId) == msg.sender,
"sender must be the token owner"
);
}
/////////////////////////////////////////////////////////////////////////
// setSalePrice
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set the token for sale. The owner of the token must be the sender and have the marketplace approved.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @param _amount uint256 wei value that the item is for sale
*/
function setSalePrice(
address _originContract,
uint256 _tokenId,
uint256 _amount
) external {
// The owner of the token must have the marketplace approved
ownerMustHaveMarketplaceApproved(_originContract, _tokenId);
// The sender must be the token owner
senderMustBeTokenOwner(_originContract, _tokenId);
require(isTokenConfirmedByAdmin(_tokenId) == true, "Token not confirmed by admin to trade");
if (_amount == 0) {
// Set not for sale and exit
_resetTokenPrice(_originContract, _tokenId);
emit SetSalePrice(_originContract, _amount, _tokenId);
return;
}
tokenPrices[_originContract][_tokenId] = SalePrice(msg.sender, _amount);
emit SetSalePrice(_originContract, _amount, _tokenId);
}
/////////////////////////////////////////////////////////////////////////
/**
* @dev Purchase the token with the expected amount. The current token owner must have the marketplace approved.
* @param _originContract address of the contract storing the token.
/////////////////////////////////////////////////////////////////////////
// safeBuy
* @param _tokenId uint256 ID of the token
* @param _amount uint256 wei amount expecting to purchase the token for.
*/
function safeBuy(
address _originContract,
uint256 _tokenId,
uint256 _amount
) external payable {
// Make sure the tokenPrice is the expected amount
require(
tokenPrices[_originContract][_tokenId].amount == _amount,
"safeBuy::Purchase amount must equal expected amount"
);
buy(_originContract, _tokenId);
}
/////////////////////////////////////////////////////////////////////////
// buy
/////////////////////////////////////////////////////////////////////////
/**
* @dev Purchases the token if it is for sale.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token.
*/
function buy(address _originContract, uint256 _tokenId) public payable {
require(hasRole(TOKEN_COLLECTOR_ROLE, msg.sender), "You are not allowed to collect Art");
// The owner of the token must have the marketplace approved
ownerMustHaveMarketplaceApproved(_originContract, _tokenId);
// Check that the person who set the price still owns the token.
require(
_priceSetterStillOwnsTheToken(_originContract, _tokenId),
"buy::Current token owner must be the person to have the latest price."
);
SalePrice memory sp = tokenPrices[_originContract][_tokenId];
// Check that token is for sale.
require(sp.amount > 0, "buy::Tokens priced at 0 are not for sale.");
// Check that enough ether was sent.
require(
tokenPriceFeeIncluded(_originContract, _tokenId) == msg.value,
"buy::Must purchase the token for the correct price(buy)"
);
// Get token contract details.
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
address tokenOwner = erc721.ownerOf(_tokenId);
// Wipe the token price.
_resetTokenPrice(_originContract, _tokenId);
// Transfer token.
erc721.safeTransferFrom(tokenOwner, msg.sender, _tokenId);
// if the buyer had an existing bid, return it
if (_addressHasBidOnToken(msg.sender, _originContract, _tokenId)) {
_refundBid(_originContract, _tokenId);
}
//Made 2 types of Payments in Payments.sol Primary and secondary
// Payout all parties.
address payable owner = _makePayable(owner());
if(!iMarketplaceSettings.hasERC721TokenSold(_originContract, _tokenId)){
Payments.payoutPrimary(
sp.amount,
!iMarketplaceSettings.hasERC721TokenSold(_originContract, _tokenId),
iMarketplaceSettings.getMarketplaceFeePercentage(),
iERC721CreatorRoyalty.getERC721TokenRoyaltyPercentage(
_originContract,
_tokenId
),
iMarketplaceSettings.getERC721ContractPrimarySaleFeePercentage(
_originContract
),
_makePayable(tokenOwner),
owner,
iERC721CreatorRoyalty.tokenCreator(_originContract, _tokenId),
owner
);
// Set token as sold
iMarketplaceSettings.markERC721Token(_originContract, _tokenId, true);
emit Sold(_originContract, msg.sender, tokenOwner, sp.amount, _tokenId);
}
else{
Payments.payoutSecondary(
sp.amount,
!iMarketplaceSettings.hasERC721TokenSold(_originContract, _tokenId),
iMarketplaceSettings.getMarketplaceFeePercentage(),
iERC721CreatorRoyalty.getERC721TokenRoyaltyPercentage(
_originContract,
_tokenId
),
iMarketplaceSettings.getERC721ContractSecondarySaleFeePercentage(
_originContract
),
_makePayable(tokenOwner),
owner,
iERC721CreatorRoyalty.tokenCreator(_originContract, _tokenId),
owner
);
// Set token as sold
iMarketplaceSettings.markERC721Token(_originContract, _tokenId, true);
emit Sold(_originContract, msg.sender, tokenOwner, sp.amount, _tokenId);
}
}
/////////////////////////////////////////////////////////////////////////
// tokenPrice
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the sale price of the token
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @return uint256 sale price of the token
*/
function tokenPrice(address _originContract, uint256 _tokenId)
external
view
returns (uint256)
{
// The owner of the token must have the marketplace approved
ownerMustHaveMarketplaceApproved(_originContract, _tokenId); // TODO: Make sure to write test to verify that this returns 0 when it fails
if (_priceSetterStillOwnsTheToken(_originContract, _tokenId)) {
return tokenPrices[_originContract][_tokenId].amount;
}
return 0;
}
/////////////////////////////////////////////////////////////////////////
// tokenPriceFeeIncluded
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the sale price of the token including the marketplace fee.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @return uint256 sale price of the token including the fee.
*/
function tokenPriceFeeIncluded(address _originContract, uint256 _tokenId)
public
view
returns (uint256)
{
// The owner of the token must have the marketplace approved
ownerMustHaveMarketplaceApproved(_originContract, _tokenId); // TODO: Make sure to write test to verify that this returns 0 when it fails
if (_priceSetterStillOwnsTheToken(_originContract, _tokenId)) {
return
tokenPrices[_originContract][_tokenId].amount.add(
iMarketplaceSettings.calculateMarketplaceFee(
tokenPrices[_originContract][_tokenId].amount
)
);
}
return 0;
}
/////////////////////////////////////////////////////////////////////////
// bid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Bids on the token, replacing the bid if the bid is higher than the current bid. You cannot bid on a token you already own.
* @param _newBidAmount uint256 value in wei to bid.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
*/
function bid(
uint256 _newBidAmount,
address _originContract,
uint256 _tokenId
) external payable {
require(hasRole(TOKEN_COLLECTOR_ROLE, msg.sender), "You are not allowed to collect Art");
// Check that bid is greater than 0.
require(_newBidAmount > 0, "bid::Cannot bid 0 Wei.");
// Check that bid is higher than previous bid
uint256 currentBidAmount =
tokenCurrentBids[_originContract][_tokenId].amount;
require(
_newBidAmount > currentBidAmount &&
_newBidAmount >=
currentBidAmount.add(
currentBidAmount.mul(minimumBidIncreasePercentage).div(100)
),
"bid::Must place higher bid than existing bid + minimum percentage."
);
// Check that enough ether was sent.
uint256 requiredCost =
_newBidAmount.add(
iMarketplaceSettings.calculateMarketplaceFee(_newBidAmount)
);
require(
requiredCost == msg.value,
"bid::Must purchase the token for the correct price."
);
// Check that bidder is not owner.
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
address tokenOwner = erc721.ownerOf(_tokenId);
require(tokenOwner != msg.sender, "bid::Bidder cannot be owner.");
// Refund previous bidder.
_refundBid(_originContract, _tokenId);
// Set the new bid.
_setBid(_newBidAmount, msg.sender, _originContract, _tokenId);
emit Bid(_originContract, msg.sender, _newBidAmount, _tokenId);
}
/////////////////////////////////////////////////////////////////////////
// safeAcceptBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Accept the bid on the token with the expected bid amount.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @param _amount uint256 wei amount of the bid
*/
function safeAcceptBid(
address _originContract,
uint256 _tokenId,
uint256 _amount
) external {
// Make sure accepting bid is the expected amount
require(
tokenCurrentBids[_originContract][_tokenId].amount == _amount,
"safeAcceptBid::Bid amount must equal expected amount"
);
acceptBid(_originContract, _tokenId);
}
/////////////////////////////////////////////////////////////////////////
// acceptBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Accept the bid on the token.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
*/
function acceptBid(address _originContract, uint256 _tokenId) public {
// The owner of the token must have the marketplace approved
ownerMustHaveMarketplaceApproved(_originContract, _tokenId);
// The sender must be the token owner
senderMustBeTokenOwner(_originContract, _tokenId);
// Check that a bid exists.
require(
_tokenHasBid(_originContract, _tokenId),
"acceptBid::Cannot accept a bid when there is none."
);
// Get current bid on token
ActiveBid memory currentBid =
tokenCurrentBids[_originContract][_tokenId];
// Wipe the token price and bid.
_resetTokenPrice(_originContract, _tokenId);
_resetBid(_originContract, _tokenId);
// Transfer token.
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
erc721.safeTransferFrom(msg.sender, currentBid.bidder, _tokenId);
// Payout all parties.
address payable owner = _makePayable(owner());
if(!iMarketplaceSettings.hasERC721TokenSold(_originContract, _tokenId)){
Payments.payoutPrimary(
currentBid.amount,
!iMarketplaceSettings.hasERC721TokenSold(_originContract, _tokenId),
iMarketplaceSettings.getMarketplaceFeePercentage(),
iERC721CreatorRoyalty.getERC721TokenRoyaltyPercentage(
_originContract,
_tokenId
),
iMarketplaceSettings.getERC721ContractPrimarySaleFeePercentage(
_originContract
),
msg.sender,
owner,
iERC721CreatorRoyalty.tokenCreator(_originContract, _tokenId),
owner
);
iMarketplaceSettings.markERC721Token(_originContract, _tokenId, true);
emit AcceptBid(
_originContract,
currentBid.bidder,
msg.sender,
currentBid.amount,
_tokenId
);
}
else{
Payments.payoutSecondary(
currentBid.amount,
!iMarketplaceSettings.hasERC721TokenSold(_originContract, _tokenId),
iMarketplaceSettings.getMarketplaceFeePercentage(),
iERC721CreatorRoyalty.getERC721TokenRoyaltyPercentage(
_originContract,
_tokenId
),
iMarketplaceSettings.getERC721ContractSecondarySaleFeePercentage(
_originContract
),
msg.sender,
owner,
iERC721CreatorRoyalty.tokenCreator(_originContract, _tokenId),
owner
);
iMarketplaceSettings.markERC721Token(_originContract, _tokenId, true);
emit AcceptBid(
_originContract,
currentBid.bidder,
msg.sender,
currentBid.amount,
_tokenId
);
}
}
/////////////////////////////////////////////////////////////////////////
// cancelBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Cancel the bid on the token.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token.
*/
function cancelBid(address _originContract, uint256 _tokenId) external {
// Check that sender has a current bid.
require(
_addressHasBidOnToken(msg.sender, _originContract, _tokenId),
"cancelBid::Cannot cancel a bid if sender hasn't made one."
);
// Refund the bidder.
_refundBid(_originContract, _tokenId);
emit CancelBid(
_originContract,
msg.sender,
tokenCurrentBids[_originContract][_tokenId].amount,
_tokenId
);
}
/////////////////////////////////////////////////////////////////////////
// currentBidDetailsOfToken
/////////////////////////////////////////////////////////////////////////
/**
* @dev Function to get current bid and bidder of a token.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function currentBidDetailsOfToken(address _originContract, uint256 _tokenId)
public
view
returns (uint256, address)
{
return (
tokenCurrentBids[_originContract][_tokenId].amount,
tokenCurrentBids[_originContract][_tokenId].bidder
);
}
/////////////////////////////////////////////////////////////////////////
// _priceSetterStillOwnsTheToken
/////////////////////////////////////////////////////////////////////////
/**
* @dev Checks that the token is owned by the same person who set the sale price.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 id of the.
*/
function _priceSetterStillOwnsTheToken(
address _originContract,
uint256 _tokenId
) internal view returns (bool) {
IERC721Upgradeable erc721 = IERC721Upgradeable(_originContract);
return
erc721.ownerOf(_tokenId) ==
tokenPrices[_originContract][_tokenId].seller;
}
/////////////////////////////////////////////////////////////////////////
// _resetTokenPrice
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to set token price to 0 for a given contract.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _resetTokenPrice(address _originContract, uint256 _tokenId)
internal
{
tokenPrices[_originContract][_tokenId] = SalePrice(address(0), 0);
}
/////////////////////////////////////////////////////////////////////////
// _addressHasBidOnToken
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function see if the given address has an existing bid on a token.
* @param _bidder address that may have a current bid.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _addressHasBidOnToken(
address _bidder,
address _originContract,
uint256 _tokenId
) internal view returns (bool) {
return tokenCurrentBids[_originContract][_tokenId].bidder == _bidder;
}
/////////////////////////////////////////////////////////////////////////
// _tokenHasBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function see if the token has an existing bid.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _tokenHasBid(address _originContract, uint256 _tokenId)
internal
view
returns (bool)
{
return tokenCurrentBids[_originContract][_tokenId].bidder != address(0);
}
/////////////////////////////////////////////////////////////////////////
// _refundBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to return an existing bid on a token to the
* bidder and reset bid.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _refundBid(address _originContract, uint256 _tokenId) internal {
ActiveBid memory currentBid =
tokenCurrentBids[_originContract][_tokenId];
if (currentBid.bidder == address(0)) {
return;
}
_resetBid(_originContract, _tokenId);
Payments.refund(
currentBid.marketplaceFee,
currentBid.bidder,
currentBid.amount
);
}
/////////////////////////////////////////////////////////////////////////
// _resetBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to reset bid by setting bidder and bid to 0.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _resetBid(address _originContract, uint256 _tokenId) internal {
tokenCurrentBids[_originContract][_tokenId] = ActiveBid(
address(0),
0,
0
);
}
/////////////////////////////////////////////////////////////////////////
// _setBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to set a bid.
* @param _amount uint256 value in wei to bid. Does not include marketplace fee.
* @param _bidder address of the bidder.
* @param _originContract address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _setBid(
uint256 _amount,
address payable _bidder,
address _originContract,
uint256 _tokenId
) internal {
// Check bidder not 0 address.
require(_bidder != address(0), "Bidder cannot be 0 address.");
// Set bid.
tokenCurrentBids[_originContract][_tokenId] = ActiveBid(
_bidder,
iMarketplaceSettings.getMarketplaceFeePercentage(),
_amount
);
}
/////////////////////////////////////////////////////////////////////////
// _makePayable
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to set a bid.
* @param _address non-payable address
* @return payable address
*/
function _makePayable(address _address)
internal
pure
returns (address payable)
{
return address(uint160(_address));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/math/SafeMathUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/OwnableUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/AccessControlUpgradeable.sol';
import "./IERC721CreatorRoyalty.sol";
/**
* @title IERC721 Non-Fungible Token Creator basic interface
*/
contract ChimeraRoyaltyRegistry is OwnableUpgradeable, IERC721CreatorRoyalty {
using SafeMathUpgradeable for uint256;
/////////////////////////////////////////////////////////////////////////
// State Variables
/////////////////////////////////////////////////////////////////////////
// Mapping of ERC721 contract to royalty percentage for all NFTs, 3 == 3%
mapping(address => uint8) private contractRoyaltyPercentage;
// Mapping of ERC721 creator to royalty percentage for all NFTs.
mapping(address => uint8) private creatorRoyaltyPercentage;
// Mapping of ERC721 token to royalty percentage for all NFTs.
mapping(address => mapping(uint256 => uint8))
private tokenRoyaltyPercentage;
IERC721TokenCreator public iERC721TokenCreator;
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
// constructor(address _iERC721TokenCreator) public {
// require(
// _iERC721TokenCreator != address(0),
// "constructor::Cannot set the null address as an _iERC721TokenCreator"
// );
// iERC721TokenCreator = IERC721TokenCreator(_iERC721TokenCreator);
// }
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
function initialize(address _iERC721TokenCreator) public initializer
{
require(
_iERC721TokenCreator != address(0),
"constructor::Cannot set the null address as an _iERC721TokenCreator"
);
__Ownable_init();
iERC721TokenCreator = IERC721TokenCreator(_iERC721TokenCreator);
}
/////////////////////////////////////////////////////////////////////////
// setIERC721TokenCreator
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set an address as an IERC721TokenCreator
* @param _contractAddress address of the IERC721TokenCreator contract
*/
function setIERC721TokenCreator(address _contractAddress)
external
onlyOwner
{
require(
_contractAddress != address(0),
"setIERC721TokenCreator::_contractAddress cannot be null"
);
iERC721TokenCreator = IERC721TokenCreator(_contractAddress);
}
/////////////////////////////////////////////////////////////////////////
// getERC721TokenRoyaltyPercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the royalty fee percentage for a specific ERC721 contract.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @return uint8 wei royalty fee.
*/
function getERC721TokenRoyaltyPercentage(
address _contractAddress,
uint256 _tokenId
) public override view returns (uint8) {
if (tokenRoyaltyPercentage[_contractAddress][_tokenId] > 0) {
return tokenRoyaltyPercentage[_contractAddress][_tokenId];
}
address creator = iERC721TokenCreator.tokenCreator(
_contractAddress,
_tokenId
);
if (creatorRoyaltyPercentage[creator] > 0) {
return creatorRoyaltyPercentage[creator];
}
return contractRoyaltyPercentage[_contractAddress];
}
/////////////////////////////////////////////////////////////////////////
// getPercentageForSetERC721TokenRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the royalty percentage set for an ERC721 token
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @return uint8 wei royalty fee.
*/
function getPercentageForSetERC721TokenRoyalty(
address _contractAddress,
uint256 _tokenId
) external view returns (uint8) {
return tokenRoyaltyPercentage[_contractAddress][_tokenId];
}
/////////////////////////////////////////////////////////////////////////
// setPercentageForSetERC721TokenRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Sets the royalty percentage set for an ERC721 token
* Requirements:
* - `_percentage` must be <= 100.
* - only the owner of this contract or the creator can call this method.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @param _percentage uint8 wei royalty fee.
*/
function setPercentageForSetERC721TokenRoyalty(
address _contractAddress,
uint256 _tokenId,
uint8 _percentage
) external returns (uint8) {
require(
msg.sender ==
iERC721TokenCreator.tokenCreator(_contractAddress, _tokenId) ||
msg.sender == owner(),
"setPercentageForSetERC721TokenRoyalty::Must be contract owner or creator "
);
require(
_percentage <= 100,
"setPercentageForSetERC721TokenRoyalty::_percentage must be <= 100"
);
tokenRoyaltyPercentage[_contractAddress][_tokenId] = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// getPercentageForSetERC721CreatorRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the royalty percentage set for an ERC721 creator
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @return uint8 wei royalty fee.
*/
function getPercentageForSetERC721CreatorRoyalty(
address _contractAddress,
uint256 _tokenId
) external view returns (uint8) {
address creator = iERC721TokenCreator.tokenCreator(
_contractAddress,
_tokenId
);
return creatorRoyaltyPercentage[creator];
}
/////////////////////////////////////////////////////////////////////////
// setPercentageForSetERC721CreatorRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Sets the royalty percentage set for an ERC721 creator
* Requirements:
* - `_percentage` must be <= 100.
* - only the owner of this contract or the creator can call this method.
* @param _creatorAddress address token ID.
* @param _percentage uint8 wei royalty fee.
*/
function setPercentageForSetERC721CreatorRoyalty(
address _creatorAddress,
uint8 _percentage
) external returns (uint8) {
require(
msg.sender == _creatorAddress || msg.sender == owner(),
"setPercentageForSetERC721CreatorRoyalty::Must be owner or creator"
);
require(
_percentage <= 100,
"setPercentageForSetERC721CreatorRoyalty::_percentage must be <= 100"
);
creatorRoyaltyPercentage[_creatorAddress] = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// getPercentageForSetERC721ContractRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the royalty percentage set for an ERC721 contract
* @param _contractAddress address ERC721Contract address.
* @return uint8 wei royalty fee.
*/
function getPercentageForSetERC721ContractRoyalty(address _contractAddress)
external
view
returns (uint8)
{
return contractRoyaltyPercentage[_contractAddress];
}
/////////////////////////////////////////////////////////////////////////
// setPercentageForSetERC721ContractRoyalty
/////////////////////////////////////////////////////////////////////////
/**
* @dev Sets the royalty percentage set for an ERC721 token
* Requirements:
* - `_percentage` must be <= 100.
* - only the owner of this contract.
* @param _contractAddress address ERC721Contract address.
* @param _percentage uint8 wei royalty fee.
*/
function setPercentageForSetERC721ContractRoyalty(
address _contractAddress,
uint8 _percentage
) external onlyOwner returns (uint8) {
require(
_percentage <= 100,
"setPercentageForSetERC721ContractRoyalty::_percentage must be <= 100"
);
contractRoyaltyPercentage[_contractAddress] = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// calculateRoyaltyFee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Utililty function to calculate the royalty fee for a token.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculateRoyaltyFee(
address _contractAddress,
uint256 _tokenId,
uint256 _amount
) external override view returns (uint256) {
return
_amount
.mul(
getERC721TokenRoyaltyPercentage(_contractAddress, _tokenId)
)
.div(100);
}
/////////////////////////////////////////////////////////////////////////
// tokenCreator
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the creator of the token
* @param _contractAddress address of the ERC721 contract
* @param _tokenId uint256 ID of the token
* @return address of the creator
*/
function tokenCreator(address _contractAddress, uint256 _tokenId)
external
override
view
returns (address payable)
{
return iERC721TokenCreator.tokenCreator(_contractAddress, _tokenId);
}
}
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
// import '@openzeppelin/contracts-upgradeable/proxy/Initializable.sol';
// import '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol';
// import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
// import '@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol';
import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/OwnableUpgradeable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/token/ERC721/ERC721Upgradeable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/math/SafeMathUpgradeable.sol";
//last contract inheritance
//contract Chimera is Initializable, ERC721Upgradeable, IERC721Creator, Ownable, Whitelist
contract Whitelist is Initializable, OwnableUpgradeable {
function initialize() public initializer {
__Ownable_init();
}
// Mapping of address to boolean indicating whether the address is whitelisted
mapping(address => bool) private whitelistMap;
// flag controlling whether whitelist is enabled.
bool private whitelistEnabled = true;
event AddToWhitelist(address indexed _newAddress);
event RemoveFromWhitelist(address indexed _removedAddress);
/**
* @dev Enable or disable the whitelist
* @param _enabled bool of whether to enable the whitelist.
*/
function enableWhitelist(bool _enabled) public onlyOwner {
whitelistEnabled = _enabled;
}
/**
* @dev Adds the provided address to the whitelist
* @param _newAddress address to be added to the whitelist
*/
function addToWhitelist(address _newAddress) public onlyOwner {
_whitelist(_newAddress);
emit AddToWhitelist(_newAddress);
}
/**
* @dev Removes the provided address to the whitelist
* @param _removedAddress address to be removed from the whitelist
*/
function removeFromWhitelist(address _removedAddress) public onlyOwner {
_unWhitelist(_removedAddress);
emit RemoveFromWhitelist(_removedAddress);
}
/**
* @dev Returns whether the address is whitelisted
* @param _address address to check
* @return bool
*/
function isWhitelisted(address _address) public view returns (bool) {
if (whitelistEnabled) {
return whitelistMap[_address];
} else {
return true;
}
}
/**
* @dev Internal function for removing an address from the whitelist
* @param _removedAddress address to unwhitelisted
*/
function _unWhitelist(address _removedAddress) internal {
whitelistMap[_removedAddress] = false;
}
/**
* @dev Internal function for adding the provided address to the whitelist
* @param _newAddress address to be added to the whitelist
*/
function _whitelist(address _newAddress) internal {
whitelistMap[_newAddress] = true;
}
}
contract Chimera is Initializable, ERC721Upgradeable, OwnableUpgradeable, Whitelist {
using SafeMathUpgradeable for uint256;
string private _name;
string private _symbol;
uint8 private _decimals;
// Mapping from token ID to the creator's address.
mapping(uint256 => address) private tokenCreators;
//Enum for storing token type whether it is physical or Digital
enum TokenType {DIGITAL,PHYSICAL}
//Enum for storing token sale type PRIMARY or SECONDARY
enum TokenSaleType {PRIMARY,SECONDARY}
struct tokenTypeStruct{
TokenType tokenType;
TokenSaleType tokensaleType;
}
//mapping to hold token Type
mapping(uint256 => tokenTypeStruct) private tokentypes;
// Counter for creating token IDs
uint256 private idCounter;
// Event indicating metadata was updated.
event TokenURIUpdated(uint256 indexed _tokenId, string _uri, TokenType _tokenType);
function initialize(string memory name, string memory symbol) public initializer {
//ERC721Upgradeable.__ERC721_init(name , symbol);
Whitelist.initialize();
__Ownable_init();
__ERC721_init(name , symbol);
}
/**
* @dev Whitelists a bunch of addresses.
* @param _whitelistees address[] of addresses to whitelist.
*/
function initWhitelist(address[] memory _whitelistees) public onlyOwner {
// Add all whitelistees.
for (uint256 i = 0; i < _whitelistees.length; i++) {
address creator = _whitelistees[i];
if (!isWhitelisted(creator)) {
_whitelist(creator);
}
}
}
/**
* @dev Checks that the token is owned by the sender.
* @param _tokenId uint256 ID of the token.
*/
modifier onlyTokenOwner(uint256 _tokenId) {
address owner = ownerOf(_tokenId);
require(owner == msg.sender, "must be the owner of the token");
_;
}
/**
* @dev Checks that the token was created by the sender.
* @param _tokenId uint256 ID of the token.
*/
modifier onlyTokenCreator(uint256 _tokenId) {
address creator = tokenCreator(_tokenId);
require(creator == msg.sender, "must be the creator of the token");
_;
}
/**
* @dev Adds a new unique token to the supply.
* @param _uri string metadata uri associated with the token.
* param _tokenType of the token to make it PHYSICAL or DIGITAL
*/
function addNewToken(string memory _uri, TokenType _tokenType) public {
require(isWhitelisted(msg.sender), "must be whitelisted to create tokens");
_createToken(_uri, msg.sender, _tokenType);
}
/**
* @dev Deletes the token with the provided ID.
* @param _tokenId uint256 ID of the token.
// */
function deleteToken(uint256 _tokenId) public onlyTokenOwner(_tokenId) {
_burn( _tokenId);
}
/**
* @dev Updates the token metadata if the owner is also the
* creator.
* @param _tokenId uint256 ID of the token.
* @param _uri string metadata URI.
* param _tokenType of the token PHYSICAL or DIGITAL 0,1
*/
function updateTokenMetadata(uint256 _tokenId, string memory _uri,TokenType _tokenType )
public
onlyTokenOwner(_tokenId)
onlyTokenCreator(_tokenId)
{
_setTokenURI(_tokenId, _uri);
_setTokenType(_tokenId,_tokenType);
emit TokenURIUpdated(_tokenId, _uri, _tokenType);
}
/**
* @dev Gets the creator of the token.
* @param _tokenId uint256 ID of the token.
* @return address of the creator.
*/
function tokenCreator(uint256 _tokenId) public view returns (address) {
return tokenCreators[_tokenId];
}
/**
* @dev Gets the Type of the token.
* @param _tokenId uint256 ID of the token.
* @return _tokentype of the token PHYSICAL or DIGITAL.
*/
function tokenType(uint256 _tokenId) public view returns (TokenType _tokentype) {
return tokentypes[_tokenId].tokenType;
}
/**
* @dev Gets the Sale Type of the token.
* @param _tokenId uint256 ID of the token.
* @return _tokensaleType of the token PHYSICAL or DIGITAL.
*/
function getTokenSaleType(uint256 _tokenId) public view returns (TokenSaleType _tokensaleType) {
return tokentypes[_tokenId].tokensaleType;
}
/**
* @dev Internal function for setting the token's creator.
* @param _tokenId uint256 id of the token.
* @param _creator address of the creator of the token.
*/
function _setTokenCreator(uint256 _tokenId, address _creator) internal {
tokenCreators[_tokenId] = _creator;
}
/**
* @dev Internal function for setting the token Type.
* @param _tokenId uint256 id of the token.
* @param _tokenType 0,1 DIGITAL,PHYSICAL of the creator of the token.
*/
function _setTokenType(uint256 _tokenId, TokenType _tokenType ) internal{
tokentypes[_tokenId].tokenType = _tokenType;
}
// function _setTokenSaleType(uint256 _tokenId ) internal returns(TokenSaleType _tokensaleType){
// TokenSaleType tokensaleType = TokenSaleType.PRIMARY;
// tokentypes[_tokenId].tokensaleType = tokensaleType;
// return tokentypes[_tokenId].tokensaleType;
// }
/**
* @dev Internal function creating a new token.
* @param _uri string metadata uri associated with the token
* @param _creator address of the creator of the token.
* @param _tokenType of the token
*/
function _createToken(string memory _uri, address _creator,TokenType _tokenType ) internal returns (uint256) {
uint256 newId = idCounter;
idCounter++;
_mint(_creator, newId);
_setTokenURI(newId, _uri);
_setTokenCreator(newId, _creator);
_setTokenType(newId,_tokenType);
return newId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/math/SafeMathUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/OwnableUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/math/SafeMath.sol";
// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/access/Ownable.sol";
import "./IERC721CreatorRoyalty.sol";
import "./IERC721Creator.sol";
/**
* @title IERC721 Non-Fungible Token Creator basic interface
*/
contract ChimeraTokenCreatorRegistry is Initializable, OwnableUpgradeable, IERC721TokenCreator {
using SafeMathUpgradeable for uint256;
/////////////////////////////////////////////////////////////////////////
// State Variables
/////////////////////////////////////////////////////////////////////////
// Mapping of ERC721 token to it's creator.
mapping(address => mapping(uint256 => address payable))
private tokenCreators;
// Mapping of addresses that implement IERC721Creator.
mapping(address => bool) private iERC721Creators;
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
/**
* @dev Initializes the contract setting the iERC721Creators with the provided addresses.
* @param _iERC721Creators address[] to set as iERC721Creators.
*/
// constructor(address[] memory _iERC721Creators) public {
// require(
// _iERC721Creators.length < 1000,
// "constructor::Cannot mark more than 1000 addresses as IERC721Creator"
// );
// for (uint8 i = 0; i < _iERC721Creators.length; i++) {
// require(
// _iERC721Creators[i] != address(0),
// "constructor::Cannot set the null address as an IERC721Creator"
// );
// iERC721Creators[_iERC721Creators[i]] = true;
// }
// }
/////////////////////////////////////////////////////////////////////////
// initializer
/////////////////////////////////////////////////////////////////////////
/**
* @dev Initializes the contract setting the iERC721Creators with the provided addresses.
* @param _iERC721Creators address[] to set as iERC721Creators.
*/
function initialize(address[] memory _iERC721Creators) public initializer {
require(
_iERC721Creators.length < 1000,
"constructor::Cannot mark more than 1000 addresses as IERC721Creator"
);
__Ownable_init();
for (uint8 i = 0; i < _iERC721Creators.length; i++) {
require(
_iERC721Creators[i] != address(0),
"constructor::Cannot set the null address as an IERC721Creator"
);
iERC721Creators[_iERC721Creators[i]] = true;
}
}
/////////////////////////////////////////////////////////////////////////
// tokenCreator
/////////////////////////////////////////////////////////////////////////
/**
* @dev Gets the creator of the token
* @param _contractAddress address of the ERC721 contract
* @param _tokenId uint256 ID of the token
* @return address of the creator
*/
function tokenCreator(address _contractAddress, uint256 _tokenId)
external
override
view
returns (address payable)
{
if (tokenCreators[_contractAddress][_tokenId] != address(0)) {
return tokenCreators[_contractAddress][_tokenId];
}
if (iERC721Creators[_contractAddress]) {
return IERC721Creator(_contractAddress).tokenCreator(_tokenId);
}
return address(0);
}
/////////////////////////////////////////////////////////////////////////
// setTokenCreator
/////////////////////////////////////////////////////////////////////////
/**
* @dev Sets the creator of the token
* @param _contractAddress address of the ERC721 contract
* @param _tokenId uint256 ID of the token
* @param _creator address of the creator for the token
*/
function setTokenCreator(
address _contractAddress,
uint256 _tokenId,
address payable _creator
) external onlyOwner {
require(
_creator != address(0),
"setTokenCreator::Cannot set null address as creator"
);
require(
_contractAddress != address(0),
"setTokenCreator::_contractAddress cannot be null"
);
tokenCreators[_contractAddress][_tokenId] = _creator;
}
/////////////////////////////////////////////////////////////////////////
// setIERC721Creator
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set an address as an IERC721Creator
* @param _contractAddress address of the IERC721Creator contract
*/
function setIERC721Creator(address _contractAddress) external onlyOwner {
require(
_contractAddress != address(0),
"setIERC721Creator::_contractAddress cannot be null"
);
iERC721Creators[_contractAddress] = true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
/**
* @title IERC721 Non-Fungible Token Creator basic interface
*/
interface IERC721Creator {
/**
* @dev Gets the creator of the token
* @param _tokenId uint256 ID of the token
* @return address of the creator
*/
function tokenCreator(uint256 _tokenId)
external
view
returns (address payable);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./IERC721TokenCreator.sol";
/**
* @title IERC721CreatorRoyalty Token level royalty interface.
*/
interface IERC721CreatorRoyalty is IERC721TokenCreator {
/**
* @dev Get the royalty fee percentage for a specific ERC721 contract.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @return uint8 wei royalty fee.
*/
function getERC721TokenRoyaltyPercentage(
address _contractAddress,
uint256 _tokenId
) external view returns (uint8);
/**
* @dev Utililty function to calculate the royalty fee for a token.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculateRoyaltyFee(
address _contractAddress,
uint256 _tokenId,
uint256 _amount
) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
/**
* @title IERC721 Non-Fungible Token Creator basic interface
*/
interface IERC721TokenCreator {
/**
* @dev Gets the creator of the token
* @param _contractAddress address of the ERC721 contract
* @param _tokenId uint256 ID of the token
* @return address of the creator
*/
function tokenCreator(address _contractAddress, uint256 _tokenId)
external
view
returns (address payable);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
/**
* @title IMarketplaceSettings Settings governing a marketplace.
*/
interface IMarketplaceSettings {
/////////////////////////////////////////////////////////////////////////
// Marketplace Min and Max Values
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the max value to be used with the marketplace.
* @return uint256 wei value.
*/
function getMarketplaceMaxValue() external view returns (uint256);
/**
* @dev Get the max value to be used with the marketplace.
* @return uint256 wei value.
*/
function getMarketplaceMinValue() external view returns (uint256);
/////////////////////////////////////////////////////////////////////////
// Marketplace Fee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the marketplace fee percentage.
* @return uint8 wei fee.
*/
function getMarketplaceFeePercentage() external view returns (uint8);
/**
* @dev Utility function for calculating the marketplace fee for given amount of wei.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculateMarketplaceFee(uint256 _amount)
external
view
returns (uint256);
/////////////////////////////////////////////////////////////////////////
// Primary Sale Fee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the primary sale fee percentage for a specific ERC721 contract.
* @param _contractAddress address ERC721Contract address.
* @return uint8 wei primary sale fee.
*/
function getERC721ContractPrimarySaleFeePercentage(address _contractAddress)
external
view
returns (uint8);
/**
* @dev Utility function for calculating the primary sale fee for given amount of wei
* @param _contractAddress address ERC721Contract address.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculatePrimarySaleFee(address _contractAddress, uint256 _amount)
external
view
returns (uint256);
//chimera
/////////////////////////////////////////////////////////////////////////
// secondary Sale Fee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the secondary sale fee percentage for a specific ERC721 contract.
* @param _contractAddress address ERC721Contract address.
* @return uint8 wei secondary sale fee.
*/
function getERC721ContractSecondarySaleFeePercentage(address _contractAddress)
external
view
returns (uint8);
/**
* @dev Utility function for calculating the secondary sale fee for given amount of wei
* @param _contractAddress address ERC721Contract address.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculateSecondarySaleFee(address _contractAddress, uint256 _amount)
external
view
returns (uint256);
/**
* @dev Check whether the ERC721 token has sold at least once.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @return bool of whether the token has sold.
*/
function hasERC721TokenSold(address _contractAddress, uint256 _tokenId)
external
view
returns (bool);
/**
* @dev Mark a token as sold.
* Requirements:
*
* - `_contractAddress` cannot be the zero address.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @param _hasSold bool of whether the token should be marked sold or not.
*/
function markERC721Token(
address _contractAddress,
uint256 _tokenId,
bool _hasSold
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
interface ISendValueProxy {
function sendValue(address payable _to) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./IMarketplaceSettings.sol";
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/OwnableUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/AccessControlUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/math/SafeMathUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/access/Ownable.sol";
// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/access/AccessControl.sol";
// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/math/SafeMath.sol";
/**
* @title MarketplaceSettings Settings governing the marketplace fees.
*/
contract MarketplaceSettings is Initializable, OwnableUpgradeable, AccessControlUpgradeable, IMarketplaceSettings {
using SafeMathUpgradeable for uint256;
/////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////
bytes32 public constant TOKEN_MARK_ROLE = "TOKEN_MARK_ROLE";
/////////////////////////////////////////////////////////////////////////
// State Variables
/////////////////////////////////////////////////////////////////////////
// Max wei value within the marketplace
uint256 private maxValue;
// Min wei value within the marketplace
uint256 private minValue;
// Percentage fee for the marketplace, 3 == 3%
uint8 private marketplaceFeePercentage;
// Mapping of ERC721 contract to the primary sale fee. If primary sale fee is 0 for an origin contract then primary sale fee is ignored. 1 == 1%
mapping(address => uint8) private primarySaleFees;
//chimera
// Mapping of ERC721 contract to the secondary sale fee. If secondary sale fee is 0 for an origin contract then secondary sale fee is ignored. 1 == 1%
mapping(address => uint8) private secondarySaleFees;
// Mapping of ERC721 contract to mapping of token ID to whether the token has been sold before.
mapping(address => mapping(uint256 => bool)) private soldTokens;
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
/**
* @dev Initializes the contract maxValue, minValues, and marketplaceFeePercentage to default settings.
* Also, sets the roles for the contract to the owner.
*/
// constructor() public {
// maxValue = 2**254; // 2 ^ 254 is max amount, prevents any overflow issues.
// minValue = 1000; // all amounts must be greater than 1000 Wei.
// marketplaceFeePercentage = 3; // 3% marketplace fee on all txs.
// _setupRole(AccessControl.DEFAULT_ADMIN_ROLE, owner());
// grantRole(TOKEN_MARK_ROLE, owner());
// }
/////////////////////////////////////////////////////////////////////////
// initializer
/////////////////////////////////////////////////////////////////////////
/**
* @dev Initializes the contract maxValue, minValues, and marketplaceFeePercentage to default settings.
* Also, sets the roles for the contract to the owner.
*/
function initialize() public initializer {
__Ownable_init();
__AccessControl_init();
maxValue = 2**254; // 2 ^ 254 is max amount, prevents any overflow issues.
minValue = 1000; // all amounts must be greater than 1000 Wei.
marketplaceFeePercentage = 3; // 3% marketplace fee on all txs.
_setupRole(AccessControlUpgradeable.DEFAULT_ADMIN_ROLE, owner());
grantRole(TOKEN_MARK_ROLE, owner());
}
/////////////////////////////////////////////////////////////////////////
// grantMarketplaceMarkTokenAccess
/////////////////////////////////////////////////////////////////////////
/**
* @dev Grants a marketplace contract access to marke
* @param _account address of the account that can perform the token mark role.
*/
function grantMarketplaceAccess(address _account) external {
require(
hasRole(AccessControlUpgradeable.DEFAULT_ADMIN_ROLE, msg.sender),
"grantMarketplaceAccess::Must be admin to call method"
);
grantRole(TOKEN_MARK_ROLE, _account);
}
/////////////////////////////////////////////////////////////////////////
// getMarketplaceMaxValue
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the max value to be used with the marketplace.
* @return uint256 wei value.
*/
function getMarketplaceMaxValue() external override view returns (uint256) {
return maxValue;
}
/////////////////////////////////////////////////////////////////////////
// setMarketplaceMaxValue
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set the maximum value of the marketplace settings.
* @param _maxValue uint256 maximum wei value.
*/
function setMarketplaceMaxValue(uint256 _maxValue) external onlyOwner {
maxValue = _maxValue;
}
/////////////////////////////////////////////////////////////////////////
// getMarketplaceMinValue
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the max value to be used with the marketplace.
* @return uint256 wei value.
*/
function getMarketplaceMinValue() external override view returns (uint256) {
return minValue;
}
/////////////////////////////////////////////////////////////////////////
// setMarketplaceMinValue
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set the minimum value of the marketplace settings.
* @param _minValue uint256 minimum wei value.
*/
function setMarketplaceMinValue(uint256 _minValue) external onlyOwner {
minValue = _minValue;
}
/////////////////////////////////////////////////////////////////////////
// getMarketplaceFeePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the marketplace fee percentage.
* @return uint8 wei fee.
*/
function getMarketplaceFeePercentage()
external
override
view
returns (uint8)
{
return marketplaceFeePercentage;
}
/////////////////////////////////////////////////////////////////////////
// setMarketplaceFeePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set the marketplace fee percentage.
* Requirements:
* - `_percentage` must be <= 100.
* @param _percentage uint8 percentage fee.
*/
function setMarketplaceFeePercentage(uint8 _percentage) external onlyOwner {
require(
_percentage <= 100,
"setMarketplaceFeePercentage::_percentage must be <= 100"
);
marketplaceFeePercentage = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// calculateMarketplaceFee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Utility function for calculating the marketplace fee for given amount of wei.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculateMarketplaceFee(uint256 _amount)
external
override
view
returns (uint256)
{
return _amount.mul(marketplaceFeePercentage).div(100);
}
/////////////////////////////////////////////////////////////////////////
// getERC721ContractPrimarySaleFeePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the primary sale fee percentage for a specific ERC721 contract.
* @param _contractAddress address ERC721Contract address.
* @return uint8 wei primary sale fee.
*/
function getERC721ContractPrimarySaleFeePercentage(address _contractAddress)
external
override
view
returns (uint8)
{
return primarySaleFees[_contractAddress];
}
/////////////////////////////////////////////////////////////////////////
// setERC721ContractPrimarySaleFeePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set the primary sale fee percentage for a specific ERC721 contract.
* Requirements:
*
* - `_contractAddress` cannot be the zero address.
* - `_percentage` must be <= 100.
* @param _contractAddress address ERC721Contract address.
* @param _percentage uint8 percentage fee for the ERC721 contract.
*/
function setERC721ContractPrimarySaleFeePercentage(
address _contractAddress,
uint8 _percentage
) external onlyOwner {
require(
_percentage <= 100,
"setERC721ContractPrimarySaleFeePercentage::_percentage must be <= 100"
);
primarySaleFees[_contractAddress] = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// calculatePrimarySaleFee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Utility function for calculating the primary sale fee for given amount of wei
* @param _contractAddress address ERC721Contract address.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculatePrimarySaleFee(address _contractAddress, uint256 _amount)
external
override
view
returns (uint256)
{
return _amount.mul(primarySaleFees[_contractAddress]).div(100);
}
//chimera
/////////////////////////////////////////////////////////////////////////
// getERC721ContractSecondarySaleFeePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the secondary sale fee percentage for a specific ERC721 contract.
* @param _contractAddress address ERC721Contract address.
* @return uint8 wei primary sale fee.
*/
function getERC721ContractSecondarySaleFeePercentage(address _contractAddress)
external
override
view
returns (uint8)
{
return secondarySaleFees[_contractAddress];
}
/////////////////////////////////////////////////////////////////////////
// setERC721ContractSecondarySaleFeePercentage
/////////////////////////////////////////////////////////////////////////
/**
* @dev Set the secondary sale fee percentage for a specific ERC721 contract.
* Requirements:
*
* - `_contractAddress` cannot be the zero address.
* - `_percentage` must be <= 100.
* @param _contractAddress address ERC721Contract address.
* @param _percentage uint8 percentage fee for the ERC721 contract.
*/
function setERC721ContractSecondarySaleFeePercentage(
address _contractAddress,
uint8 _percentage
) external onlyOwner {
require(
_percentage <= 100,
"setERC721ContractSecondarySaleFeePercentage::_percentage must be <= 100"
);
secondarySaleFees[_contractAddress] = _percentage;
}
/////////////////////////////////////////////////////////////////////////
// calculateSecondarySaleFee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Utility function for calculating the secondary sale fee for given amount of wei
* @param _contractAddress address ERC721Contract address.
* @param _amount uint256 wei amount.
* @return uint256 wei fee.
*/
function calculateSecondarySaleFee(address _contractAddress, uint256 _amount)
external
override
view
returns (uint256)
{
return _amount.mul(secondarySaleFees[_contractAddress]).div(100);
}
/////////////////////////////////////////////////////////////////////////
// hasERC721TokenSold
/////////////////////////////////////////////////////////////////////////
/**
* @dev Check whether the ERC721 token has sold at least once.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @return bool of whether the token has sold.
*/
function hasERC721TokenSold(address _contractAddress, uint256 _tokenId)
external
override
view
returns (bool)
{
return soldTokens[_contractAddress][_tokenId];
}
/////////////////////////////////////////////////////////////////////////
// markERC721TokenAsSold
/////////////////////////////////////////////////////////////////////////
/**
* @dev Mark a token as sold.
* Requirements:
*
* - `_contractAddress` cannot be the zero address.
* @param _contractAddress address ERC721Contract address.
* @param _tokenId uint256 token ID.
* @param _hasSold bool of whether the token should be marked sold or not.
*/
function markERC721Token(
address _contractAddress,
uint256 _tokenId,
bool _hasSold
) external override {
require(
hasRole(TOKEN_MARK_ROLE, msg.sender),
"markERC721Token::Must have TOKEN_MARK_ROLE role to call method"
);
soldTokens[_contractAddress][_tokenId] = _hasSold;
}
/////////////////////////////////////////////////////////////////////////
// markTokensAsSold
/////////////////////////////////////////////////////////////////////////
/**
* @dev Function to set an array of tokens for a contract as sold, thus not being subject to the primary sale fee, if one exists.
* @param _originContract address of ERC721 contract.
* @param _tokenIds uint256[] array of token ids.
*/
function markTokensAsSold(
address _originContract,
uint256[] calldata _tokenIds
) external {
require(
hasRole(TOKEN_MARK_ROLE, msg.sender),
"markERC721Token::Must have TOKEN_MARK_ROLE role to call method"
);
// limit to batches of 2000
require(
_tokenIds.length <= 2000,
"markTokensAsSold::Attempted to mark more than 2000 tokens as sold"
);
// Mark provided tokens as sold.
for (uint256 i = 0; i < _tokenIds.length; i++) {
soldTokens[_originContract][_tokenIds[i]] = true;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
import "./SendValueProxy.sol";
/**
* @dev Contract with a ISendValueProxy that will catch reverts when attempting to transfer funds.
*/
contract MaybeSendValue is Initializable {
SendValueProxy proxy;
// constructor() internal {
// proxy = new SendValueProxy();
// }
function SendValueProxyInitialize() public initializer {
proxy = new SendValueProxy();
}
/**
* @dev Maybe send some wei to the address via a proxy. Returns true on success and false if transfer fails.
* @param _to address to send some value to.
* @param _value uint256 amount to send.
*/
// function maybeSendValue(address payable _to, uint256 _value)
// internal
// returns (bool)
// {
// // Call sendValue on the proxy contract and forward the mesg.value.
// /* solium-disable-next-line */
// // (bool success, bytes memory _) = address(proxy).call.value(_value)(
// // abi.encodeWithSignature("sendValue(address)", _to)
// // );
// (bool success, bytes memory _) = address(proxy).call{value:(_value)}(
// abi.encodeWithSignature("sendValue(address)", _to)
// );
// return success;
// }
/**
* @dev Maybe send some wei to the address via a proxy. Returns true on success and false if transfer fails.
* @param _to address to send some value to.
* @param _value uint256 amount to send.
*/
function maybeSendValue(address payable _to, uint256 _value)
internal
returns (bool)
{
_to.transfer(_value);
return true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/math/SafeMathUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
import "./SendValueOrEscrow.sol";
/**
* @title Payments contract for Chimera Marketplaces.
*/
contract Payments is Initializable ,SendValueOrEscrow {
using SafeMathUpgradeable for uint256;
using SafeMathUpgradeable for uint8;
//primaryEvents
event PrimarymarketplacePayment(address indexed _marketplacePayee,uint indexed _marketplacePayment);
event PrimarySalePayment(address indexed _primarySalePayee,uint primarySalePayment);
event PrimarypayeePayment(address indexed _payee,uint payeePayment);
//SecondaryEvents
event SecondarymarketplacePayment(address indexed _marketplacePayee,uint indexed _marketplacePayment);
event SecondarySalePayment(address indexed _secondarySalePayee,uint primarySalePayment);
event SecondarypayeePayment(address indexed _payee,uint payeePayment);
function initializePayment() public initializer {
SendValueOrEscrow.initialize();
}
/////////////////////////////////////////////////////////////////////////
// refund
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to refund an address. Typically for canceled bids or offers.
* Requirements:
*
* - _payee cannot be the zero address
*
* @param _marketplacePercentage uint8 percentage of the fee for the marketplace.
* @param _amount uint256 value to be split.
* @param _payee address seller of the token.
*/
function refund(
uint8 _marketplacePercentage,
address payable _payee,
uint256 _amount
) internal {
require(
_payee != address(0),
"refund::no payees can be the zero address"
);
if (_amount > 0) {
SendValueOrEscrow.sendValueOrEscrow(
_payee,
_amount.add(
calcPercentagePayment(_amount, _marketplacePercentage)
)
);
}
}
/////////////////////////////////////////////////////////////////////////
// payout Primay
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to pay the seller, creator, and maintainer.
* Requirements:
*
* - _marketplacePercentage + _royaltyPercentage + _primarySalePercentage <= 100
* - no payees can be the zero address
*
* @param _amount uint256 value to be split.
* @param _isPrimarySale bool of whether this is a primary sale.
* @param _marketplacePercentage uint8 percentage of the fee for the marketplace.
* @param _royaltyPercentage uint8 percentage of the fee for the royalty.
* @param _primarySalePercentage uint8 percentage primary sale fee for the marketplace.
* @param _payee address seller of the token.
* @param _marketplacePayee address seller of the token.
* @param _royaltyPayee address seller of the token.
* @param _primarySalePayee address seller of the token.
*/
function payoutPrimary(
uint256 _amount,
bool _isPrimarySale,
uint8 _marketplacePercentage,
uint8 _royaltyPercentage,
uint8 _primarySalePercentage,
address payable _payee,
address payable _marketplacePayee,
address payable _royaltyPayee,
address payable _primarySalePayee
) internal {
require(
_marketplacePercentage <= 100,
"payout::marketplace percentage cannot be above 100"
);
require(
_royaltyPercentage.add(_primarySalePercentage) <= 100,
"payout::percentages cannot go beyond 100"
);
require(
_payee != address(0) &&
_primarySalePayee != address(0) &&
_marketplacePayee != address(0) &&
_royaltyPayee != address(0),
"payout::no payees can be the zero address"
);
// Note:: Solidity is kind of terrible in that there is a limit to local
// variables that can be put into the stack. The real pain is that
// one can put structs, arrays, or mappings into memory but not basic
// data types. Hence our payments array that stores these values.
uint256[4] memory payments;
// uint256 marketplacePayment
payments[0] = calcPercentagePayment(_amount, _marketplacePercentage);
// uint256 royaltyPayment
payments[1] = calcRoyaltyPayment(
_isPrimarySale,
_amount,
_royaltyPercentage
);
// uint256 primarySalePayment
payments[2] = calcPrimarySalePayment(
_isPrimarySale,
_amount,
_primarySalePercentage
);
// uint256 payeePayment
// payments[3] = _amount.sub(payments[2]);
payments[3] = _amount.sub(payments[1]).sub(payments[2]);
// marketplacePayment
if (payments[0] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_marketplacePayee, payments[0]);
emit PrimarymarketplacePayment(_marketplacePayee,payments[0]);
}
// royaltyPayment
if (payments[1] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_royaltyPayee, payments[1]);
}
// primarySalePayment
if (payments[2] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_primarySalePayee, payments[2]);
emit PrimarySalePayment(_primarySalePayee, payments[2]);
}
// payeePayment
if (payments[3] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_payee, payments[3]);
emit PrimarypayeePayment(_payee, payments[3]);
}
}
/////////////////////////////////////////////////////////////////////////
// payout With Secondary Fee
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to pay the seller, creator, and maintainer.
* Requirements:
*
* - _marketplacePercentage + _royaltyPercentage + _secondarySalePercentage <= 100
* - no payees can be the zero address
*
* @param _amount uint256 value to be split.
* @param _isPrimarySale bool of whether this is a primary sale.
* @param _marketplacePercentage uint8 percentage of the fee for the marketplace.
* @param _royaltyPercentage uint8 percentage of the fee for the royalty.
* @param _secondarySalePercentage uint8 percentage primary sale fee for the marketplace.
* @param _payee address seller of the token.
* @param _marketplacePayee address seller of the token.
* @param _royaltyPayee address seller of the token.
* @param _secondarySalePayee address seller of the token.
*/
function payoutSecondary(
uint256 _amount,
bool _isPrimarySale, //false
uint8 _marketplacePercentage,
uint8 _royaltyPercentage,
uint8 _secondarySalePercentage,
address payable _payee,
address payable _marketplacePayee,
address payable _royaltyPayee,
address payable _secondarySalePayee
) internal {
require(
_marketplacePercentage <= 100,
"payout::marketplace percentage cannot be above 100"
);
require(
_royaltyPercentage.add(_secondarySalePercentage) <= 100,
"payout::percentages cannot go beyond 100"
);
require(
_payee != address(0) &&
_secondarySalePayee != address(0) &&
_marketplacePayee != address(0) &&
_royaltyPayee != address(0),
"payout::no payees can be the zero address"
);
// Note:: Solidity is kind of terrible in that there is a limit to local
// variables that can be put into the stack. The real pain is that
// one can put structs, arrays, or mappings into memory but not basic
// data types. Hence our payments array that stores these values.
uint256[4] memory payments;
// uint256 marketplacePayment
payments[0] = calcPercentagePayment(_amount, _marketplacePercentage);
// uint256 royaltyPayment
payments[1] = calcRoyaltyPayment(
_isPrimarySale,
_amount,
_royaltyPercentage
);
// uint256 SecondarySalePayment
payments[2] = calcSecondarySalePayment(
_isPrimarySale,
_amount,
_secondarySalePercentage
);
// uint256 payeePayment
payments[3] = _amount.sub(payments[2]);
// marketplacePayment
if (payments[0] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_marketplacePayee, payments[0]);
emit SecondarymarketplacePayment(_marketplacePayee,payments[0] );
}
// // royaltyPayment
if (payments[1] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_royaltyPayee, payments[1]);
}
// primarySalePayment
if (payments[2] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_secondarySalePayee, payments[2]);
emit SecondarySalePayment(_secondarySalePayee, payments[2]);
}
// payeePayment
if (payments[3] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_payee, payments[3]);
emit SecondarypayeePayment(_payee,payments[3]);
}
}
/////////////////////////////////////////////////////////////////////////
// calcRoyaltyPayment
/////////////////////////////////////////////////////////////////////////
/**
* @dev Private function to calculate Royalty amount.
* If primary sale: 0
* If no royalty percentage: 0
* otherwise: royalty in wei
* @param _isPrimarySale bool of whether this is a primary sale
* @param _amount uint256 value to be split
* @param _percentage uint8 royalty percentage
* @return uint256 wei value owed for royalty
*/
function calcRoyaltyPayment(
bool _isPrimarySale,
uint256 _amount,
uint8 _percentage
) private pure returns (uint256) {
if (_isPrimarySale) {
return 0;
}
return calcPercentagePayment(_amount, _percentage);
}
/////////////////////////////////////////////////////////////////////////
// calcPrimarySalePayment
/////////////////////////////////////////////////////////////////////////
/**
* @dev Private function to calculate PrimarySale amount.
* If not primary sale: 0
* otherwise: primary sale in wei
* @param _isPrimarySale bool of whether this is a primary sale
* @param _amount uint256 value to be split
* @param _percentage uint8 royalty percentage
* @return uint256 wei value owed for primary sale
*/
function calcPrimarySalePayment(
bool _isPrimarySale,
uint256 _amount,
uint8 _percentage
) private pure returns (uint256) {
if (_isPrimarySale) {
return calcPercentagePayment(_amount, _percentage);
}
return 0;
}
/////////////////////////////////////////////////////////////////////////
// calcSecondarySalePayment
/////////////////////////////////////////////////////////////////////////
/**
* @dev Private function to calculate PrimarySale amount.
* If not primary sale: 0
* otherwise: primary sale in wei
* @param _isPrimarySale bool of whether this is a primary sale
* @param _amount uint256 value to be split
* @param _percentage uint8 royalty percentage
* @return uint256 wei value owed for primary sale
*/
function calcSecondarySalePayment(
bool _isPrimarySale,
uint256 _amount,
uint8 _percentage
) private pure returns (uint256) {
if (!_isPrimarySale) {
return calcPercentagePayment(_amount, _percentage);
}
return 0;
}
/////////////////////////////////////////////////////////////////////////
// calcPercentagePayment
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to calculate percentage value.
* @param _amount uint256 wei value
* @param _percentage uint8 percentage
* @return uint256 wei value based on percentage.
*/
function calcPercentagePayment(uint256 _amount, uint8 _percentage)
internal
pure
returns (uint256)
{
return _amount.mul(_percentage).div(100);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/payment/PullPaymentUpgradeable.sol";
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
import "./MaybeSendValue.sol";
/**
* @dev Contract to make payments. If a direct transfer fails, it will store the payment in escrow until the address decides to pull the payment.
*/
contract SendValueOrEscrow is MaybeSendValue, PullPaymentUpgradeable {
/////////////////////////////////////////////////////////////////////////
// Events
/////////////////////////////////////////////////////////////////////////
event SendValue(address indexed _payee, uint256 amount);
//initializer
function initialize() public initializer {
__PullPayment_init();
}
/////////////////////////////////////////////////////////////////////////
// sendValueOrEscrow
/////////////////////////////////////////////////////////////////////////
/**
* @dev Send some value to an address.
* @param _to address to send some value to.
* @param _value uint256 amount to send.
*/
function sendValueOrEscrow(address payable _to, uint256 _value) internal {
// attempt to make the transfer
bool successfulTransfer = MaybeSendValue.maybeSendValue(_to, _value);
// if it fails, transfer it into escrow for them to redeem at their will.
if (!successfulTransfer) {
_asyncTransfer(_to, _value);
}
emit SendValue(_to, _value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./ISendValueProxy.sol";
/**
* @dev Contract that attempts to send value to an address.
*/
contract SendValueProxy is ISendValueProxy {
/**
* @dev Send some wei to the address.
* @param _to address to send some value to.
*/
function sendValue(address payable _to) external override payable {
// Note that `<address>.transfer` limits gas sent to receiver. It may
// not support complex contract operations in the future.
_to.transfer(msg.value);
}
}
pragma solidity 0.6.12;
import "./ChimeraMarketAuctionV2.sol";
contract TestAssertFailOnPay {
/**
* @dev A payment method that will fail by an assertion
*/
receive() external payable {
assert(false);
}
/**
* @dev Place a bid for the owner
* @param _newBidAmount uint256 value in wei to bid, plus marketplace fee.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @param _market address of the marketplace to make the bid
*/
function bid(
uint256 _newBidAmount,
address _originContract,
uint256 _tokenId,
address _market
) public payable {
ChimeraMarketAuctionV2(_market).bid{value: msg.value}(
_newBidAmount,
_originContract,
_tokenId
);
}
}
pragma solidity 0.6.12;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/access/OwnableUpgradeable.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v3.3/contracts/proxy/Initializable.sol';
import "./ChimeraMarketAuctionV2.sol";
contract TestExpensiveWallet is OwnableUpgradeable {
function initialize() public initializer {
__Ownable_init();
}
/**
* @dev A costly payment method. Should fail on `<address>.transfer`
*/
receive() external payable {
uint256 a = 0;
while (a < 1500000) {
a = a + 1;
}
_makePayable(owner()).transfer(msg.value);
}
/**
* @dev Claim the money as the owner.
* @param _escrowAddress address of the contract escrowing the money to be claimed
*/
function claimMoney(address _escrowAddress) external onlyOwner {
ChimeraMarketAuctionV2(_escrowAddress).withdrawPayments(
_makePayable(address(this))
);
}
/**
* @dev Place a bid for the owner
* @param _newBidAmount uint256 value in wei to bid, plus marketplace fee.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @param _market address of the marketplace to make the bid
*/
function bid(
uint256 _newBidAmount,
address _originContract,
uint256 _tokenId,
address _market
) public payable {
ChimeraMarketAuctionV2(_market).bid{value: msg.value}(
_newBidAmount,
_originContract,
_tokenId
);
}
/////////////////////////////////////////////////////////////////////////
// _makePayable
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to set a bid.
* @param _address non-payable address
* @return payable address
*/
function _makePayable(address _address)
internal
pure
returns (address payable)
{
return address(uint160(_address));
}
}
pragma solidity 0.6.12;
import "./ChimeraMarketAuctionV2.sol";
contract TestRequireFailOnPay {
/**
* @dev A payment method that will fail by a failed require
*/
receive() external payable {
require(false, "ready to fail!!!");
}
/**
* @dev Place a bid for the owner
* @param _newBidAmount uint256 value in wei to bid, plus marketplace fee.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @param _market address of the marketplace to make the bid
*/
function bid(
uint256 _newBidAmount,
address _originContract,
uint256 _tokenId,
address _market
) public payable {
ChimeraMarketAuctionV2(_market).bid{value: msg.value}(
_newBidAmount,
_originContract,
_tokenId
);
}
}
pragma solidity 0.6.12;
import "./ChimeraMarketAuctionV2.sol";
contract TestRevertOnPay {
/**
* @dev A payment method that will fail by a failed revert
*/
receive() external payable {
revert("Will always");
}
/**
* @dev Place a bid for the owner
* @param _newBidAmount uint256 value in wei to bid, plus marketplace fee.
* @param _originContract address of the contract storing the token.
* @param _tokenId uint256 ID of the token
* @param _market address of the marketplace to make the bid
*/
function bid(
uint256 _newBidAmount,
address _originContract,
uint256 _tokenId,
address _market
) public payable {
ChimeraMarketAuctionV2(_market).bid{value: msg.value}(
_newBidAmount,
_originContract,
_tokenId
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment