Transaction Hash:
Block:
11510147 at Dec-23-2020 01:38:27 PM +UTC
Transaction Fee:
0.02000602113587514 ETH
$50.36
Gas Used:
209,268 Gas / 95.600001605 Gwei
Emitted Events:
211 |
GenArt721Core.Transfer( from=0xe7925D19...B9389Cc2b, to=0x80acb3b55132c4fe408f8bcda583aaf98824e206, tokenId=5000060 )
|
212 |
0x55945d98a5d37552f1f8163262c275a8512bc20f.0x30d0b1544cabbf52b74d6df1eb5af510e230111d857b36e36cb89a29766419d2( 0x30d0b1544cabbf52b74d6df1eb5af510e230111d857b36e36cb89a29766419d2, 0x00000000000000000000000081bb11c41efc6cbf5710b2d6d47a7b43a9c28df8, 0x000000000000000000000000a7d8d9ef8d8ce8992df33d8b8cf4aebabd5bd270, 0000000000000000000000000000000000000000000000000005af3107a40000, 00000000000000000000000000000000000000000000000000000000004c4b7c, 00000000000000000000000080acb3b55132c4fe408f8bcda583aaf98824e206 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x04668Ec2...D451c8F7F
Miner
| (zhizhu.top) | 1,761.098111214464402054 Eth | 1,761.118117235600277194 Eth | 0.02000602113587514 | |
0x2C5641c0...A5a53Cea1 |
1.415857089879860481 Eth
Nonce: 2661
|
1.405851068743985341 Eth
Nonce: 2662
| 0.01000602113587514 | ||
0x55945d98...8512bc20f | 0.7888 Eth | 0.7772 Eth | 0.0116 | ||
0x80Acb3b5...98824e206 |
0 Eth
Nonce: 0
|
0.0016 Eth
Nonce: 0
| 0.0016 | ||
0xa7d8d9ef...abd5bD270 |
Execution Trace
LinkdropFactory.claimERC721( _weiAmount=1600000000000000, _nftAddress=0xa7d8d9ef8D8Ce8992Df33D8b8CF4Aebabd5bD270, _tokenId=5000060, _expiration=1900000000000, _linkId=0x81BB11C41Efc6cbf5710B2d6D47a7b43a9C28Df8, _linkdropMaster=0xe7925D190aea9279400cD9a005E33CEB9389Cc2b, _campaignId=1, _linkdropSignerSignature=0x04252B5E3C57A1F57D418732C87FF6DBC93147DE3D2403F3A878F1BDAFB29003734B1160C0ED53011D97206BBFC537FBF5615F881797EAEDEEA35CFBB2B5BB581B, _receiver=0x80Acb3b55132C4fe408f8BCDa583AAF98824e206, _receiverSignature=0xD33155B65C278F375B703D5B23EA7FCFBA7DB2C51D337BECFA248DCA3C0B4B471C9B0F183C4BC4FB8DD4B956859C846D17FC6A8ED6A9962DBE86C7BCF003B97D1B ) => ( True )
0x55945d98a5d37552f1f8163262c275a8512bc20f.db7b363c( )
0xc737976bf98acedb0e4211813cf98cb6de85e304.db7b363c( )
-
GenArt721Core.ownerOf( tokenId=5000060 ) => ( 0xe7925D190aea9279400cD9a005E33CEB9389Cc2b )
-
Null: 0x000...001.03ebd617( )
-
Null: 0x000...001.ac9b7922( )
- ETH 0.01
0x2c5641c0075b7e9d25c5f597b4d80b7a5a53cea1.CALL( )
- ETH 0.0016
0x80acb3b55132c4fe408f8bcda583aaf98824e206.CALL( )
-
GenArt721Core.transferFrom( from=0xe7925D190aea9279400cD9a005E33CEB9389Cc2b, to=0x80Acb3b55132C4fe408f8BCDa583AAF98824e206, tokenId=5000060 )
-
File 1 of 2: LinkdropFactory
File 2 of 2: GenArt721Core
pragma solidity ^0.5.6; interface ILinkdropERC20 { function verifyLinkdropSignerSignature ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, bytes calldata _signature ) external view returns (bool); function verifyReceiverSignature ( address _linkId, address _receiver, bytes calldata _signature ) external view returns (bool); function checkClaimParams ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, bytes calldata _linkdropSignerSignature, address _receiver, bytes calldata _receiverSignature, uint _fee ) external view returns (bool); function claim ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, bytes calldata _linkdropSignerSignature, address payable _receiver, bytes calldata _receiverSignature, address payable _feeReceiver, uint _fee ) external returns (bool); } interface ILinkdropFactoryERC20 { function checkClaimParams ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes calldata _linkdropSignerSignature, address _receiver, bytes calldata _receiverSignature ) external view returns (bool); function claim ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes calldata _linkdropSignerSignature, address payable _receiver, bytes calldata _receiverSignature ) external returns (bool); } /** * @dev Interface of the ERC20 standard as defined in the EIP. Does not include * the optional functions; to access them see `ERC20Detailed`. */ 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. * * > 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); } interface ILinkdropERC721 { function verifyLinkdropSignerSignatureERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, bytes calldata _signature ) external view returns (bool); function verifyReceiverSignatureERC721 ( address _linkId, address _receiver, bytes calldata _signature ) external view returns (bool); function checkClaimParamsERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, bytes calldata _linkdropSignerSignature, address _receiver, bytes calldata _receiverSignature, uint _fee ) external view returns (bool); function claimERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, bytes calldata _linkdropSignerSignature, address payable _receiver, bytes calldata _receiverSignature, address payable _feeReceiver, uint _fee ) external returns (bool); } interface ILinkdropFactoryERC721 { function checkClaimParamsERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes calldata _linkdropSignerSignature, address _receiver, bytes calldata _receiverSignature ) external view returns (bool); function claimERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes calldata _linkdropSignerSignature, address payable _receiver, bytes calldata _receiverSignature ) external returns (bool); } interface ILinkdropCommon { function initialize ( address _owner, address payable _linkdropMaster, uint _version, uint _chainId ) external returns (bool); function isClaimedLink(address _linkId) external view returns (bool); function isCanceledLink(address _linkId) external view returns (bool); function paused() external view returns (bool); function cancel(address _linkId) external returns (bool); function withdraw() external returns (bool); function pause() external returns (bool); function unpause() external returns (bool); function addSigner(address _linkdropSigner) external payable returns (bool); function removeSigner(address _linkdropSigner) external returns (bool); function destroy() external; function getMasterCopyVersion() external view returns (uint); function () external payable; } /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * (.note) This call _does not revert_ if the signature is invalid, or * if the signer is otherwise unable to be retrieved. In those scenarios, * the zero address is returned. * * (.warning) `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise) * be too long), and then calling `toEthSignedMessageHash` on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { return (address(0)); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } if (v != 27 && v != 28) { return address(0); } // If the signature is valid (and not malleable), return the signer address return ecrecover(hash, v, r, s); } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * [`eth_sign`](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign) * JSON-RPC method. * * See `recover`. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } } /** * @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) { require(b <= a, "SafeMath: subtraction overflow"); 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-solidity/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) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); 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) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } /** * @dev Interface of the ERC165 standard, as defined in the * [EIP](https://eips.ethereum.org/EIPS/eip-165). * * 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 * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * 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); } /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be aplied to your functions to restrict their use to * the owner. */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * > Note: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @dev Required interface of an ERC721 compliant contract. */ contract IERC721 is IERC165 { event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of NFTs in `owner`'s account. */ function balanceOf(address owner) public view returns (uint256 balance); /** * @dev Returns the owner of the NFT specified by `tokenId`. */ function ownerOf(uint256 tokenId) public view returns (address owner); /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * * * Requirements: * - `from`, `to` cannot be zero. * - `tokenId` must be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this * NFT by either `approve` or `setApproveForAll`. */ 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 `setApproveForAll`. */ function transferFrom(address from, address to, uint256 tokenId) public; function approve(address to, uint256 tokenId) public; function getApproved(uint256 tokenId) public view returns (address operator); function setApprovalForAll(address operator, bool _approved) public; function isApprovedForAll(address owner, address operator) public view returns (bool); function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; } contract LinkdropFactoryStorage is Ownable { // Current version of mastercopy contract uint public masterCopyVersion; // Contract bytecode to be installed when deploying proxy bytes internal _bytecode; // Bootstrap initcode to fetch the actual contract bytecode. Used to generate repeatable contract addresses bytes internal _initcode; // Network id uint public chainId; // Maps hash(sender address, campaign id) to its corresponding proxy address mapping (bytes32 => address) public deployed; // Events event Deployed(address payable indexed owner, uint campaignId, address payable proxy, bytes32 salt); event Destroyed(address payable owner, address payable proxy); event SetMasterCopy(address masterCopy, uint version); } contract FeeManager is Ownable { event FeeChanged(address proxy, uint fee); mapping (address => uint) fees; uint public standardFee = 0.002 ether; function setFee(address _proxy, uint _fee) external onlyOwner returns (bool) { _setFee(_proxy, _fee); return true; } function _setFee(address _proxy, uint _fee) internal { if (fees[_proxy] != 0) { require(_fee < fees[_proxy], "CANNOT_INCREASE_FEE"); } fees[_proxy] = _fee; emit FeeChanged(_proxy, _fee); } function setStandardFee(uint _fee) external onlyOwner { standardFee = _fee; } } contract RelayerManager is Ownable { mapping (address => bool) public isRelayer; event RelayerAdded(address indexed relayer); event RelayerRemoved(address indexed relayer); function addRelayer(address _relayer) external onlyOwner returns (bool) { require(_relayer != address(0) && !isRelayer[_relayer], "INVALID_RELAYER_ADDRESS"); isRelayer[_relayer] = true; emit RelayerAdded(_relayer); return true; } function removeRelayer(address _relayer) external onlyOwner returns (bool) { require(isRelayer[_relayer], "INVALID_RELAYER_ADDRESS"); isRelayer[_relayer] = false; emit RelayerRemoved(_relayer); return true; } } contract LinkdropFactoryCommon is LinkdropFactoryStorage, FeeManager, RelayerManager { using SafeMath for uint; /** * @dev Indicates whether a proxy contract for linkdrop master is deployed or not * @param _linkdropMaster Address of linkdrop master * @param _campaignId Campaign id * @return True if deployed */ function isDeployed(address _linkdropMaster, uint _campaignId) public view returns (bool) { return (deployed[salt(_linkdropMaster, _campaignId)] != address(0)); } /** * @dev Indicates whether a link is claimed or not * @param _linkdropMaster Address of lindkrop master * @param _campaignId Campaign id * @param _linkId Address corresponding to link key * @return True if claimed */ function isClaimedLink(address payable _linkdropMaster, uint _campaignId, address _linkId) public view returns (bool) { if (!isDeployed(_linkdropMaster, _campaignId)) { return false; } else { address payable proxy = address(uint160(deployed[salt(_linkdropMaster, _campaignId)])); return ILinkdropCommon(proxy).isClaimedLink(_linkId); } } /** * @dev Function to deploy a proxy contract for msg.sender * @param _campaignId Campaign id * @return Proxy contract address */ function deployProxy(uint _campaignId) public payable returns (address payable proxy) { proxy = _deployProxy(msg.sender, _campaignId); } /** * @dev Function to deploy a proxy contract for msg.sender and add a new signing key * @param _campaignId Campaign id * @param _signer Address corresponding to signing key * @return Proxy contract address */ function deployProxyWithSigner(uint _campaignId, address _signer) public payable returns (address payable proxy) { proxy = deployProxy(_campaignId); ILinkdropCommon(proxy).addSigner(_signer); } /** * @dev Internal function to deploy a proxy contract for linkdrop master * @param _linkdropMaster Address of linkdrop master * @param _campaignId Campaign id * @return Proxy contract address */ function _deployProxy(address payable _linkdropMaster, uint _campaignId) internal returns (address payable proxy) { require(!isDeployed(_linkdropMaster, _campaignId), "LINKDROP_PROXY_CONTRACT_ALREADY_DEPLOYED"); require(_linkdropMaster != address(0), "INVALID_LINKDROP_MASTER_ADDRESS"); bytes32 salt = salt(_linkdropMaster, _campaignId); bytes memory initcode = getInitcode(); assembly { proxy := create2(0, add(initcode, 0x20), mload(initcode), salt) if iszero(extcodesize(proxy)) { revert(0, 0) } } deployed[salt] = proxy; // Initialize owner address, linkdrop master address master copy version in proxy contract require ( ILinkdropCommon(proxy).initialize ( address(this), // Owner address _linkdropMaster, // Linkdrop master address masterCopyVersion, chainId ), "INITIALIZATION_FAILED" ); // Send funds attached to proxy contract proxy.transfer(msg.value); // Set standard fee for the proxy _setFee(proxy, standardFee); emit Deployed(_linkdropMaster, _campaignId, proxy, salt); return proxy; } /** * @dev Function to destroy proxy contract, called by proxy owner * @param _campaignId Campaign id * @return True if destroyed successfully */ function destroyProxy(uint _campaignId) public returns (bool) { require(isDeployed(msg.sender, _campaignId), "LINKDROP_PROXY_CONTRACT_NOT_DEPLOYED"); address payable proxy = address(uint160(deployed[salt(msg.sender, _campaignId)])); ILinkdropCommon(proxy).destroy(); delete deployed[salt(msg.sender, _campaignId)]; delete fees[proxy]; emit Destroyed(msg.sender, proxy); return true; } /** * @dev Function to get bootstrap initcode for generating repeatable contract addresses * @return Static bootstrap initcode */ function getInitcode() public view returns (bytes memory) { return _initcode; } /** * @dev Function to fetch the actual contract bytecode to install. Called by proxy when executing initcode * @return Contract bytecode to install */ function getBytecode() public view returns (bytes memory) { return _bytecode; } /** * @dev Function to set new master copy and update contract bytecode to install. Can only be called by factory owner * @param _masterCopy Address of linkdrop mastercopy contract to calculate bytecode from * @return True if updated successfully */ function setMasterCopy(address payable _masterCopy) public onlyOwner returns (bool) { require(_masterCopy != address(0), "INVALID_MASTER_COPY_ADDRESS"); masterCopyVersion = masterCopyVersion.add(1); require ( ILinkdropCommon(_masterCopy).initialize ( address(0), // Owner address address(0), // Linkdrop master address masterCopyVersion, chainId ), "INITIALIZATION_FAILED" ); bytes memory bytecode = abi.encodePacked ( hex"363d3d373d3d3d363d73", _masterCopy, hex"5af43d82803e903d91602b57fd5bf3" ); _bytecode = bytecode; emit SetMasterCopy(_masterCopy, masterCopyVersion); return true; } /** * @dev Function to fetch the master copy version installed (or to be installed) to proxy * @param _linkdropMaster Address of linkdrop master * @param _campaignId Campaign id * @return Master copy version */ function getProxyMasterCopyVersion(address _linkdropMaster, uint _campaignId) external view returns (uint) { if (!isDeployed(_linkdropMaster, _campaignId)) { return masterCopyVersion; } else { address payable proxy = address(uint160(deployed[salt(_linkdropMaster, _campaignId)])); return ILinkdropCommon(proxy).getMasterCopyVersion(); } } /** * @dev Function to hash `_linkdropMaster` and `_campaignId` params. Used as salt when deploying with create2 * @param _linkdropMaster Address of linkdrop master * @param _campaignId Campaign id * @return Hash of passed arguments */ function salt(address _linkdropMaster, uint _campaignId) public pure returns (bytes32) { return keccak256(abi.encodePacked(_linkdropMaster, _campaignId)); } } contract LinkdropFactoryERC20 is ILinkdropFactoryERC20, LinkdropFactoryCommon { /** * @dev Function to verify claim params, make sure the link is not claimed or canceled and proxy has sufficient balance * @param _weiAmount Amount of wei to be claimed * @param _tokenAddress Token address * @param _tokenAmount Amount of tokens to be claimed (in atomic value) * @param _expiration Unix timestamp of link expiration time * @param _linkId Address corresponding to link key * @param _linkdropMaster Address corresponding to linkdrop master key * @param _campaignId Campaign id * @param _linkdropSignerSignature ECDSA signature of linkdrop signer * @param _receiver Address of linkdrop receiver * @param _receiverSignature ECDSA signature of linkdrop receiver * @return True if success */ function checkClaimParams ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes memory _linkdropSignerSignature, address _receiver, bytes memory _receiverSignature ) public view returns (bool) { // Make sure proxy contract is deployed require(isDeployed(_linkdropMaster, _campaignId), "LINKDROP_PROXY_CONTRACT_NOT_DEPLOYED"); uint fee = fees[deployed[salt(_linkdropMaster, _campaignId)]]; return ILinkdropERC20(deployed[salt(_linkdropMaster, _campaignId)]).checkClaimParams ( _weiAmount, _tokenAddress, _tokenAmount, _expiration, _linkId, _linkdropSignerSignature, _receiver, _receiverSignature, fee ); } /** * @dev Function to claim ETH and/or ERC20 tokens * @param _weiAmount Amount of wei to be claimed * @param _tokenAddress Token address * @param _tokenAmount Amount of tokens to be claimed (in atomic value) * @param _expiration Unix timestamp of link expiration time * @param _linkId Address corresponding to link key * @param _linkdropMaster Address corresponding to linkdrop master key * @param _campaignId Campaign id * @param _linkdropSignerSignature ECDSA signature of linkdrop signer * @param _receiver Address of linkdrop receiver * @param _receiverSignature ECDSA signature of linkdrop receiver * @return True if success */ function claim ( uint _weiAmount, address _tokenAddress, uint _tokenAmount, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes calldata _linkdropSignerSignature, address payable _receiver, bytes calldata _receiverSignature ) external returns (bool) { // Make sure proxy contract is deployed require(isDeployed(_linkdropMaster, _campaignId), "LINKDROP_PROXY_CONTRACT_NOT_DEPLOYED"); // Make sure only whitelisted relayer calls this function require(isRelayer[msg.sender], "ONLY_RELAYER"); uint fee = fees[deployed[salt(_linkdropMaster, _campaignId)]]; // Call claim function in the context of proxy contract ILinkdropERC20(deployed[salt(_linkdropMaster, _campaignId)]).claim ( _weiAmount, _tokenAddress, _tokenAmount, _expiration, _linkId, _linkdropSignerSignature, _receiver, _receiverSignature, msg.sender, // Fee receiver fee ); return true; } } contract LinkdropFactoryERC721 is ILinkdropFactoryERC721, LinkdropFactoryCommon { /** * @dev Function to verify claim params, make sure the link is not claimed or canceled and proxy is allowed to spend token * @param _weiAmount Amount of wei to be claimed * @param _nftAddress NFT address * @param _tokenId Token id to be claimed * @param _expiration Unix timestamp of link expiration time * @param _linkId Address corresponding to link key * @param _linkdropMaster Address corresponding to linkdrop master key * @param _campaignId Campaign id * @param _linkdropSignerSignature ECDSA signature of linkdrop signer * @param _receiver Address of linkdrop receiver * @param _receiverSignature ECDSA signature of linkdrop receiver * @return True if success */ function checkClaimParamsERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes memory _linkdropSignerSignature, address _receiver, bytes memory _receiverSignature ) public view returns (bool) { // Make sure proxy contract is deployed require(isDeployed(_linkdropMaster, _campaignId), "LINKDROP_PROXY_CONTRACT_NOT_DEPLOYED"); uint fee = fees[deployed[salt(_linkdropMaster, _campaignId)]]; return ILinkdropERC721(deployed[salt(_linkdropMaster, _campaignId)]).checkClaimParamsERC721 ( _weiAmount, _nftAddress, _tokenId, _expiration, _linkId, _linkdropSignerSignature, _receiver, _receiverSignature, fee ); } /** * @dev Function to claim ETH and/or ERC721 token * @param _weiAmount Amount of wei to be claimed * @param _nftAddress NFT address * @param _tokenId Token id to be claimed * @param _expiration Unix timestamp of link expiration time * @param _linkId Address corresponding to link key * @param _linkdropMaster Address corresponding to linkdrop master key * @param _campaignId Campaign id * @param _linkdropSignerSignature ECDSA signature of linkdrop signer * @param _receiver Address of linkdrop receiver * @param _receiverSignature ECDSA signature of linkdrop receiver * @return True if success */ function claimERC721 ( uint _weiAmount, address _nftAddress, uint _tokenId, uint _expiration, address _linkId, address payable _linkdropMaster, uint _campaignId, bytes calldata _linkdropSignerSignature, address payable _receiver, bytes calldata _receiverSignature ) external returns (bool) { // Make sure proxy contract is deployed require(isDeployed(_linkdropMaster, _campaignId), "LINKDROP_PROXY_CONTRACT_NOT_DEPLOYED"); // Make sure only whitelisted relayer calls this function require(isRelayer[msg.sender], "ONLY_RELAYER"); uint fee = fees[deployed[salt(_linkdropMaster, _campaignId)]]; // Call claim function in the context of proxy contract ILinkdropERC721(deployed[salt(_linkdropMaster, _campaignId)]).claimERC721 ( _weiAmount, _nftAddress, _tokenId, _expiration, _linkId, _linkdropSignerSignature, _receiver, _receiverSignature, msg.sender, // Fee receiver fee ); return true; } } contract LinkdropFactory is LinkdropFactoryERC20, LinkdropFactoryERC721 { /** * @dev Constructor that sets bootstap initcode, factory owner, chainId and master copy * @param _masterCopy Linkdrop mastercopy contract address to calculate bytecode from * @param _chainId Chain id */ constructor(address payable _masterCopy, uint _chainId) public { _initcode = (hex"6352c7420d6000526103ff60206004601c335afa6040516060f3"); chainId = _chainId; setMasterCopy(_masterCopy); } }
File 2 of 2: GenArt721Core
// File contracts/libs/IERC165.sol // File: openzeppelin-solidity/contracts/introspection/IERC165.sol pragma solidity ^0.5.0; /** * @dev Interface of the ERC165 standard, as defined in the * [EIP](https://eips.ethereum.org/EIPS/eip-165). * * 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 * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * 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 contracts/libs/ERC165.sol // File: openzeppelin-solidity/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 IERC165 { /* * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 */ bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; constructor () internal { // Derived contracts need only register support for their own interfaces, // we register support for ERC165 itself here _registerInterface(_INTERFACE_ID_ERC165); } /** * @dev See `IERC165.supportsInterface`. * * Time complexity O(1), guaranteed to always use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool) { return _supportedInterfaces[interfaceId]; } /** * @dev Registers the contract as an implementer of the interface defined by * `interfaceId`. Support of the actual ERC165 interface is automatic and * registering its interface id is not required. * * See `IERC165.supportsInterface`. * * Requirements: * * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). */ function _registerInterface(bytes4 interfaceId) internal { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } } // File contracts/libs/IERC721.sol // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol pragma solidity ^0.5.0; /** * @dev Required interface of an ERC721 compliant contract. */ contract IERC721 is IERC165 { event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of NFTs in `owner`'s account. */ function balanceOf(address owner) public view returns (uint256 balance); /** * @dev Returns the owner of the NFT specified by `tokenId`. */ function ownerOf(uint256 tokenId) public view returns (address owner); /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * * * Requirements: * - `from`, `to` cannot be zero. * - `tokenId` must be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this * NFT by either `approve` or `setApproveForAll`. */ 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 `setApproveForAll`. */ 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 contracts/libs/SafeMath.sol // File: openzeppelin-solidity/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) { require(b <= a, "SafeMath: subtraction overflow"); 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-solidity/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) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } } // File contracts/libs/Address.sol // File: openzeppelin-solidity/contracts/utils/Address.sol pragma solidity ^0.5.0; /** * @dev Collection of functions related to the address type, */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * > It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File contracts/libs/Counters.sol // File: openzeppelin-solidity/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 { counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } // File contracts/libs/IERC721Receiver.sol // File: openzeppelin-solidity/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 { function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) public returns (bytes4); } // File contracts/libs/ERC721.sol // File: openzeppelin-solidity/contracts/token/ERC721/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 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 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; constructor () public { // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721); } function balanceOf(address owner) public view returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _ownedTokensCount[owner].current(); } function ownerOf(uint256 tokenId) public view returns (address) { address owner = _tokenOwner[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } function approve(address to, uint256 tokenId) public { address owner = ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "ERC721: approve caller is not owner nor approved for all" ); _tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); } function getApproved(uint256 tokenId) public view returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } function setApprovalForAll(address to, bool approved) public { require(to != msg.sender, "ERC721: approve to caller"); _operatorApprovals[msg.sender][to] = approved; emit ApprovalForAll(msg.sender, to, approved); } function isApprovedForAll(address owner, address operator) public view returns (bool) { return _operatorApprovals[owner][operator]; } function transferFrom(address from, address to, uint256 tokenId) public { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); _transferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public { safeTransferFrom(from, to, tokenId, ""); } function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { transferFrom(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } function _exists(uint256 tokenId) internal view returns (bool) { address owner = _tokenOwner[tokenId]; return owner != address(0); } 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)); } 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); } function _burn(address owner, uint256 tokenId) internal { require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own"); _clearApproval(tokenId); _ownedTokensCount[owner].decrement(); _tokenOwner[tokenId] = address(0); emit Transfer(owner, address(0), tokenId); } function _burn(uint256 tokenId) internal { _burn(ownerOf(tokenId), tokenId); } 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); } function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) { if (!to.isContract()) { return true; } bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data); return (retval == _ERC721_RECEIVED); } function _clearApproval(uint256 tokenId) private { if (_tokenApprovals[tokenId] != address(0)) { _tokenApprovals[tokenId] = address(0); } } } // File contracts/libs/IERC721Enumerable.sol // File: openzeppelin-solidity/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 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/libs/ERC721Enumerable.sol // File: openzeppelin-solidity/contracts/token/ERC721/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 ERC165, ERC721, IERC721Enumerable { // Mapping from owner to list of owned token IDs mapping(address => uint256[]) private _ownedTokens; // Mapping from token ID to index of the owner tokens list mapping(uint256 => uint256) private _ownedTokensIndex; // Array with all token ids, used for enumeration uint256[] private _allTokens; // Mapping from token id to position in the allTokens array mapping(uint256 => uint256) private _allTokensIndex; /* * bytes4(keccak256('totalSupply()')) == 0x18160ddd * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59 * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7 * * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63 */ bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; /** * @dev Constructor function. */ constructor () public { // register the supported interface to conform to ERC721Enumerable via ERC165 _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } /** * @dev Gets the token ID at a given index of the tokens list of the requested owner. * @param owner address owning the tokens list to be accessed * @param index uint256 representing the index to be accessed of the requested tokens list * @return uint256 token ID at the given index of the tokens list owned by the requested address */ function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) { require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); return _ownedTokens[owner][index]; } /** * @dev Gets the total amount of tokens stored by the contract. * @return uint256 representing the total amount of tokens */ function totalSupply() public view returns (uint256) { return _allTokens.length; } /** * @dev Gets the token ID at a given index of all the tokens in this contract * Reverts if the index is greater or equal to the total number of tokens. * @param index uint256 representing the index to be accessed of the tokens list * @return uint256 token ID at the given index of the tokens list */ function tokenByIndex(uint256 index) public view returns (uint256) { require(index < totalSupply(), "ERC721Enumerable: global index out of bounds"); return _allTokens[index]; } /** * @dev Internal function to transfer ownership of a given token ID to another address. * As opposed to transferFrom, this imposes no restrictions on msg.sender. * @param from current owner of the token * @param to address to receive the ownership of the given token ID * @param tokenId uint256 ID of the token to be transferred */ function _transferFrom(address from, address to, uint256 tokenId) internal { super._transferFrom(from, to, tokenId); _removeTokenFromOwnerEnumeration(from, tokenId); _addTokenToOwnerEnumeration(to, tokenId); } /** * @dev Internal function to mint a new token. * Reverts if the given token ID already exists. * @param to address the beneficiary that will own the minted token * @param tokenId uint256 ID of the token to be minted */ function _mint(address to, uint256 tokenId) internal { super._mint(to, tokenId); _addTokenToOwnerEnumeration(to, tokenId); _addTokenToAllTokensEnumeration(tokenId); } /** * @dev Internal function to burn a specific token. * Reverts if the token does not exist. * Deprecated, use _burn(uint256) 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; } } // File contracts/libs/CustomERC721Metadata.sol // File: contracts/CustomERC721Metadata.sol pragma solidity ^0.5.0; /** * ERC721 base contract without the concept of tokenUri as this is managed by the parent */ contract CustomERC721Metadata is ERC165, ERC721, ERC721Enumerable { // Token name string private _name; // Token symbol string private _symbol; bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; /** * @dev Constructor function */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721_METADATA); } /** * @dev Gets the token name * @return string representing the token name */ function name() external view returns (string memory) { return _name; } /** * @dev Gets the token symbol * @return string representing the token symbol */ function symbol() external view returns (string memory) { return _symbol; } } // File contracts/libs/Strings.sol // File: contracts/Strings.sol pragma solidity ^0.5.0; //https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol library Strings { function strConcat(string memory _a, string memory _b) internal pure returns (string memory _concatenatedString) { return strConcat(_a, _b, "", "", ""); } function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory _concatenatedString) { return strConcat(_a, _b, _c, "", ""); } function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory _concatenatedString) { return strConcat(_a, _b, _c, _d, ""); } function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory _concatenatedString) { bytes memory _ba = bytes(_a); bytes memory _bb = bytes(_b); bytes memory _bc = bytes(_c); bytes memory _bd = bytes(_d); bytes memory _be = bytes(_e); string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); bytes memory babcde = bytes(abcde); uint k = 0; uint i = 0; for (i = 0; i < _ba.length; i++) { babcde[k++] = _ba[i]; } for (i = 0; i < _bb.length; i++) { babcde[k++] = _bb[i]; } for (i = 0; i < _bc.length; i++) { babcde[k++] = _bc[i]; } for (i = 0; i < _bd.length; i++) { babcde[k++] = _bd[i]; } for (i = 0; i < _be.length; i++) { babcde[k++] = _be[i]; } return string(babcde); } function uint2str(uint _i) internal pure returns (string memory _uintAsString) { if (_i == 0) { return "0"; } uint j = _i; uint len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint k = len - 1; while (_i != 0) { bstr[k--] = byte(uint8(48 + _i % 10)); _i /= 10; } return string(bstr); } } // File contracts/GenArt721Core.sol // File: contracts/GenArt721Core.sol //0x1454EFCa69FA654e5A7d83CB61c1aD81790c44B7 //https://oneclickdapp.com/radar-valery/ pragma solidity ^0.5.0; interface Randomizer { function returnValue() external view returns(bytes32); } contract GenArt721Core is CustomERC721Metadata { using SafeMath for uint256; event Mint( address indexed _to, uint256 indexed _tokenId, uint256 indexed _projectId ); Randomizer public randomizerContract; struct Project { string name; string artist; string description; string website; string license; bool dynamic; string projectBaseURI; string projectBaseIpfsURI; uint256 invocations; uint256 maxInvocations; string scriptJSON; mapping(uint256 => string) scripts; uint scriptCount; string ipfsHash; bool useHashString; bool useIpfs; bool active; bool locked; bool paused; } uint256 constant ONE_MILLION = 1_000_000; mapping(uint256 => Project) projects; //All financial functions are stripped from struct for visibility mapping(uint256 => address) public projectIdToArtistAddress; mapping(uint256 => string) public projectIdToCurrencySymbol; mapping(uint256 => address) public projectIdToCurrencyAddress; mapping(uint256 => uint256) public projectIdToPricePerTokenInWei; mapping(uint256 => address) public projectIdToAdditionalPayee; mapping(uint256 => uint256) public projectIdToAdditionalPayeePercentage; mapping(uint256 => uint256) public projectIdToSecondaryMarketRoyaltyPercentage; address public artblocksAddress; uint256 public artblocksPercentage = 10; mapping(uint256 => string) public staticIpfsImageLink; mapping(uint256 => uint256) public tokenIdToProjectId; mapping(uint256 => uint256[]) internal projectIdToTokenIds; mapping(uint256 => bytes32) public tokenIdToHash; mapping(bytes32 => uint256) public hashToTokenId; address public admin; mapping(address => bool) public isWhitelisted; mapping(address => bool) public isMintWhitelisted; uint256 public nextProjectId = 3; modifier onlyValidTokenId(uint256 _tokenId) { require(_exists(_tokenId), "Token ID does not exist"); _; } modifier onlyUnlocked(uint256 _projectId) { require(!projects[_projectId].locked, "Only if unlocked"); _; } modifier onlyArtist(uint256 _projectId) { require(msg.sender == projectIdToArtistAddress[_projectId], "Only artist"); _; } modifier onlyAdmin() { require(msg.sender == admin, "Only admin"); _; } modifier onlyWhitelisted() { require(isWhitelisted[msg.sender], "Only whitelisted"); _; } modifier onlyArtistOrWhitelisted(uint256 _projectId) { require(isWhitelisted[msg.sender] || msg.sender == projectIdToArtistAddress[_projectId], "Only artist or whitelisted"); _; } constructor(string memory _tokenName, string memory _tokenSymbol, address _randomizerContract) CustomERC721Metadata(_tokenName, _tokenSymbol) public { admin = msg.sender; isWhitelisted[msg.sender] = true; artblocksAddress = msg.sender; randomizerContract = Randomizer(_randomizerContract); } function mint(address _to, uint256 _projectId, address _by) external returns (uint256 _tokenId) { require(isMintWhitelisted[msg.sender], "Must mint from whitelisted minter contract."); require(projects[_projectId].invocations.add(1) <= projects[_projectId].maxInvocations, "Must not exceed max invocations"); require(projects[_projectId].active || _by == projectIdToArtistAddress[_projectId], "Project must exist and be active"); require(!projects[_projectId].paused || _by == projectIdToArtistAddress[_projectId], "Purchases are paused."); uint256 tokenId = _mintToken(_to, _projectId); return tokenId; } function _mintToken(address _to, uint256 _projectId) internal returns (uint256 _tokenId) { uint256 tokenIdToBe = (_projectId * ONE_MILLION) + projects[_projectId].invocations; projects[_projectId].invocations = projects[_projectId].invocations.add(1); bytes32 hash = keccak256(abi.encodePacked(projects[_projectId].invocations, block.number, blockhash(block.number - 1), msg.sender, randomizerContract.returnValue())); tokenIdToHash[tokenIdToBe]=hash; hashToTokenId[hash] = tokenIdToBe; _mint(_to, tokenIdToBe); tokenIdToProjectId[tokenIdToBe] = _projectId; projectIdToTokenIds[_projectId].push(tokenIdToBe); emit Mint(_to, tokenIdToBe, _projectId); return tokenIdToBe; } function updateArtblocksAddress(address _artblocksAddress) public onlyAdmin { artblocksAddress = _artblocksAddress; } function updateArtblocksPercentage(uint256 _artblocksPercentage) public onlyAdmin { require(_artblocksPercentage <= 25, "Max of 25%"); artblocksPercentage = _artblocksPercentage; } function addWhitelisted(address _address) public onlyAdmin { isWhitelisted[_address] = true; } function removeWhitelisted(address _address) public onlyAdmin { isWhitelisted[_address] = false; } function addMintWhitelisted(address _address) public onlyAdmin { isMintWhitelisted[_address] = true; } function removeMintWhitelisted(address _address) public onlyAdmin { isMintWhitelisted[_address] = false; } function updateRandomizerAddress(address _randomizerAddress) public onlyWhitelisted { randomizerContract = Randomizer(_randomizerAddress); } function toggleProjectIsLocked(uint256 _projectId) public onlyWhitelisted onlyUnlocked(_projectId) { projects[_projectId].locked = true; } function toggleProjectIsActive(uint256 _projectId) public onlyWhitelisted { projects[_projectId].active = !projects[_projectId].active; } function updateProjectArtistAddress(uint256 _projectId, address _artistAddress) public onlyArtistOrWhitelisted(_projectId) { projectIdToArtistAddress[_projectId] = _artistAddress; } function toggleProjectIsPaused(uint256 _projectId) public onlyArtist(_projectId) { projects[_projectId].paused = !projects[_projectId].paused; } function addProject(string memory _projectName, address _artistAddress, uint256 _pricePerTokenInWei, bool _dynamic) public onlyWhitelisted { uint256 projectId = nextProjectId; projectIdToArtistAddress[projectId] = _artistAddress; projects[projectId].name = _projectName; projectIdToCurrencySymbol[projectId] = "ETH"; projectIdToPricePerTokenInWei[projectId] = _pricePerTokenInWei; projects[projectId].paused=true; projects[projectId].dynamic=_dynamic; projects[projectId].maxInvocations = ONE_MILLION; if (!_dynamic) { projects[projectId].useHashString = false; } else { projects[projectId].useHashString = true; } nextProjectId = nextProjectId.add(1); } function updateProjectCurrencyInfo(uint256 _projectId, string memory _currencySymbol, address _currencyAddress) onlyArtist(_projectId) public { projectIdToCurrencySymbol[_projectId] = _currencySymbol; projectIdToCurrencyAddress[_projectId] = _currencyAddress; } function updateProjectPricePerTokenInWei(uint256 _projectId, uint256 _pricePerTokenInWei) onlyArtist(_projectId) public { projectIdToPricePerTokenInWei[_projectId] = _pricePerTokenInWei; } function updateProjectName(uint256 _projectId, string memory _projectName) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { projects[_projectId].name = _projectName; } function updateProjectArtistName(uint256 _projectId, string memory _projectArtistName) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { projects[_projectId].artist = _projectArtistName; } function updateProjectAdditionalPayeeInfo(uint256 _projectId, address _additionalPayee, uint256 _additionalPayeePercentage) onlyArtist(_projectId) public { require(_additionalPayeePercentage <= 100, "Max of 100%"); projectIdToAdditionalPayee[_projectId] = _additionalPayee; projectIdToAdditionalPayeePercentage[_projectId] = _additionalPayeePercentage; } function updateProjectSecondaryMarketRoyaltyPercentage(uint256 _projectId, uint256 _secondMarketRoyalty) onlyArtist(_projectId) public { require(_secondMarketRoyalty <= 100, "Max of 100%"); projectIdToSecondaryMarketRoyaltyPercentage[_projectId] = _secondMarketRoyalty; } function updateProjectDescription(uint256 _projectId, string memory _projectDescription) onlyArtist(_projectId) public { projects[_projectId].description = _projectDescription; } function updateProjectWebsite(uint256 _projectId, string memory _projectWebsite) onlyArtist(_projectId) public { projects[_projectId].website = _projectWebsite; } function updateProjectLicense(uint256 _projectId, string memory _projectLicense) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { projects[_projectId].license = _projectLicense; } function updateProjectMaxInvocations(uint256 _projectId, uint256 _maxInvocations) onlyArtist(_projectId) public { require((!projects[_projectId].locked || _maxInvocations<projects[_projectId].maxInvocations), "Only if unlocked"); require(_maxInvocations > projects[_projectId].invocations, "You must set max invocations greater than current invocations"); require(_maxInvocations <= ONE_MILLION, "Cannot exceed 1,000,000"); projects[_projectId].maxInvocations = _maxInvocations; } function toggleProjectUseHashString(uint256 _projectId) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { require(projects[_projectId].invocations == 0, "Cannot modify after a token is minted."); projects[_projectId].useHashString = !projects[_projectId].useHashString; } function addProjectScript(uint256 _projectId, string memory _script) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { projects[_projectId].scripts[projects[_projectId].scriptCount] = _script; projects[_projectId].scriptCount = projects[_projectId].scriptCount.add(1); } function updateProjectScript(uint256 _projectId, uint256 _scriptId, string memory _script) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { require(_scriptId < projects[_projectId].scriptCount, "scriptId out of range"); projects[_projectId].scripts[_scriptId] = _script; } function removeProjectLastScript(uint256 _projectId) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { require(projects[_projectId].scriptCount > 0, "there are no scripts to remove"); delete projects[_projectId].scripts[projects[_projectId].scriptCount - 1]; projects[_projectId].scriptCount = projects[_projectId].scriptCount.sub(1); } function updateProjectScriptJSON(uint256 _projectId, string memory _projectScriptJSON) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { projects[_projectId].scriptJSON = _projectScriptJSON; } function updateProjectIpfsHash(uint256 _projectId, string memory _ipfsHash) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { projects[_projectId].ipfsHash = _ipfsHash; } function updateProjectBaseURI(uint256 _projectId, string memory _newBaseURI) onlyArtist(_projectId) public { projects[_projectId].projectBaseURI = _newBaseURI; } function updateProjectBaseIpfsURI(uint256 _projectId, string memory _projectBaseIpfsURI) onlyArtist(_projectId) public { projects[_projectId].projectBaseIpfsURI = _projectBaseIpfsURI; } function toggleProjectUseIpfsForStatic(uint256 _projectId) onlyArtist(_projectId) public { require(!projects[_projectId].dynamic, "can only set static IPFS hash for static projects"); projects[_projectId].useIpfs = !projects[_projectId].useIpfs; } function toggleProjectIsDynamic(uint256 _projectId) onlyUnlocked(_projectId) onlyArtistOrWhitelisted(_projectId) public { require(projects[_projectId].invocations == 0, "Can not switch after a token is minted."); if (projects[_projectId].dynamic) { projects[_projectId].useHashString = false; } else { projects[_projectId].useHashString = true; } projects[_projectId].dynamic = !projects[_projectId].dynamic; } function overrideTokenDynamicImageWithIpfsLink(uint256 _tokenId, string memory _ipfsHash) onlyArtist(tokenIdToProjectId[_tokenId]) public { staticIpfsImageLink[_tokenId] = _ipfsHash; } function clearTokenIpfsImageUri(uint256 _tokenId) onlyArtist(tokenIdToProjectId[_tokenId]) public { delete staticIpfsImageLink[tokenIdToProjectId[_tokenId]]; } function projectDetails(uint256 _projectId) view public returns (string memory projectName, string memory artist, string memory description, string memory website, string memory license, bool dynamic) { projectName = projects[_projectId].name; artist = projects[_projectId].artist; description = projects[_projectId].description; website = projects[_projectId].website; license = projects[_projectId].license; dynamic = projects[_projectId].dynamic; } function projectTokenInfo(uint256 _projectId) view public returns (address artistAddress, uint256 pricePerTokenInWei, uint256 invocations, uint256 maxInvocations, bool active, address additionalPayee, uint256 additionalPayeePercentage ,string memory currency, address currencyAddress) { artistAddress = projectIdToArtistAddress[_projectId]; pricePerTokenInWei = projectIdToPricePerTokenInWei[_projectId]; invocations = projects[_projectId].invocations; maxInvocations = projects[_projectId].maxInvocations; active = projects[_projectId].active; additionalPayee = projectIdToAdditionalPayee[_projectId]; additionalPayeePercentage = projectIdToAdditionalPayeePercentage[_projectId]; currency = projectIdToCurrencySymbol[_projectId]; currencyAddress = projectIdToCurrencyAddress[_projectId]; } function projectScriptInfo(uint256 _projectId) view public returns (string memory scriptJSON, uint256 scriptCount, bool useHashString, string memory ipfsHash, bool locked, bool paused) { scriptJSON = projects[_projectId].scriptJSON; scriptCount = projects[_projectId].scriptCount; useHashString = projects[_projectId].useHashString; ipfsHash = projects[_projectId].ipfsHash; locked = projects[_projectId].locked; paused = projects[_projectId].paused; } function projectScriptByIndex(uint256 _projectId, uint256 _index) view public returns (string memory){ return projects[_projectId].scripts[_index]; } function projectURIInfo(uint256 _projectId) view public returns (string memory projectBaseURI, string memory projectBaseIpfsURI, bool useIpfs) { projectBaseURI = projects[_projectId].projectBaseURI; projectBaseIpfsURI = projects[_projectId].projectBaseIpfsURI; useIpfs = projects[_projectId].useIpfs; } function projectShowAllTokens(uint _projectId) public view returns (uint256[] memory){ return projectIdToTokenIds[_projectId]; } function tokensOfOwner(address owner) external view returns (uint256[] memory) { return _tokensOfOwner(owner); } function getRoyaltyData(uint256 _tokenId) public view returns (address artistAddress, address additionalPayee, uint256 additionalPayeePercentage, uint256 royaltyFeeByID) { artistAddress = projectIdToArtistAddress[tokenIdToProjectId[_tokenId]]; additionalPayee = projectIdToAdditionalPayee[tokenIdToProjectId[_tokenId]]; additionalPayeePercentage = projectIdToAdditionalPayeePercentage[tokenIdToProjectId[_tokenId]]; royaltyFeeByID = projectIdToSecondaryMarketRoyaltyPercentage[tokenIdToProjectId[_tokenId]]; } function tokenURI(uint256 _tokenId) external view onlyValidTokenId(_tokenId) returns (string memory) { if (bytes(staticIpfsImageLink[_tokenId]).length > 0) { return Strings.strConcat(projects[tokenIdToProjectId[_tokenId]].projectBaseIpfsURI, staticIpfsImageLink[_tokenId]); } if (!projects[tokenIdToProjectId[_tokenId]].dynamic && projects[tokenIdToProjectId[_tokenId]].useIpfs) { return Strings.strConcat(projects[tokenIdToProjectId[_tokenId]].projectBaseIpfsURI, projects[tokenIdToProjectId[_tokenId]].ipfsHash); } return Strings.strConcat(projects[tokenIdToProjectId[_tokenId]].projectBaseURI, Strings.uint2str(_tokenId)); } }