Transaction Hash:
Block:
13380036 at Oct-08-2021 07:24:11 PM +UTC
Transaction Fee:
0.01941828 ETH
$48.00
Gas Used:
161,819 Gas / 120 Gwei
Emitted Events:
220 |
SMARTCREDIT.Transfer( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000fab46f723fc1bcbbb74b471d8dbe9514943caee9, 0x00000000000000000000000078f0269f5b1ca914c8c58993f41181f89e212b27, 0x0000000000000000000000000000000000000000000000000000000000000ebc )
|
221 |
SMARTCREDIT.0xe8038e253f57f3f9c7277af1d801786319db71cc5491fe5db55a0e04f1b3466f( 0xe8038e253f57f3f9c7277af1d801786319db71cc5491fe5db55a0e04f1b3466f, 0000000000000000000000000000000000000000000000000000000000000ebc, 00000000000000000000000000000000000000000000000003782dace9d90000, 00000000000000000000000078f0269f5b1ca914c8c58993f41181f89e212b27 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 3,585.642666090535218823 Eth | 3,585.642871766739087609 Eth | 0.000205676203868786 | |
0x78f0269f...89e212B27 |
23.893483482163898261 Eth
Nonce: 3442
|
23.624065202163898261 Eth
Nonce: 3443
| 0.26941828 | ||
0xb6dAe651...76AC1e0c8 | |||||
0xdB8ac702...e78B95536 | (Async Art: Deployer) | 6.877963597142103492 Eth | 6.902963597142103492 Eth | 0.025 | |
0xFab46F72...4943caee9 | 0.086884124 Eth | 0.311884124 Eth | 0.225 |
Execution Trace
ETH 0.25
SMARTCREDIT.2d4fbf83( )

ETH 0.25
AsyncArtwork_v2.takeBuyPrice( tokenId=3772, expectedRemainingUpdates=-1 )
- ETH 0.025
Async Art: Deployer.CALL( )
- ETH 0.225
0xfab46f723fc1bcbbb74b471d8dbe9514943caee9.CALL( )
- ETH 0.025
File 1 of 2: SMARTCREDIT
File 2 of 2: AsyncArtwork_v2
// File: @openzeppelin/contracts/GSN/Context.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File: @openzeppelin/contracts/token/ERC20/IERC20.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File: @openzeppelin/contracts/math/SafeMath.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ 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. */ 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. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 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. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: @openzeppelin/contracts/utils/Address.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.2; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is 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. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ 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. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @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]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); return _functionCallWithValue(target, data, value, errorMessage); } function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File: @openzeppelin/contracts/token/ERC20/ERC20.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // File: contracts/SmartCreditToken.sol pragma solidity ^0.6.0; contract SMARTCREDIT is ERC20 { constructor( address initialAccount, uint256 initialBalance ) public ERC20("SMARTCREDIT Token", "SMARTCREDIT") { _mint(initialAccount, initialBalance); } }
File 2 of 2: AsyncArtwork_v2
// File: @openzeppelin/upgrades/contracts/Initializable.sol pragma solidity >=0.4.24 <0.7.0; /** * @title Initializable * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol pragma solidity ^0.5.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ contract Context is Initializable { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File: @openzeppelin/contracts-ethereum-package/contracts/introspection/IERC165.sol pragma solidity ^0.5.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ 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); } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721.sol pragma solidity ^0.5.0; /** * @dev Required interface of an ERC721 compliant contract. */ contract IERC721 is Initializable, 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; } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721Receiver.sol pragma solidity ^0.5.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ 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); } // File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol pragma solidity ^0.5.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ 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; } } // File: @openzeppelin/contracts-ethereum-package/contracts/utils/Address.sol pragma solidity ^0.5.5; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is 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. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // 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 != accountHash && codehash != 0x0); } /** * @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"); } } // File: @openzeppelin/contracts-ethereum-package/contracts/drafts/Counters.sol pragma solidity ^0.5.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never * directly accessed. */ 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 { // The {SafeMath} overflow check can be skipped here, see the comment at the top counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } // File: @openzeppelin/contracts-ethereum-package/contracts/introspection/ERC165.sol pragma solidity ^0.5.0; /** * @dev Implementation of the {IERC165} interface. * * Contracts may inherit from this and call {_registerInterface} to declare * their support of an interface. */ contract ERC165 is Initializable, 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; function initialize() public initializer { // 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) public 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; } uint256[50] private ______gap; } // File: contracts/ERC721.sol pragma solidity ^0.5.0; /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ contract ERC721 is Initializable, Context, ERC165, IERC721 { using SafeMath for uint256; using Address for address; using Counters for Counters.Counter; // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; // 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; function initialize() public initializer { ERC165.initialize(); // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721); } function _hasBeenInitialized() internal view returns (bool) { return supportsInterface(_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), "ERC721: balance query for the zero address"); 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), "ERC721: owner query for nonexistent token"); 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, "ERC721: approval to current owner"); require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _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 != _msgSender(), "ERC721: approve to caller"); _operatorApprovals[_msgSender()][to] = approved; emit ApprovalForAll(_msgSender(), 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(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transferFrom(from, to, tokenId); } /** * @dev Safely transfers the ownership of a given token ID to another address * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received}, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * 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 * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received}, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * 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(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransferFrom(from, to, tokenId, _data); } /** * @dev Safely transfers the ownership of a given token ID to another address * If the target address is a contract, it must implement `onERC721Received`, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * 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) internal { _transferFrom(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @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) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); 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. * If the target address is a contract, it must implement `onERC721Received`, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * @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. * If the target address is a contract, it must implement `onERC721Received`, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * @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); require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @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), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _tokenOwner[tokenId] = to; _ownedTokensCount[to].increment(); emit Transfer(address(0), to, 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, "ERC721: transfer of token that is not own"); require(to != address(0), "ERC721: transfer to the zero address"); _clearApproval(tokenId); _ownedTokensCount[from].decrement(); _ownedTokensCount[to].increment(); _tokenOwner[tokenId] = to; emit Transfer(from, to, tokenId); } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * This is an internal detail of the `ERC721` contract and its use is deprecated. * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) { if (!to.isContract()) { return true; } // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = to.call(abi.encodeWithSelector( IERC721Receiver(to).onERC721Received.selector, _msgSender(), from, tokenId, _data )); if (!success) { if (returndata.length > 0) { // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert("ERC721: transfer to non ERC721Receiver implementer"); } } else { bytes4 retval = abi.decode(returndata, (bytes4)); return (retval == _ERC721_RECEIVED); } } /** * @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); } } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721Enumerable.sol pragma solidity ^0.5.0; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ contract IERC721Enumerable is Initializable, 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); } // File: contracts/ERC721Enumerable.sol pragma solidity ^0.5.0; /** * @title ERC-721 Non-Fungible Token with optional enumeration extension logic * @dev See https://eips.ethereum.org/EIPS/eip-721 */ contract ERC721Enumerable is Initializable, Context, 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. */ function initialize() public initializer { require(ERC721._hasBeenInitialized()); // register the supported interface to conform to ERC721Enumerable via ERC165 _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } function _hasBeenInitialized() internal view returns (bool) { return supportsInterface(_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; // } uint256[50] private ______gap; } // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721Metadata.sol pragma solidity ^0.5.0; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ contract IERC721Metadata is Initializable, IERC721 { function name() external view returns (string memory); function symbol() external view returns (string memory); function tokenURI(uint256 tokenId) external view returns (string memory); } // File: contracts/ERC721Metadata.sol pragma solidity ^0.5.0; contract ERC721Metadata is Initializable, Context, 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 */ function initialize(string memory name, string memory symbol) public initializer { require(ERC721._hasBeenInitialized()); _name = name; _symbol = symbol; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721_METADATA); } function _hasBeenInitialized() internal view returns (bool) { return supportsInterface(_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), "ERC721Metadata: 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]; // } // } uint256[50] private ______gap; } // File: contracts/AsyncArtwork_v2.sol pragma solidity ^0.5.12; // interface for the v1 contract interface AsyncArtwork_v1 { function getControlToken(uint256 controlTokenId) external view returns (int256[] memory); function tokenURI(uint256 tokenId) external view returns (string memory); } // Copyright (C) 2020 Asynchronous Art, Inc. // GNU General Public License v3.0 // Full notice https://github.com/asyncart/async-contracts/blob/master/LICENSE contract AsyncArtwork_v2 is Initializable, ERC721, ERC721Enumerable, ERC721Metadata { // An event whenever the platform address is updated event PlatformAddressUpdated(address platformAddress); event PermissionUpdated( uint256 tokenId, address tokenOwner, address permissioned ); // An event whenever a creator is whitelisted with the token id and the layer count event CreatorWhitelisted( uint256 tokenId, uint256 layerCount, address creator ); // An event whenever royalty amount for a token is updated event PlatformSalePercentageUpdated( uint256 tokenId, uint256 platformFirstPercentage, uint256 platformSecondPercentage ); event DefaultPlatformSalePercentageUpdated( uint256 defaultPlatformFirstSalePercentage, uint256 defaultPlatformSecondSalePercentage ); // An event whenever artist secondary sale percentage is updated event ArtistSecondSalePercentUpdated(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 when a token(s) first sale requirement has been waived event FirstSaleWaived( // the ids of the token uint256[] tokenIds ); // 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 number of times this control lever can now be updated int256 numRemainingUpdates, // 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; // The number of update calls this token has (-1 for infinite) int256 numRemainingUpdates; // 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; } struct WhitelistReservation { // the address of the creator address creator; // the amount of layers they're expected to mint uint256 layerCount; } // 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; // if a token's URI has been locked or not mapping(uint256 => bool) public tokenURILocked; // map control token ID to its buy price mapping(uint256 => uint256) public buyPrices; // mapping of addresses to credits for failed transfers mapping(address => uint256) public failedTransferCredits; // mapping of tokenId to percentage of sale that the platform gets on first sales mapping(uint256 => uint256) public platformFirstSalePercentages; // mapping of tokenId to percentage of sale that the platform gets on secondary sales mapping(uint256 => uint256) public platformSecondSalePercentages; // what tokenId creators are allowed to mint (and how many layers) mapping(uint256 => WhitelistReservation) public creatorWhitelist; // 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 its highest bid mapping(uint256 => PendingBid) public pendingBids; // map a control token id to a control token struct mapping(uint256 => ControlToken) public controlTokenMapping; // mapping of addresses that are allowed to control tokens on your behalf mapping(address => mapping(uint256 => address)) public permissionedControllers; // 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 minimum % increase for new bids coming uint256 public minBidIncreasePercent; // the address of the platform (for receving commissions and royalties) address payable public platformAddress; // the address of the contract that can upgrade from v1 to v2 tokens address public upgraderAddress; // the address of the contract that can whitelist artists to mint address public minterAddress; // v3 vairables uint256 public defaultPlatformFirstSalePercentage; uint256 public defaultPlatformSecondSalePercentage; function setup( string memory name, string memory symbol, uint256 initialExpectedTokenSupply, address _upgraderAddress ) public initializer { ERC721.initialize(); ERC721Enumerable.initialize(); ERC721Metadata.initialize(name, symbol); // starting royalty amounts artistSecondSalePercentage = 10; // intitialize the minimum bid increase percent minBidIncreasePercent = 1; // by default, the platformAddress is the address that mints this contract platformAddress = msg.sender; // set the upgrader address upgraderAddress = _upgraderAddress; // set the initial expected token supply expectedTokenSupply = initialExpectedTokenSupply; require(expectedTokenSupply > 0); } // modifier for only allowing the platform to make a call modifier onlyPlatform() { require(msg.sender == platformAddress); _; } // modifier for only allowing the minter to make a call modifier onlyMinter() { require(msg.sender == minterAddress); _; } modifier onlyWhitelistedCreator(uint256 masterTokenId, uint256 layerCount) { require(creatorWhitelist[masterTokenId].creator == msg.sender); require(creatorWhitelist[masterTokenId].layerCount == layerCount); _; } function setExpectedTokenSupply(uint256 newExpectedTokenSupply) external onlyPlatform { expectedTokenSupply = newExpectedTokenSupply; } // reserve a tokenID and layer count for a creator. Define a platform royalty percentage per art piece (some pieces have higher or lower amount) function whitelistTokenForCreator( address creator, uint256 masterTokenId, uint256 layerCount, uint256 platformFirstSalePercentage, uint256 platformSecondSalePercentage ) external onlyMinter { // the tokenID we're reserving must be the current expected token supply require(masterTokenId == expectedTokenSupply); // reserve the tokenID for this creator creatorWhitelist[masterTokenId] = WhitelistReservation( creator, layerCount ); // increase the expected token supply expectedTokenSupply = masterTokenId.add(layerCount).add(1); // define the platform percentages for this token here platformFirstSalePercentages[ masterTokenId ] = platformFirstSalePercentage; platformSecondSalePercentages[ masterTokenId ] = platformSecondSalePercentage; emit CreatorWhitelisted(masterTokenId, layerCount, creator); } // Allows the platform to change the minter address function updateMinterAddress(address newMinterAddress) external onlyPlatform { minterAddress = newMinterAddress; } // Allows the current platform address to update to something different function updatePlatformAddress(address payable newPlatformAddress) external onlyPlatform { platformAddress = newPlatformAddress; emit PlatformAddressUpdated(newPlatformAddress); } // Allows platform to waive the first sale requirement for a token (for charity events, special cases, etc) function waiveFirstSaleRequirement(uint256[] calldata tokenIds) external onlyPlatform { // This allows the token sale proceeds to go to the current owner (rather than be distributed amongst the token's creators) for (uint256 k = 0; k < tokenIds.length; k++) { tokenDidHaveFirstSale[tokenIds[k]] = true; } emit FirstSaleWaived(tokenIds); } // Allows platform to change the royalty percentage for a specific token function updatePlatformSalePercentage( uint256 tokenId, uint256 platformFirstSalePercentage, uint256 platformSecondSalePercentage ) external onlyPlatform { // set the percentages for this token platformFirstSalePercentages[tokenId] = platformFirstSalePercentage; platformSecondSalePercentages[tokenId] = platformSecondSalePercentage; // emit an event to notify that the platform percent for this token has changed emit PlatformSalePercentageUpdated( tokenId, platformFirstSalePercentage, platformSecondSalePercentage ); } // Allows platform to change the default sales percentages function updateDefaultPlatformSalePercentage( uint256 _defaultPlatformFirstSalePercentage, uint256 _defaultPlatformSecondSalePercentage ) external onlyPlatform { defaultPlatformFirstSalePercentage = _defaultPlatformFirstSalePercentage; defaultPlatformSecondSalePercentage = _defaultPlatformSecondSalePercentage; // emit an event to notify that the platform percent has changed emit DefaultPlatformSalePercentageUpdated( defaultPlatformFirstSalePercentage, defaultPlatformSecondSalePercentage ); } // Allows the platform to change the minimum percent increase for incoming bids function updateMinimumBidIncreasePercent(uint256 _minBidIncreasePercent) external onlyPlatform { require( (_minBidIncreasePercent > 0) && (_minBidIncreasePercent <= 50), "Bid increases must be within 0-50%" ); // set the new bid increase percent minBidIncreasePercent = _minBidIncreasePercent; } // Allow the platform to update a token's URI if it's not locked yet (for fixing tokens post mint process) function updateTokenURI(uint256 tokenId, string calldata tokenURI) external onlyPlatform { // ensure that this token exists require(_exists(tokenId)); // ensure that the URI for this token is not locked yet require(tokenURILocked[tokenId] == false); // update the token URI super._setTokenURI(tokenId, tokenURI); } // Locks a token's URI from being updated function lockTokenURI(uint256 tokenId) external onlyPlatform { // ensure that this token exists require(_exists(tokenId)); // lock this token's URI from being changed tokenURILocked[tokenId] = true; } // Allows platform to change the percentage that artists receive on secondary sales function updateArtistSecondSalePercentage( uint256 _artistSecondSalePercentage ) external onlyPlatform { // update the percentage that artists get on secondary sales artistSecondSalePercentage = _artistSecondSalePercentage; // emit an event to notify that the artist second sale percent has updated emit ArtistSecondSalePercentUpdated(artistSecondSalePercentage); } function setupControlToken( uint256 controlTokenId, string calldata controlTokenURI, int256[] calldata leverMinValues, int256[] calldata leverMaxValues, int256[] calldata leverStartValues, int256 numAllowedUpdates, address payable[] calldata additionalCollaborators ) external { // Hard cap the number of levers a single control token can have require(leverMinValues.length <= 500, "Too many control levers."); // Hard cap the number of collaborators a single control token can have require( additionalCollaborators.length <= 50, "Too many collaborators." ); // 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" ); // require the number of allowed updates to be infinite (-1) or some finite number require( (numAllowedUpdates == -1) || (numAllowedUpdates > 0), "Invalid allowed updates" ); // 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, numAllowedUpdates, 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] ); } } // upgrade a token from the v1 contract to this v2 version function upgradeV1Token( uint256 tokenId, address v1Address, bool isControlToken, address to, uint256 platformFirstPercentageForToken, uint256 platformSecondPercentageForToken, bool hasTokenHadFirstSale, address payable[] calldata uniqueTokenCreatorsForToken ) external { // get reference to v1 token contract AsyncArtwork_v1 v1Token = AsyncArtwork_v1(v1Address); // require that only the upgrader address is calling this method require(msg.sender == upgraderAddress, "Only upgrader can call."); // preserve the unique token creators uniqueTokenCreators[tokenId] = uniqueTokenCreatorsForToken; if (isControlToken) { // preserve the control token details if it's a control token int256[] memory controlToken = v1Token.getControlToken(tokenId); // Require control token to be a valid size (multiple of 3) require(controlToken.length % 3 == 0, "Invalid control token."); // Require control token to have at least 1 lever require(controlToken.length > 0, "Control token must have levers"); // Setup the control token // Use -1 for numRemainingUpdates since v1 tokens were infinite use controlTokenMapping[tokenId] = ControlToken( controlToken.length / 3, -1, true, true ); // set each lever for the control token. getControlToken returns levers like: // [minValue, maxValue, curValue, minValue, maxValue, curValue, ...] so they always come in groups of 3 for (uint256 k = 0; k < controlToken.length; k += 3) { controlTokenMapping[tokenId].levers[k / 3] = ControlLever( controlToken[k], controlToken[k + 1], controlToken[k + 2], true ); } } // Set the royalty percentage for this token platformFirstSalePercentages[tokenId] = platformFirstPercentageForToken; platformSecondSalePercentages[ tokenId ] = platformSecondPercentageForToken; // whether this token has already had its first sale tokenDidHaveFirstSale[tokenId] = hasTokenHadFirstSale; // Mint and transfer the token to the original v1 token owner super._safeMint(to, tokenId); // set the same token URI super._setTokenURI(tokenId, v1Token.tokenURI(tokenId)); } function mintArtwork( uint256 masterTokenId, string calldata artworkTokenURI, address payable[] calldata controlTokenArtists, address payable[] calldata uniqueArtists ) external onlyWhitelistedCreator(masterTokenId, controlTokenArtists.length) { // Can't mint a token with ID 0 anymore require(masterTokenId > 0); // Mint the token that represents ownership of the entire artwork super._safeMint(msg.sender, masterTokenId); // set the token URI for this art super._setTokenURI(masterTokenId, artworkTokenURI); // set the unique artists array for future royalties uniqueTokenCreators[masterTokenId] = uniqueArtists; // 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)); // determine the tokenID for this control token uint256 controlTokenId = masterTokenId + i + 1; // add this control token artist to the unique creator list for that control token uniqueTokenCreators[controlTokenId].push(controlTokenArtists[i]); } } // Bidder functions function bid(uint256 tokenId) external payable { // don't allow bids of 0 require(msg.value > 0); // 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 by at least the minimum required percent increase require( msg.value >= ( pendingBids[tokenId] .amount .mul(minBidIncreasePercent.add(100)) .div(100) ), "Bid must increase by min %" ); // Return bid amount back to bidder safeFundsTransfer( pendingBids[tokenId].bidder, 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) external { // check that there is a bid from the sender to withdraw (also allows platform address to withdraw a bid on someone's behalf) require( (pendingBids[tokenId].bidder == msg.sender) || (msg.sender == platformAddress) ); // attempt to withdraw the bid _withdrawBid(tokenId); } function _withdrawBid(uint256 tokenId) internal { require(pendingBids[tokenId].exists); // Return bid amount back to bidder safeFundsTransfer( pendingBids[tokenId].bidder, 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 // Allows the buyer to specify an expected remaining uses they'll accept function takeBuyPrice(uint256 tokenId, int256 expectedRemainingUpdates) external 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 exact amount to purchase require(msg.value == saleAmount); // if this is a control token if (controlTokenMapping[tokenId].exists) { // ensure that the remaining uses on the token is equal to what buyer expects require( controlTokenMapping[tokenId].numRemainingUpdates == expectedRemainingUpdates ); } // Return all highest bidder's money if (pendingBids[tokenId].exists) { // Return bid amount back to bidder safeFundsTransfer( pendingBids[tokenId].bidder, pendingBids[tokenId].amount ); // clear highest bid pendingBids[tokenId] = PendingBid(address(0), 0, false); } onTokenSold(tokenId, saleAmount, msg.sender); } // Take an amount and distribute it evenly amongst a list of creator addresses function distributeFundsToCreators( uint256 amount, address payable[] memory creators ) private { if (creators.length > 0) { uint256 creatorShare = amount.div(creators.length); for (uint256 i = 0; i < creators.length; i++) { safeFundsTransfer(creators[i], creatorShare); } } } // When a token is sold via list price or bid. Distributes the sale amount to the unique token creators and transfer // the token to the new owner 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; if (platformSecondSalePercentages[tokenId] == 0) { // default amount platformAmount = saleAmount .mul(defaultPlatformSecondSalePercentage) .div(100); } else { platformAmount = saleAmount .mul(platformSecondSalePercentages[tokenId]) .div(100); } safeFundsTransfer(platformAddress, 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 safeFundsTransfer( payableOwner, saleAmount.sub(platformAmount).sub(creatorAmount) ); } else { tokenDidHaveFirstSale[tokenId] = true; // give platform its first sale percentage uint256 platformAmount; if (platformFirstSalePercentages[tokenId] == 0) { // default value platformAmount = saleAmount .mul(defaultPlatformFirstSalePercentage) .div(100); } else { platformAmount = saleAmount .mul(platformFirstSalePercentages[tokenId]) .div(100); } safeFundsTransfer(platformAddress, 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 _transferFrom(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, uint256 minAcceptedAmount) external { // 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); // check that the current pending bid amount is at least what the accepting owner expects require(pendingBids[tokenId].amount >= minAcceptedAmount); // 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) external { // 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 number of times that a control token can be used function getNumRemainingControlUpdates(uint256 controlTokenId) external view returns (int256) { require( controlTokenMapping[controlTokenId].isSetup, "Token does not exist." ); return controlTokenMapping[controlTokenId].numRemainingUpdates; } // return the min, max, and current value of a control lever function getControlToken(uint256 controlTokenId) external view returns (int256[] memory) { require( controlTokenMapping[controlTokenId].isSetup, "Token does not exist." ); 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 a specific token on their behalf. Set to Address(0) to reset. function grantControlPermission(uint256 tokenId, address permissioned) external { permissionedControllers[msg.sender][tokenId] = permissioned; emit PermissionUpdated(tokenId, 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[] calldata leverIds, int256[] calldata newValues ) external 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)][ controlTokenId ] == msg.sender), "Owner or permissioned only" ); // check if control exists require( controlTokenMapping[controlTokenId].isSetup, "Token does not exist." ); // get the control token reference ControlToken storage controlToken = controlTokenMapping[controlTokenId]; // check that number of uses for control token is either infinite or is positive require( (controlToken.numRemainingUpdates == -1) || (controlToken.numRemainingUpdates > 0), "No more updates allowed" ); // 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 previousValues[i] = lever.currentValue; // Update token current value lever.currentValue = newValues[i]; } // if there's a payment then send it to the platform (for higher priority updates) if (msg.value > 0) { safeFundsTransfer(platformAddress, msg.value); } // if this control token is finite in its uses if (controlToken.numRemainingUpdates > 0) { // decrease it down by 1 controlToken.numRemainingUpdates = controlToken.numRemainingUpdates - 1; // since we used one of those updates, withdraw any existing bid for this token if exists if (pendingBids[controlTokenId].exists) { _withdrawBid(controlTokenId); } } // emit event emit ControlLeverUpdated( controlTokenId, msg.value, controlToken.numRemainingUpdates, leverIds, previousValues, newValues ); } // Allows a user to withdraw all failed transaction credits function withdrawAllFailedCredits() external { uint256 amount = failedTransferCredits[msg.sender]; require(amount != 0); require(address(this).balance >= amount); failedTransferCredits[msg.sender] = 0; (bool successfulWithdraw, ) = msg.sender.call.value(amount)(""); require(successfulWithdraw); } // Safely transfer funds and if fail then store that amount as credits for a later pull function safeFundsTransfer(address payable recipient, uint256 amount) internal { // attempt to send the funds to the recipient (bool success, ) = recipient.call.value(amount).gas(2300)(""); // if it failed, update their credit balance so they can pull it later if (success == false) { failedTransferCredits[recipient] = failedTransferCredits[recipient] .add(amount); } } // override the default transfer function _transferFrom( address from, address to, uint256 tokenId ) internal { // clear a buy now price buyPrices[tokenId] = 0; // transfer the token super._transferFrom(from, to, tokenId); } }