Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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=
REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.
SCRIPTS
The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
Scripts have full access to the web3.js and ethers.js libraries.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
// 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);
/**
* @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;
/**
* @dev Interface for interacting with the SupeRare contract that holds SuperRare beta tokens.
*/
interface ISupeRare {
/**
* @notice A descriptive name for a collection of NFTs in this contract
*/
function name() external pure returns (string memory _name);
/**
* @notice An abbreviated name for NFTs in this contract
*/
function symbol() external pure returns (string memory _symbol);
/**
* @dev Returns whether the creator is whitelisted
* @param _creator address to check
* @return bool
*/
function isWhitelisted(address _creator) external view returns (bool);
/**
* @notice A distinct Uniform Resource Identifier (URI) for a given asset.
* @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
* 3986. The URI may point to a JSON file that conforms to the "ERC721
* Metadata JSON Schema".
*/
function tokenURI(uint256 _tokenId) external view returns (string memory);
/**
* @dev Gets the creator of the token
* @param _tokenId uint256 ID of the token
* @return address of the creator
*/
function creatorOfToken(uint256 _tokenId)
external
view
returns (address payable);
/**
* @dev Gets the total amount of tokens stored by the contract
* @return uint256 representing the total amount of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @dev Gets the owner of the specified token ID
* @param _tokenId uint256 ID of the token to query the owner of
* @return owner address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 _tokenId) external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./IMarketplaceSettings.sol";
import "openzeppelin-solidity-solc6/contracts/math/SafeMath.sol";
import "openzeppelin-solidity-solc6/contracts/access/Ownable.sol";
import "openzeppelin-solidity-solc6/contracts/access/AccessControl.sol";
/**
* @title MarketplaceSettings Settings governing the marketplace fees.
*/
contract MarketplaceSettings is Ownable, AccessControl, IMarketplaceSettings {
using SafeMath 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;
// 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());
}
/////////////////////////////////////////////////////////////////////////
// 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(AccessControl.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 view override 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 view override 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
view
override
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
view
override
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
view
override
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
view
override
returns (uint256)
{
return _amount.mul(primarySaleFees[_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
view
override
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 "./SendValueProxy.sol";
/**
* @dev Contract with a ISendValueProxy that will catch reverts when attempting to transfer funds.
*/
contract MaybeSendValue {
SendValueProxy proxy;
constructor() internal {
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)
);
return success;
}
}
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
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/token/ERC721/ERC721.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/AccessControl.sol";
/**
*Submitted for verification at Etherscan.io on 2020-05-11
*/
// contract Whitelist is Ownable {
// // 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 Nifter is ERC721, Ownable {
using SafeMath for uint256;
// Mapping from token ID to the creator's address.
mapping(uint256 => address) private tokenCreators;
// Counter for creating token IDs
uint256 private idCounter;
// Old SuperRare contract to look up token details.
// ISuperRare private oldSuperRare;
// Event indicating metadata was updated.
event TokenURIUpdated(uint256 indexed _tokenId, string _uri);
constructor(
string memory _name,
string memory _symbol
) public
ERC721(_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.
*/
function addNewToken(string memory _uri) public {
// require(isWhitelisted(msg.sender), "must be whitelisted to create tokens");
_createToken(_uri, msg.sender);
}
/**
* @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.
*/
function updateTokenMetadata(uint256 _tokenId, string memory _uri)
public
onlyTokenOwner(_tokenId)
onlyTokenCreator(_tokenId)
{
_setTokenURI(_tokenId, _uri);
emit TokenURIUpdated(_tokenId, _uri);
}
/**
* @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 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 creating a new token.
* @param _uri string metadata uri associated with the token
* @param _creator address of the creator of the token.
*/
function _createToken(string memory _uri, address _creator) internal returns (uint256) {
uint256 newId = idCounter;
idCounter++;
_mint(_creator, newId);
_setTokenURI(newId, _uri);
_setTokenCreator(newId, _creator);
return newId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "openzeppelin-solidity-solc6/contracts/math/SafeMath.sol";
import "./SendValueOrEscrow.sol";
/**
* @title Payments contract for SuperRare Marketplaces.
*/
contract Payments is SendValueOrEscrow {
using SafeMath for uint256;
using SafeMath for uint8;
/////////////////////////////////////////////////////////////////////////
// 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
/////////////////////////////////////////////////////////////////////////
/**
* @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 payout(
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[1]).sub(payments[2]);
// marketplacePayment
if (payments[0] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_marketplacePayee, payments[0]);
}
// royaltyPayment
if (payments[1] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_royaltyPayee, payments[1]);
}
// primarySalePayment
if (payments[2] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_primarySalePayee, payments[2]);
}
// payeePayment
if (payments[3] > 0) {
SendValueOrEscrow.sendValueOrEscrow(_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;
}
/////////////////////////////////////////////////////////////////////////
// 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);
}
}
REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.
SCRIPTS
The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
Scripts have full access to the web3.js and ethers.js libraries.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "openzeppelin-solidity-solc6/contracts/payment/PullPayment.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, PullPayment {
/////////////////////////////////////////////////////////////////////////
// Events
/////////////////////////////////////////////////////////////////////////
event SendValue(address indexed _payee, uint256 amount);
/////////////////////////////////////////////////////////////////////////
// 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 payable override {
// Note that `<address>.transfer` limits gas sent to receiver. It may
// not support complex contract operations in the future.
_to.transfer(msg.value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "openzeppelin-solidity-solc6/contracts/token/ERC721/IERC721.sol";
import "openzeppelin-solidity-solc6/contracts/math/SafeMath.sol";
import "openzeppelin-solidity-solc6/contracts/access/Ownable.sol";
import "./IERC721CreatorRoyalty.sol";
import "./Marketplace/IMarketplaceSettings.sol";
import "./Payments.sol";
contract SuperRareAuctionHouse is Ownable, Payments {
using SafeMath for uint256;
/////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////
// Types of Auctions
bytes32 public constant COLDIE_AUCTION = "COLDIE_AUCTION";
bytes32 public constant SCHEDULED_AUCTION = "SCHEDULED_AUCTION";
bytes32 public constant NO_AUCTION = bytes32(0);
/////////////////////////////////////////////////////////////////////////
// Structs
/////////////////////////////////////////////////////////////////////////
// A reserve auction.
struct Auction {
address payable auctionCreator;
uint256 creationBlock;
uint256 lengthOfAuction;
uint256 startingBlock;
uint256 reservePrice;
uint256 minimumBid;
bytes32 auctionType;
}
// 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;
}
/////////////////////////////////////////////////////////////////////////
// State Variables
/////////////////////////////////////////////////////////////////////////
// Marketplace Settings Interface
IMarketplaceSettings public iMarketSettings;
// Creator Royalty Interface
IERC721CreatorRoyalty public iERC721CreatorRoyalty;
// Mapping from ERC721 contract to mapping of tokenId to Auctions.
mapping(address => mapping(uint256 => Auction)) private auctions;
// Mapping of ERC721 contract to mapping of token ID to the current bid amount.
mapping(address => mapping(uint256 => ActiveBid)) private currentBids;
// Number of blocks to begin refreshing auction lengths
uint256 public auctionLengthExtension;
// Max Length that an auction can be
uint256 public maxLength;
// A minimum increase in bid amount when out bidding someone.
uint8 public minimumBidIncreasePercentage; // 10 = 10%
/////////////////////////////////////////////////////////////////////////
// Events
/////////////////////////////////////////////////////////////////////////
event NewColdieAuction(
address indexed _contractAddress,
uint256 indexed _tokenId,
address indexed _auctionCreator,
uint256 _reservePrice,
uint256 _lengthOfAuction
);
event CancelAuction(
address indexed _contractAddress,
uint256 indexed _tokenId,
address indexed _auctionCreator
);
event NewScheduledAuction(
address indexed _contractAddress,
uint256 indexed _tokenId,
address indexed _auctionCreator,
uint256 _startingBlock,
uint256 _minimumBid,
uint256 _lengthOfAuction
);
event AuctionBid(
address indexed _contractAddress,
address indexed _bidder,
uint256 indexed _tokenId,
uint256 _amount,
bool _startedAuction,
uint256 _newAuctionLength,
address _previousBidder
);
event AuctionSettled(
address indexed _contractAddress,
address indexed _bidder,
address _seller,
uint256 indexed _tokenId,
uint256 _amount
);
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
/**
* @dev Initializes the contract setting the market settings and creator royalty interfaces.
* @param _iMarketSettings address to set as iMarketSettings.
* @param _iERC721CreatorRoyalty address to set as iERC721CreatorRoyalty.
*/
constructor(address _iMarketSettings, address _iERC721CreatorRoyalty)
public
{
maxLength = 43200; // ~ 7 days == 7 days * 24 hours * 3600s / 14s per block
auctionLengthExtension = 65; // ~ 15 min == 15 min * 60s / 14s per block
require(
_iMarketSettings != address(0),
"constructor::Cannot have null address for _iMarketSettings"
);
require(
_iERC721CreatorRoyalty != address(0),
"constructor::Cannot have null address for _iERC721CreatorRoyalty"
);
// Set iMarketSettings
iMarketSettings = IMarketplaceSettings(_iMarketSettings);
// Set iERC721CreatorRoyalty
iERC721CreatorRoyalty = IERC721CreatorRoyalty(_iERC721CreatorRoyalty);
minimumBidIncreasePercentage = 10;
}
/////////////////////////////////////////////////////////////////////////
// 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"
);
iMarketSettings = 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);
}
/////////////////////////////////////////////////////////////////////////
// setMaxLength
/////////////////////////////////////////////////////////////////////////
/**
* @dev Admin function to set the maxLength of an auction.
* Rules:
* - only owner
* - _maxLangth > 0
* @param _maxLength uint256 max length of an auction.
*/
function setMaxLength(uint256 _maxLength) public onlyOwner {
require(
_maxLength > 0,
"setMaxLength::_maxLength must be greater than 0"
);
maxLength = _maxLength;
}
/////////////////////////////////////////////////////////////////////////
// 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;
}
/////////////////////////////////////////////////////////////////////////
// setAuctionLengthExtension
/////////////////////////////////////////////////////////////////////////
/**
* @dev Admin function to set the auctionLengthExtension of an auction.
* Rules:
* - only owner
* - _auctionLengthExtension > 0
* @param _auctionLengthExtension uint256 max length of an auction.
*/
function setAuctionLengthExtension(uint256 _auctionLengthExtension)
public
onlyOwner
{
require(
_auctionLengthExtension > 0,
"setAuctionLengthExtension::_auctionLengthExtension must be greater than 0"
);
auctionLengthExtension = _auctionLengthExtension;
}
/////////////////////////////////////////////////////////////////////////
// createColdieAuction
/////////////////////////////////////////////////////////////////////////
/**
* @dev create a reserve auction token contract address, token id, price
* Rules:
* - Cannot create an auction if contract isn't approved by owner
* - lengthOfAuction (in blocks) > 0
* - lengthOfAuction (in blocks) <= maxLength
* - Reserve price must be >= 0
* - Must be owner of the token
* - Cannot have a current auction going
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
* @param _reservePrice uint256 Wei value of the reserve price.
* @param _lengthOfAuction uint256 length of auction in blocks.
*/
function createColdieAuction(
address _contractAddress,
uint256 _tokenId,
uint256 _reservePrice,
uint256 _lengthOfAuction
) public {
// Rules
_requireOwnerApproval(_contractAddress, _tokenId);
_requireOwnerAsSender(_contractAddress, _tokenId);
require(
_lengthOfAuction <= maxLength,
"createColdieAuction::Cannot have auction longer than maxLength"
);
require(
auctions[_contractAddress][_tokenId].auctionType == NO_AUCTION ||
(msg.sender !=
auctions[_contractAddress][_tokenId].auctionCreator),
"createColdieAuction::Cannot have a current auction"
);
require(
_lengthOfAuction > 0,
"createColdieAuction::_lengthOfAuction must be > 0"
);
require(
_reservePrice >= 0,
"createColdieAuction::_reservePrice must be >= 0"
);
require(
_reservePrice <= iMarketSettings.getMarketplaceMaxValue(),
"createColdieAuction::Cannot set reserve price higher than max value"
);
// Create the auction
auctions[_contractAddress][_tokenId] = Auction(
msg.sender,
block.number,
_lengthOfAuction,
0,
_reservePrice,
0,
COLDIE_AUCTION
);
emit NewColdieAuction(
_contractAddress,
_tokenId,
msg.sender,
_reservePrice,
_lengthOfAuction
);
}
/////////////////////////////////////////////////////////////////////////
// cancelAuction
/////////////////////////////////////////////////////////////////////////
/**
* @dev cancel an auction
* Rules:
* - Must have an auction for the token
* - Auction cannot have started
* - Must be the creator of the auction
* - Must return token to owner if escrowed
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
*/
function cancelAuction(address _contractAddress, uint256 _tokenId)
external
{
require(
auctions[_contractAddress][_tokenId].auctionType != NO_AUCTION,
"cancelAuction::Must have a current auction"
);
require(
auctions[_contractAddress][_tokenId].startingBlock == 0 ||
auctions[_contractAddress][_tokenId].startingBlock >
block.number,
"cancelAuction::auction cannot be started"
);
require(
auctions[_contractAddress][_tokenId].auctionCreator == msg.sender,
"cancelAuction::must be the creator of the auction"
);
Auction memory auction = auctions[_contractAddress][_tokenId];
auctions[_contractAddress][_tokenId] = Auction(
address(0),
0,
0,
0,
0,
0,
NO_AUCTION
);
// Return the token if this contract escrowed it
IERC721 erc721 = IERC721(_contractAddress);
if (erc721.ownerOf(_tokenId) == address(this)) {
erc721.transferFrom(address(this), msg.sender, _tokenId);
}
emit CancelAuction(_contractAddress, _tokenId, auction.auctionCreator);
}
/////////////////////////////////////////////////////////////////////////
// createScheduledAuction
/////////////////////////////////////////////////////////////////////////
/**
* @dev create a scheduled auction token contract address, token id
* Rules:
* - lengthOfAuction (in blocks) > 0
* - startingBlock > currentBlock
* - Cannot create an auction if contract isn't approved by owner
* - Minimum bid must be >= 0
* - Must be owner of the token
* - Cannot have a current auction going for this token
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
* @param _minimumBid uint256 Wei value of the reserve price.
* @param _lengthOfAuction uint256 length of auction in blocks.
* @param _startingBlock uint256 block number to start the auction on.
*/
function createScheduledAuction(
address _contractAddress,
uint256 _tokenId,
uint256 _minimumBid,
uint256 _lengthOfAuction,
uint256 _startingBlock
) external {
require(
_lengthOfAuction > 0,
"createScheduledAuction::_lengthOfAuction must be greater than 0"
);
require(
_lengthOfAuction <= maxLength,
"createScheduledAuction::Cannot have auction longer than maxLength"
);
require(
_startingBlock > block.number,
"createScheduledAuction::_startingBlock must be greater than block.number"
);
require(
_minimumBid <= iMarketSettings.getMarketplaceMaxValue(),
"createScheduledAuction::Cannot set minimum bid higher than max value"
);
_requireOwnerApproval(_contractAddress, _tokenId);
_requireOwnerAsSender(_contractAddress, _tokenId);
require(
auctions[_contractAddress][_tokenId].auctionType == NO_AUCTION ||
(msg.sender !=
auctions[_contractAddress][_tokenId].auctionCreator),
"createScheduledAuction::Cannot have a current auction"
);
// Create the scheduled auction.
auctions[_contractAddress][_tokenId] = Auction(
msg.sender,
block.number,
_lengthOfAuction,
_startingBlock,
0,
_minimumBid,
SCHEDULED_AUCTION
);
// Transfer the token to this contract to act as escrow.
IERC721 erc721 = IERC721(_contractAddress);
erc721.transferFrom(msg.sender, address(this), _tokenId);
emit NewScheduledAuction(
_contractAddress,
_tokenId,
msg.sender,
_startingBlock,
_minimumBid,
_lengthOfAuction
);
}
/////////////////////////////////////////////////////////////////////////
// bid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Bid on artwork with an auction.
* Rules:
* - if auction creator is still owner, owner must have contract approved
* - There must be a running auction or a reserve price auction for the token
* - bid > 0
* - if startingBlock - block.number < auctionLengthExtension
* - then auctionLength = Starting block - (currentBlock + extension)
* - Auction creator != bidder
* - bid >= minimum bid
* - bid >= reserve price
* - block.number < startingBlock + lengthOfAuction
* - bid > current bid
* - if previous bid then returned
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
* @param _amount uint256 Wei value of the bid.
*/
function bid(
address _contractAddress,
uint256 _tokenId,
uint256 _amount
) external payable {
Auction memory auction = auctions[_contractAddress][_tokenId];
// Must have existing auction.
require(
auction.auctionType != NO_AUCTION,
"bid::Must have existing auction"
);
// Must have existing auction.
require(
auction.auctionCreator != msg.sender,
"bid::Cannot bid on your own auction"
);
// Must have pending coldie auction or running auction.
require(
auction.startingBlock <= block.number,
"bid::Must have a running auction or pending coldie auction"
);
// Check that bid is greater than 0.
require(_amount > 0, "bid::Cannot bid 0 Wei.");
// Check that bid is less than max value.
require(
_amount <= iMarketSettings.getMarketplaceMaxValue(),
"bid::Cannot bid higher than max value"
);
// Check that bid is larger than min value.
require(
_amount >= iMarketSettings.getMarketplaceMinValue(),
"bid::Cannot bid lower than min value"
);
// Check that bid is larger than minimum bid value or the reserve price.
require(
(_amount >= auction.reservePrice && auction.minimumBid == 0) ||
(_amount >= auction.minimumBid && auction.reservePrice == 0),
"bid::Cannot bid lower than reserve or minimum bid"
);
// Auction cannot have ended.
require(
auction.startingBlock == 0 ||
block.number <
auction.startingBlock.add(auction.lengthOfAuction),
"bid::Cannot have ended"
);
// Check that enough ether was sent.
uint256 requiredCost =
_amount.add(iMarketSettings.calculateMarketplaceFee(_amount));
require(requiredCost == msg.value, "bid::Must bid the correct amount.");
// If owner of token is auction creator make sure they have contract approved
IERC721 erc721 = IERC721(_contractAddress);
address owner = erc721.ownerOf(_tokenId);
// Check that token is owned by creator or by this contract
require(
auction.auctionCreator == owner || owner == address(this),
"bid::Cannot bid on auction if auction creator is no longer owner."
);
if (auction.auctionCreator == owner) {
_requireOwnerApproval(_contractAddress, _tokenId);
}
ActiveBid memory currentBid = currentBids[_contractAddress][_tokenId];
// Must bid higher than current bid.
require(
_amount > currentBid.amount &&
_amount >=
currentBid.amount.add(
currentBid.amount.mul(minimumBidIncreasePercentage).div(100)
),
"bid::must bid higher than previous bid + minimum percentage increase."
);
// Return previous bid
// We do this here because it clears the bid for the refund. This makes it safe from reentrence.
if (currentBid.amount != 0) {
_refundBid(_contractAddress, _tokenId);
}
// Set the new bid
currentBids[_contractAddress][_tokenId] = ActiveBid(
msg.sender,
iMarketSettings.getMarketplaceFeePercentage(),
_amount
);
// If is a pending coldie auction, start the auction
if (auction.startingBlock == 0) {
auctions[_contractAddress][_tokenId].startingBlock = block.number;
erc721.transferFrom(
auction.auctionCreator,
address(this),
_tokenId
);
emit AuctionBid(
_contractAddress,
msg.sender,
_tokenId,
_amount,
true,
0,
currentBid.bidder
);
}
// If the time left for the auction is less than the extension limit bump the length of the auction.
else if (
(auction.startingBlock.add(auction.lengthOfAuction)).sub(
block.number
) < auctionLengthExtension
) {
auctions[_contractAddress][_tokenId].lengthOfAuction = (
block.number.add(auctionLengthExtension)
)
.sub(auction.startingBlock);
emit AuctionBid(
_contractAddress,
msg.sender,
_tokenId,
_amount,
false,
auctions[_contractAddress][_tokenId].lengthOfAuction,
currentBid.bidder
);
}
// Otherwise, it's a normal bid
else {
emit AuctionBid(
_contractAddress,
msg.sender,
_tokenId,
_amount,
false,
0,
currentBid.bidder
);
}
}
/////////////////////////////////////////////////////////////////////////
// settleAuction
/////////////////////////////////////////////////////////////////////////
/**
* @dev Settles the auction, transferring the auctioned token to the bidder and the bid to auction creator.
* Rules:
* - There must be an unsettled auction for the token
* - current bidder becomes new owner
* - auction creator gets paid
* - there is no longer an auction for the token
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
*/
function settleAuction(address _contractAddress, uint256 _tokenId)
external
{
Auction memory auction = auctions[_contractAddress][_tokenId];
require(
auction.auctionType != NO_AUCTION && auction.startingBlock != 0,
"settleAuction::Must have a current auction that has started"
);
require(
block.number >= auction.startingBlock.add(auction.lengthOfAuction),
"settleAuction::Can only settle ended auctions."
);
ActiveBid memory currentBid = currentBids[_contractAddress][_tokenId];
currentBids[_contractAddress][_tokenId] = ActiveBid(address(0), 0, 0);
auctions[_contractAddress][_tokenId] = Auction(
address(0),
0,
0,
0,
0,
0,
NO_AUCTION
);
IERC721 erc721 = IERC721(_contractAddress);
// If there were no bids then end the auction and return the token to its original owner.
if (currentBid.bidder == address(0)) {
// Transfer the token to back to original owner.
erc721.transferFrom(
address(this),
auction.auctionCreator,
_tokenId
);
emit AuctionSettled(
_contractAddress,
address(0),
auction.auctionCreator,
_tokenId,
0
);
return;
}
// Transfer the token to the winner of the auction.
erc721.transferFrom(address(this), currentBid.bidder, _tokenId);
address payable owner = _makePayable(owner());
Payments.payout(
currentBid.amount,
!iMarketSettings.hasERC721TokenSold(_contractAddress, _tokenId),
currentBid.marketplaceFee,
iERC721CreatorRoyalty.getERC721TokenRoyaltyPercentage(
_contractAddress,
_tokenId
),
iMarketSettings.getERC721ContractPrimarySaleFeePercentage(
_contractAddress
),
auction.auctionCreator,
owner,
iERC721CreatorRoyalty.tokenCreator(_contractAddress, _tokenId),
owner
);
iMarketSettings.markERC721Token(_contractAddress, _tokenId, true);
emit AuctionSettled(
_contractAddress,
currentBid.bidder,
auction.auctionCreator,
_tokenId,
currentBid.amount
);
}
/////////////////////////////////////////////////////////////////////////
// getAuctionDetails
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get current auction details for a token
* Rules:
* - Return empty when there's no auction
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
*/
function getAuctionDetails(address _contractAddress, uint256 _tokenId)
external
view
returns (
bytes32,
uint256,
address,
uint256,
uint256,
uint256,
uint256
)
{
Auction memory auction = auctions[_contractAddress][_tokenId];
return (
auction.auctionType,
auction.creationBlock,
auction.auctionCreator,
auction.lengthOfAuction,
auction.startingBlock,
auction.minimumBid,
auction.reservePrice
);
}
/////////////////////////////////////////////////////////////////////////
// getCurrentBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Get the current bid
* Rules:
* - Return empty when there's no bid
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
*/
function getCurrentBid(address _contractAddress, uint256 _tokenId)
external
view
returns (address, uint256)
{
return (
currentBids[_contractAddress][_tokenId].bidder,
currentBids[_contractAddress][_tokenId].amount
);
}
/////////////////////////////////////////////////////////////////////////
// _requireOwnerApproval
/////////////////////////////////////////////////////////////////////////
/**
* @dev Require that the owner have the SuperRareAuctionHouse approved.
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
*/
function _requireOwnerApproval(address _contractAddress, uint256 _tokenId)
internal
view
{
IERC721 erc721 = IERC721(_contractAddress);
address owner = erc721.ownerOf(_tokenId);
require(
erc721.isApprovedForAll(owner, address(this)),
"owner must have approved contract"
);
}
/////////////////////////////////////////////////////////////////////////
// _requireOwnerAsSender
/////////////////////////////////////////////////////////////////////////
/**
* @dev Require that the owner be the sender.
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uint256 id of the token.
*/
function _requireOwnerAsSender(address _contractAddress, uint256 _tokenId)
internal
view
{
IERC721 erc721 = IERC721(_contractAddress);
address owner = erc721.ownerOf(_tokenId);
require(owner == msg.sender, "owner must be message sender");
}
/////////////////////////////////////////////////////////////////////////
// _refundBid
/////////////////////////////////////////////////////////////////////////
/**
* @dev Internal function to return an existing bid on a token to the
* bidder and reset bid.
* @param _contractAddress address of ERC721 contract.
* @param _tokenId uin256 id of the token.
*/
function _refundBid(address _contractAddress, uint256 _tokenId) internal {
ActiveBid memory currentBid = currentBids[_contractAddress][_tokenId];
if (currentBid.bidder == address(0)) {
return;
}
currentBids[_contractAddress][_tokenId] = ActiveBid(address(0), 0, 0);
// refund the bidder
Payments.refund(
currentBid.marketplaceFee,
currentBid.bidder,
currentBid.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 "openzeppelin-solidity-pixura/contracts/token/ERC721/ERC721.sol";
// import "openzeppelin-solidity-pixura/contracts/access/Ownable.sol";
// import "openzeppelin-solidity-pixura/contracts/math/SafeMath.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/token/ERC721/ERC721.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/math/SafeMath.sol";
import "./ISupeRare.sol";
import "./IERC721Creator.sol";
/**
* @title SuperRare Legacy Tokens
* @dev This contract acts the new SuperRare Legacy contract (formerly known as SupeRare).
* It is used to upgrade SupeRare tokens to make them fully ERC721 compliant.
*
* Steps for upgrading:
* 1.) As the token owner, make sure you are the `preUpgradeOwner` to ensure you are the receiver of the new token.
* 2.) Transfer your old token to this contract's address.
* 3.) Boom! You're now the owner of the upgraded token.
*
*/
contract SuperRareLegacy is ERC721, IERC721Creator, Ownable {
using SafeMath for uint256;
/////////////////////////////////////////////////////////////////////////
// State Variables
/////////////////////////////////////////////////////////////////////////
// Old SuperRare contract to look up token details.
ISupeRare private oldSuperRare;
// Mapping from token ID to the pre upgrade token owner.
mapping(uint256 => address) private _tokenOwnerPreUpgrade;
// Boolean for when minting has completed.
bool private _mintingCompleted;
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
constructor(
string memory _name,
string memory _symbol,
address _oldSuperRare
) public ERC721(_name, _symbol) {
require(
_oldSuperRare != address(0),
"constructor::Cannot have null address for _oldSuperRare"
);
// Set old SuperRare.
oldSuperRare = ISupeRare(_oldSuperRare);
// Mark minting as not completed
_mintingCompleted = false;
}
/////////////////////////////////////////////////////////////////////////
// Admin Methods
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// mintLegacyTokens
/////////////////////////////////////////////////////////////////////////
/**
* @dev Mints the legacy tokens without emitting any events.
* @param _tokenIds uint256 array of token ids to mint.
*/
function mintLegacyTokens(uint256[] calldata _tokenIds) external onlyOwner {
require(
!_mintingCompleted,
"SuperRareLegacy: Cannot mint tokens once minting has completed."
);
for (uint256 i = 0; i < _tokenIds.length; i++) {
_createLegacyToken(_tokenIds[i]);
}
}
/////////////////////////////////////////////////////////////////////////
// markMintingCompleted
/////////////////////////////////////////////////////////////////////////
/**
* @dev Marks _mintedCompleted as true which forever prevents any more minting.
*/
function markMintingCompleted() external onlyOwner {
require(
!_mintingCompleted,
"SuperRareLegacy: Cannot mark completed if already completed."
);
_mintingCompleted = true;
}
/////////////////////////////////////////////////////////////////////////
// Public Methods
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// ownerOf
/////////////////////////////////////////////////////////////////////////
/**
* @dev Returns the owner of the NFT specified by `tokenId`
* @param _tokenId uint256 token id to get the owner of.
* @return owner address of the token owner.
*/
function ownerOf(uint256 _tokenId)
public
view
override
returns (address owner)
{
if (!isUpgraded((_tokenId))) {
return address(this);
}
return ERC721.ownerOf(_tokenId);
}
/////////////////////////////////////////////////////////////////////////
// preUpgradeOwnerOf
/////////////////////////////////////////////////////////////////////////
/**
* @dev Returns the pre-upgrade token owner of the NFT specified by `tokenId`.
* This owner will become the owner of the upgraded token.
* @param _tokenId uint256 token id to get the pre-upgrade owner of.
* @return address of the token pre-upgrade owner.
*/
function preUpgradeOwnerOf(uint256 _tokenId) public view returns (address) {
address preUpgradeOwner = _tokenOwnerPreUpgrade[_tokenId];
require(
preUpgradeOwner != address(0),
"SuperRareLegacy: pre-upgrade owner query for nonexistent token"
);
return preUpgradeOwner;
}
/////////////////////////////////////////////////////////////////////////
// isUpgraded
/////////////////////////////////////////////////////////////////////////
/**
* @dev Returns whether the token has been upgraded.
* @param _tokenId uint256 token id to get the owner of.
* @return bool of whether the token has been upgraded.
*/
function isUpgraded(uint256 _tokenId) public view returns (bool) {
address ownerOnOldSuperRare = oldSuperRare.ownerOf(_tokenId);
return address(this) == ownerOnOldSuperRare;
}
/////////////////////////////////////////////////////////////////////////
// refreshPreUpgradeOwnerOf
/////////////////////////////////////////////////////////////////////////
/**
* @dev Refreshes the pre-upgrade token owner. Useful in the event of a
* non-upgraded token transferring ownership. Throws if token has upgraded
* or if there is nothing to refresh.
* @param _tokenId uint256 token id to refresh the pre-upgrade token owner.
*/
function refreshPreUpgradeOwnerOf(uint256 _tokenId) external {
require(
!isUpgraded(_tokenId),
"SuperRareLegacy: cannot refresh an upgraded token"
);
address ownerOnOldSuperRare = oldSuperRare.ownerOf(_tokenId);
address outdatedOwner = preUpgradeOwnerOf(_tokenId);
require(
ownerOnOldSuperRare != outdatedOwner,
"SuperRareLegacy: cannot refresh when pre-upgrade owners match"
);
// _transferFromNoEvent(outdatedOwner, ownerOnOldSuperRare, _tokenId);
_tokenOwnerPreUpgrade[_tokenId] = ownerOnOldSuperRare;
}
/////////////////////////////////////////////////////////////////////////
// tokenCreator
/////////////////////////////////////////////////////////////////////////
/**
* @dev Refreshes the pre-upgrade token owner. Useful in the event of a
* non-upgraded token transferring ownership. Throws if token has upgraded
* or if there is nothing to refresh.
* @param _tokenId uint256 token id to refresh the pre-upgrade token owner.
* @return address of the token pre-upgrade owner.
*/
function tokenCreator(uint256 _tokenId)
external
view
override
returns (address payable)
{
return oldSuperRare.creatorOfToken(_tokenId);
}
/////////////////////////////////////////////////////////////////////////
// tokenURI
/////////////////////////////////////////////////////////////////////////
/**
* @dev Returns the URI for a given token ID. May return an empty string.
* If the token's URI is non-empty and a base URI was set
* Reverts if the token ID does not exist.
* @param tokenId uint256 token id to refresh the pre-upgrade token owner.
* @return string URI of the given token ID.
*/
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
require(
_exists(tokenId),
"SuperRareLegacy: URI query for nonexistent token"
);
return oldSuperRare.tokenURI(tokenId);
}
/////////////////////////////////////////////////////////////////////////
// Internal Methods
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// _createLegacyToken
/////////////////////////////////////////////////////////////////////////
/**
* @dev Mints a legacy token with the appropriate metadata and owner.
* @param _tokenId uint256 token id to get the owner of.
*/
function _createLegacyToken(uint256 _tokenId) internal {
address ownerOnOldSuperRare = oldSuperRare.ownerOf(_tokenId);
// _mintWithNoEvent(ownerOnOldSuperRare, _tokenId);
_tokenOwnerPreUpgrade[_tokenId] = ownerOnOldSuperRare;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "openzeppelin-solidity-solc6/contracts/token/ERC721/IERC721.sol";
import "openzeppelin-solidity-solc6/contracts/math/SafeMath.sol";
import "openzeppelin-solidity-solc6/contracts/access/Ownable.sol";
import "./IERC721CreatorRoyalty.sol";
import "./Marketplace/IMarketplaceSettings.sol";
import "./Payments.sol";
contract SuperRareMarketAuctionV2 is Ownable, Payments {
using SafeMath 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;
}
/////////////////////////////////////////////////////////////////////////
// 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%
/////////////////////////////////////////////////////////////////////////////
// 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
);
/////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////
/**
* @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.
*/
constructor(address _iMarketSettings, address _iERC721CreatorRoyalty)
public
{
require(
_iMarketSettings != address(0),
"constructor::Cannot have null address for _iMarketSettings"
);
require(
_iERC721CreatorRoyalty != address(0),
"constructor::Cannot have null address for _iERC721CreatorRoyalty"
);
// Set iMarketSettings
iMarketplaceSettings = IMarketplaceSettings(_iMarketSettings);
// Set iERC721CreatorRoyalty
iERC721CreatorRoyalty = IERC721CreatorRoyalty(_iERC721CreatorRoyalty);
minimumBidIncreasePercentage = 10;
}
/////////////////////////////////////////////////////////////////////////
// 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 {
IERC721 erc721 = IERC721(_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
{
IERC721 erc721 = IERC721(_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);
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);
}
/////////////////////////////////////////////////////////////////////////
// safeBuy
/////////////////////////////////////////////////////////////////////////
/**
* @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.
* @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 {
// 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"
);
// Get token contract details.
IERC721 erc721 = IERC721(_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);
}
// Payout all parties.
address payable owner = _makePayable(owner());
Payments.payout(
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);
}
/////////////////////////////////////////////////////////////////////////
// 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 {
// 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.
IERC721 erc721 = IERC721(_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.
IERC721 erc721 = IERC721(_originContract);
erc721.safeTransferFrom(msg.sender, currentBid.bidder, _tokenId);
// Payout all parties.
address payable owner = _makePayable(owner());
Payments.payout(
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
);
}
/////////////////////////////////////////////////////////////////////////
// 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) {
IERC721 erc721 = IERC721(_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 "openzeppelin-solidity-solc6/contracts/math/SafeMath.sol";
import "openzeppelin-solidity-solc6/contracts/access/Ownable.sol";
import "openzeppelin-solidity-solc6/contracts/access/AccessControl.sol";
import "./IERC721CreatorRoyalty.sol";
/**
* @title IERC721 Non-Fungible Token Creator basic interface
*/
contract SuperRareRoyaltyRegistry is Ownable, IERC721CreatorRoyalty {
using SafeMath 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);
}
/////////////////////////////////////////////////////////////////////////
// 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 view override 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) ||