Created
June 15, 2021 13:16
-
-
Save mwaqasaslam/f7b1541bb6d6e25a11e06178b7929ed2 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=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.6.12; | |
interface ISendValueProxy { | |
function sendValue(address payable _to) external payable; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.6.12; | |
/** | |
* @dev Interface for interacting with the VHS contract that holds VHS beta tokens. | |
*/ | |
interface IVHS { | |
/** | |
* @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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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"; | |
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 "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/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 = 0; // 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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
{ | |
_to.transfer(_value); | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.6.12; | |
// import "openzeppelin-solidity-solc6/contracts/math/SafeMath.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/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 _amount uint256 value to be split. | |
* @param _payee address seller of the token. | |
*/ | |
function refund( | |
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); | |
} | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// payout | |
///////////////////////////////////////////////////////////////////////// | |
/** | |
* @dev Internal function to pay the seller. | |
* @param _amount uint256 value to be split. | |
* @param _payee address seller of the token. | |
*/ | |
function payout( | |
uint256 _amount, | |
address payable _payee | |
) internal { | |
require( | |
_payee != address(0) | |
); | |
// 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[1] memory payments; | |
// uint256 payeePayment | |
payments[0] = _amount; | |
// payeePayment | |
if (payments[0] > 0) { | |
SendValueOrEscrow.sendValueOrEscrow(_payee, payments[0]); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.6.12; | |
// import "openzeppelin-solidity-solc6/contracts/payment/PullPayment.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 VHS 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 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 , address payable tokenMintFor) onlyOwner public { | |
_createToken(_uri, tokenMintFor); | |
} | |
/** | |
* @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; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 "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/IERC721.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.6/contracts/math/SafeMath.sol"; | |
import "./IMarketplaceSettings.sol"; | |
import "./Payments.sol"; | |
contract VHSAuction 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; | |
// 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 | |
*/ | |
constructor(address _iMarketSettings) | |
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" | |
); | |
// Set iMarketSettings | |
iMarketSettings = IMarketplaceSettings(_iMarketSettings); | |
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); | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// 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; | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// 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, | |
auction.auctionCreator | |
); | |
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 VHSAuction 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.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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 "./IVHS.sol"; | |
import "./IERC721Creator.sol"; | |
/** | |
* @title SuperRare Legacy Tokens | |
* @dev This contract acts the new SuperRare Legacy contract (formerly known as VHS). | |
* It is used to upgrade VHS 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 VHSLegacy is ERC721, IERC721Creator, Ownable { | |
using SafeMath for uint256; | |
///////////////////////////////////////////////////////////////////////// | |
// State Variables | |
///////////////////////////////////////////////////////////////////////// | |
// Old SuperRare contract to look up token details. | |
IVHS 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 = IVHS(_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, | |
"VHSLegacy: 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, | |
"VHSLegacy: 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), | |
"VHSLegacy: 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), | |
"VHSLegacy: cannot refresh an upgraded token" | |
); | |
address ownerOnOldSuperRare = oldSuperRare.ownerOf(_tokenId); | |
address outdatedOwner = preUpgradeOwnerOf(_tokenId); | |
require( | |
ownerOnOldSuperRare != outdatedOwner, | |
"VHSLegacy: 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), | |
"VHSLegacy: 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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 "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 "./IERC721TokenCreator.sol"; | |
import "./IERC721Creator.sol"; | |
/** | |
* @title IERC721 Non-Fungible Token Creator basic interface | |
*/ | |
contract VHSTokenCreatorRegistry is Ownable, IERC721TokenCreator { | |
using SafeMath 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; | |
} | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// 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 | |
view | |
override | |
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment