/**
Copyright (C) 2020 Asynchronous Art, Inc.
GNU General Public License v3.0
Full notice https://github.com/asyncart/async-contracts/blob/master/LICENSE
*/
pragma solidity ^0.5.12;
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of NFTs in `owner`'s account.
*/
function balanceOf(address owner) public view returns (uint256 balance);
/**
* @dev Returns the owner of the NFT specified by `tokenId`.
*/
function ownerOf(uint256 tokenId) public view returns (address owner);
/**
* @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
* another (`to`).
*
*
*
* Requirements:
* - `from`, `to` cannot be zero.
* - `tokenId` must be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this
* NFT by either {approve} or {setApprovalForAll}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public;
/**
* @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
* another (`to`).
*
* Requirements:
* - If the caller is not `from`, it must be approved to move this NFT by
* either {approve} or {setApprovalForAll}.
*/
function transferFrom(address from, address to, uint256 tokenId) public;
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
contract IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4);
}
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* IMPORTANT: It is unsafe to assume that an address for which this
* function returns false is an externally-owned account (EOA) and not a
* contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != 0x0 && codehash != accountHash);
}
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* _Available since v2.4.0._
*/
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*
* _Available since v2.4.0._
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-call-value
(bool success, ) = recipient.call.value(amount)("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}
library Counters {
using SafeMath for uint256;
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
counter._value += 1;
}
function decrement(Counter storage counter) internal {
counter._value = counter._value.sub(1);
}
}
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
contract ERC721 is ERC165, IERC721 {
using SafeMath for uint256;
using Address for address;
using Counters for Counters.Counter;
// Mapping from token ID to owner
mapping (uint256 => address) private _tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to number of owned token
mapping (address => Counters.Counter) private _ownedTokensCount;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
constructor () public {
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
}
/**
* @dev Gets the balance of the specified address.
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
return _ownedTokensCount[owner].current();
}
/**
* @dev Gets the owner of the specified token ID.
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
// require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf.
* @param to operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender);
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
/**
* @dev Tells whether an operator is approved by a given owner.
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address.
* Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
* Requires the msg.sender to be the owner, approved, or operator.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function transferFrom(address from, address to, uint256 tokenId) public {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(msg.sender, tokenId));
_transferFrom(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* Requires the _msgSender() to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes data to send along with a safe transfer check
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
_transferFrom(from, to, tokenId);
}
/**
* @dev Returns whether the specified token exists.
* @param tokenId uint256 ID of the token to query the existence of
* @return bool whether the token exists
*/
function _exists(uint256 tokenId) internal view returns (bool) {
address owner = _tokenOwner[tokenId];
return owner != address(0);
}
/**
* @dev Returns whether the given spender can transfer a given token ID.
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Internal function to safely mint a new token.
* Reverts if the given token ID already exists.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Internal function to safely mint a new token.
* Reverts if the given token ID already exists.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
* @param _data bytes data to send along with a safe transfer check
*/
function _safeMint(address to, uint256 tokenId, bytes memory _data) internal {
_mint(to, tokenId);
}
/**
* @dev Internal function to mint a new token.
* Reverts if the given token ID already exists.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
require(to != address(0));
require(!_exists(tokenId), "token already minted");
_tokenOwner[tokenId] = to;
_ownedTokensCount[to].increment();
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Internal function to burn a specific token.
* Reverts if the token does not exist.
* Deprecated, use {_burn} instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
require(ownerOf(tokenId) == owner);
_clearApproval(tokenId);
_ownedTokensCount[owner].decrement();
_tokenOwner[tokenId] = address(0);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Internal function to burn a specific token.
* Reverts if the token does not exist.
* @param tokenId uint256 ID of the token being burned
*/
function _burn(uint256 tokenId) internal {
_burn(ownerOf(tokenId), tokenId);
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from, "not owner");
require(to != address(0));
_clearApproval(tokenId);
_ownedTokensCount[from].decrement();
_ownedTokensCount[to].increment();
_tokenOwner[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Private function to clear current approval of a given token ID.
* @param tokenId uint256 ID of the token to be transferred
*/
function _clearApproval(uint256 tokenId) private {
if (_tokenApprovals[tokenId] != address(0)) {
_tokenApprovals[tokenId] = address(0);
}
}
}
contract IERC721Enumerable is IERC721 {
function totalSupply() public view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId);
function tokenByIndex(uint256 index) public view returns (uint256);
}
contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
// Mapping from owner to list of owned token IDs
mapping(address => uint256[]) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumeration
uint256[] private _allTokens;
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Constructor function.
*/
constructor () public {
// register the supported interface to conform to ERC721Enumerable via ERC165
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev Gets the token ID at a given index of the tokens list of the requested owner.
* @param owner address owning the tokens list to be accessed
* @param index uint256 representing the index to be accessed of the requested tokens list
* @return uint256 token ID at the given index of the tokens list owned by the requested address
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
// require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
/**
* @dev Gets the total amount of tokens stored by the contract.
* @return uint256 representing the total amount of tokens
*/
function totalSupply() public view returns (uint256) {
return _allTokens.length;
}
/**
* @dev Gets the token ID at a given index of all the tokens in this contract
* Reverts if the index is greater or equal to the total number of tokens.
* @param index uint256 representing the index to be accessed of the tokens list
* @return uint256 token ID at the given index of the tokens list
*/
function tokenByIndex(uint256 index) public view returns (uint256) {
// require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to transferFrom, this imposes no restrictions on msg.sender.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
super._transferFrom(from, to, tokenId);
_removeTokenFromOwnerEnumeration(from, tokenId);
_addTokenToOwnerEnumeration(to, tokenId);
}
/**
* @dev Internal function to mint a new token.
* Reverts if the given token ID already exists.
* @param to address the beneficiary that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
super._mint(to, tokenId);
_addTokenToOwnerEnumeration(to, tokenId);
_addTokenToAllTokensEnumeration(tokenId);
}
/**
* @dev Internal function to burn a specific token.
* Reverts if the token does not exist.
* Deprecated, use {ERC721-_burn} instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
super._burn(owner, tokenId);
_removeTokenFromOwnerEnumeration(owner, tokenId);
// Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
_ownedTokensIndex[tokenId] = 0;
_removeTokenFromAllTokensEnumeration(tokenId);
}
/**
* @dev Gets the list of token IDs of the requested owner.
* @param owner address owning the tokens
* @return uint256[] List of token IDs owned by the requested address
*/
function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
return _ownedTokens[owner];
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
_ownedTokensIndex[tokenId] = _ownedTokens[to].length;
_ownedTokens[to].push(tokenId);
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
_ownedTokens[from].length--;
// Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
// lastTokenId, or just over the end of the array if the token was the last one).
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length.sub(1);
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
_allTokens.length--;
_allTokensIndex[tokenId] = 0;
}
}
contract IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping(uint256 => string) private _tokenURIs;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/**
* @dev Constructor function
*/
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
}
/**
* @dev Gets the token name.
* @return string representing the token name
*/
function name() external view returns (string memory) {
return _name;
}
/**
* @dev Gets the token symbol.
* @return string representing the token symbol
*/
function symbol() external view returns (string memory) {
return _symbol;
}
/**
* @dev Returns an URI for a given token ID.
* Throws if the token ID does not exist. May return an empty string.
* @param tokenId uint256 ID of the token to query
*/
function tokenURI(uint256 tokenId) external view returns (string memory) {
// require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
return _tokenURIs[tokenId];
}
/**
* @dev Internal function to set the token URI for a given token.
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to set its URI
* @param uri string URI to assign
*/
function _setTokenURI(uint256 tokenId, string memory uri) internal {
require(_exists(tokenId), "URI set of nonexistent token");
_tokenURIs[tokenId] = uri;
}
/**
* @dev Internal function to burn a specific token.
* Reverts if the token does not exist.
* Deprecated, use _burn(uint256) instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned by the msg.sender
*/
function _burn(address owner, uint256 tokenId) internal {
super._burn(owner, tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
}
}
contract AsyncArtwork is ERC721, ERC721Enumerable, ERC721Metadata {
// An event whenever the platform address is updated
event PlatformAddressUpdated (
address platformAddress
);
// An event whenever royalty amounts are updated
event RoyaltyAmountUpdated (
uint256 platformFirstPercentage,
uint256 platformSecondPercentage,
uint256 artistSecondPercentage
);
// An event whenever a bid is proposed
event BidProposed (
uint256 tokenId,
uint256 bidAmount,
address bidder
);
// An event whenever an bid is withdrawn
event BidWithdrawn (
uint256 tokenId
);
// An event whenever a buy now price has been set
event BuyPriceSet (
uint256 tokenId,
uint256 price
);
// An event when a token has been sold
event TokenSale (
// the id of the token
uint256 tokenId,
// the price that the token was sold for
uint256 salePrice,
// the address of the buyer
address buyer
);
// An event whenever a control token has been updated
event ControlLeverUpdated (
// the id of the token
uint256 tokenId,
// an optional amount that the updater sent to boost priority of the rendering
uint256 priorityTip,
// the ids of the levers that were updated
uint256[] leverIds,
// the previous values that the levers had before this update (for clients who want to animate the change)
int256[] previousValues,
// the new updated value
int256[] updatedValues
);
// struct for a token that controls part of the artwork
struct ControlToken {
// number that tracks how many levers there are
uint256 numControlLevers;
// false by default, true once instantiated
bool exists;
// false by default, true once setup by the artist
bool isSetup;
// the levers that this control token can use
mapping (uint256 => ControlLever) levers;
}
// struct for a lever on a control token that can be changed
struct ControlLever {
// // The minimum value this token can have (inclusive)
int256 minValue;
// The maximum value this token can have (inclusive)
int256 maxValue;
// The current value for this token
int256 currentValue;
// false by default, true once instantiated
bool exists;
}
// struct for a pending bid
struct PendingBid {
// the address of the bidder
address payable bidder;
// the amount that they bid
uint256 amount;
// false by default, true once instantiated
bool exists;
}
// creators who are allowed to mint on this contract
mapping (address => bool) public whitelistedCreators;
// for each token, holds an array of the creator collaborators. For layer tokens it will likely just be [artist], for master tokens it may hold multiples
mapping (uint256 => address payable[]) public uniqueTokenCreators;
// map a control token id to a control token struct
mapping (uint256 => ControlToken) controlTokenMapping;
// map control token ID to its buy price
mapping (uint256 => uint256) public buyPrices;
// map a control token ID to its highest bid
mapping (uint256 => PendingBid) public pendingBids;
// track whether this token was sold the first time or not (used for determining whether to use first or secondary sale percentage)
mapping (uint256 => bool) public tokenDidHaveFirstSale;
// mapping of addresses that are allowed to control tokens on your behalf
mapping (address => address) public permissionedControllers;
// the percentage of sale that the platform gets on first sales
uint256 public platformFirstSalePercentage;
// the percentage of sale that the platform gets on secondary sales
uint256 public platformSecondSalePercentage;
// the percentage of sale that an artist gets on secondary sales
uint256 public artistSecondSalePercentage;
// gets incremented to placehold for tokens not minted yet
uint256 public expectedTokenSupply;
// the address of the platform (for receving commissions and royalties)
address payable public platformAddress;
constructor (string memory name, string memory symbol) public ERC721Metadata(name, symbol) {
// starting royalty amounts
platformFirstSalePercentage = 10;
platformSecondSalePercentage = 1;
artistSecondSalePercentage = 4;
// by default, the platformAddress is the address that mints this contract
platformAddress = msg.sender;
// by default, platform is whitelisted
updateWhitelist(platformAddress, true);
}
// modifier for only allowing the platform to make a call
modifier onlyPlatform() {
require(msg.sender == platformAddress);
_;
}
modifier onlyWhitelistedCreator() {
require(whitelistedCreators[msg.sender] == true);
_;
}
function updateWhitelist(address creator, bool state) public onlyPlatform {
whitelistedCreators[creator] = state;
}
// Allows the current platform address to update to something different
function updatePlatformAddress(address payable newPlatformAddress) public onlyPlatform {
platformAddress = newPlatformAddress;
emit PlatformAddressUpdated(newPlatformAddress);
}
// Update the royalty percentages that platform and artists receive on first or secondary sales
function updateRoyaltyPercentages(uint256 _platformFirstSalePercentage, uint256 _platformSecondSalePercentage,
uint256 _artistSecondSalePercentage) public onlyPlatform {
// don't let the platform take all of a first sale
require (_platformFirstSalePercentage < 100);
// don't let secondary percentages take all of a sale either
require (_platformSecondSalePercentage.add(_artistSecondSalePercentage) < 100);
// update the percentage that the platform gets on first sale
platformFirstSalePercentage = _platformFirstSalePercentage;
// update the percentage that the platform gets on secondary sales
platformSecondSalePercentage = _platformSecondSalePercentage;
// update the percentage that artists get on secondary sales
artistSecondSalePercentage = _artistSecondSalePercentage;
// emit an event that contains the new royalty percentage values
emit RoyaltyAmountUpdated(platformFirstSalePercentage, platformSecondSalePercentage, artistSecondSalePercentage);
}
function setupControlToken(uint256 controlTokenId, string memory controlTokenURI,
int256[] memory leverMinValues,
int256[] memory leverMaxValues,
int256[] memory leverStartValues,
address payable[] memory additionalCollaborators
) public {
// check that a control token exists for this token id
require (controlTokenMapping[controlTokenId].exists, "No control token found");
// ensure that this token is not setup yet
require (controlTokenMapping[controlTokenId].isSetup == false, "Already setup");
// ensure that only the control token artist is attempting this mint
require(uniqueTokenCreators[controlTokenId][0] == msg.sender, "Must be control token artist");
// enforce that the length of all the array lengths are equal
require((leverMinValues.length == leverMaxValues.length) && (leverMaxValues.length == leverStartValues.length), "Values array mismatch");
// mint the control token here
super._safeMint(msg.sender, controlTokenId);
// set token URI
super._setTokenURI(controlTokenId, controlTokenURI);
// create the control token
controlTokenMapping[controlTokenId] = ControlToken(leverStartValues.length, true, true);
// create the control token levers now
for (uint256 k = 0; k < leverStartValues.length; k++) {
// enforce that maxValue is greater than or equal to minValue
require (leverMaxValues[k] >= leverMinValues[k], "Max val must >= min");
// enforce that currentValue is valid
require((leverStartValues[k] >= leverMinValues[k]) && (leverStartValues[k] <= leverMaxValues[k]), "Invalid start val");
// add the lever to this token
controlTokenMapping[controlTokenId].levers[k] = ControlLever(leverMinValues[k],
leverMaxValues[k], leverStartValues[k], true);
}
// the control token artist can optionally specify additional collaborators on this layer
for (uint256 i = 0; i < additionalCollaborators.length; i++) {
// can't provide burn address as collaborator
require(additionalCollaborators[i] != address(0));
uniqueTokenCreators[controlTokenId].push(additionalCollaborators[i]);
}
}
function mintArtwork(uint256 artworkTokenId, string memory artworkTokenURI, address payable[] memory controlTokenArtists
) public onlyWhitelistedCreator {
require (artworkTokenId == expectedTokenSupply, "ExpectedTokenSupply different");
// Mint the token that represents ownership of the entire artwork
super._safeMint(msg.sender, artworkTokenId);
expectedTokenSupply++;
super._setTokenURI(artworkTokenId, artworkTokenURI);
// track the msg.sender address as the artist address for future royalties
uniqueTokenCreators[artworkTokenId].push(msg.sender);
// iterate through all control token URIs (1 for each control token)
for (uint256 i = 0; i < controlTokenArtists.length; i++) {
// can't provide burn address as artist
require(controlTokenArtists[i] != address(0));
// use the curren token supply as the next token id
uint256 controlTokenId = expectedTokenSupply;
expectedTokenSupply++;
uniqueTokenCreators[controlTokenId].push(controlTokenArtists[i]);
// stub in an existing control token so exists is true
controlTokenMapping[controlTokenId] = ControlToken(0, true, false);
if (controlTokenArtists[i] != msg.sender) {
bool containsControlTokenArtist = false;
for (uint256 k = 0; k < uniqueTokenCreators[artworkTokenId].length; k++) {
if (uniqueTokenCreators[artworkTokenId][k] == controlTokenArtists[i]) {
containsControlTokenArtist = true;
break;
}
}
if (containsControlTokenArtist == false) {
uniqueTokenCreators[artworkTokenId].push(controlTokenArtists[i]);
}
}
}
}
// Bidder functions
function bid(uint256 tokenId) public payable {
// don't let owners/approved bid on their own tokens
require(_isApprovedOrOwner(msg.sender, tokenId) == false);
// check if there's a high bid
if (pendingBids[tokenId].exists) {
// enforce that this bid is higher
require(msg.value > pendingBids[tokenId].amount, "Bid must be > than current bid");
// Return bid amount back to bidder
pendingBids[tokenId].bidder.transfer(pendingBids[tokenId].amount);
}
// set the new highest bid
pendingBids[tokenId] = PendingBid(msg.sender, msg.value, true);
// Emit event for the bid proposal
emit BidProposed(tokenId, msg.value, msg.sender);
}
// allows an address with a pending bid to withdraw it
function withdrawBid(uint256 tokenId) public {
// check that there is a bid from the sender to withdraw
require (pendingBids[tokenId].exists && (pendingBids[tokenId].bidder == msg.sender));
// Return bid amount back to bidder
pendingBids[tokenId].bidder.transfer(pendingBids[tokenId].amount);
// clear highest bid
pendingBids[tokenId] = PendingBid(address(0), 0, false);
// emit an event when the highest bid is withdrawn
emit BidWithdrawn(tokenId);
}
// Buy the artwork for the currently set price
function takeBuyPrice(uint256 tokenId) public payable {
// don't let owners/approved buy their own tokens
require(_isApprovedOrOwner(msg.sender, tokenId) == false);
// get the sale amount
uint256 saleAmount = buyPrices[tokenId];
// check that there is a buy price
require(saleAmount > 0);
// check that the buyer sent enough to purchase
require (msg.value >= saleAmount);
// Return all highest bidder's money
if (pendingBids[tokenId].exists) {
// Return bid amount back to bidder
pendingBids[tokenId].bidder.transfer(pendingBids[tokenId].amount);
// clear highest bid
pendingBids[tokenId] = PendingBid(address(0), 0, false);
}
onTokenSold(tokenId, saleAmount, msg.sender);
}
function distributeFundsToCreators(uint256 amount, address payable[] memory creators) private {
uint256 creatorShare = amount.div(creators.length);
for (uint256 i = 0; i < creators.length; i++) {
creators[i].transfer(creatorShare);
}
}
function onTokenSold(uint256 tokenId, uint256 saleAmount, address to) private {
// if the first sale already happened, then give the artist + platform the secondary royalty percentage
if (tokenDidHaveFirstSale[tokenId]) {
// give platform its secondary sale percentage
uint256 platformAmount = saleAmount.mul(platformSecondSalePercentage).div(100);
platformAddress.transfer(platformAmount);
// distribute the creator royalty amongst the creators (all artists involved for a base token, sole artist creator for layer )
uint256 creatorAmount = saleAmount.mul(artistSecondSalePercentage).div(100);
distributeFundsToCreators(creatorAmount, uniqueTokenCreators[tokenId]);
// cast the owner to a payable address
address payable payableOwner = address(uint160(ownerOf(tokenId)));
// transfer the remaining amount to the owner of the token
payableOwner.transfer(saleAmount.sub(platformAmount).sub(creatorAmount));
} else {
tokenDidHaveFirstSale[tokenId] = true;
// give platform its first sale percentage
uint256 platformAmount = saleAmount.mul(platformFirstSalePercentage).div(100);
platformAddress.transfer(platformAmount);
// this is a token first sale, so distribute the remaining funds to the unique token creators of this token
// (if it's a base token it will be all the unique creators, if it's a control token it will be that single artist)
distributeFundsToCreators(saleAmount.sub(platformAmount), uniqueTokenCreators[tokenId]);
}
// clear highest bid
pendingBids[tokenId] = PendingBid(address(0), 0, false);
// Transfer token to msg.sender
safeTransferFrom(ownerOf(tokenId), to, tokenId, "");
// Emit event
emit TokenSale(tokenId, saleAmount, to);
}
// Owner functions
// Allow owner to accept the highest bid for a token
function acceptBid(uint256 tokenId) public {
// check if sender is owner/approved of token
require(_isApprovedOrOwner(msg.sender, tokenId));
// check if there's a bid to accept
require (pendingBids[tokenId].exists);
// process the sale
onTokenSold(tokenId, pendingBids[tokenId].amount, pendingBids[tokenId].bidder);
}
// Allows owner of a control token to set an immediate buy price. Set to 0 to reset.
function makeBuyPrice(uint256 tokenId, uint256 amount) public {
// check if sender is owner/approved of token
require(_isApprovedOrOwner(msg.sender, tokenId));
// set the buy price
buyPrices[tokenId] = amount;
// emit event
emit BuyPriceSet(tokenId, amount);
}
// return the min, max, and current value of a control lever
function getControlToken(uint256 controlTokenId) public view returns (int256[] memory) {
require(controlTokenMapping[controlTokenId].exists);
ControlToken storage controlToken = controlTokenMapping[controlTokenId];
int256[] memory returnValues = new int256[](controlToken.numControlLevers.mul(3));
uint256 returnValIndex = 0;
// iterate through all the control levers for this control token
for (uint256 i = 0; i < controlToken.numControlLevers; i++) {
returnValues[returnValIndex] = controlToken.levers[i].minValue;
returnValIndex = returnValIndex.add(1);
returnValues[returnValIndex] = controlToken.levers[i].maxValue;
returnValIndex = returnValIndex.add(1);
returnValues[returnValIndex] = controlToken.levers[i].currentValue;
returnValIndex = returnValIndex.add(1);
}
return returnValues;
}
// anyone can grant permission to another address to control tokens on their behalf. Set to Address(0) to reset.
function grantControlPermission(address permissioned) public {
permissionedControllers[msg.sender] = permissioned;
}
// Allows owner (or permissioned user) of a control token to update its lever values
// Optionally accept a payment to increase speed of rendering priority
function useControlToken(uint256 controlTokenId, uint256[] memory leverIds, int256[] memory newValues) public payable {
// check if sender is owner/approved of token OR if they're a permissioned controller for the token owner
require(_isApprovedOrOwner(msg.sender, controlTokenId) || (permissionedControllers[ownerOf(controlTokenId)] == msg.sender),
"Owner or permissioned only");
// collect the previous lever values for the event emit below
int256[] memory previousValues = new int256[](newValues.length);
for (uint256 i = 0; i < leverIds.length; i++) {
// get the control lever
ControlLever storage lever = controlTokenMapping[controlTokenId].levers[leverIds[i]];
// Enforce that the new value is valid
require((newValues[i] >= lever.minValue) && (newValues[i] <= lever.maxValue), "Invalid val");
// Enforce that the new value is different
require(newValues[i] != lever.currentValue, "Must provide different val");
// grab previous value for the event emit
int256 previousValue = lever.currentValue;
// Update token current value
lever.currentValue = newValues[i];
// collect the previous lever values for the event emit below
previousValues[i] = previousValue;
}
// if there's a payment then send it to the platform (for higher priority updates)
if (msg.value > 0) {
platformAddress.transfer(msg.value);
}
// emit event
emit ControlLeverUpdated(controlTokenId, msg.value, leverIds, previousValues, newValues);
}
// override the default transfer
function _transferFrom(address from, address to, uint256 tokenId) internal {
super._transferFrom(from, to, tokenId);
// clear a buy now price after being transferred
buyPrices[tokenId] = 0;
}
}
{
"compilationTarget": {
"AsyncArtwork.sol": "AsyncArtwork"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bidAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"bidder","type":"address"}],"name":"BidProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BidWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"BuyPriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"priorityTip","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"leverIds","type":"uint256[]"},{"indexed":false,"internalType":"int256[]","name":"previousValues","type":"int256[]"},{"indexed":false,"internalType":"int256[]","name":"updatedValues","type":"int256[]"}],"name":"ControlLeverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"platformAddress","type":"address"}],"name":"PlatformAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"platformFirstPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"platformSecondPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"artistSecondPercentage","type":"uint256"}],"name":"RoyaltyAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"salePrice","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"}],"name":"TokenSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"acceptBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"artistSecondSalePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"bid","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"buyPrices","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"expectedTokenSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"controlTokenId","type":"uint256"}],"name":"getControlToken","outputs":[{"internalType":"int256[]","name":"","type":"int256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"permissioned","type":"address"}],"name":"grantControlPermission","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"makeBuyPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"artworkTokenId","type":"uint256"},{"internalType":"string","name":"artworkTokenURI","type":"string"},{"internalType":"address payable[]","name":"controlTokenArtists","type":"address[]"}],"name":"mintArtwork","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pendingBids","outputs":[{"internalType":"address payable","name":"bidder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"permissionedControllers","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"platformAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"platformFirstSalePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"platformSecondSalePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"controlTokenId","type":"uint256"},{"internalType":"string","name":"controlTokenURI","type":"string"},{"internalType":"int256[]","name":"leverMinValues","type":"int256[]"},{"internalType":"int256[]","name":"leverMaxValues","type":"int256[]"},{"internalType":"int256[]","name":"leverStartValues","type":"int256[]"},{"internalType":"address payable[]","name":"additionalCollaborators","type":"address[]"}],"name":"setupControlToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"takeBuyPrice","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenDidHaveFirstSale","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"uniqueTokenCreators","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"newPlatformAddress","type":"address"}],"name":"updatePlatformAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_platformFirstSalePercentage","type":"uint256"},{"internalType":"uint256","name":"_platformSecondSalePercentage","type":"uint256"},{"internalType":"uint256","name":"_artistSecondSalePercentage","type":"uint256"}],"name":"updateRoyaltyPercentages","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"updateWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"controlTokenId","type":"uint256"},{"internalType":"uint256[]","name":"leverIds","type":"uint256[]"},{"internalType":"int256[]","name":"newValues","type":"int256[]"}],"name":"useControlToken","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedCreators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]