├── .env-sample ├── .gitignore ├── LICENSE ├── README.md ├── addresses └── rinkeby.json ├── buidler.config.js ├── contracts ├── Address.sol ├── Context.sol ├── ControllerBase.sol ├── Counter.sol ├── D2Token.sol ├── ERC165.sol ├── ERC20.sol ├── ERC20Burnable.sol ├── ERC721.sol ├── ERC721Holder.sol ├── ERC721Public.sol ├── EnumerableMap.sol ├── EnumerableSet.sol ├── IERC165.sol ├── IERC20.sol ├── IERC721.sol ├── IERC721Enumerable.sol ├── IERC721Metadata.sol ├── IERC721Receiver.sol ├── INFTX.sol ├── ITokenManager.sol ├── ITransparentUpgradeableProxy.sol ├── IXStore.sol ├── IXToken.sol ├── Initializable.sol ├── NFTX.sol ├── NFTXv2.sol ├── Ownable.sol ├── Pausable.sol ├── ProxyController.sol ├── ReentrancyGuard.sol ├── SafeERC20.sol ├── SafeMath.sol ├── Strings.sol ├── TimeDelay.sol ├── Timelocked.sol ├── TokenAppController.sol ├── UpgradeController.sol ├── XController.sol ├── XSale.sol ├── XStore.sol ├── XToken.sol └── utils │ └── console.sol ├── data └── punk │ ├── punkAttr4.json │ ├── punkAttr5.json │ └── punkZombie.json ├── package.json ├── scripts ├── deployCounter.js ├── deployTAC.js ├── deployToMainnet.js ├── rinkeby │ ├── createVaults.js │ ├── deploy.js │ └── setupPunkElig.js └── temp.js ├── test ├── _helpers.js ├── _runVaultTests.js └── main.js ├── utils └── expectRevert.js └── yarn.lock /.env-sample: -------------------------------------------------------------------------------- 1 | INFURA_PROJECT_ID=af4ce5d960654e26bfcb8a84e92101b8 2 | 3 | PRIVATE_KEY=privateKeyGoesHere -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bin 3 | .env 4 | **/.DS_Store 5 | yarn-error.log 6 | 7 | #Buidler files 8 | cache 9 | artifacts 10 | .openzeppelin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 NFTX-project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addresses/rinkeby.json: -------------------------------------------------------------------------------- 1 | { 2 | "xStore": "0xd2C04902346159308Ddd515ca6Ba14786017684d", 3 | "nftx": "0x4fB354674aD074f43735f9F035f2342e65736B9D", 4 | "proxyController": "0xD0fF011bBbb8e40818d0db8E6aCD159C32ffB20A", 5 | "dao": "0xeddb1b92b9ad55a5bb1dcc22c23e7839cd3dc99c", 6 | "cryptopunks": "0xcC495748Df37dCfb0C1041a6FDfA257D350aFD60", 7 | "cryptokitties": "0xc860383974FB0D4b1FCD988024c3632C23b506f7", 8 | "axies": "0xCf4CeD73Cdc2725cbE8DCd837CE2F46716D5b6D3", 9 | "avastars": "0xD8f9c5A7d228b99e7307FA37872A339359ED111B", 10 | "autoglyphs": "0x476895959C3E2a775a433B0DFA7744Ec9726b6bc", 11 | "joys": "0x50BBddbF12A97B2BfbCC7B728Eb08Dc40d123FAd" 12 | } 13 | -------------------------------------------------------------------------------- /buidler.config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | usePlugin("@nomiclabs/buidler-waffle"); 3 | usePlugin("@nomiclabs/buidler-web3"); 4 | usePlugin("@nomiclabs/buidler-ethers"); 5 | usePlugin("@openzeppelin/buidler-upgrades"); 6 | usePlugin("buidler-contract-sizer"); 7 | 8 | module.exports = { 9 | networks: { 10 | mainnet: { 11 | url: `https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`, 12 | accounts: [`${process.env.PRIVATE_KEY}`], 13 | }, 14 | goerli: { 15 | url: `https://goerli.infura.io/v3/${process.env.INFURA_PROJECT_ID}`, 16 | accounts: [`${process.env.PRIVATE_KEY}`], 17 | }, 18 | rinkeby: { 19 | url: `https://rinkeby.infura.io/v3/${process.env.INFURA_PROJECT_ID}`, 20 | accounts: [`${process.env.PRIVATE_KEY}`], 21 | gasPrice: 5000000000, 22 | timeout: 30000, 23 | }, 24 | }, 25 | solc: { 26 | version: "0.6.8", 27 | optimizer: { 28 | enabled: true, 29 | runs: 200, 30 | }, 31 | }, 32 | contractSizer: { 33 | alphaSort: true, 34 | runOnCompile: true, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /contracts/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies in extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | // solhint-disable-next-line no-inline-assembly 33 | assembly { 34 | size := extcodesize(account) 35 | } 36 | return size > 0; 37 | } 38 | 39 | /** 40 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 41 | * `recipient`, forwarding all available gas and reverting on errors. 42 | * 43 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 44 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 45 | * imposed by `transfer`, making them unable to receive vaults via 46 | * `transfer`. {sendValue} removes this limitation. 47 | * 48 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 49 | * 50 | * IMPORTANT: because control is transferred to `recipient`, care must be 51 | * taken to not create reentrancy vulnerabilities. Consider using 52 | * {ReentrancyGuard} or the 53 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 54 | */ 55 | function sendValue(address payable recipient, uint256 amount) internal { 56 | require( 57 | address(this).balance >= amount, 58 | "Address: insufficient balance" 59 | ); 60 | 61 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 62 | (bool success, ) = recipient.call{value: amount}(""); 63 | require( 64 | success, 65 | "Address: unable to send value, recipient may have reverted" 66 | ); 67 | } 68 | 69 | /** 70 | * @dev Performs a Solidity function call using a low level `call`. A 71 | * plain`call` is an unsafe replacement for a function call: use this 72 | * function instead. 73 | * 74 | * If `target` reverts with a revert reason, it is bubbled up by this 75 | * function (like regular Solidity function calls). 76 | * 77 | * Returns the raw returned data. To convert to the expected return value, 78 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 79 | * 80 | * Requirements: 81 | * 82 | * - `target` must be a contract. 83 | * - calling `target` with `data` must not revert. 84 | * 85 | * _Available since v3.1._ 86 | */ 87 | function functionCall(address target, bytes memory data) 88 | internal 89 | returns (bytes memory) 90 | { 91 | return functionCall(target, data, "Address: low-level call failed"); 92 | } 93 | 94 | /** 95 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 96 | * `errorMessage` as a fallback revert reason when `target` reverts. 97 | * 98 | * _Available since v3.1._ 99 | */ 100 | function functionCall( 101 | address target, 102 | bytes memory data, 103 | string memory errorMessage 104 | ) internal returns (bytes memory) { 105 | return _functionCallWithValue(target, data, 0, errorMessage); 106 | } 107 | 108 | /** 109 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 110 | * but also transferring `value` wei to `target`. 111 | * 112 | * Requirements: 113 | * 114 | * - the calling contract must have an ETH balance of at least `value`. 115 | * - the called Solidity function must be `payable`. 116 | * 117 | * _Available since v3.1._ 118 | */ 119 | function functionCallWithValue( 120 | address target, 121 | bytes memory data, 122 | uint256 value 123 | ) internal returns (bytes memory) { 124 | return 125 | functionCallWithValue( 126 | target, 127 | data, 128 | value, 129 | "Address: low-level call with value failed" 130 | ); 131 | } 132 | 133 | /** 134 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 135 | * with `errorMessage` as a fallback revert reason when `target` reverts. 136 | * 137 | * _Available since v3.1._ 138 | */ 139 | function functionCallWithValue( 140 | address target, 141 | bytes memory data, 142 | uint256 value, 143 | string memory errorMessage 144 | ) internal returns (bytes memory) { 145 | require( 146 | address(this).balance >= value, 147 | "Address: insufficient balance for call" 148 | ); 149 | return _functionCallWithValue(target, data, value, errorMessage); 150 | } 151 | 152 | function _functionCallWithValue( 153 | address target, 154 | bytes memory data, 155 | uint256 weiValue, 156 | string memory errorMessage 157 | ) private returns (bytes memory) { 158 | require(isContract(target), "Address: call to non-contract"); 159 | 160 | // solhint-disable-next-line avoid-low-level-calls 161 | (bool success, bytes memory returndata) = target.call{value: weiValue}( 162 | data 163 | ); 164 | if (success) { 165 | return returndata; 166 | } else { 167 | // Look for revert reason and bubble it up if present 168 | if (returndata.length > 0) { 169 | // The easiest way to bubble the revert reason is using memory via assembly 170 | 171 | // solhint-disable-next-line no-inline-assembly 172 | assembly { 173 | let returndata_size := mload(returndata) 174 | revert(add(32, returndata), returndata_size) 175 | } 176 | } else { 177 | revert(errorMessage); 178 | } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /contracts/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | /* 6 | * @dev Provides information about the current execution context, including the 7 | * sender of the transaction and its data. While these are generally available 8 | * via msg.sender and msg.data, they should not be accessed in such a direct 9 | * manner, since when dealing with GSN meta-transactions the account sending and 10 | * paying for execution may not be the actual sender (as far as an application 11 | * is concerned). 12 | * 13 | * This contract is only required for intermediate, library-like contracts. 14 | */ 15 | abstract contract Context { 16 | function _msgSender() internal view virtual returns (address payable) { 17 | return msg.sender; 18 | } 19 | 20 | function _msgData() internal view virtual returns (bytes memory) { 21 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 22 | return msg.data; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/ControllerBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Timelocked.sol"; 6 | import "./SafeMath.sol"; 7 | import "./Initializable.sol"; 8 | 9 | abstract contract ControllerBase is Timelocked { 10 | using SafeMath for uint256; 11 | 12 | address public leadDev; 13 | 14 | uint256 numFuncCalls; 15 | 16 | mapping(uint256 => uint256) public time; 17 | mapping(uint256 => uint256) public funcIndex; 18 | mapping(uint256 => address payable) public addressParam; 19 | mapping(uint256 => uint256[]) public uintArrayParam; 20 | 21 | function transferOwnership(address newOwner) public override virtual { 22 | uint256 fcId = numFuncCalls; 23 | numFuncCalls = numFuncCalls.add(1); 24 | time[fcId] = now; 25 | funcIndex[fcId] = 0; 26 | addressParam[fcId] = payable(newOwner); 27 | } 28 | 29 | function initialize() public initializer { 30 | initOwnable(); 31 | } 32 | 33 | function setLeadDev(address newLeadDev) public virtual onlyOwner { 34 | leadDev = newLeadDev; 35 | } 36 | 37 | function stageFuncCall( 38 | uint256 _funcIndex, 39 | address payable _addressParam, 40 | uint256[] memory _uintArrayParam 41 | ) public virtual onlyOwner { 42 | uint256 fcId = numFuncCalls; 43 | numFuncCalls = numFuncCalls.add(1); 44 | time[fcId] = now; 45 | funcIndex[fcId] = _funcIndex; 46 | addressParam[fcId] = _addressParam; 47 | uintArrayParam[fcId] = _uintArrayParam; 48 | } 49 | 50 | function cancelFuncCall(uint256 fcId) public virtual onlyOwner { 51 | funcIndex[fcId] = 0; 52 | } 53 | 54 | function executeFuncCall(uint256 fcId) public virtual { 55 | if (funcIndex[fcId] == 0) { 56 | return; 57 | } else if (funcIndex[fcId] == 1) { 58 | require( 59 | uintArrayParam[fcId][2] >= uintArrayParam[fcId][1] && 60 | uintArrayParam[fcId][1] >= uintArrayParam[fcId][0], 61 | "Invalid delays" 62 | ); 63 | if (uintArrayParam[fcId][2] != longDelay) { 64 | onlyIfPastDelay(2, time[fcId]); 65 | } else if (uintArrayParam[fcId][1] != mediumDelay) { 66 | onlyIfPastDelay(1, time[fcId]); 67 | } else { 68 | onlyIfPastDelay(0, time[fcId]); 69 | } 70 | setDelays( 71 | uintArrayParam[fcId][0], 72 | uintArrayParam[fcId][1], 73 | uintArrayParam[fcId][2] 74 | ); 75 | } else if (funcIndex[fcId] == 2) { 76 | onlyIfPastDelay(1, time[fcId]); 77 | Ownable.transferOwnership(addressParam[fcId]); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/Counter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | contract Counter { 6 | uint256 internal number; 7 | 8 | function getNumber() public view returns (uint256) { 9 | return number; 10 | } 11 | 12 | function increaseNumberBy(uint256 amount) public { 13 | number += amount; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /contracts/D2Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./Context.sol"; 7 | import "./ERC20.sol"; 8 | import "./ERC20Burnable.sol"; 9 | 10 | contract D2Token is Context, Ownable, ERC20Burnable { 11 | address private vaultAddress; 12 | 13 | constructor(string memory name, string memory symbol) 14 | public 15 | ERC20(name, symbol) 16 | { 17 | initOwnable(); 18 | _mint(msg.sender, 0); 19 | } 20 | 21 | function mint(address to, uint256 amount) public onlyOwner { 22 | _mint(to, amount); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/ERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "./IERC165.sol"; 6 | 7 | /** 8 | * @dev Implementation of the {IERC165} interface. 9 | * 10 | * Contracts may inherit from this and call {_registerInterface} to declare 11 | * their support of an interface. 12 | */ 13 | contract ERC165 is IERC165 { 14 | /* 15 | * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 16 | */ 17 | bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; 18 | 19 | /** 20 | * @dev Mapping of interface ids to whether or not it's supported. 21 | */ 22 | mapping(bytes4 => bool) private _supportedInterfaces; 23 | 24 | constructor () internal { 25 | // Derived contracts need only register support for their own interfaces, 26 | // we register support for ERC165 itself here 27 | _registerInterface(_INTERFACE_ID_ERC165); 28 | } 29 | 30 | /** 31 | * @dev See {IERC165-supportsInterface}. 32 | * 33 | * Time complexity O(1), guaranteed to always use less than 30 000 gas. 34 | */ 35 | function supportsInterface(bytes4 interfaceId) public view override returns (bool) { 36 | return _supportedInterfaces[interfaceId]; 37 | } 38 | 39 | /** 40 | * @dev Registers the contract as an implementer of the interface defined by 41 | * `interfaceId`. Support of the actual ERC165 interface is automatic and 42 | * registering its interface id is not required. 43 | * 44 | * See {IERC165-supportsInterface}. 45 | * 46 | * Requirements: 47 | * 48 | * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). 49 | */ 50 | function _registerInterface(bytes4 interfaceId) internal virtual { 51 | require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); 52 | _supportedInterfaces[interfaceId] = true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Context.sol"; 6 | import "./IERC20.sol"; 7 | import "./SafeMath.sol"; 8 | import "./Address.sol"; 9 | 10 | /** 11 | * @dev Implementation of the {IERC20} interface. 12 | * 13 | * This implementation is agnostic to the way tokens are created. This means 14 | * that a supply mechanism has to be added in a derived contract using {_mint}. 15 | * For a generic mechanism see {ERC20PresetMinterPauser}. 16 | * 17 | * TIP: For a detailed writeup see our guide 18 | * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How 19 | * to implement supply mechanisms]. 20 | * 21 | * We have followed general OpenZeppelin guidelines: functions revert instead 22 | * of returning `false` on failure. This behavior is nonetheless conventional 23 | * and does not conflict with the expectations of ERC20 applications. 24 | * 25 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 26 | * This allows applications to reconstruct the allowance for all accounts just 27 | * by listening to said events. Other implementations of the EIP may not emit 28 | * these events, as it isn't required by the specification. 29 | * 30 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 31 | * functions have been added to mitigate the well-known issues around setting 32 | * allowances. See {IERC20-approve}. 33 | */ 34 | contract ERC20 is Context, IERC20 { 35 | using SafeMath for uint256; 36 | using Address for address; 37 | 38 | mapping(address => uint256) private _balances; 39 | 40 | mapping(address => mapping(address => uint256)) private _allowances; 41 | 42 | uint256 private _totalSupply; 43 | 44 | string private _name; 45 | string private _symbol; 46 | uint8 private _decimals; 47 | 48 | /** 49 | * @dev Sets the values for {name} and {symbol}, initializes {decimals} with 50 | * a default value of 18. 51 | * 52 | * To select a different value for {decimals}, use {_setupDecimals}. 53 | * 54 | * All three of these values are immutable: they can only be set once during 55 | * construction. 56 | */ 57 | constructor(string memory name, string memory symbol) public { 58 | _name = name; 59 | _symbol = symbol; 60 | _decimals = 18; 61 | } 62 | 63 | /** 64 | * @dev Returns the name of the token. 65 | */ 66 | function name() public view returns (string memory) { 67 | return _name; 68 | } 69 | 70 | /** 71 | * @dev Returns the symbol of the token, usually a shorter version of the 72 | * name. 73 | */ 74 | function symbol() public view returns (string memory) { 75 | return _symbol; 76 | } 77 | 78 | /** 79 | * @dev Returns the number of decimals used to get its user representation. 80 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 81 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 82 | * 83 | * Tokens usually opt for a value of 18, imitating the relationship between 84 | * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is 85 | * called. 86 | * 87 | * NOTE: This information is only used for _display_ purposes: it in 88 | * no way affects any of the arithmetic of the contract, including 89 | * {IERC20-balanceOf} and {IERC20-transfer}. 90 | */ 91 | function decimals() public view returns (uint8) { 92 | return _decimals; 93 | } 94 | 95 | /** 96 | * @dev See {IERC20-totalSupply}. 97 | */ 98 | function totalSupply() public view override returns (uint256) { 99 | return _totalSupply; 100 | } 101 | 102 | /** 103 | * @dev See {IERC20-balanceOf}. 104 | */ 105 | function balanceOf(address account) public view override returns (uint256) { 106 | return _balances[account]; 107 | } 108 | 109 | /** 110 | * @dev See {IERC20-transfer}. 111 | * 112 | * Requirements: 113 | * 114 | * - `recipient` cannot be the zero address. 115 | * - the caller must have a balance of at least `amount`. 116 | */ 117 | function transfer(address recipient, uint256 amount) 118 | public 119 | virtual 120 | override 121 | returns (bool) 122 | { 123 | _transfer(_msgSender(), recipient, amount); 124 | return true; 125 | } 126 | 127 | /** 128 | * @dev See {IERC20-allowance}. 129 | */ 130 | function allowance(address owner, address spender) 131 | public 132 | view 133 | virtual 134 | override 135 | returns (uint256) 136 | { 137 | return _allowances[owner][spender]; 138 | } 139 | 140 | /** 141 | * @dev See {IERC20-approve}. 142 | * 143 | * Requirements: 144 | * 145 | * - `spender` cannot be the zero address. 146 | */ 147 | function approve(address spender, uint256 amount) 148 | public 149 | virtual 150 | override 151 | returns (bool) 152 | { 153 | _approve(_msgSender(), spender, amount); 154 | return true; 155 | } 156 | 157 | /** 158 | * @dev See {IERC20-transferFrom}. 159 | * 160 | * Emits an {Approval} event indicating the updated allowance. This is not 161 | * required by the EIP. See the note at the beginning of {ERC20}; 162 | * 163 | * Requirements: 164 | * - `sender` and `recipient` cannot be the zero address. 165 | * - `sender` must have a balance of at least `amount`. 166 | * - the caller must have allowance for ``sender``'s tokens of at least 167 | * `amount`. 168 | */ 169 | function transferFrom(address sender, address recipient, uint256 amount) 170 | public 171 | virtual 172 | override 173 | returns (bool) 174 | { 175 | _transfer(sender, recipient, amount); 176 | _approve( 177 | sender, 178 | _msgSender(), 179 | _allowances[sender][_msgSender()].sub( 180 | amount, 181 | "ERC20: transfer amount exceeds allowance" 182 | ) 183 | ); 184 | return true; 185 | } 186 | 187 | /** 188 | * @dev Atomically increases the allowance granted to `spender` by the caller. 189 | * 190 | * This is an alternative to {approve} that can be used as a mitigation for 191 | * problems described in {IERC20-approve}. 192 | * 193 | * Emits an {Approval} event indicating the updated allowance. 194 | * 195 | * Requirements: 196 | * 197 | * - `spender` cannot be the zero address. 198 | */ 199 | function increaseAllowance(address spender, uint256 addedValue) 200 | public 201 | virtual 202 | returns (bool) 203 | { 204 | _approve( 205 | _msgSender(), 206 | spender, 207 | _allowances[_msgSender()][spender].add(addedValue) 208 | ); 209 | return true; 210 | } 211 | 212 | /** 213 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 214 | * 215 | * This is an alternative to {approve} that can be used as a mitigation for 216 | * problems described in {IERC20-approve}. 217 | * 218 | * Emits an {Approval} event indicating the updated allowance. 219 | * 220 | * Requirements: 221 | * 222 | * - `spender` cannot be the zero address. 223 | * - `spender` must have allowance for the caller of at least 224 | * `subtractedValue`. 225 | */ 226 | function decreaseAllowance(address spender, uint256 subtractedValue) 227 | public 228 | virtual 229 | returns (bool) 230 | { 231 | _approve( 232 | _msgSender(), 233 | spender, 234 | _allowances[_msgSender()][spender].sub( 235 | subtractedValue, 236 | "ERC20: decreased allowance below zero" 237 | ) 238 | ); 239 | return true; 240 | } 241 | 242 | /** 243 | * @dev Moves tokens `amount` from `sender` to `recipient`. 244 | * 245 | * This is internal function is equivalent to {transfer}, and can be used to 246 | * e.g. implement automatic token fees, slashing mechanisms, etc. 247 | * 248 | * Emits a {Transfer} event. 249 | * 250 | * Requirements: 251 | * 252 | * - `sender` cannot be the zero address. 253 | * - `recipient` cannot be the zero address. 254 | * - `sender` must have a balance of at least `amount`. 255 | */ 256 | function _transfer(address sender, address recipient, uint256 amount) 257 | internal 258 | virtual 259 | { 260 | require(sender != address(0), "ERC20: transfer from the zero address"); 261 | require(recipient != address(0), "ERC20: transfer to the zero address"); 262 | 263 | _beforeTokenTransfer(sender, recipient, amount); 264 | 265 | _balances[sender] = _balances[sender].sub( 266 | amount, 267 | "ERC20: transfer amount exceeds balance" 268 | ); 269 | _balances[recipient] = _balances[recipient].add(amount); 270 | emit Transfer(sender, recipient, amount); 271 | } 272 | 273 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 274 | * the total supply. 275 | * 276 | * Emits a {Transfer} event with `from` set to the zero address. 277 | * 278 | * Requirements 279 | * 280 | * - `to` cannot be the zero address. 281 | */ 282 | function _mint(address account, uint256 amount) internal virtual { 283 | require(account != address(0), "ERC20: mint to the zero address"); 284 | 285 | _beforeTokenTransfer(address(0), account, amount); 286 | 287 | _totalSupply = _totalSupply.add(amount); 288 | _balances[account] = _balances[account].add(amount); 289 | emit Transfer(address(0), account, amount); 290 | } 291 | 292 | /** 293 | * @dev Destroys `amount` tokens from `account`, reducing the 294 | * total supply. 295 | * 296 | * Emits a {Transfer} event with `to` set to the zero address. 297 | * 298 | * Requirements 299 | * 300 | * - `account` cannot be the zero address. 301 | * - `account` must have at least `amount` tokens. 302 | */ 303 | function _burn(address account, uint256 amount) internal virtual { 304 | require(account != address(0), "ERC20: burn from the zero address"); 305 | 306 | _beforeTokenTransfer(account, address(0), amount); 307 | 308 | _balances[account] = _balances[account].sub( 309 | amount, 310 | "ERC20: burn amount exceeds balance" 311 | ); 312 | _totalSupply = _totalSupply.sub(amount); 313 | emit Transfer(account, address(0), amount); 314 | } 315 | 316 | /** 317 | * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. 318 | * 319 | * This internal function is equivalent to `approve`, and can be used to 320 | * e.g. set automatic allowances for certain subsystems, etc. 321 | * 322 | * Emits an {Approval} event. 323 | * 324 | * Requirements: 325 | * 326 | * - `owner` cannot be the zero address. 327 | * - `spender` cannot be the zero address. 328 | */ 329 | function _approve(address owner, address spender, uint256 amount) 330 | internal 331 | virtual 332 | { 333 | require(owner != address(0), "ERC20: approve from the zero address"); 334 | require(spender != address(0), "ERC20: approve to the zero address"); 335 | 336 | _allowances[owner][spender] = amount; 337 | emit Approval(owner, spender, amount); 338 | } 339 | 340 | /** 341 | * @dev Sets {decimals} to a value other than the default one of 18. 342 | * 343 | * WARNING: This function should only be called from the constructor. Most 344 | * applications that interact with token contracts will not expect 345 | * {decimals} to ever change, and may work incorrectly if it does. 346 | */ 347 | function _setupDecimals(uint8 decimals_) internal { 348 | _decimals = decimals_; 349 | } 350 | 351 | function _changeName(string memory name_) internal { 352 | _name = name_; 353 | } 354 | 355 | function _changeSymbol(string memory symbol_) internal { 356 | _symbol = symbol_; 357 | } 358 | 359 | /** 360 | * @dev Hook that is called before any transfer of tokens. This includes 361 | * minting and burning. 362 | * 363 | * Calling conditions: 364 | * 365 | * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens 366 | * will be to transferred to `to`. 367 | * - when `from` is zero, `amount` tokens will be minted for `to`. 368 | * - when `to` is zero, `amount` of ``from``'s tokens will be burned. 369 | * - `from` and `to` are never both zero. 370 | * 371 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 372 | */ 373 | function _beforeTokenTransfer(address from, address to, uint256 amount) 374 | internal 375 | virtual 376 | {} 377 | } 378 | -------------------------------------------------------------------------------- /contracts/ERC20Burnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Context.sol"; 6 | import "./ERC20.sol"; 7 | 8 | /** 9 | * @dev Extension of {ERC20} that allows token holders to destroy both their own 10 | * tokens and those that they have an allowance for, in a way that can be 11 | * recognized off-chain (via event analysis). 12 | */ 13 | abstract contract ERC20Burnable is Context, ERC20 { 14 | /** 15 | * @dev Destroys `amount` tokens from the caller. 16 | * 17 | * See {ERC20-_burn}. 18 | */ 19 | function burn(uint256 amount) public virtual { 20 | _burn(_msgSender(), amount); 21 | } 22 | 23 | /** 24 | * @dev Destroys `amount` tokens from `account`, deducting from the caller's 25 | * allowance. 26 | * 27 | * See {ERC20-_burn} and {ERC20-allowance}. 28 | * 29 | * Requirements: 30 | * 31 | * - the caller must have allowance for ``accounts``'s tokens of at least 32 | * `amount`. 33 | */ 34 | function burnFrom(address account, uint256 amount) public virtual { 35 | uint256 decreasedAllowance = allowance(account, _msgSender()).sub( 36 | amount, 37 | "ERC20: burn amount exceeds allowance" 38 | ); 39 | 40 | _approve(account, _msgSender(), decreasedAllowance); 41 | _burn(account, amount); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/ERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "./Context.sol"; 6 | import "./IERC721.sol"; 7 | import "./IERC721Metadata.sol"; 8 | import "./IERC721Enumerable.sol"; 9 | import "./IERC721Receiver.sol"; 10 | import "./ERC165.sol"; 11 | import "./SafeMath.sol"; 12 | import "./Address.sol"; 13 | import "./EnumerableSet.sol"; 14 | import "./EnumerableMap.sol"; 15 | import "./Strings.sol"; 16 | 17 | /** 18 | * @title ERC721 Non-Fungible Token Standard basic implementation 19 | * @dev see https://eips.ethereum.org/EIPS/eip-721 20 | */ 21 | contract ERC721 is 22 | Context, 23 | ERC165, 24 | IERC721, 25 | IERC721Metadata, 26 | IERC721Enumerable 27 | { 28 | using SafeMath for uint256; 29 | using Address for address; 30 | using EnumerableSet for EnumerableSet.UintSet; 31 | using EnumerableMap for EnumerableMap.UintToAddressMap; 32 | using Strings for uint256; 33 | 34 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 35 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 36 | bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; 37 | 38 | // Mapping from holder address to their (enumerable) set of owned tokens 39 | mapping(address => EnumerableSet.UintSet) private _holderTokens; 40 | 41 | // Enumerable mapping from token ids to their owners 42 | EnumerableMap.UintToAddressMap private _tokenOwners; 43 | 44 | // Mapping from token ID to approved address 45 | mapping(uint256 => address) private _tokenApprovals; 46 | 47 | // Mapping from owner to operator approvals 48 | mapping(address => mapping(address => bool)) private _operatorApprovals; 49 | 50 | // Token name 51 | string private _name; 52 | 53 | // Token symbol 54 | string private _symbol; 55 | 56 | // Optional mapping for token URIs 57 | mapping(uint256 => string) private _tokenURIs; 58 | 59 | // Base URI 60 | string private _baseURI; 61 | 62 | /* 63 | * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 64 | * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e 65 | * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 66 | * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc 67 | * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 68 | * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 69 | * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd 70 | * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e 71 | * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde 72 | * 73 | * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ 74 | * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd 75 | */ 76 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 77 | 78 | /* 79 | * bytes4(keccak256('name()')) == 0x06fdde03 80 | * bytes4(keccak256('symbol()')) == 0x95d89b41 81 | * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd 82 | * 83 | * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f 84 | */ 85 | bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; 86 | 87 | /* 88 | * bytes4(keccak256('totalSupply()')) == 0x18160ddd 89 | * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59 90 | * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7 91 | * 92 | * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63 93 | */ 94 | bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; 95 | 96 | /** 97 | * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. 98 | */ 99 | constructor(string memory name, string memory symbol) public { 100 | _name = name; 101 | _symbol = symbol; 102 | 103 | // register the supported interfaces to conform to ERC721 via ERC165 104 | _registerInterface(_INTERFACE_ID_ERC721); 105 | _registerInterface(_INTERFACE_ID_ERC721_METADATA); 106 | _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); 107 | } 108 | 109 | /** 110 | * @dev See {IERC721-balanceOf}. 111 | */ 112 | function balanceOf(address owner) public view override returns (uint256) { 113 | require( 114 | owner != address(0), 115 | "ERC721: balance query for the zero address" 116 | ); 117 | 118 | return _holderTokens[owner].length(); 119 | } 120 | 121 | /** 122 | * @dev See {IERC721-ownerOf}. 123 | */ 124 | function ownerOf(uint256 tokenId) public view override returns (address) { 125 | return 126 | _tokenOwners.get( 127 | tokenId, 128 | "ERC721: owner query for nonexistent token" 129 | ); 130 | } 131 | 132 | /** 133 | * @dev See {IERC721Metadata-name}. 134 | */ 135 | function name() public view override returns (string memory) { 136 | return _name; 137 | } 138 | 139 | /** 140 | * @dev See {IERC721Metadata-symbol}. 141 | */ 142 | function symbol() public view override returns (string memory) { 143 | return _symbol; 144 | } 145 | 146 | /** 147 | * @dev See {IERC721Metadata-tokenURI}. 148 | */ 149 | function tokenURI(uint256 tokenId) 150 | public 151 | view 152 | override 153 | returns (string memory) 154 | { 155 | require( 156 | _exists(tokenId), 157 | "ERC721Metadata: URI query for nonexistent token" 158 | ); 159 | 160 | string memory _tokenURI = _tokenURIs[tokenId]; 161 | 162 | // If there is no base URI, return the token URI. 163 | if (bytes(_baseURI).length == 0) { 164 | return _tokenURI; 165 | } 166 | // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). 167 | if (bytes(_tokenURI).length > 0) { 168 | return string(abi.encodePacked(_baseURI, _tokenURI)); 169 | } 170 | // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI. 171 | return string(abi.encodePacked(_baseURI, tokenId.toString())); 172 | } 173 | 174 | /** 175 | * @dev Returns the base URI set via {_setBaseURI}. This will be 176 | * automatically added as a prefix in {tokenURI} to each token's URI, or 177 | * to the token ID if no specific URI is set for that token ID. 178 | */ 179 | function baseURI() public view returns (string memory) { 180 | return _baseURI; 181 | } 182 | 183 | /** 184 | * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. 185 | */ 186 | function tokenOfOwnerByIndex(address owner, uint256 index) 187 | public 188 | view 189 | override 190 | returns (uint256) 191 | { 192 | return _holderTokens[owner].at(index); 193 | } 194 | 195 | /** 196 | * @dev See {IERC721Enumerable-totalSupply}. 197 | */ 198 | function totalSupply() public view override returns (uint256) { 199 | // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds 200 | return _tokenOwners.length(); 201 | } 202 | 203 | /** 204 | * @dev See {IERC721Enumerable-tokenByIndex}. 205 | */ 206 | function tokenByIndex(uint256 index) 207 | public 208 | view 209 | override 210 | returns (uint256) 211 | { 212 | (uint256 tokenId, ) = _tokenOwners.at(index); 213 | return tokenId; 214 | } 215 | 216 | /** 217 | * @dev See {IERC721-approve}. 218 | */ 219 | function approve(address to, uint256 tokenId) public virtual override { 220 | address owner = ownerOf(tokenId); 221 | require(to != owner, "ERC721: approval to current owner"); 222 | 223 | require( 224 | _msgSender() == owner || isApprovedForAll(owner, _msgSender()), 225 | "ERC721: approve caller is not owner nor approved for all" 226 | ); 227 | 228 | _approve(to, tokenId); 229 | } 230 | 231 | /** 232 | * @dev See {IERC721-getApproved}. 233 | */ 234 | function getApproved(uint256 tokenId) 235 | public 236 | view 237 | override 238 | returns (address) 239 | { 240 | require( 241 | _exists(tokenId), 242 | "ERC721: approved query for nonexistent token" 243 | ); 244 | 245 | return _tokenApprovals[tokenId]; 246 | } 247 | 248 | /** 249 | * @dev See {IERC721-setApprovalForAll}. 250 | */ 251 | function setApprovalForAll(address operator, bool approved) 252 | public 253 | virtual 254 | override 255 | { 256 | require(operator != _msgSender(), "ERC721: approve to caller"); 257 | 258 | _operatorApprovals[_msgSender()][operator] = approved; 259 | emit ApprovalForAll(_msgSender(), operator, approved); 260 | } 261 | 262 | /** 263 | * @dev See {IERC721-isApprovedForAll}. 264 | */ 265 | function isApprovedForAll(address owner, address operator) 266 | public 267 | view 268 | override 269 | returns (bool) 270 | { 271 | return _operatorApprovals[owner][operator]; 272 | } 273 | 274 | /** 275 | * @dev See {IERC721-transferFrom}. 276 | */ 277 | function transferFrom(address from, address to, uint256 tokenId) 278 | public 279 | virtual 280 | override 281 | { 282 | //solhint-disable-next-line max-line-length 283 | require( 284 | _isApprovedOrOwner(_msgSender(), tokenId), 285 | "ERC721: transfer caller is not owner nor approved" 286 | ); 287 | 288 | _transfer(from, to, tokenId); 289 | } 290 | 291 | /** 292 | * @dev See {IERC721-safeTransferFrom}. 293 | */ 294 | function safeTransferFrom(address from, address to, uint256 tokenId) 295 | public 296 | virtual 297 | override 298 | { 299 | safeTransferFrom(from, to, tokenId, ""); 300 | } 301 | 302 | /** 303 | * @dev See {IERC721-safeTransferFrom}. 304 | */ 305 | function safeTransferFrom( 306 | address from, 307 | address to, 308 | uint256 tokenId, 309 | bytes memory _data 310 | ) public virtual override { 311 | require( 312 | _isApprovedOrOwner(_msgSender(), tokenId), 313 | "ERC721: transfer caller is not owner nor approved" 314 | ); 315 | _safeTransfer(from, to, tokenId, _data); 316 | } 317 | 318 | /** 319 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 320 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 321 | * 322 | * `_data` is additional data, it has no specified format and it is sent in call to `to`. 323 | * 324 | * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. 325 | * implement alternative mechanisms to perform token transfer, such as signature-based. 326 | * 327 | * Requirements: 328 | * 329 | * - `from` cannot be the zero address. 330 | * - `to` cannot be the zero address. 331 | * - `tokenId` token must exist and be owned by `from`. 332 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 333 | * 334 | * Emits a {Transfer} event. 335 | */ 336 | function _safeTransfer( 337 | address from, 338 | address to, 339 | uint256 tokenId, 340 | bytes memory _data 341 | ) internal virtual { 342 | _transfer(from, to, tokenId); 343 | require( 344 | _checkOnERC721Received(from, to, tokenId, _data), 345 | "ERC721: transfer to non ERC721Receiver implementer" 346 | ); 347 | } 348 | 349 | /** 350 | * @dev Returns whether `tokenId` exists. 351 | * 352 | * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. 353 | * 354 | * Tokens start existing when they are minted (`_mint`), 355 | * and stop existing when they are burned (`_burn`). 356 | */ 357 | function _exists(uint256 tokenId) internal view returns (bool) { 358 | return _tokenOwners.contains(tokenId); 359 | } 360 | 361 | /** 362 | * @dev Returns whether `spender` is allowed to manage `tokenId`. 363 | * 364 | * Requirements: 365 | * 366 | * - `tokenId` must exist. 367 | */ 368 | function _isApprovedOrOwner(address spender, uint256 tokenId) 369 | internal 370 | view 371 | returns (bool) 372 | { 373 | require( 374 | _exists(tokenId), 375 | "ERC721: operator query for nonexistent token" 376 | ); 377 | address owner = ownerOf(tokenId); 378 | return (spender == owner || 379 | getApproved(tokenId) == spender || 380 | isApprovedForAll(owner, spender)); 381 | } 382 | 383 | /** 384 | * @dev Safely mints `tokenId` and transfers it to `to`. 385 | * 386 | * Requirements: 387 | d* 388 | * - `tokenId` must not exist. 389 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 390 | * 391 | * Emits a {Transfer} event. 392 | */ 393 | function _safeMint(address to, uint256 tokenId) internal virtual { 394 | _safeMint(to, tokenId, ""); 395 | } 396 | 397 | // For testing 398 | function safeMint(address to, uint256 tokenId) public virtual { 399 | _safeMint(to, tokenId, ""); 400 | } 401 | 402 | /** 403 | * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is 404 | * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. 405 | */ 406 | function _safeMint(address to, uint256 tokenId, bytes memory _data) 407 | internal 408 | virtual 409 | { 410 | _mint(to, tokenId); 411 | require( 412 | _checkOnERC721Received(address(0), to, tokenId, _data), 413 | "ERC721: transfer to non ERC721Receiver implementer" 414 | ); 415 | } 416 | 417 | /** 418 | * @dev Mints `tokenId` and transfers it to `to`. 419 | * 420 | * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible 421 | * 422 | * Requirements: 423 | * 424 | * - `tokenId` must not exist. 425 | * - `to` cannot be the zero address. 426 | * 427 | * Emits a {Transfer} event. 428 | */ 429 | function _mint(address to, uint256 tokenId) internal virtual { 430 | require(to != address(0), "ERC721: mint to the zero address"); 431 | require(!_exists(tokenId), "ERC721: token already minted"); 432 | 433 | _beforeTokenTransfer(address(0), to, tokenId); 434 | 435 | _holderTokens[to].add(tokenId); 436 | 437 | _tokenOwners.set(tokenId, to); 438 | 439 | emit Transfer(address(0), to, tokenId); 440 | } 441 | 442 | /** 443 | * @dev Destroys `tokenId`. 444 | * The approval is cleared when the token is burned. 445 | * 446 | * Requirements: 447 | * 448 | * - `tokenId` must exist. 449 | * 450 | * Emits a {Transfer} event. 451 | */ 452 | function _burn(uint256 tokenId) internal virtual { 453 | address owner = ownerOf(tokenId); 454 | 455 | _beforeTokenTransfer(owner, address(0), tokenId); 456 | 457 | // Clear approvals 458 | _approve(address(0), tokenId); 459 | 460 | // Clear metadata (if any) 461 | if (bytes(_tokenURIs[tokenId]).length != 0) { 462 | delete _tokenURIs[tokenId]; 463 | } 464 | 465 | _holderTokens[owner].remove(tokenId); 466 | 467 | _tokenOwners.remove(tokenId); 468 | 469 | emit Transfer(owner, address(0), tokenId); 470 | } 471 | 472 | /** 473 | * @dev Transfers `tokenId` from `from` to `to`. 474 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. 475 | * 476 | * Requirements: 477 | * 478 | * - `to` cannot be the zero address. 479 | * - `tokenId` token must be owned by `from`. 480 | * 481 | * Emits a {Transfer} event. 482 | */ 483 | function _transfer(address from, address to, uint256 tokenId) 484 | internal 485 | virtual 486 | { 487 | require( 488 | ownerOf(tokenId) == from, 489 | "ERC721: transfer of token that is not own" 490 | ); 491 | require(to != address(0), "ERC721: transfer to the zero address"); 492 | 493 | _beforeTokenTransfer(from, to, tokenId); 494 | 495 | // Clear approvals from the previous owner 496 | _approve(address(0), tokenId); 497 | 498 | _holderTokens[from].remove(tokenId); 499 | _holderTokens[to].add(tokenId); 500 | 501 | _tokenOwners.set(tokenId, to); 502 | 503 | emit Transfer(from, to, tokenId); 504 | } 505 | 506 | /** 507 | * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. 508 | * 509 | * Requirements: 510 | * 511 | * - `tokenId` must exist. 512 | */ 513 | function _setTokenURI(uint256 tokenId, string memory _tokenURI) 514 | internal 515 | virtual 516 | { 517 | require( 518 | _exists(tokenId), 519 | "ERC721Metadata: URI set of nonexistent token" 520 | ); 521 | _tokenURIs[tokenId] = _tokenURI; 522 | } 523 | 524 | /** 525 | * @dev Internal function to set the base URI for all token IDs. It is 526 | * automatically added as a prefix to the value returned in {tokenURI}, 527 | * or to the token ID if {tokenURI} is empty. 528 | */ 529 | function _setBaseURI(string memory baseURI_) internal virtual { 530 | _baseURI = baseURI_; 531 | } 532 | 533 | /** 534 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. 535 | * The call is not executed if the target address is not a contract. 536 | * 537 | * @param from address representing the previous owner of the given token ID 538 | * @param to target address that will receive the tokens 539 | * @param tokenId uint256 ID of the token to be transferred 540 | * @param _data bytes optional data to send along with the call 541 | * @return bool whether the call correctly returned the expected magic value 542 | */ 543 | function _checkOnERC721Received( 544 | address from, 545 | address to, 546 | uint256 tokenId, 547 | bytes memory _data 548 | ) private returns (bool) { 549 | if (!to.isContract()) { 550 | return true; 551 | } 552 | bytes memory returndata = to.functionCall( 553 | abi.encodeWithSelector( 554 | IERC721Receiver(to).onERC721Received.selector, 555 | _msgSender(), 556 | from, 557 | tokenId, 558 | _data 559 | ), 560 | "ERC721: transfer to non ERC721Receiver implementer" 561 | ); 562 | bytes4 retval = abi.decode(returndata, (bytes4)); 563 | return (retval == _ERC721_RECEIVED); 564 | } 565 | 566 | function _approve(address to, uint256 tokenId) private { 567 | _tokenApprovals[tokenId] = to; 568 | emit Approval(ownerOf(tokenId), to, tokenId); 569 | } 570 | 571 | /** 572 | * @dev Hook that is called before any token transfer. This includes minting 573 | * and burning. 574 | * 575 | * Calling conditions: 576 | * 577 | * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be 578 | * transferred to `to`. 579 | * - When `from` is zero, `tokenId` will be minted for `to`. 580 | * - When `to` is zero, ``from``'s `tokenId` will be burned. 581 | * - `from` cannot be the zero address. 582 | * - `to` cannot be the zero address. 583 | * 584 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 585 | */ 586 | function _beforeTokenTransfer(address from, address to, uint256 tokenId) 587 | internal 588 | virtual 589 | {} 590 | } 591 | -------------------------------------------------------------------------------- /contracts/ERC721Holder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "./IERC721Receiver.sol"; 6 | 7 | /** 8 | * @dev Implementation of the {IERC721Receiver} interface. 9 | * 10 | * Accepts all token transfers. 11 | * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. 12 | */ 13 | contract ERC721Holder is IERC721Receiver { 14 | /** 15 | * @dev See {IERC721Receiver-onERC721Received}. 16 | * 17 | * Always returns `IERC721Receiver.onERC721Received.selector`. 18 | */ 19 | function onERC721Received( 20 | address, 21 | address, 22 | uint256, 23 | bytes memory 24 | ) public virtual override returns (bytes4) { 25 | return this.onERC721Received.selector; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/ERC721Public.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Context.sol"; 6 | import "./ERC721.sol"; 7 | 8 | contract ERC721Public is Context, ERC721 { 9 | uint256 public minTokenId; 10 | uint256 public maxTokenId; 11 | 12 | constructor( 13 | string memory name, 14 | string memory symbol, 15 | uint256 _minTokenId, 16 | uint256 _maxTokenId 17 | ) public ERC721(name, symbol) { 18 | minTokenId = _minTokenId; 19 | maxTokenId = _maxTokenId; 20 | } 21 | 22 | function mint(uint256 tokenId, address recipient) public { 23 | require(tokenId >= minTokenId, "tokenId < minTokenId"); 24 | require(tokenId <= maxTokenId, "tokenId > maxTokenId"); 25 | _mint(recipient, tokenId); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /contracts/EnumerableMap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | /** 6 | * @dev Library for managing an enumerable variant of Solidity's 7 | * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] 8 | * type. 9 | * 10 | * Maps have the following properties: 11 | * 12 | * - Entries are added, removed, and checked for existence in constant time 13 | * (O(1)). 14 | * - Entries are enumerated in O(n). No guarantees are made on the ordering. 15 | * 16 | * ``` 17 | * contract Example { 18 | * // Add the library methods 19 | * using EnumerableMap for EnumerableMap.UintToAddressMap; 20 | * 21 | * // Declare a set state variable 22 | * EnumerableMap.UintToAddressMap private myMap; 23 | * } 24 | * ``` 25 | * 26 | * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are 27 | * supported. 28 | */ 29 | library EnumerableMap { 30 | // To implement this library for multiple types with as little code 31 | // repetition as possible, we write it in terms of a generic Map type with 32 | // bytes32 keys and values. 33 | // The Map implementation uses private functions, and user-facing 34 | // implementations (such as Uint256ToAddressMap) are just wrappers around 35 | // the underlying Map. 36 | // This means that we can only create new EnumerableMaps for types that fit 37 | // in bytes32. 38 | 39 | struct MapEntry { 40 | bytes32 _key; 41 | bytes32 _value; 42 | } 43 | 44 | struct Map { 45 | // Storage of map keys and values 46 | MapEntry[] _entries; 47 | 48 | // Position of the entry defined by a key in the `entries` array, plus 1 49 | // because index 0 means a key is not in the map. 50 | mapping (bytes32 => uint256) _indexes; 51 | } 52 | 53 | /** 54 | * @dev Adds a key-value pair to a map, or updates the value for an existing 55 | * key. O(1). 56 | * 57 | * Returns true if the key was added to the map, that is if it was not 58 | * already present. 59 | */ 60 | function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) { 61 | // We read and store the key's index to prevent multiple reads from the same storage slot 62 | uint256 keyIndex = map._indexes[key]; 63 | 64 | if (keyIndex == 0) { // Equivalent to !contains(map, key) 65 | map._entries.push(MapEntry({ _key: key, _value: value })); 66 | // The entry is stored at length-1, but we add 1 to all indexes 67 | // and use 0 as a sentinel value 68 | map._indexes[key] = map._entries.length; 69 | return true; 70 | } else { 71 | map._entries[keyIndex - 1]._value = value; 72 | return false; 73 | } 74 | } 75 | 76 | /** 77 | * @dev Removes a key-value pair from a map. O(1). 78 | * 79 | * Returns true if the key was removed from the map, that is if it was present. 80 | */ 81 | function _remove(Map storage map, bytes32 key) private returns (bool) { 82 | // We read and store the key's index to prevent multiple reads from the same storage slot 83 | uint256 keyIndex = map._indexes[key]; 84 | 85 | if (keyIndex != 0) { // Equivalent to contains(map, key) 86 | // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one 87 | // in the array, and then remove the last entry (sometimes called as 'swap and pop'). 88 | // This modifies the order of the array, as noted in {at}. 89 | 90 | uint256 toDeleteIndex = keyIndex - 1; 91 | uint256 lastIndex = map._entries.length - 1; 92 | 93 | // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs 94 | // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. 95 | 96 | MapEntry storage lastEntry = map._entries[lastIndex]; 97 | 98 | // Move the last entry to the index where the entry to delete is 99 | map._entries[toDeleteIndex] = lastEntry; 100 | // Update the index for the moved entry 101 | map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based 102 | 103 | // Delete the slot where the moved entry was stored 104 | map._entries.pop(); 105 | 106 | // Delete the index for the deleted slot 107 | delete map._indexes[key]; 108 | 109 | return true; 110 | } else { 111 | return false; 112 | } 113 | } 114 | 115 | /** 116 | * @dev Returns true if the key is in the map. O(1). 117 | */ 118 | function _contains(Map storage map, bytes32 key) private view returns (bool) { 119 | return map._indexes[key] != 0; 120 | } 121 | 122 | /** 123 | * @dev Returns the number of key-value pairs in the map. O(1). 124 | */ 125 | function _length(Map storage map) private view returns (uint256) { 126 | return map._entries.length; 127 | } 128 | 129 | /** 130 | * @dev Returns the key-value pair stored at position `index` in the map. O(1). 131 | * 132 | * Note that there are no guarantees on the ordering of entries inside the 133 | * array, and it may change when more entries are added or removed. 134 | * 135 | * Requirements: 136 | * 137 | * - `index` must be strictly less than {length}. 138 | */ 139 | function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { 140 | require(map._entries.length > index, "EnumerableMap: index out of bounds"); 141 | 142 | MapEntry storage entry = map._entries[index]; 143 | return (entry._key, entry._value); 144 | } 145 | 146 | /** 147 | * @dev Returns the value associated with `key`. O(1). 148 | * 149 | * Requirements: 150 | * 151 | * - `key` must be in the map. 152 | */ 153 | function _get(Map storage map, bytes32 key) private view returns (bytes32) { 154 | return _get(map, key, "EnumerableMap: nonexistent key"); 155 | } 156 | 157 | /** 158 | * @dev Same as {_get}, with a custom error message when `key` is not in the map. 159 | */ 160 | function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) { 161 | uint256 keyIndex = map._indexes[key]; 162 | require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key) 163 | return map._entries[keyIndex - 1]._value; // All indexes are 1-based 164 | } 165 | 166 | // UintToAddressMap 167 | 168 | struct UintToAddressMap { 169 | Map _inner; 170 | } 171 | 172 | /** 173 | * @dev Adds a key-value pair to a map, or updates the value for an existing 174 | * key. O(1). 175 | * 176 | * Returns true if the key was added to the map, that is if it was not 177 | * already present. 178 | */ 179 | function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) { 180 | return _set(map._inner, bytes32(key), bytes32(uint256(value))); 181 | } 182 | 183 | /** 184 | * @dev Removes a value from a set. O(1). 185 | * 186 | * Returns true if the key was removed from the map, that is if it was present. 187 | */ 188 | function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { 189 | return _remove(map._inner, bytes32(key)); 190 | } 191 | 192 | /** 193 | * @dev Returns true if the key is in the map. O(1). 194 | */ 195 | function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { 196 | return _contains(map._inner, bytes32(key)); 197 | } 198 | 199 | /** 200 | * @dev Returns the number of elements in the map. O(1). 201 | */ 202 | function length(UintToAddressMap storage map) internal view returns (uint256) { 203 | return _length(map._inner); 204 | } 205 | 206 | /** 207 | * @dev Returns the element stored at position `index` in the set. O(1). 208 | * Note that there are no guarantees on the ordering of values inside the 209 | * array, and it may change when more values are added or removed. 210 | * 211 | * Requirements: 212 | * 213 | * - `index` must be strictly less than {length}. 214 | */ 215 | function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { 216 | (bytes32 key, bytes32 value) = _at(map._inner, index); 217 | return (uint256(key), address(uint256(value))); 218 | } 219 | 220 | /** 221 | * @dev Returns the value associated with `key`. O(1). 222 | * 223 | * Requirements: 224 | * 225 | * - `key` must be in the map. 226 | */ 227 | function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { 228 | return address(uint256(_get(map._inner, bytes32(key)))); 229 | } 230 | 231 | /** 232 | * @dev Same as {get}, with a custom error message when `key` is not in the map. 233 | */ 234 | function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) { 235 | return address(uint256(_get(map._inner, bytes32(key), errorMessage))); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /contracts/EnumerableSet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | /** 6 | * @dev Library for managing 7 | * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive 8 | * types. 9 | * 10 | * Sets have the following properties: 11 | * 12 | * - Elements are added, removed, and checked for existence in constant time 13 | * (O(1)). 14 | * - Elements are enumerated in O(n). No guarantees are made on the ordering. 15 | * 16 | * ``` 17 | * contract Example { 18 | * // Add the library methods 19 | * using EnumerableSet for EnumerableSet.AddressSet; 20 | * 21 | * // Declare a set state variable 22 | * EnumerableSet.AddressSet private mySet; 23 | * } 24 | * ``` 25 | * 26 | * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256` 27 | * (`UintSet`) are supported. 28 | */ 29 | library EnumerableSet { 30 | // To implement this library for multiple types with as little code 31 | // repetition as possible, we write it in terms of a generic Set type with 32 | // bytes32 values. 33 | // The Set implementation uses private functions, and user-facing 34 | // implementations (such as AddressSet) are just wrappers around the 35 | // underlying Set. 36 | // This means that we can only create new EnumerableSets for types that fit 37 | // in bytes32. 38 | 39 | struct Set { 40 | // Storage of set values 41 | bytes32[] _values; 42 | // Position of the value in the `values` array, plus 1 because index 0 43 | // means a value is not in the set. 44 | mapping(bytes32 => uint256) _indexes; 45 | } 46 | 47 | /** 48 | * @dev Add a value to a set. O(1). 49 | * 50 | * Returns true if the value was added to the set, that is if it was not 51 | * already present. 52 | */ 53 | function _add(Set storage set, bytes32 value) private returns (bool) { 54 | if (!_contains(set, value)) { 55 | set._values.push(value); 56 | // The value is stored at length-1, but we add 1 to all indexes 57 | // and use 0 as a sentinel value 58 | set._indexes[value] = set._values.length; 59 | return true; 60 | } else { 61 | return false; 62 | } 63 | } 64 | 65 | /** 66 | * @dev Removes a value from a set. O(1). 67 | * 68 | * Returns true if the value was removed from the set, that is if it was 69 | * present. 70 | */ 71 | function _remove(Set storage set, bytes32 value) private returns (bool) { 72 | // We read and store the value's index to prevent multiple reads from the same storage slot 73 | uint256 valueIndex = set._indexes[value]; 74 | 75 | if (valueIndex != 0) { 76 | // Equivalent to contains(set, value) 77 | // To delete an element from the _values array in O(1), we swap the element to delete with the last one in 78 | // the array, and then remove the last element (sometimes called as 'swap and pop'). 79 | // This modifies the order of the array, as noted in {at}. 80 | 81 | uint256 toDeleteIndex = valueIndex - 1; 82 | uint256 lastIndex = set._values.length - 1; 83 | 84 | // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs 85 | // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. 86 | 87 | bytes32 lastvalue = set._values[lastIndex]; 88 | 89 | // Move the last value to the index where the value to delete is 90 | set._values[toDeleteIndex] = lastvalue; 91 | // Update the index for the moved value 92 | set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based 93 | 94 | // Delete the slot where the moved value was stored 95 | set._values.pop(); 96 | 97 | // Delete the index for the deleted slot 98 | delete set._indexes[value]; 99 | 100 | return true; 101 | } else { 102 | return false; 103 | } 104 | } 105 | 106 | /** 107 | * @dev Returns true if the value is in the set. O(1). 108 | */ 109 | function _contains(Set storage set, bytes32 value) 110 | private 111 | view 112 | returns (bool) 113 | { 114 | return set._indexes[value] != 0; 115 | } 116 | 117 | /** 118 | * @dev Returns the number of values on the set. O(1). 119 | */ 120 | function _length(Set storage set) private view returns (uint256) { 121 | return set._values.length; 122 | } 123 | 124 | /** 125 | * @dev Returns the value stored at position `index` in the set. O(1). 126 | * 127 | * Note that there are no guarantees on the ordering of values inside the 128 | * array, and it may change when more values are added or removed. 129 | * 130 | * Requirements: 131 | * 132 | * - `index` must be strictly less than {length}. 133 | */ 134 | function _at(Set storage set, uint256 index) 135 | private 136 | view 137 | returns (bytes32) 138 | { 139 | require( 140 | set._values.length > index, 141 | "EnumerableSet: index out of bounds" 142 | ); 143 | return set._values[index]; 144 | } 145 | 146 | // AddressSet 147 | 148 | struct AddressSet { 149 | Set _inner; 150 | } 151 | 152 | /** 153 | * @dev Add a value to a set. O(1). 154 | * 155 | * Returns true if the value was added to the set, that is if it was not 156 | * already present. 157 | */ 158 | function add(AddressSet storage set, address value) 159 | internal 160 | returns (bool) 161 | { 162 | return _add(set._inner, bytes32(uint256(value))); 163 | } 164 | 165 | /** 166 | * @dev Removes a value from a set. O(1). 167 | * 168 | * Returns true if the value was removed from the set, that is if it was 169 | * present. 170 | */ 171 | function remove(AddressSet storage set, address value) 172 | internal 173 | returns (bool) 174 | { 175 | return _remove(set._inner, bytes32(uint256(value))); 176 | } 177 | 178 | /** 179 | * @dev Returns true if the value is in the set. O(1). 180 | */ 181 | function contains(AddressSet storage set, address value) 182 | internal 183 | view 184 | returns (bool) 185 | { 186 | return _contains(set._inner, bytes32(uint256(value))); 187 | } 188 | 189 | /** 190 | * @dev Returns the number of values in the set. O(1). 191 | */ 192 | function length(AddressSet storage set) internal view returns (uint256) { 193 | return _length(set._inner); 194 | } 195 | 196 | /** 197 | * @dev Returns the value stored at position `index` in the set. O(1). 198 | * 199 | * Note that there are no guarantees on the ordering of values inside the 200 | * array, and it may change when more values are added or removed. 201 | * 202 | * Requirements: 203 | * 204 | * - `index` must be strictly less than {length}. 205 | */ 206 | function at(AddressSet storage set, uint256 index) 207 | internal 208 | view 209 | returns (address) 210 | { 211 | return address(uint256(_at(set._inner, index))); 212 | } 213 | 214 | // UintSet 215 | 216 | struct UintSet { 217 | Set _inner; 218 | } 219 | 220 | /** 221 | * @dev Add a value to a set. O(1). 222 | * 223 | * Returns true if the value was added to the set, that is if it was not 224 | * already present. 225 | */ 226 | function add(UintSet storage set, uint256 value) internal returns (bool) { 227 | return _add(set._inner, bytes32(value)); 228 | } 229 | 230 | /** 231 | * @dev Removes a value from a set. O(1). 232 | * 233 | * Returns true if the value was removed from the set, that is if it was 234 | * present. 235 | */ 236 | function remove(UintSet storage set, uint256 value) 237 | internal 238 | returns (bool) 239 | { 240 | return _remove(set._inner, bytes32(value)); 241 | } 242 | 243 | /** 244 | * @dev Returns true if the value is in the set. O(1). 245 | */ 246 | function contains(UintSet storage set, uint256 value) 247 | internal 248 | view 249 | returns (bool) 250 | { 251 | return _contains(set._inner, bytes32(value)); 252 | } 253 | 254 | /** 255 | * @dev Returns the number of values on the set. O(1). 256 | */ 257 | function length(UintSet storage set) internal view returns (uint256) { 258 | return _length(set._inner); 259 | } 260 | 261 | /** 262 | * @dev Returns the value stored at position `index` in the set. O(1). 263 | * 264 | * Note that there are no guarantees on the ordering of values inside the 265 | * array, and it may change when more values are added or removed. 266 | * 267 | * Requirements: 268 | * 269 | * - `index` must be strictly less than {length}. 270 | */ 271 | function at(UintSet storage set, uint256 index) 272 | internal 273 | view 274 | returns (uint256) 275 | { 276 | return uint256(_at(set._inner, index)); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /contracts/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | /** 6 | * @dev Interface of the ERC165 standard, as defined in the 7 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 8 | * 9 | * Implementers can declare support of contract interfaces, which can then be 10 | * queried by others ({ERC165Checker}). 11 | * 12 | * For an implementation, see {ERC165}. 13 | */ 14 | interface IERC165 { 15 | /** 16 | * @dev Returns true if this contract implements the interface defined by 17 | * `interfaceId`. See the corresponding 18 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 19 | * to learn more about how these ids are created. 20 | * 21 | * This function call must use less than 30 000 gas. 22 | */ 23 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) 27 | external 28 | returns (bool); 29 | 30 | /** 31 | * @dev Returns the remaining number of tokens that `spender` will be 32 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 33 | * zero by default. 34 | * 35 | * This value changes when {approve} or {transferFrom} are called. 36 | */ 37 | function allowance(address owner, address spender) 38 | external 39 | view 40 | returns (uint256); 41 | 42 | /** 43 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 44 | * 45 | * Returns a boolean value indicating whether the operation succeeded. 46 | * 47 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 48 | * that someone may use both the old and the new allowance by unfortunate 49 | * transaction ordering. One possible solution to mitigate this race 50 | * condition is to first reduce the spender's allowance to 0 and set the 51 | * desired value afterwards: 52 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 53 | * 54 | * Emits an {Approval} event. 55 | */ 56 | function approve(address spender, uint256 amount) external returns (bool); 57 | 58 | /** 59 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 60 | * allowance mechanism. `amount` is then deducted from the caller's 61 | * allowance. 62 | * 63 | * Returns a boolean value indicating whether the operation succeeded. 64 | * 65 | * Emits a {Transfer} event. 66 | */ 67 | function transferFrom(address sender, address recipient, uint256 amount) 68 | external 69 | returns (bool); 70 | 71 | /** 72 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 73 | * another (`to`). 74 | * 75 | * Note that `value` may be zero. 76 | */ 77 | event Transfer(address indexed from, address indexed to, uint256 value); 78 | 79 | /** 80 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 81 | * a call to {approve}. `value` is the new allowance. 82 | */ 83 | event Approval( 84 | address indexed owner, 85 | address indexed spender, 86 | uint256 value 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /contracts/IERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.2; 4 | 5 | import "./IERC165.sol"; 6 | 7 | /** 8 | * @dev Required interface of an ERC721 compliant contract. 9 | */ 10 | interface IERC721 is IERC165 { 11 | /** 12 | * @dev Emitted when `tokenId` token is transferred from `from` to `to`. 13 | */ 14 | event Transfer( 15 | address indexed from, 16 | address indexed to, 17 | uint256 indexed tokenId 18 | ); 19 | 20 | /** 21 | * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. 22 | */ 23 | event Approval( 24 | address indexed owner, 25 | address indexed approved, 26 | uint256 indexed tokenId 27 | ); 28 | 29 | /** 30 | * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. 31 | */ 32 | event ApprovalForAll( 33 | address indexed owner, 34 | address indexed operator, 35 | bool approved 36 | ); 37 | 38 | /** 39 | * @dev Returns the number of tokens in ``owner``'s account. 40 | */ 41 | function balanceOf(address owner) external view returns (uint256 balance); 42 | 43 | /** 44 | * @dev Returns the owner of the `tokenId` token. 45 | * 46 | * Requirements: 47 | * 48 | * - `tokenId` must exist. 49 | */ 50 | function ownerOf(uint256 tokenId) external view returns (address owner); 51 | 52 | /** 53 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 54 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 55 | * 56 | * Requirements: 57 | * 58 | * - `from` cannot be the zero address. 59 | * - `to` cannot be the zero address. 60 | * - `tokenId` token must exist and be owned by `from`. 61 | * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. 62 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 63 | * 64 | * Emits a {Transfer} event. 65 | */ 66 | function safeTransferFrom( 67 | address from, 68 | address to, 69 | uint256 tokenId 70 | ) external; 71 | 72 | /** 73 | * @dev Transfers `tokenId` token from `from` to `to`. 74 | * 75 | * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. 76 | * 77 | * Requirements: 78 | * 79 | * - `from` cannot be the zero address. 80 | * - `to` cannot be the zero address. 81 | * - `tokenId` token must be owned by `from`. 82 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. 83 | * 84 | * Emits a {Transfer} event. 85 | */ 86 | function transferFrom( 87 | address from, 88 | address to, 89 | uint256 tokenId 90 | ) external; 91 | 92 | /** 93 | * @dev Gives permission to `to` to transfer `tokenId` token to another account. 94 | * The approval is cleared when the token is transferred. 95 | * 96 | * Only a single account can be approved at a time, so approving the zero address clears previous approvals. 97 | * 98 | * Requirements: 99 | * 100 | * - The caller must own the token or be an approved operator. 101 | * - `tokenId` must exist. 102 | * 103 | * Emits an {Approval} event. 104 | */ 105 | function approve(address to, uint256 tokenId) external; 106 | 107 | /** 108 | * @dev Returns the account approved for `tokenId` token. 109 | * 110 | * Requirements: 111 | * 112 | * - `tokenId` must exist. 113 | */ 114 | function getApproved(uint256 tokenId) 115 | external 116 | view 117 | returns (address operator); 118 | 119 | /** 120 | * @dev Approve or remove `operator` as an operator for the caller. 121 | * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. 122 | * 123 | * Requirements: 124 | * 125 | * - The `operator` cannot be the caller. 126 | * 127 | * Emits an {ApprovalForAll} event. 128 | */ 129 | function setApprovalForAll(address operator, bool _approved) external; 130 | 131 | /** 132 | * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. 133 | * 134 | * See {setApprovalForAll} 135 | */ 136 | function isApprovedForAll(address owner, address operator) 137 | external 138 | view 139 | returns (bool); 140 | 141 | /** 142 | * @dev Safely transfers `tokenId` token from `from` to `to`. 143 | * 144 | * Requirements: 145 | * 146 | * - `from` cannot be the zero address. 147 | * - `to` cannot be the zero address. 148 | * - `tokenId` token must exist and be owned by `from`. 149 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. 150 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 151 | * 152 | * Emits a {Transfer} event. 153 | */ 154 | function safeTransferFrom( 155 | address from, 156 | address to, 157 | uint256 tokenId, 158 | bytes calldata data 159 | ) external; 160 | } 161 | -------------------------------------------------------------------------------- /contracts/IERC721Enumerable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.2; 4 | 5 | import "./IERC721.sol"; 6 | 7 | /** 8 | * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension 9 | * @dev See https://eips.ethereum.org/EIPS/eip-721 10 | */ 11 | interface IERC721Enumerable is IERC721 { 12 | 13 | /** 14 | * @dev Returns the total amount of tokens stored by the contract. 15 | */ 16 | function totalSupply() external view returns (uint256); 17 | 18 | /** 19 | * @dev Returns a token ID owned by `owner` at a given `index` of its token list. 20 | * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. 21 | */ 22 | function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); 23 | 24 | /** 25 | * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. 26 | * Use along with {totalSupply} to enumerate all tokens. 27 | */ 28 | function tokenByIndex(uint256 index) external view returns (uint256); 29 | } 30 | -------------------------------------------------------------------------------- /contracts/IERC721Metadata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.2; 4 | 5 | import "./IERC721.sol"; 6 | 7 | /** 8 | * @title ERC-721 Non-Fungible Token Standard, optional metadata extension 9 | * @dev See https://eips.ethereum.org/EIPS/eip-721 10 | */ 11 | interface IERC721Metadata is IERC721 { 12 | 13 | /** 14 | * @dev Returns the token collection name. 15 | */ 16 | function name() external view returns (string memory); 17 | 18 | /** 19 | * @dev Returns the token collection symbol. 20 | */ 21 | function symbol() external view returns (string memory); 22 | 23 | /** 24 | * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. 25 | */ 26 | function tokenURI(uint256 tokenId) external view returns (string memory); 27 | } 28 | -------------------------------------------------------------------------------- /contracts/IERC721Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | /** 6 | * @title ERC721 token receiver interface 7 | * @dev Interface for any contract that wants to support safeTransfers 8 | * from ERC721 asset contracts. 9 | */ 10 | interface IERC721Receiver { 11 | /** 12 | * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} 13 | * by `operator` from `from`, this function is called. 14 | * 15 | * It must return its Solidity selector to confirm the token transfer. 16 | * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. 17 | * 18 | * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. 19 | */ 20 | function onERC721Received( 21 | address operator, 22 | address from, 23 | uint256 tokenId, 24 | bytes calldata data 25 | ) external returns (bytes4); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/INFTX.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Pausable.sol"; 6 | import "./IXToken.sol"; 7 | import "./IERC721.sol"; 8 | import "./EnumerableSet.sol"; 9 | import "./ReentrancyGuard.sol"; 10 | import "./SafeMath.sol"; 11 | 12 | interface INFTX { 13 | event NFTsDeposited(uint256 vaultId, uint256[] nftIds, address from); 14 | event NFTsRedeemed(uint256 vaultId, uint256[] nftIds, address to); 15 | event TokensMinted(uint256 vaultId, uint256 amount, address to); 16 | event TokensBurned(uint256 vaultId, uint256 amount, address from); 17 | 18 | event EligibilitySet(uint256 vaultId, uint256[] nftIds, bool _boolean); 19 | event ReservesIncreased(uint256 vaultId, uint256 nftId); 20 | event ReservesDecreased(uint256 vaultId, uint256 nftId); 21 | 22 | function store() external returns (address); 23 | 24 | function transferOwnership(address newOwner) external; 25 | 26 | function vaultSize(uint256 vaultId) external view returns (uint256); 27 | 28 | function isEligible(uint256 vaultId, uint256 nftId) 29 | external 30 | view 31 | returns (bool); 32 | 33 | function createVault(address _erc20Address, address _nftAddress) 34 | external 35 | returns (uint256); 36 | 37 | function depositETH(uint256 vaultId) external payable; 38 | 39 | function setIsEligible( 40 | uint256 vaultId, 41 | uint256[] calldata nftIds, 42 | bool _boolean 43 | ) external; 44 | 45 | function setNegateEligibility(uint256 vaultId, bool shouldNegate) external; 46 | 47 | function setShouldReserve( 48 | uint256 vaultId, 49 | uint256[] calldata nftIds, 50 | bool _boolean 51 | ) external; 52 | 53 | function setIsReserved( 54 | uint256 vaultId, 55 | uint256[] calldata nftIds, 56 | bool _boolean 57 | ) external; 58 | 59 | function setExtension(address contractAddress, bool _boolean) external; 60 | 61 | function directRedeem(uint256 vaultId, uint256[] calldata nftIds) 62 | external 63 | payable; 64 | 65 | function mint(uint256 vaultId, uint256[] calldata nftIds, uint256 d2Amount) 66 | external 67 | payable; 68 | 69 | function redeem(uint256 vaultId, uint256 numNFTs) external payable; 70 | 71 | function mintAndRedeem(uint256 vaultId, uint256[] calldata nftIds) 72 | external 73 | payable; 74 | 75 | function changeTokenName(uint256 vaultId, string calldata newName) external; 76 | 77 | function changeTokenSymbol(uint256 vaultId, string calldata newSymbol) 78 | external; 79 | 80 | function setManager(uint256 vaultId, address newManager) external; 81 | 82 | function finalizeVault(uint256 vaultId) external; 83 | 84 | function closeVault(uint256 vaultId) external; 85 | 86 | function setMintFees(uint256 vaultId, uint256 _ethBase, uint256 _ethStep) 87 | external; 88 | 89 | function setBurnFees(uint256 vaultId, uint256 _ethBase, uint256 _ethStep) 90 | external; 91 | 92 | function setDualFees(uint256 vaultId, uint256 _ethBase, uint256 _ethStep) 93 | external; 94 | 95 | function setSupplierBounty(uint256 vaultId, uint256 ethMax, uint256 length) 96 | external; 97 | } 98 | -------------------------------------------------------------------------------- /contracts/ITokenManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | interface ITokenManager { 6 | function mint(address _receiver, uint256 _amount) external; 7 | function issue(uint256 _amount) external; 8 | function assign(address _receiver, uint256 _amount) external; 9 | function burn(address _holder, uint256 _amount) external; 10 | function assignVested( 11 | address _receiver, 12 | uint256 _amount, 13 | uint64 _start, 14 | uint64 _cliff, 15 | uint64 _vested, 16 | bool _revokable 17 | ) external returns (uint256); 18 | function revokeVesting(address _holder, uint256 _vestingId) external; 19 | } 20 | -------------------------------------------------------------------------------- /contracts/ITransparentUpgradeableProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface ITransparentUpgradeableProxy { 6 | function admin() external returns (address); 7 | 8 | function implementation() external returns (address); 9 | 10 | function changeAdmin(address newAdmin) external; 11 | 12 | function upgradeTo(address newImplementation) external; 13 | 14 | function upgradeToAndCall(address newImplementation, bytes calldata data) 15 | external 16 | payable; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/IXStore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./EnumerableSet.sol"; 6 | import "./Ownable.sol"; 7 | import "./SafeMath.sol"; 8 | import "./IXToken.sol"; 9 | import "./IERC721.sol"; 10 | import "./EnumerableSet.sol"; 11 | 12 | interface IXStore { 13 | struct FeeParams { 14 | uint256 ethBase; 15 | uint256 ethStep; 16 | } 17 | 18 | struct BountyParams { 19 | uint256 ethMax; 20 | uint256 length; 21 | } 22 | 23 | struct Vault { 24 | address xTokenAddress; 25 | address assetAddress; 26 | address manager; 27 | IXToken xToken; 28 | IERC721 nft; 29 | EnumerableSet.UintSet holdings; 30 | EnumerableSet.UintSet reserves; 31 | mapping(uint256 => address) requester; 32 | mapping(uint256 => bool) isEligible; 33 | mapping(uint256 => bool) shouldReserve; 34 | bool flipEligOnRedeem; 35 | bool negateEligibility; 36 | bool isFinalized; 37 | bool isClosed; 38 | FeeParams mintFees; 39 | FeeParams burnFees; 40 | FeeParams dualFees; 41 | BountyParams supplierBounty; 42 | uint256 ethBalance; 43 | uint256 tokenBalance; 44 | bool isD2Vault; 45 | address d2AssetAddress; 46 | IERC20 d2Asset; 47 | uint256 d2Holdings; 48 | } 49 | 50 | function isExtension(address addr) external view returns (bool); 51 | 52 | function randNonce() external view returns (uint256); 53 | 54 | function vaultsLength() external view returns (uint256); 55 | 56 | function xTokenAddress(uint256 vaultId) external view returns (address); 57 | 58 | function assetAddress(uint256 vaultId) external view returns (address); 59 | 60 | function manager(uint256 vaultId) external view returns (address); 61 | 62 | function xToken(uint256 vaultId) external view returns (IXToken); 63 | 64 | function nft(uint256 vaultId) external view returns (IERC721); 65 | 66 | function holdingsLength(uint256 vaultId) external view returns (uint256); 67 | 68 | function holdingsContains(uint256 vaultId, uint256 elem) 69 | external 70 | view 71 | returns (bool); 72 | 73 | function holdingsAt(uint256 vaultId, uint256 index) 74 | external 75 | view 76 | returns (uint256); 77 | 78 | function reservesLength(uint256 vaultId) external view returns (uint256); 79 | 80 | function reservesContains(uint256 vaultId, uint256 elem) 81 | external 82 | view 83 | returns (bool); 84 | 85 | function reservesAt(uint256 vaultId, uint256 index) 86 | external 87 | view 88 | returns (uint256); 89 | 90 | function requester(uint256 vaultId, uint256 id) 91 | external 92 | view 93 | returns (address); 94 | 95 | function isEligible(uint256 vaultId, uint256 id) 96 | external 97 | view 98 | returns (bool); 99 | 100 | function shouldReserve(uint256 vaultId, uint256 id) 101 | external 102 | view 103 | returns (bool); 104 | 105 | function flipEligOnRedeem(uint256 vaultId) external view returns (bool); 106 | 107 | function negateEligibility(uint256 vaultId) external view returns (bool); 108 | 109 | function isFinalized(uint256 vaultId) external view returns (bool); 110 | 111 | function isClosed(uint256 vaultId) external view returns (bool); 112 | 113 | function mintFees(uint256 vaultId) external view returns (uint256, uint256); 114 | 115 | function burnFees(uint256 vaultId) external view returns (uint256, uint256); 116 | 117 | function dualFees(uint256 vaultId) external view returns (uint256, uint256); 118 | 119 | function supplierBounty(uint256 vaultId) 120 | external 121 | view 122 | returns (uint256, uint256); 123 | 124 | function ethBalance(uint256 vaultId) external view returns (uint256); 125 | 126 | function tokenBalance(uint256 vaultId) external view returns (uint256); 127 | 128 | function isD2Vault(uint256 vaultId) external view returns (bool); 129 | 130 | function d2AssetAddress(uint256 vaultId) external view returns (address); 131 | 132 | function d2Asset(uint256 vaultId) external view returns (IERC20); 133 | 134 | function d2Holdings(uint256 vaultId) external view returns (uint256); 135 | 136 | function setXTokenAddress(uint256 vaultId, address _xTokenAddress) external; 137 | 138 | function setNftAddress(uint256 vaultId, address _assetAddress) external; 139 | 140 | function setManager(uint256 vaultId, address _manager) external; 141 | 142 | function setXToken(uint256 vaultId) external; 143 | 144 | function setNft(uint256 vaultId) external; 145 | 146 | function holdingsAdd(uint256 vaultId, uint256 elem) external; 147 | 148 | function holdingsRemove(uint256 vaultId, uint256 elem) external; 149 | 150 | function reservesAdd(uint256 vaultId, uint256 elem) external; 151 | 152 | function reservesRemove(uint256 vaultId, uint256 elem) external; 153 | 154 | function setRequester(uint256 vaultId, uint256 id, address _requester) 155 | external; 156 | 157 | function setIsEligible(uint256 vaultId, uint256 id, bool _bool) external; 158 | 159 | function setShouldReserve(uint256 vaultId, uint256 id, bool _shouldReserve) 160 | external; 161 | 162 | function setFlipEligOnRedeem(uint256 vaultId, bool flipElig) external; 163 | 164 | function setNegateEligibility(uint256 vaultId, bool negateElig) external; 165 | 166 | function setIsFinalized(uint256 vaultId, bool _isFinalized) external; 167 | 168 | function setIsClosed(uint256 vaultId, bool _isClosed) external; 169 | 170 | function setMintFees(uint256 vaultId, uint256 ethBase, uint256 ethStep) 171 | external; 172 | 173 | function setBurnFees(uint256 vaultId, uint256 ethBase, uint256 ethStep) 174 | external; 175 | 176 | function setDualFees(uint256 vaultId, uint256 ethBase, uint256 ethStep) 177 | external; 178 | 179 | function setSupplierBounty(uint256 vaultId, uint256 ethMax, uint256 length) 180 | external; 181 | 182 | function setEthBalance(uint256 vaultId, uint256 _ethBalance) external; 183 | 184 | function setTokenBalance(uint256 vaultId, uint256 _tokenBalance) external; 185 | 186 | function setIsD2Vault(uint256 vaultId, bool _isD2Vault) external; 187 | 188 | function setD2AssetAddress(uint256 vaultId, address _assetAddress) external; 189 | 190 | function setD2Asset(uint256 vaultId) external; 191 | 192 | function setD2Holdings(uint256 vaultId, uint256 _d2Holdings) external; 193 | 194 | //////////////////////////////////////////////////////////// 195 | 196 | function setIsExtension(address addr, bool _isExtension) external; 197 | 198 | function setRandNonce(uint256 _randNonce) external; 199 | 200 | function addNewVault() external returns (uint256); 201 | } 202 | -------------------------------------------------------------------------------- /contracts/IXToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./IERC20.sol"; 6 | 7 | interface IXToken is IERC20 { 8 | function owner() external returns (address); 9 | 10 | function burn(uint256 amount) external; 11 | 12 | function burnFrom(address account, uint256 amount) external; 13 | 14 | function mint(address to, uint256 amount) external; 15 | 16 | function changeName(string calldata name) external; 17 | 18 | function changeSymbol(string calldata symbol) external; 19 | 20 | function setVaultAddress(address vaultAddress) external; 21 | 22 | function transferOwnership(address newOwner) external; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.4.24 <0.7.0; 4 | 5 | /** 6 | * @title Initializable 7 | * 8 | * @dev Helper contract to support initializer functions. To use it, replace 9 | * the constructor with a function that has the `initializer` modifier. 10 | * WARNING: Unlike constructors, initializer functions must be manually 11 | * invoked. This applies both to deploying an Initializable contract, as well 12 | * as extending an Initializable contract via inheritance. 13 | * WARNING: When used with inheritance, manual care must be taken to not invoke 14 | * a parent initializer twice, or ensure that all initializers are idempotent, 15 | * because this is not dealt with automatically as with constructors. 16 | */ 17 | contract Initializable { 18 | /** 19 | * @dev Indicates that the contract has been initialized. 20 | */ 21 | bool private initialized; 22 | 23 | /** 24 | * @dev Indicates that the contract is in the process of being initialized. 25 | */ 26 | bool private initializing; 27 | 28 | /** 29 | * @dev Modifier to use in the initializer function of a contract. 30 | */ 31 | modifier initializer() { 32 | require( 33 | initializing || isConstructor() || !initialized, 34 | "Contract instance has already been initialized" 35 | ); 36 | 37 | bool isTopLevelCall = !initializing; 38 | if (isTopLevelCall) { 39 | initializing = true; 40 | initialized = true; 41 | } 42 | 43 | _; 44 | 45 | if (isTopLevelCall) { 46 | initializing = false; 47 | } 48 | } 49 | 50 | /// @dev Returns true if and only if the function is running in the constructor 51 | function isConstructor() private view returns (bool) { 52 | // extcodesize checks the size of the code stored in an address, and 53 | // address returns the current address. Since the code is still not 54 | // deployed when running a constructor, any checks on its code size will 55 | // yield zero, making it an effective way to detect if a contract is 56 | // under construction or not. 57 | address self = address(this); 58 | uint256 cs; 59 | assembly { 60 | cs := extcodesize(self) 61 | } 62 | return cs == 0; 63 | } 64 | 65 | // Reserved storage space to allow for layout changes in the future. 66 | uint256[50] private ______gap; 67 | } 68 | -------------------------------------------------------------------------------- /contracts/NFTXv2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./NFTX.sol"; 6 | 7 | contract NFTXv2 is NFTX { 8 | function transferERC721(uint256 vaultId, uint256 tokenId, address to) 9 | public 10 | virtual 11 | onlyOwner 12 | { 13 | store.nft(vaultId).transferFrom(address(this), to, tokenId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Context.sol"; 6 | import "./Initializable.sol"; 7 | 8 | /** 9 | * @dev Contract module which provides a basic access control mechanism, where 10 | * there is an account (an owner) that can be granted exclusive access to 11 | * specific functions. 12 | * 13 | * By default, the owner account will be the one that deploys the contract. This 14 | * can later be changed with {transferOwnership}. 15 | * 16 | * This module is used through inheritance. It will make available the modifier 17 | * `onlyOwner`, which can be applied to your functions to restrict their use to 18 | * the owner. 19 | */ 20 | contract Ownable is Context, Initializable { 21 | address private _owner; 22 | 23 | event OwnershipTransferred( 24 | address indexed previousOwner, 25 | address indexed newOwner 26 | ); 27 | 28 | /** 29 | * @dev Initializes the contract setting the deployer as the initial owner. 30 | */ 31 | function initOwnable() internal virtual initializer { 32 | address msgSender = _msgSender(); 33 | _owner = msgSender; 34 | emit OwnershipTransferred(address(0), msgSender); 35 | } 36 | 37 | /** 38 | * @dev Returns the address of the current owner. 39 | */ 40 | function owner() public view returns (address) { 41 | return _owner; 42 | } 43 | 44 | /** 45 | * @dev Throws if called by any account other than the owner. 46 | */ 47 | modifier onlyOwner() { 48 | require(_owner == _msgSender(), "Ownable: caller is not the owner"); 49 | _; 50 | } 51 | 52 | /** 53 | * @dev Leaves the contract without owner. It will not be possible to call 54 | * `onlyOwner` functions anymore. Can only be called by the current owner. 55 | * 56 | * NOTE: Renouncing ownership will leave the contract without an owner, 57 | * thereby removing any functionality that is only available to the owner. 58 | */ 59 | function renounceOwnership() public virtual onlyOwner { 60 | emit OwnershipTransferred(_owner, address(0)); 61 | _owner = address(0); 62 | } 63 | 64 | /** 65 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 66 | * Can only be called by the current owner. 67 | */ 68 | function transferOwnership(address newOwner) public virtual onlyOwner { 69 | require( 70 | newOwner != address(0), 71 | "Ownable: new owner is the zero address" 72 | ); 73 | emit OwnershipTransferred(_owner, newOwner); 74 | _owner = newOwner; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /contracts/Pausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./SafeMath.sol"; 7 | 8 | contract Pausable is Ownable { 9 | mapping(uint256 => bool) isPaused; 10 | // 0 : createVault 11 | // 1 : mint 12 | // 2 : redeem 13 | // 3 : mintAndRedeem 14 | 15 | function onlyOwnerIfPaused(uint256 pauserId) public view virtual { 16 | require(!isPaused[pauserId] || msg.sender == owner(), "Paused"); 17 | } 18 | 19 | function setPaused(uint256 pauserId, bool _isPaused) 20 | public 21 | virtual 22 | onlyOwner 23 | { 24 | isPaused[pauserId] = _isPaused; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/ProxyController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./SafeMath.sol"; 7 | import "./ITransparentUpgradeableProxy.sol"; 8 | 9 | contract ProxyController is Ownable { 10 | using SafeMath for uint256; 11 | 12 | ITransparentUpgradeableProxy private nftxProxy; 13 | address public implAddress; 14 | 15 | constructor(address nftx) public { 16 | initOwnable(); 17 | nftxProxy = ITransparentUpgradeableProxy(nftx); 18 | } 19 | 20 | function getAdmin() public returns (address) { 21 | return nftxProxy.admin(); 22 | } 23 | 24 | function fetchImplAddress() public { 25 | implAddress = nftxProxy.implementation(); 26 | } 27 | 28 | function changeProxyAdmin(address newAdmin) public onlyOwner { 29 | nftxProxy.changeAdmin(newAdmin); 30 | } 31 | 32 | function upgradeProxyTo(address newImpl) public onlyOwner { 33 | nftxProxy.upgradeTo(newImpl); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Initializable.sol"; 6 | 7 | /** 8 | * @dev Contract module that helps prevent reentrant calls to a function. 9 | * 10 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 11 | * available, which can be applied to functions to make sure there are no nested 12 | * (reentrant) calls to them. 13 | * 14 | * Note that because there is a single `nonReentrant` guard, functions marked as 15 | * `nonReentrant` may not call one another. This can be worked around by making 16 | * those functions `private`, and then adding `external` `nonReentrant` entry 17 | * points to them. 18 | * 19 | * TIP: If you would like to learn more about reentrancy and alternative ways 20 | * to protect against it, check out our blog post 21 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 22 | */ 23 | contract ReentrancyGuard is Initializable { 24 | // Booleans are more expensive than uint256 or any type that takes up a full 25 | // word because each write operation emits an extra SLOAD to first read the 26 | // slot's contents, replace the bits taken up by the boolean, and then write 27 | // back. This is the compiler's defense against contract upgrades and 28 | // pointer aliasing, and it cannot be disabled. 29 | 30 | // The values being non-zero value makes deployment a bit more expensive, 31 | // but in exchange the revault on every call to nonReentrant will be lower in 32 | // amount. Since revaults are capped to a percentage of the total 33 | // transaction's gas, it is best to keep them low in cases like this one, to 34 | // increase the likelihood of the full revault coming into effect. 35 | uint256 private constant _NOT_ENTERED = 1; 36 | uint256 private constant _ENTERED = 2; 37 | 38 | uint256 private _status; 39 | 40 | function initReentrancyGuard() internal { 41 | _status = _NOT_ENTERED; 42 | } 43 | 44 | /** 45 | * @dev Prevents a contract from calling itself, directly or indirectly. 46 | * Calling a `nonReentrant` function from another `nonReentrant` 47 | * function is not supported. It is possible to prevent this from happening 48 | * by making the `nonReentrant` function external, and make it call a 49 | * `private` function that does the actual work. 50 | */ 51 | modifier nonReentrant() { 52 | // On the first call to nonReentrant, _notEntered will be true 53 | require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); 54 | 55 | // Any calls to nonReentrant after this point will fail 56 | _status = _ENTERED; 57 | 58 | _; 59 | 60 | // By storing the original value once again, a revault is triggered (see 61 | // https://eips.ethereum.org/EIPS/eip-2200) 62 | _status = _NOT_ENTERED; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/SafeERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./IERC20.sol"; 6 | import "./SafeMath.sol"; 7 | import "./Address.sol"; 8 | 9 | /** 10 | * @title SafeERC20 11 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 12 | * contract returns false). Tokens that return no value (and instead revert or 13 | * throw on failure) are also supported, non-reverting calls are assumed to be 14 | * successful. 15 | * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, 16 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 17 | */ 18 | library SafeERC20 { 19 | using SafeMath for uint256; 20 | using Address for address; 21 | 22 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 23 | _callOptionalReturn( 24 | token, 25 | abi.encodeWithSelector(token.transfer.selector, to, value) 26 | ); 27 | } 28 | 29 | function safeTransferFrom( 30 | IERC20 token, 31 | address from, 32 | address to, 33 | uint256 value 34 | ) internal { 35 | _callOptionalReturn( 36 | token, 37 | abi.encodeWithSelector(token.transferFrom.selector, from, to, value) 38 | ); 39 | } 40 | 41 | /** 42 | * @dev Deprecated. This function has issues similar to the ones found in 43 | * {IERC20-approve}, and its usage is discouraged. 44 | * 45 | * Whenever possible, use {safeIncreaseAllowance} and 46 | * {safeDecreaseAllowance} instead. 47 | */ 48 | function safeApprove(IERC20 token, address spender, uint256 value) 49 | internal 50 | { 51 | // safeApprove should only be called when setting an initial allowance, 52 | // or when resetting it to zero. To increase and decrease it, use 53 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 54 | // solhint-disable-next-line max-line-length 55 | require( 56 | (value == 0) || (token.allowance(address(this), spender) == 0), 57 | "SafeERC20: approve from non-zero to non-zero allowance" 58 | ); 59 | _callOptionalReturn( 60 | token, 61 | abi.encodeWithSelector(token.approve.selector, spender, value) 62 | ); 63 | } 64 | 65 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) 66 | internal 67 | { 68 | uint256 newAllowance = token.allowance(address(this), spender).add( 69 | value 70 | ); 71 | _callOptionalReturn( 72 | token, 73 | abi.encodeWithSelector( 74 | token.approve.selector, 75 | spender, 76 | newAllowance 77 | ) 78 | ); 79 | } 80 | 81 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) 82 | internal 83 | { 84 | uint256 newAllowance = token.allowance(address(this), spender).sub( 85 | value, 86 | "SafeERC20: decreased allowance below zero" 87 | ); 88 | _callOptionalReturn( 89 | token, 90 | abi.encodeWithSelector( 91 | token.approve.selector, 92 | spender, 93 | newAllowance 94 | ) 95 | ); 96 | } 97 | 98 | /** 99 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 100 | * on the return value: the return value is optional (but if data is returned, it must not be false). 101 | * @param token The token targeted by the call. 102 | * @param data The call data (encoded using abi.encode or one of its variants). 103 | */ 104 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 105 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 106 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that 107 | // the target address contains contract code and also asserts for success in the low-level call. 108 | 109 | bytes memory returndata = address(token).functionCall( 110 | data, 111 | "SafeERC20: low-level call failed" 112 | ); 113 | if (returndata.length > 0) { 114 | // Return data is optional 115 | // solhint-disable-next-line max-line-length 116 | require( 117 | abi.decode(returndata, (bool)), 118 | "SafeERC20: ERC20 operation did not succeed" 119 | ); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | /** 6 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 7 | * checks. 8 | * 9 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 10 | * in bugs, because programmers usually assume that an overflow raises an 11 | * error, which is the standard behavior in high level programming languages. 12 | * `SafeMath` restores this intuition by reverting the transaction when an 13 | * operation overflows. 14 | * 15 | * Using this library instead of the unchecked operations eliminates an entire 16 | * class of bugs, so it's recommended to use it always. 17 | */ 18 | library SafeMath { 19 | /** 20 | * @dev Returns the addition of two unsigned integers, reverting on 21 | * overflow. 22 | * 23 | * Counterpart to Solidity's `+` operator. 24 | * 25 | * Requirements: 26 | * 27 | * - Addition cannot overflow. 28 | */ 29 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 30 | uint256 c = a + b; 31 | require(c >= a, "SafeMath: addition overflow"); 32 | 33 | return c; 34 | } 35 | 36 | /** 37 | * @dev Returns the subtraction of two unsigned integers, reverting on 38 | * overflow (when the result is negative). 39 | * 40 | * Counterpart to Solidity's `-` operator. 41 | * 42 | * Requirements: 43 | * 44 | * - Subtraction cannot overflow. 45 | */ 46 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 47 | return sub(a, b, "SafeMath: subtraction overflow"); 48 | } 49 | 50 | /** 51 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 52 | * overflow (when the result is negative). 53 | * 54 | * Counterpart to Solidity's `-` operator. 55 | * 56 | * Requirements: 57 | * 58 | * - Subtraction cannot overflow. 59 | */ 60 | function sub(uint256 a, uint256 b, string memory errorMessage) 61 | internal 62 | pure 63 | returns (uint256) 64 | { 65 | require(b <= a, errorMessage); 66 | uint256 c = a - b; 67 | 68 | return c; 69 | } 70 | 71 | /** 72 | * @dev Returns the multiplication of two unsigned integers, reverting on 73 | * overflow. 74 | * 75 | * Counterpart to Solidity's `*` operator. 76 | * 77 | * Requirements: 78 | * 79 | * - Multiplication cannot overflow. 80 | */ 81 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 82 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 83 | // benefit is lost if 'b' is also tested. 84 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 85 | if (a == 0) { 86 | return 0; 87 | } 88 | 89 | uint256 c = a * b; 90 | require(c / a == b, "SafeMath: multiplication overflow"); 91 | 92 | return c; 93 | } 94 | 95 | /** 96 | * @dev Returns the integer division of two unsigned integers. Reverts on 97 | * division by zero. The result is rounded towards zero. 98 | * 99 | * Counterpart to Solidity's `/` operator. Note: this function uses a 100 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 101 | * uses an invalid opcode to revert (consuming all remaining gas). 102 | * 103 | * Requirements: 104 | * 105 | * - The divisor cannot be zero. 106 | */ 107 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 108 | return div(a, b, "SafeMath: division by zero"); 109 | } 110 | 111 | /** 112 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 113 | * division by zero. The result is rounded towards zero. 114 | * 115 | * Counterpart to Solidity's `/` operator. Note: this function uses a 116 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 117 | * uses an invalid opcode to revert (consuming all remaining gas). 118 | * 119 | * Requirements: 120 | * 121 | * - The divisor cannot be zero. 122 | */ 123 | function div(uint256 a, uint256 b, string memory errorMessage) 124 | internal 125 | pure 126 | returns (uint256) 127 | { 128 | require(b > 0, errorMessage); 129 | uint256 c = a / b; 130 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 131 | 132 | return c; 133 | } 134 | 135 | /** 136 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 137 | * Reverts when dividing by zero. 138 | * 139 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 140 | * opcode (which leaves remaining gas untouched) while Solidity uses an 141 | * invalid opcode to revert (consuming all remaining gas). 142 | * 143 | * Requirements: 144 | * 145 | * - The divisor cannot be zero. 146 | */ 147 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 148 | return mod(a, b, "SafeMath: modulo by zero"); 149 | } 150 | 151 | /** 152 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 153 | * Reverts with custom message when dividing by zero. 154 | * 155 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 156 | * opcode (which leaves remaining gas untouched) while Solidity uses an 157 | * invalid opcode to revert (consuming all remaining gas). 158 | * 159 | * Requirements: 160 | * 161 | * - The divisor cannot be zero. 162 | */ 163 | function mod(uint256 a, uint256 b, string memory errorMessage) 164 | internal 165 | pure 166 | returns (uint256) 167 | { 168 | require(b != 0, errorMessage); 169 | return a % b; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /contracts/Strings.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | /** 6 | * @dev String operations. 7 | */ 8 | library Strings { 9 | /** 10 | * @dev Converts a `uint256` to its ASCII `string` representation. 11 | */ 12 | function toString(uint256 value) internal pure returns (string memory) { 13 | // Inspired by OraclizeAPI's implementation - MIT licence 14 | // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol 15 | 16 | if (value == 0) { 17 | return "0"; 18 | } 19 | uint256 temp = value; 20 | uint256 digits; 21 | while (temp != 0) { 22 | digits++; 23 | temp /= 10; 24 | } 25 | bytes memory buffer = new bytes(digits); 26 | uint256 index = digits - 1; 27 | temp = value; 28 | while (temp != 0) { 29 | buffer[index--] = byte(uint8(48 + temp % 10)); 30 | temp /= 10; 31 | } 32 | return string(buffer); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/TimeDelay.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./SafeMath.sol"; 7 | 8 | contract TimeDelay is Ownable { 9 | using SafeMath for uint256; 10 | 11 | uint256 public shortDelay; 12 | uint256 public mediumDelay; 13 | uint256 public longDelay; 14 | 15 | function setDelays( 16 | uint256 _shortDelay, 17 | uint256 _mediumDelay, 18 | uint256 _longDelay 19 | ) internal virtual { 20 | shortDelay = _shortDelay; 21 | mediumDelay = _mediumDelay; 22 | longDelay = _longDelay; 23 | } 24 | 25 | function timeInDays(uint256 num) internal pure returns (uint256) { 26 | return num * 60 * 60 * 24; 27 | } 28 | 29 | function getDelay(uint256 delayIndex) public view returns (uint256) { 30 | if (delayIndex == 0) { 31 | return shortDelay; 32 | } else if (delayIndex == 1) { 33 | return mediumDelay; 34 | } else if (delayIndex == 2) { 35 | return longDelay; 36 | } 37 | } 38 | 39 | function onlyIfPastDelay(uint256 delayIndex, uint256 startTime) 40 | internal 41 | view 42 | { 43 | require(1 >= startTime.add(getDelay(delayIndex)), "Delay not over"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/Timelocked.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./SafeMath.sol"; 7 | 8 | contract Timelocked is Ownable { 9 | using SafeMath for uint256; 10 | 11 | uint256 public shortDelay; 12 | uint256 public mediumDelay; 13 | uint256 public longDelay; 14 | 15 | function setDelays( 16 | uint256 _shortDelay, 17 | uint256 _mediumDelay, 18 | uint256 _longDelay 19 | ) internal virtual { 20 | shortDelay = _shortDelay; 21 | mediumDelay = _mediumDelay; 22 | longDelay = _longDelay; 23 | } 24 | 25 | function timeInDays(uint256 num) internal pure returns (uint256) { 26 | return num * 60 * 60 * 24; 27 | } 28 | 29 | function getDelay(uint256 delayIndex) public view returns (uint256) { 30 | if (delayIndex == 0) { 31 | return shortDelay; 32 | } else if (delayIndex == 1) { 33 | return mediumDelay; 34 | } else if (delayIndex == 2) { 35 | return longDelay; 36 | } 37 | } 38 | 39 | function onlyIfPastDelay(uint256 delayIndex, uint256 startTime) 40 | internal 41 | view 42 | { 43 | require(1 >= startTime.add(getDelay(delayIndex)), "Delay not over"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/TokenAppController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./ITokenManager.sol"; 7 | 8 | contract TokenAppController is Ownable { 9 | ITokenManager public tokenManager; 10 | 11 | uint256 public num; 12 | 13 | constructor() public { 14 | initOwnable(); 15 | } 16 | 17 | function addXToNum(uint256 x) public returns (uint256) { 18 | num = num + x; 19 | return num; 20 | } 21 | 22 | function setTokenManager(address tokenManagerAddress) public onlyOwner { 23 | tokenManager = ITokenManager(tokenManagerAddress); 24 | } 25 | 26 | function callMint(address _receiver, uint256 _amount) public onlyOwner { 27 | tokenManager.mint(_receiver, _amount); 28 | } 29 | 30 | function callIssue(uint256 _amount) public onlyOwner { 31 | tokenManager.issue(_amount); 32 | } 33 | 34 | function callAssign(address _receiver, uint256 _amount) public onlyOwner { 35 | tokenManager.assign(_receiver, _amount); 36 | } 37 | 38 | function callBurn(address _holder, uint256 _amount) public onlyOwner { 39 | tokenManager.burn(_holder, _amount); 40 | } 41 | 42 | function callAssignVested( 43 | address _receiver, 44 | uint256 _amount, 45 | uint64 _start, 46 | uint64 _cliff, 47 | uint64 _vested, 48 | bool _revokable 49 | ) public returns (uint256) { 50 | return 51 | tokenManager.assignVested( 52 | _receiver, 53 | _amount, 54 | _start, 55 | _cliff, 56 | _vested, 57 | _revokable 58 | ); 59 | } 60 | 61 | function callRevokeVesting(address _holder, uint256 _vestingId) 62 | public 63 | onlyOwner 64 | { 65 | tokenManager.revokeVesting(_holder, _vestingId); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /contracts/UpgradeController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./ITransparentUpgradeableProxy.sol"; 6 | import "./ControllerBase.sol"; 7 | 8 | contract UpgradeController is ControllerBase { 9 | using SafeMath for uint256; 10 | 11 | 12 | ITransparentUpgradeableProxy private nftxProxy; 13 | ITransparentUpgradeableProxy private xControllerProxy; 14 | 15 | constructor(address nftx, address xController) public { 16 | ControllerBase.initialize(); 17 | nftxProxy = ITransparentUpgradeableProxy(nftx); 18 | xControllerProxy = ITransparentUpgradeableProxy(xController); 19 | } 20 | 21 | function executeFuncCall(uint256 fcId) public override onlyOwner { 22 | super.executeFuncCall(fcId); 23 | if (funcIndex[fcId] == 3) { 24 | nftxProxy.changeAdmin(addressParam[fcId]); 25 | } else if (funcIndex[fcId] == 4) { 26 | nftxProxy.upgradeTo(addressParam[fcId]); 27 | } else if (funcIndex[fcId] == 5) { 28 | xControllerProxy.changeAdmin(addressParam[fcId]); 29 | } else if (funcIndex[fcId] == 6) { 30 | xControllerProxy.upgradeTo(addressParam[fcId]); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /contracts/XController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./ControllerBase.sol"; 6 | import "./INFTX.sol"; 7 | import "./IXStore.sol"; 8 | import "./Initializable.sol"; 9 | 10 | contract XController is ControllerBase { 11 | INFTX private nftx; 12 | IXStore store; 13 | 14 | /* uint256 numFuncCalls; 15 | 16 | mapping(uint256 => uint256) public time; 17 | mapping(uint256 => uint256) public funcIndex; 18 | mapping(uint256 => address payable) public addressParam; 19 | mapping(uint256 => uint256[]) public uintArrayParam; */ 20 | mapping(uint256 => uint256) public uintParam; 21 | mapping(uint256 => string) public stringParam; 22 | mapping(uint256 => bool) public boolParam; 23 | 24 | mapping(uint256 => uint256) public pendingEligAdditions; 25 | 26 | function initXController(address nftxAddress) public initializer { 27 | initOwnable(); 28 | nftx = INFTX(nftxAddress); 29 | } 30 | 31 | function onlyOwnerOrLeadDev(uint256 funcIndex) public view virtual { 32 | if (funcIndex > 3) { 33 | require( 34 | _msgSender() == leadDev || _msgSender() == owner(), 35 | "Not owner or leadDev" 36 | ); 37 | } else { 38 | require(_msgSender() == owner(), "Not owner"); 39 | } 40 | } 41 | 42 | function stageFuncCall( 43 | uint256 _funcIndex, 44 | address payable _addressParam, 45 | uint256 _uintParam, 46 | string memory _stringParam, 47 | uint256[] memory _uintArrayParam, 48 | bool _boolParam 49 | ) public virtual { 50 | onlyOwnerOrLeadDev(_funcIndex); 51 | uint256 fcId = numFuncCalls; 52 | numFuncCalls = numFuncCalls.add(1); 53 | time[fcId] = 1; 54 | funcIndex[fcId] = _funcIndex; 55 | addressParam[fcId] = _addressParam; 56 | uintParam[fcId] = _uintParam; 57 | stringParam[fcId] = _stringParam; 58 | uintArrayParam[fcId] = _uintArrayParam; 59 | boolParam[fcId] = _boolParam; 60 | if ( 61 | funcIndex[fcId] == 4 && 62 | store.negateEligibility(uintParam[fcId]) != !boolParam[fcId] 63 | ) { 64 | pendingEligAdditions[uintParam[fcId]] = pendingEligAdditions[uintParam[fcId]] 65 | .add(uintArrayParam[fcId].length); 66 | } 67 | } 68 | 69 | function cancelFuncCall(uint256 fcId) public override virtual { 70 | onlyOwnerOrLeadDev(funcIndex[fcId]); 71 | require(funcIndex[fcId] != 0, "Already cancelled"); 72 | funcIndex[fcId] = 0; 73 | if ( 74 | funcIndex[fcId] == 3 && 75 | store.negateEligibility(uintParam[fcId]) != !boolParam[fcId] 76 | ) { 77 | pendingEligAdditions[uintParam[fcId]] = pendingEligAdditions[uintParam[fcId]] 78 | .sub(uintArrayParam[fcId].length); 79 | } 80 | } 81 | 82 | function executeFuncCall(uint256 fcId) public override virtual { 83 | super.executeFuncCall(fcId); 84 | if (funcIndex[fcId] == 3) { 85 | onlyIfPastDelay(2, time[fcId]); 86 | nftx.transferOwnership(addressParam[fcId]); 87 | } else if (funcIndex[fcId] == 4) { 88 | uint256 percentInc = pendingEligAdditions[uintParam[fcId]] 89 | .mul(100) 90 | .div(nftx.vaultSize(uintParam[fcId])); 91 | if (percentInc > 10) { 92 | onlyIfPastDelay(2, time[fcId]); 93 | } else if (percentInc > 1) { 94 | onlyIfPastDelay(1, time[fcId]); 95 | } else { 96 | onlyIfPastDelay(0, time[fcId]); 97 | } 98 | nftx.setIsEligible( 99 | uintParam[fcId], 100 | uintArrayParam[fcId], 101 | boolParam[fcId] 102 | ); 103 | pendingEligAdditions[uintParam[fcId]] = pendingEligAdditions[uintParam[fcId]] 104 | .sub(uintArrayParam[fcId].length); 105 | } else if (funcIndex[fcId] == 5) { 106 | onlyIfPastDelay(0, time[fcId]); // vault must be empty 107 | nftx.setNegateEligibility(funcIndex[fcId], boolParam[fcId]); 108 | } else if (funcIndex[fcId] == 6) { 109 | onlyIfPastDelay(0, time[fcId]); 110 | nftx.setShouldReserve( 111 | uintParam[fcId], 112 | uintArrayParam[fcId], 113 | boolParam[fcId] 114 | ); 115 | } else if (funcIndex[fcId] == 7) { 116 | onlyIfPastDelay(0, time[fcId]); 117 | nftx.setIsReserved( 118 | uintParam[fcId], 119 | uintArrayParam[fcId], 120 | boolParam[fcId] 121 | ); 122 | } else if (funcIndex[fcId] == 8) { 123 | onlyIfPastDelay(1, time[fcId]); 124 | nftx.changeTokenName(uintParam[fcId], stringParam[fcId]); 125 | } else if (funcIndex[fcId] == 9) { 126 | onlyIfPastDelay(1, time[fcId]); 127 | nftx.changeTokenSymbol(uintParam[fcId], stringParam[fcId]); 128 | } else if (funcIndex[fcId] == 10) { 129 | onlyIfPastDelay(0, time[fcId]); 130 | nftx.closeVault(uintParam[fcId]); 131 | } else if (funcIndex[fcId] == 11) { 132 | onlyIfPastDelay(0, time[fcId]); 133 | nftx.setMintFees( 134 | uintArrayParam[fcId][0], 135 | uintArrayParam[fcId][1], 136 | uintArrayParam[fcId][2] 137 | ); 138 | } else if (funcIndex[fcId] == 12) { 139 | (uint256 ethBase, uint256 ethStep) = store.burnFees( 140 | uintArrayParam[fcId][0] 141 | ); 142 | uint256 ethBasePercentInc = uintArrayParam[fcId][1].mul(100).div( 143 | ethBase 144 | ); 145 | uint256 ethStepPercentInc = uintArrayParam[fcId][2].mul(100).div( 146 | ethStep 147 | ); 148 | if (ethBasePercentInc.add(ethStepPercentInc) > 15) { 149 | onlyIfPastDelay(2, time[fcId]); 150 | } else if (ethBasePercentInc.add(ethStepPercentInc) > 5) { 151 | onlyIfPastDelay(1, time[fcId]); 152 | } else { 153 | onlyIfPastDelay(0, time[fcId]); 154 | } 155 | nftx.setBurnFees( 156 | uintArrayParam[fcId][0], 157 | uintArrayParam[fcId][1], 158 | uintArrayParam[fcId][2] 159 | ); 160 | } else if (funcIndex[fcId] == 13) { 161 | onlyIfPastDelay(0, time[fcId]); 162 | nftx.setDualFees( 163 | uintArrayParam[fcId][0], 164 | uintArrayParam[fcId][1], 165 | uintArrayParam[fcId][2] 166 | ); 167 | } else if (funcIndex[fcId] == 14) { 168 | (uint256 ethMax, uint256 length) = store.supplierBounty( 169 | uintArrayParam[fcId][0] 170 | ); 171 | uint256 ethMaxPercentInc = uintArrayParam[fcId][1].mul(100).div( 172 | ethMax 173 | ); 174 | uint256 lengthPercentInc = uintArrayParam[fcId][2].mul(100).div( 175 | length 176 | ); 177 | if (ethMaxPercentInc.add(lengthPercentInc) > 20) { 178 | onlyIfPastDelay(2, time[fcId]); 179 | } else if (ethMaxPercentInc.add(lengthPercentInc) > 5) { 180 | onlyIfPastDelay(1, time[fcId]); 181 | } else { 182 | onlyIfPastDelay(0, time[fcId]); 183 | } 184 | nftx.setSupplierBounty( 185 | uintArrayParam[fcId][0], 186 | uintArrayParam[fcId][1], 187 | uintArrayParam[fcId][2] 188 | ); 189 | } 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /contracts/XSale.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./SafeMath.sol"; 6 | import "./Pausable.sol"; 7 | import "./INFTX.sol"; 8 | import "./IXStore.sol"; 9 | import "./IERC721.sol"; 10 | import "./ITokenManager.sol"; 11 | import "./Context.sol"; 12 | import "./ReentrancyGuard.sol"; 13 | 14 | contract XSale is Pausable, ReentrancyGuard { 15 | using SafeMath for uint256; 16 | using EnumerableSet for EnumerableSet.UintSet; 17 | 18 | INFTX public nftx; 19 | IXStore public xStore; 20 | IERC20 public nftxToken; 21 | ITokenManager public tokenManager; 22 | 23 | uint64 public constant vestedUntil = 1610697600000; // Fri Jan 15 2021 00:00:00 GMT-0800 24 | 25 | // Bounty[] public ethBounties; 26 | mapping(uint256 => Bounty[]) public xBounties; 27 | 28 | struct Bounty { 29 | uint256 reward; 30 | uint256 request; 31 | } 32 | 33 | constructor(address _nftx, address _nftxToken, address _tokenManager) 34 | public 35 | { 36 | initOwnable(); 37 | nftx = INFTX(_nftx); 38 | xStore = IXStore(nftx.store()); 39 | nftxToken = IERC20(_nftxToken); 40 | tokenManager = ITokenManager(_tokenManager); 41 | } 42 | 43 | function addXBounty(uint256 vaultId, uint256 reward, uint256 request) 44 | public 45 | onlyOwner 46 | { 47 | Bounty memory newXBounty; 48 | newXBounty.reward = reward; 49 | newXBounty.request = request; 50 | xBounties[vaultId].push(newXBounty); 51 | } 52 | 53 | function setXBounty( 54 | uint256 vaultId, 55 | uint256 xBountyIndex, 56 | uint256 newReward, 57 | uint256 newRequest 58 | ) public onlyOwner { 59 | Bounty storage xBounty = xBounties[vaultId][xBountyIndex]; 60 | xBounty.reward = newReward; 61 | xBounty.request = newRequest; 62 | } 63 | 64 | function withdrawNFTX(address to, uint256 amount) public onlyOwner { 65 | nftxToken.transfer(to, amount); 66 | } 67 | 68 | function withdrawXToken(uint256 vaultId, address to, uint256 amount) 69 | public 70 | onlyOwner 71 | { 72 | xStore.xToken(vaultId).transfer(to, amount); 73 | } 74 | 75 | function withdrawETH(address payable to, uint256 amount) public onlyOwner { 76 | to.transfer(amount); 77 | } 78 | 79 | function fillXBounty(uint256 vaultId, uint256 xBountyIndex, uint256 amount) 80 | public 81 | nonReentrant 82 | { 83 | Bounty storage xBounty = xBounties[vaultId][xBountyIndex]; 84 | require(amount <= xBounty.request, "Amount > bounty"); 85 | require( 86 | amount <= nftxToken.balanceOf(address(nftx)), 87 | "Amount > balance" 88 | ); 89 | xStore.xToken(vaultId).transferFrom( 90 | _msgSender(), 91 | address(nftx), 92 | amount 93 | ); 94 | uint256 reward = xBounty.reward.mul(amount).div(xBounty.request); 95 | xBounty.request = xBounty.request.sub(amount); 96 | xBounty.reward = xBounty.reward.sub(reward); 97 | nftxToken.transfer(address(tokenManager), reward); 98 | tokenManager.assignVested( 99 | _msgSender(), 100 | reward, 101 | vestedUntil, 102 | vestedUntil, 103 | vestedUntil, 104 | false 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /contracts/XStore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./EnumerableSet.sol"; 6 | import "./Ownable.sol"; 7 | import "./SafeMath.sol"; 8 | import "./IXToken.sol"; 9 | import "./IERC721.sol"; 10 | import "./SafeERC20.sol"; 11 | 12 | contract XStore is Ownable { 13 | using SafeMath for uint256; 14 | using SafeERC20 for IERC20; 15 | using EnumerableSet for EnumerableSet.UintSet; 16 | 17 | struct FeeParams { 18 | uint256 ethBase; 19 | uint256 ethStep; 20 | } 21 | 22 | struct BountyParams { 23 | uint256 ethMax; 24 | uint256 length; 25 | } 26 | 27 | struct Vault { 28 | address xTokenAddress; 29 | address nftAddress; 30 | address manager; 31 | IXToken xToken; 32 | IERC721 nft; 33 | EnumerableSet.UintSet holdings; 34 | EnumerableSet.UintSet reserves; 35 | mapping(uint256 => address) requester; 36 | mapping(uint256 => bool) isEligible; 37 | mapping(uint256 => bool) shouldReserve; 38 | bool flipEligOnRedeem; 39 | bool negateEligibility; 40 | bool isFinalized; 41 | bool isClosed; 42 | FeeParams mintFees; 43 | FeeParams burnFees; 44 | FeeParams dualFees; 45 | BountyParams supplierBounty; 46 | uint256 ethBalance; 47 | uint256 tokenBalance; 48 | bool isD2Vault; 49 | address d2AssetAddress; 50 | IERC20 d2Asset; 51 | uint256 d2Holdings; 52 | } 53 | 54 | event XTokenAddressSet(uint256 indexed vaultId, address token); 55 | event NftAddressSet(uint256 indexed vaultId, address asset); 56 | event ManagerSet(uint256 indexed vaultId, address manager); 57 | event XTokenSet(uint256 indexed vaultId); 58 | event NftSet(uint256 indexed vaultId); 59 | event HoldingsAdded(uint256 indexed vaultId, uint256 id); 60 | event HoldingsRemoved(uint256 indexed vaultId, uint256 id); 61 | event ReservesAdded(uint256 indexed vaultId, uint256 id); 62 | event ReservesRemoved(uint256 indexed vaultId, uint256 id); 63 | event RequesterSet(uint256 indexed vaultId, uint256 id, address requester); 64 | event IsEligibleSet(uint256 indexed vaultId, uint256 id, bool _bool); 65 | event ShouldReserveSet(uint256 indexed vaultId, uint256 id, bool _bool); 66 | event FlipEligOnRedeemSet(uint256 indexed vaultId, bool _bool); 67 | event NegateEligibilitySet(uint256 indexed vaultId, bool _bool); 68 | event IsFinalizedSet(uint256 indexed vaultId, bool _isFinalized); 69 | event IsClosedSet(uint256 indexed vaultId, bool _isClosed); 70 | event MintFeesSet( 71 | uint256 indexed vaultId, 72 | uint256 ethBase, 73 | uint256 ethStep 74 | ); 75 | event BurnFeesSet( 76 | uint256 indexed vaultId, 77 | uint256 ethBase, 78 | uint256 ethStep 79 | ); 80 | event DualFeesSet( 81 | uint256 indexed vaultId, 82 | uint256 ethBase, 83 | uint256 ethStep 84 | ); 85 | event SupplierBountySet( 86 | uint256 indexed vaultId, 87 | uint256 ethMax, 88 | uint256 length 89 | ); 90 | event EthBalanceSet(uint256 indexed vaultId, uint256 _ethBalance); 91 | event TokenBalanceSet(uint256 indexed vaultId, uint256 _tokenBalance); 92 | event IsD2VaultSet(uint256 indexed vaultId, bool _isD2Vault); 93 | event D2AssetAddressSet(uint256 indexed vaultId, address _d2Asset); 94 | event D2AssetSet(uint256 indexed vaultId); 95 | event D2HoldingsSet(uint256 indexed vaultId, uint256 _d2Holdings); 96 | event NewVaultAdded(uint256 indexed vaultId); 97 | event IsExtensionSet(address addr, bool _isExtension); 98 | event RandNonceSet(uint256 _randNonce); 99 | 100 | Vault[] internal vaults; 101 | 102 | mapping(address => bool) public isExtension; 103 | uint256 public randNonce; 104 | 105 | constructor() public { 106 | initOwnable(); 107 | } 108 | 109 | function _getVault(uint256 vaultId) internal view returns (Vault storage) { 110 | require(vaultId < vaults.length, "Invalid vaultId"); 111 | return vaults[vaultId]; 112 | } 113 | 114 | function vaultsLength() public view returns (uint256) { 115 | return vaults.length; 116 | } 117 | 118 | function xTokenAddress(uint256 vaultId) public view returns (address) { 119 | Vault storage vault = _getVault(vaultId); 120 | return vault.xTokenAddress; 121 | } 122 | 123 | function nftAddress(uint256 vaultId) public view returns (address) { 124 | Vault storage vault = _getVault(vaultId); 125 | return vault.nftAddress; 126 | } 127 | 128 | function manager(uint256 vaultId) public view returns (address) { 129 | Vault storage vault = _getVault(vaultId); 130 | return vault.manager; 131 | } 132 | 133 | function xToken(uint256 vaultId) public view returns (IXToken) { 134 | Vault storage vault = _getVault(vaultId); 135 | return vault.xToken; 136 | } 137 | 138 | function nft(uint256 vaultId) public view returns (IERC721) { 139 | Vault storage vault = _getVault(vaultId); 140 | return vault.nft; 141 | } 142 | 143 | function holdingsLength(uint256 vaultId) public view returns (uint256) { 144 | Vault storage vault = _getVault(vaultId); 145 | return vault.holdings.length(); 146 | } 147 | 148 | function holdingsContains(uint256 vaultId, uint256 elem) 149 | public 150 | view 151 | returns (bool) 152 | { 153 | Vault storage vault = _getVault(vaultId); 154 | return vault.holdings.contains(elem); 155 | } 156 | 157 | function holdingsAt(uint256 vaultId, uint256 index) 158 | public 159 | view 160 | returns (uint256) 161 | { 162 | Vault storage vault = _getVault(vaultId); 163 | return vault.holdings.at(index); 164 | } 165 | 166 | function reservesLength(uint256 vaultId) public view returns (uint256) { 167 | Vault storage vault = _getVault(vaultId); 168 | return vault.holdings.length(); 169 | } 170 | 171 | function reservesContains(uint256 vaultId, uint256 elem) 172 | public 173 | view 174 | returns (bool) 175 | { 176 | Vault storage vault = _getVault(vaultId); 177 | return vault.holdings.contains(elem); 178 | } 179 | 180 | function reservesAt(uint256 vaultId, uint256 index) 181 | public 182 | view 183 | returns (uint256) 184 | { 185 | Vault storage vault = _getVault(vaultId); 186 | return vault.holdings.at(index); 187 | } 188 | 189 | function requester(uint256 vaultId, uint256 id) 190 | public 191 | view 192 | returns (address) 193 | { 194 | Vault storage vault = _getVault(vaultId); 195 | return vault.requester[id]; 196 | } 197 | 198 | function isEligible(uint256 vaultId, uint256 id) 199 | public 200 | view 201 | returns (bool) 202 | { 203 | Vault storage vault = _getVault(vaultId); 204 | return vault.isEligible[id]; 205 | } 206 | 207 | function shouldReserve(uint256 vaultId, uint256 id) 208 | public 209 | view 210 | returns (bool) 211 | { 212 | Vault storage vault = _getVault(vaultId); 213 | return vault.shouldReserve[id]; 214 | } 215 | 216 | function flipEligOnRedeem(uint256 vaultId) public view returns (bool) { 217 | Vault storage vault = _getVault(vaultId); 218 | return vault.flipEligOnRedeem; 219 | } 220 | 221 | function negateEligibility(uint256 vaultId) public view returns (bool) { 222 | Vault storage vault = _getVault(vaultId); 223 | return vault.negateEligibility; 224 | } 225 | 226 | function isFinalized(uint256 vaultId) public view returns (bool) { 227 | Vault storage vault = _getVault(vaultId); 228 | return vault.isFinalized; 229 | } 230 | 231 | function isClosed(uint256 vaultId) public view returns (bool) { 232 | Vault storage vault = _getVault(vaultId); 233 | return vault.isClosed; 234 | } 235 | 236 | function mintFees(uint256 vaultId) public view returns (uint256, uint256) { 237 | Vault storage vault = _getVault(vaultId); 238 | return (vault.mintFees.ethBase, vault.mintFees.ethStep); 239 | } 240 | 241 | function burnFees(uint256 vaultId) public view returns (uint256, uint256) { 242 | Vault storage vault = _getVault(vaultId); 243 | return (vault.burnFees.ethBase, vault.burnFees.ethStep); 244 | } 245 | 246 | function dualFees(uint256 vaultId) public view returns (uint256, uint256) { 247 | Vault storage vault = _getVault(vaultId); 248 | return (vault.dualFees.ethBase, vault.dualFees.ethStep); 249 | } 250 | 251 | function supplierBounty(uint256 vaultId) 252 | public 253 | view 254 | returns (uint256, uint256) 255 | { 256 | Vault storage vault = _getVault(vaultId); 257 | return (vault.supplierBounty.ethMax, vault.supplierBounty.length); 258 | } 259 | 260 | function ethBalance(uint256 vaultId) public view returns (uint256) { 261 | Vault storage vault = _getVault(vaultId); 262 | return vault.ethBalance; 263 | } 264 | 265 | function tokenBalance(uint256 vaultId) public view returns (uint256) { 266 | Vault storage vault = _getVault(vaultId); 267 | return vault.tokenBalance; 268 | } 269 | 270 | function isD2Vault(uint256 vaultId) public view returns (bool) { 271 | Vault storage vault = _getVault(vaultId); 272 | return vault.isD2Vault; 273 | } 274 | 275 | function d2AssetAddress(uint256 vaultId) public view returns (address) { 276 | Vault storage vault = _getVault(vaultId); 277 | return vault.d2AssetAddress; 278 | } 279 | 280 | function d2Asset(uint256 vaultId) public view returns (IERC20) { 281 | Vault storage vault = _getVault(vaultId); 282 | return vault.d2Asset; 283 | } 284 | 285 | function d2Holdings(uint256 vaultId) public view returns (uint256) { 286 | Vault storage vault = _getVault(vaultId); 287 | return vault.d2Holdings; 288 | } 289 | 290 | function setXTokenAddress(uint256 vaultId, address _xTokenAddress) 291 | public 292 | onlyOwner 293 | { 294 | Vault storage vault = _getVault(vaultId); 295 | vault.xTokenAddress = _xTokenAddress; 296 | emit XTokenAddressSet(vaultId, _xTokenAddress); 297 | } 298 | 299 | function setNftAddress(uint256 vaultId, address _nft) public onlyOwner { 300 | Vault storage vault = _getVault(vaultId); 301 | vault.nftAddress = _nft; 302 | emit NftAddressSet(vaultId, _nft); 303 | } 304 | 305 | function setManager(uint256 vaultId, address _manager) public onlyOwner { 306 | Vault storage vault = _getVault(vaultId); 307 | vault.manager = _manager; 308 | emit ManagerSet(vaultId, _manager); 309 | } 310 | 311 | function setXToken(uint256 vaultId) public onlyOwner { 312 | Vault storage vault = _getVault(vaultId); 313 | vault.xToken = IXToken(vault.xTokenAddress); 314 | emit XTokenSet(vaultId); 315 | } 316 | 317 | function setNft(uint256 vaultId) public onlyOwner { 318 | Vault storage vault = _getVault(vaultId); 319 | vault.nft = IERC721(vault.nftAddress); 320 | emit NftSet(vaultId); 321 | } 322 | 323 | function holdingsAdd(uint256 vaultId, uint256 elem) public onlyOwner { 324 | Vault storage vault = _getVault(vaultId); 325 | vault.holdings.add(elem); 326 | emit HoldingsAdded(vaultId, elem); 327 | } 328 | 329 | function holdingsRemove(uint256 vaultId, uint256 elem) public onlyOwner { 330 | Vault storage vault = _getVault(vaultId); 331 | vault.holdings.remove(elem); 332 | emit HoldingsRemoved(vaultId, elem); 333 | } 334 | 335 | function reservesAdd(uint256 vaultId, uint256 elem) public onlyOwner { 336 | Vault storage vault = _getVault(vaultId); 337 | vault.reserves.add(elem); 338 | emit ReservesAdded(vaultId, elem); 339 | } 340 | 341 | function reservesRemove(uint256 vaultId, uint256 elem) public onlyOwner { 342 | Vault storage vault = _getVault(vaultId); 343 | vault.reserves.remove(elem); 344 | emit ReservesRemoved(vaultId, elem); 345 | } 346 | 347 | function setRequester(uint256 vaultId, uint256 id, address _requester) 348 | public 349 | onlyOwner 350 | { 351 | Vault storage vault = _getVault(vaultId); 352 | vault.requester[id] = _requester; 353 | emit RequesterSet(vaultId, id, _requester); 354 | } 355 | 356 | function setIsEligible(uint256 vaultId, uint256 id, bool _bool) 357 | public 358 | onlyOwner 359 | { 360 | Vault storage vault = _getVault(vaultId); 361 | vault.isEligible[id] = _bool; 362 | emit IsEligibleSet(vaultId, id, _bool); 363 | } 364 | 365 | function setShouldReserve(uint256 vaultId, uint256 id, bool _shouldReserve) 366 | public 367 | onlyOwner 368 | { 369 | Vault storage vault = _getVault(vaultId); 370 | vault.shouldReserve[id] = _shouldReserve; 371 | emit ShouldReserveSet(vaultId, id, _shouldReserve); 372 | } 373 | 374 | function setFlipEligOnRedeem(uint256 vaultId, bool flipElig) 375 | public 376 | onlyOwner 377 | { 378 | Vault storage vault = _getVault(vaultId); 379 | vault.flipEligOnRedeem = flipElig; 380 | emit FlipEligOnRedeemSet(vaultId, flipElig); 381 | } 382 | 383 | function setNegateEligibility(uint256 vaultId, bool negateElig) 384 | public 385 | onlyOwner 386 | { 387 | Vault storage vault = _getVault(vaultId); 388 | vault.negateEligibility = negateElig; 389 | emit NegateEligibilitySet(vaultId, negateElig); 390 | } 391 | 392 | function setIsFinalized(uint256 vaultId, bool _isFinalized) 393 | public 394 | onlyOwner 395 | { 396 | Vault storage vault = _getVault(vaultId); 397 | vault.isFinalized = _isFinalized; 398 | emit IsFinalizedSet(vaultId, _isFinalized); 399 | } 400 | 401 | function setIsClosed(uint256 vaultId, bool _isClosed) public onlyOwner { 402 | Vault storage vault = _getVault(vaultId); 403 | vault.isClosed = _isClosed; 404 | emit IsClosedSet(vaultId, _isClosed); 405 | } 406 | 407 | function setMintFees(uint256 vaultId, uint256 ethBase, uint256 ethStep) 408 | public 409 | onlyOwner 410 | { 411 | Vault storage vault = _getVault(vaultId); 412 | vault.mintFees = FeeParams(ethBase, ethStep); 413 | emit MintFeesSet(vaultId, ethBase, ethStep); 414 | } 415 | 416 | function setBurnFees(uint256 vaultId, uint256 ethBase, uint256 ethStep) 417 | public 418 | onlyOwner 419 | { 420 | Vault storage vault = _getVault(vaultId); 421 | vault.burnFees = FeeParams(ethBase, ethStep); 422 | emit BurnFeesSet(vaultId, ethBase, ethStep); 423 | } 424 | 425 | function setDualFees(uint256 vaultId, uint256 ethBase, uint256 ethStep) 426 | public 427 | onlyOwner 428 | { 429 | Vault storage vault = _getVault(vaultId); 430 | vault.dualFees = FeeParams(ethBase, ethStep); 431 | emit DualFeesSet(vaultId, ethBase, ethStep); 432 | } 433 | 434 | function setSupplierBounty(uint256 vaultId, uint256 ethMax, uint256 length) 435 | public 436 | onlyOwner 437 | { 438 | Vault storage vault = _getVault(vaultId); 439 | vault.supplierBounty = BountyParams(ethMax, length); 440 | emit SupplierBountySet(vaultId, ethMax, length); 441 | } 442 | 443 | function setEthBalance(uint256 vaultId, uint256 _ethBalance) 444 | public 445 | onlyOwner 446 | { 447 | Vault storage vault = _getVault(vaultId); 448 | vault.ethBalance = _ethBalance; 449 | emit EthBalanceSet(vaultId, _ethBalance); 450 | } 451 | 452 | function setTokenBalance(uint256 vaultId, uint256 _tokenBalance) 453 | public 454 | onlyOwner 455 | { 456 | Vault storage vault = _getVault(vaultId); 457 | vault.tokenBalance = _tokenBalance; 458 | emit TokenBalanceSet(vaultId, _tokenBalance); 459 | } 460 | 461 | function setIsD2Vault(uint256 vaultId, bool _isD2Vault) public onlyOwner { 462 | Vault storage vault = _getVault(vaultId); 463 | vault.isD2Vault = _isD2Vault; 464 | emit IsD2VaultSet(vaultId, _isD2Vault); 465 | } 466 | 467 | function setD2AssetAddress(uint256 vaultId, address _d2Asset) 468 | public 469 | onlyOwner 470 | { 471 | Vault storage vault = _getVault(vaultId); 472 | vault.d2AssetAddress = _d2Asset; 473 | emit D2AssetAddressSet(vaultId, _d2Asset); 474 | } 475 | 476 | function setD2Asset(uint256 vaultId) public onlyOwner { 477 | Vault storage vault = _getVault(vaultId); 478 | vault.d2Asset = IERC20(vault.d2AssetAddress); 479 | emit D2AssetSet(vaultId); 480 | } 481 | 482 | function setD2Holdings(uint256 vaultId, uint256 _d2Holdings) 483 | public 484 | onlyOwner 485 | { 486 | Vault storage vault = _getVault(vaultId); 487 | vault.d2Holdings = _d2Holdings; 488 | emit D2HoldingsSet(vaultId, _d2Holdings); 489 | } 490 | 491 | //////////////////////////////////////////////////////////// 492 | 493 | function addNewVault() public onlyOwner returns (uint256) { 494 | Vault memory newVault; 495 | vaults.push(newVault); 496 | uint256 vaultId = vaults.length.sub(1); 497 | emit NewVaultAdded(vaultId); 498 | return vaultId; 499 | } 500 | 501 | function setIsExtension(address addr, bool _isExtension) public onlyOwner { 502 | isExtension[addr] = _isExtension; 503 | emit IsExtensionSet(addr, _isExtension); 504 | } 505 | 506 | function setRandNonce(uint256 _randNonce) public onlyOwner { 507 | randNonce = _randNonce; 508 | emit RandNonceSet(_randNonce); 509 | } 510 | } 511 | -------------------------------------------------------------------------------- /contracts/XToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.8; 4 | 5 | import "./Ownable.sol"; 6 | import "./Context.sol"; 7 | import "./ERC20.sol"; 8 | import "./ERC20Burnable.sol"; 9 | 10 | contract XToken is Context, Ownable, ERC20Burnable { 11 | constructor(string memory name, string memory symbol, address _owner) 12 | public 13 | ERC20(name, symbol) 14 | { 15 | initOwnable(); 16 | transferOwnership(_owner); 17 | _mint(msg.sender, 0); 18 | } 19 | 20 | function mint(address to, uint256 amount) public onlyOwner { 21 | _mint(to, amount); 22 | } 23 | 24 | function changeName(string memory name) public onlyOwner { 25 | _changeName(name); 26 | } 27 | 28 | function changeSymbol(string memory symbol) public onlyOwner { 29 | _changeSymbol(symbol); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /data/punk/punkAttr4.json: -------------------------------------------------------------------------------- 1 | [ 2 | 4, 3 | 52, 4 | 62, 5 | 70, 6 | 72, 7 | 86, 8 | 104, 9 | 110, 10 | 112, 11 | 116, 12 | 126, 13 | 128, 14 | 129, 15 | 176, 16 | 192, 17 | 201, 18 | 218, 19 | 219, 20 | 221, 21 | 224, 22 | 243, 23 | 256, 24 | 269, 25 | 274, 26 | 310, 27 | 340, 28 | 355, 29 | 370, 30 | 415, 31 | 422, 32 | 433, 33 | 445, 34 | 456, 35 | 476, 36 | 485, 37 | 496, 38 | 525, 39 | 542, 40 | 563, 41 | 589, 42 | 619, 43 | 622, 44 | 623, 45 | 625, 46 | 628, 47 | 633, 48 | 638, 49 | 645, 50 | 646, 51 | 649, 52 | 657, 53 | 660, 54 | 684, 55 | 689, 56 | 710, 57 | 714, 58 | 716, 59 | 726, 60 | 735, 61 | 736, 62 | 754, 63 | 768, 64 | 777, 65 | 781, 66 | 784, 67 | 788, 68 | 791, 69 | 819, 70 | 825, 71 | 843, 72 | 862, 73 | 865, 74 | 867, 75 | 874, 76 | 879, 77 | 902, 78 | 904, 79 | 921, 80 | 924, 81 | 928, 82 | 952, 83 | 960, 84 | 966, 85 | 986, 86 | 993, 87 | 994, 88 | 995, 89 | 998, 90 | 1016, 91 | 1020, 92 | 1024, 93 | 1034, 94 | 1041, 95 | 1075, 96 | 1093, 97 | 1102, 98 | 1110, 99 | 1113, 100 | 1124, 101 | 1136, 102 | 1139, 103 | 1162, 104 | 1172, 105 | 1185, 106 | 1186, 107 | 1190, 108 | 1193, 109 | 1198, 110 | 1210, 111 | 1222, 112 | 1226, 113 | 1231, 114 | 1235, 115 | 1245, 116 | 1246, 117 | 1249, 118 | 1250, 119 | 1259, 120 | 1260, 121 | 1265, 122 | 1283, 123 | 1285, 124 | 1295, 125 | 1320, 126 | 1348, 127 | 1351, 128 | 1357, 129 | 1363, 130 | 1366, 131 | 1368, 132 | 1370, 133 | 1380, 134 | 1382, 135 | 1393, 136 | 1400, 137 | 1424, 138 | 1430, 139 | 1435, 140 | 1439, 141 | 1441, 142 | 1447, 143 | 1452, 144 | 1455, 145 | 1462, 146 | 1504, 147 | 1508, 148 | 1513, 149 | 1544, 150 | 1561, 151 | 1578, 152 | 1598, 153 | 1599, 154 | 1608, 155 | 1624, 156 | 1634, 157 | 1649, 158 | 1652, 159 | 1653, 160 | 1662, 161 | 1703, 162 | 1717, 163 | 1726, 164 | 1762, 165 | 1795, 166 | 1797, 167 | 1800, 168 | 1802, 169 | 1812, 170 | 1819, 171 | 1831, 172 | 1851, 173 | 1854, 174 | 1863, 175 | 1878, 176 | 1879, 177 | 1890, 178 | 1910, 179 | 1914, 180 | 1921, 181 | 1932, 182 | 1937, 183 | 1948, 184 | 1955, 185 | 1974, 186 | 1984, 187 | 1990, 188 | 2006, 189 | 2032, 190 | 2050, 191 | 2054, 192 | 2072, 193 | 2074, 194 | 2097, 195 | 2109, 196 | 2120, 197 | 2124, 198 | 2127, 199 | 2131, 200 | 2137, 201 | 2142, 202 | 2158, 203 | 2168, 204 | 2187, 205 | 2192, 206 | 2197, 207 | 2208, 208 | 2236, 209 | 2264, 210 | 2267, 211 | 2268, 212 | 2271, 213 | 2274, 214 | 2276, 215 | 2282, 216 | 2327, 217 | 2342, 218 | 2343, 219 | 2344, 220 | 2350, 221 | 2356, 222 | 2358, 223 | 2359, 224 | 2362, 225 | 2365, 226 | 2375, 227 | 2397, 228 | 2398, 229 | 2413, 230 | 2430, 231 | 2447, 232 | 2450, 233 | 2452, 234 | 2469, 235 | 2472, 236 | 2474, 237 | 2490, 238 | 2495, 239 | 2496, 240 | 2497, 241 | 2498, 242 | 2505, 243 | 2518, 244 | 2525, 245 | 2533, 246 | 2560, 247 | 2562, 248 | 2588, 249 | 2590, 250 | 2595, 251 | 2608, 252 | 2623, 253 | 2632, 254 | 2642, 255 | 2650, 256 | 2653, 257 | 2659, 258 | 2672, 259 | 2673, 260 | 2680, 261 | 2687, 262 | 2688, 263 | 2692, 264 | 2701, 265 | 2715, 266 | 2721, 267 | 2726, 268 | 2728, 269 | 2731, 270 | 2737, 271 | 2739, 272 | 2742, 273 | 2745, 274 | 2753, 275 | 2772, 276 | 2786, 277 | 2788, 278 | 2807, 279 | 2814, 280 | 2815, 281 | 2819, 282 | 2830, 283 | 2835, 284 | 2836, 285 | 2852, 286 | 2866, 287 | 2901, 288 | 2903, 289 | 2904, 290 | 2915, 291 | 2923, 292 | 2935, 293 | 2945, 294 | 2947, 295 | 2949, 296 | 2953, 297 | 2957, 298 | 2961, 299 | 2962, 300 | 3004, 301 | 3016, 302 | 3017, 303 | 3066, 304 | 3067, 305 | 3074, 306 | 3084, 307 | 3085, 308 | 3088, 309 | 3090, 310 | 3094, 311 | 3096, 312 | 3098, 313 | 3099, 314 | 3123, 315 | 3156, 316 | 3173, 317 | 3183, 318 | 3184, 319 | 3194, 320 | 3205, 321 | 3212, 322 | 3214, 323 | 3217, 324 | 3221, 325 | 3225, 326 | 3229, 327 | 3237, 328 | 3242, 329 | 3249, 330 | 3254, 331 | 3262, 332 | 3268, 333 | 3283, 334 | 3284, 335 | 3289, 336 | 3293, 337 | 3302, 338 | 3311, 339 | 3330, 340 | 3331, 341 | 3342, 342 | 3349, 343 | 3352, 344 | 3353, 345 | 3358, 346 | 3360, 347 | 3372, 348 | 3375, 349 | 3376, 350 | 3379, 351 | 3392, 352 | 3394, 353 | 3395, 354 | 3402, 355 | 3406, 356 | 3409, 357 | 3423, 358 | 3425, 359 | 3434, 360 | 3441, 361 | 3460, 362 | 3468, 363 | 3469, 364 | 3484, 365 | 3490, 366 | 3504, 367 | 3506, 368 | 3508, 369 | 3519, 370 | 3522, 371 | 3540, 372 | 3542, 373 | 3558, 374 | 3566, 375 | 3574, 376 | 3587, 377 | 3613, 378 | 3618, 379 | 3620, 380 | 3622, 381 | 3625, 382 | 3629, 383 | 3649, 384 | 3668, 385 | 3671, 386 | 3672, 387 | 3677, 388 | 3681, 389 | 3684, 390 | 3691, 391 | 3698, 392 | 3699, 393 | 3705, 394 | 3713, 395 | 3739, 396 | 3754, 397 | 3758, 398 | 3760, 399 | 3770, 400 | 3779, 401 | 3784, 402 | 3798, 403 | 3801, 404 | 3805, 405 | 3807, 406 | 3812, 407 | 3820, 408 | 3821, 409 | 3833, 410 | 3839, 411 | 3843, 412 | 3847, 413 | 3849, 414 | 3860, 415 | 3869, 416 | 3872, 417 | 3888, 418 | 3900, 419 | 3907, 420 | 3930, 421 | 3933, 422 | 3934, 423 | 3942, 424 | 3943, 425 | 3950, 426 | 3964, 427 | 3966, 428 | 3968, 429 | 3974, 430 | 3976, 431 | 3977, 432 | 3985, 433 | 3986, 434 | 3991, 435 | 3996, 436 | 4001, 437 | 4002, 438 | 4007, 439 | 4018, 440 | 4030, 441 | 4033, 442 | 4034, 443 | 4035, 444 | 4036, 445 | 4056, 446 | 4065, 447 | 4067, 448 | 4068, 449 | 4072, 450 | 4075, 451 | 4100, 452 | 4141, 453 | 4147, 454 | 4151, 455 | 4162, 456 | 4166, 457 | 4168, 458 | 4173, 459 | 4191, 460 | 4192, 461 | 4204, 462 | 4210, 463 | 4212, 464 | 4234, 465 | 4239, 466 | 4245, 467 | 4251, 468 | 4254, 469 | 4255, 470 | 4259, 471 | 4260, 472 | 4261, 473 | 4272, 474 | 4280, 475 | 4283, 476 | 4285, 477 | 4286, 478 | 4296, 479 | 4298, 480 | 4302, 481 | 4307, 482 | 4309, 483 | 4319, 484 | 4332, 485 | 4350, 486 | 4352, 487 | 4364, 488 | 4368, 489 | 4371, 490 | 4383, 491 | 4390, 492 | 4404, 493 | 4414, 494 | 4425, 495 | 4426, 496 | 4428, 497 | 4440, 498 | 4449, 499 | 4461, 500 | 4462, 501 | 4465, 502 | 4483, 503 | 4494, 504 | 4496, 505 | 4508, 506 | 4510, 507 | 4525, 508 | 4550, 509 | 4552, 510 | 4554, 511 | 4572, 512 | 4574, 513 | 4575, 514 | 4584, 515 | 4596, 516 | 4620, 517 | 4625, 518 | 4632, 519 | 4635, 520 | 4637, 521 | 4642, 522 | 4647, 523 | 4653, 524 | 4655, 525 | 4660, 526 | 4662, 527 | 4667, 528 | 4668, 529 | 4680, 530 | 4683, 531 | 4684, 532 | 4702, 533 | 4706, 534 | 4723, 535 | 4730, 536 | 4731, 537 | 4745, 538 | 4746, 539 | 4763, 540 | 4766, 541 | 4767, 542 | 4774, 543 | 4776, 544 | 4785, 545 | 4790, 546 | 4799, 547 | 4805, 548 | 4820, 549 | 4831, 550 | 4837, 551 | 4839, 552 | 4847, 553 | 4853, 554 | 4863, 555 | 4872, 556 | 4875, 557 | 4884, 558 | 4894, 559 | 4898, 560 | 4903, 561 | 4920, 562 | 4921, 563 | 4934, 564 | 4953, 565 | 4973, 566 | 4981, 567 | 4982, 568 | 4984, 569 | 4993, 570 | 4998, 571 | 4999, 572 | 5002, 573 | 5004, 574 | 5010, 575 | 5015, 576 | 5016, 577 | 5023, 578 | 5024, 579 | 5032, 580 | 5036, 581 | 5051, 582 | 5053, 583 | 5055, 584 | 5056, 585 | 5060, 586 | 5074, 587 | 5079, 588 | 5080, 589 | 5081, 590 | 5083, 591 | 5096, 592 | 5106, 593 | 5107, 594 | 5109, 595 | 5110, 596 | 5112, 597 | 5114, 598 | 5118, 599 | 5127, 600 | 5133, 601 | 5135, 602 | 5140, 603 | 5145, 604 | 5154, 605 | 5159, 606 | 5166, 607 | 5175, 608 | 5176, 609 | 5182, 610 | 5202, 611 | 5226, 612 | 5229, 613 | 5232, 614 | 5241, 615 | 5257, 616 | 5276, 617 | 5280, 618 | 5286, 619 | 5295, 620 | 5296, 621 | 5298, 622 | 5299, 623 | 5300, 624 | 5304, 625 | 5306, 626 | 5307, 627 | 5313, 628 | 5320, 629 | 5332, 630 | 5334, 631 | 5342, 632 | 5356, 633 | 5370, 634 | 5373, 635 | 5377, 636 | 5391, 637 | 5393, 638 | 5415, 639 | 5418, 640 | 5429, 641 | 5441, 642 | 5446, 643 | 5447, 644 | 5450, 645 | 5453, 646 | 5456, 647 | 5457, 648 | 5458, 649 | 5459, 650 | 5460, 651 | 5464, 652 | 5465, 653 | 5469, 654 | 5477, 655 | 5490, 656 | 5493, 657 | 5497, 658 | 5499, 659 | 5500, 660 | 5509, 661 | 5510, 662 | 5512, 663 | 5513, 664 | 5526, 665 | 5527, 666 | 5529, 667 | 5538, 668 | 5547, 669 | 5548, 670 | 5550, 671 | 5559, 672 | 5563, 673 | 5572, 674 | 5596, 675 | 5606, 676 | 5609, 677 | 5612, 678 | 5625, 679 | 5628, 680 | 5629, 681 | 5642, 682 | 5653, 683 | 5655, 684 | 5659, 685 | 5665, 686 | 5675, 687 | 5681, 688 | 5682, 689 | 5692, 690 | 5695, 691 | 5699, 692 | 5707, 693 | 5711, 694 | 5716, 695 | 5721, 696 | 5723, 697 | 5725, 698 | 5729, 699 | 5732, 700 | 5736, 701 | 5748, 702 | 5754, 703 | 5763, 704 | 5765, 705 | 5774, 706 | 5782, 707 | 5787, 708 | 5790, 709 | 5797, 710 | 5804, 711 | 5825, 712 | 5834, 713 | 5836, 714 | 5841, 715 | 5846, 716 | 5858, 717 | 5866, 718 | 5872, 719 | 5885, 720 | 5892, 721 | 5896, 722 | 5900, 723 | 5912, 724 | 5925, 725 | 5942, 726 | 5959, 727 | 5968, 728 | 5973, 729 | 5986, 730 | 6002, 731 | 6005, 732 | 6009, 733 | 6012, 734 | 6016, 735 | 6018, 736 | 6020, 737 | 6034, 738 | 6044, 739 | 6047, 740 | 6054, 741 | 6055, 742 | 6056, 743 | 6057, 744 | 6061, 745 | 6068, 746 | 6074, 747 | 6086, 748 | 6088, 749 | 6104, 750 | 6112, 751 | 6116, 752 | 6117, 753 | 6125, 754 | 6126, 755 | 6132, 756 | 6138, 757 | 6142, 758 | 6143, 759 | 6144, 760 | 6162, 761 | 6172, 762 | 6178, 763 | 6188, 764 | 6193, 765 | 6195, 766 | 6203, 767 | 6218, 768 | 6221, 769 | 6224, 770 | 6226, 771 | 6233, 772 | 6240, 773 | 6243, 774 | 6245, 775 | 6246, 776 | 6251, 777 | 6252, 778 | 6254, 779 | 6265, 780 | 6267, 781 | 6269, 782 | 6274, 783 | 6277, 784 | 6289, 785 | 6293, 786 | 6300, 787 | 6301, 788 | 6313, 789 | 6318, 790 | 6321, 791 | 6333, 792 | 6334, 793 | 6340, 794 | 6344, 795 | 6347, 796 | 6349, 797 | 6350, 798 | 6353, 799 | 6360, 800 | 6365, 801 | 6369, 802 | 6372, 803 | 6374, 804 | 6376, 805 | 6380, 806 | 6385, 807 | 6392, 808 | 6396, 809 | 6399, 810 | 6408, 811 | 6416, 812 | 6417, 813 | 6420, 814 | 6422, 815 | 6442, 816 | 6444, 817 | 6451, 818 | 6468, 819 | 6469, 820 | 6476, 821 | 6478, 822 | 6483, 823 | 6505, 824 | 6508, 825 | 6527, 826 | 6528, 827 | 6529, 828 | 6538, 829 | 6548, 830 | 6554, 831 | 6560, 832 | 6565, 833 | 6569, 834 | 6600, 835 | 6602, 836 | 6609, 837 | 6611, 838 | 6627, 839 | 6633, 840 | 6634, 841 | 6644, 842 | 6647, 843 | 6660, 844 | 6665, 845 | 6666, 846 | 6669, 847 | 6674, 848 | 6691, 849 | 6693, 850 | 6694, 851 | 6697, 852 | 6703, 853 | 6705, 854 | 6715, 855 | 6718, 856 | 6724, 857 | 6729, 858 | 6730, 859 | 6733, 860 | 6753, 861 | 6758, 862 | 6760, 863 | 6786, 864 | 6814, 865 | 6827, 866 | 6836, 867 | 6838, 868 | 6848, 869 | 6859, 870 | 6871, 871 | 6880, 872 | 6883, 873 | 6886, 874 | 6896, 875 | 6897, 876 | 6910, 877 | 6922, 878 | 6926, 879 | 6944, 880 | 6945, 881 | 6975, 882 | 6987, 883 | 6990, 884 | 6992, 885 | 6995, 886 | 7001, 887 | 7019, 888 | 7032, 889 | 7034, 890 | 7039, 891 | 7044, 892 | 7046, 893 | 7055, 894 | 7065, 895 | 7071, 896 | 7076, 897 | 7085, 898 | 7093, 899 | 7094, 900 | 7105, 901 | 7122, 902 | 7125, 903 | 7131, 904 | 7137, 905 | 7139, 906 | 7141, 907 | 7143, 908 | 7153, 909 | 7154, 910 | 7155, 911 | 7158, 912 | 7162, 913 | 7163, 914 | 7167, 915 | 7170, 916 | 7183, 917 | 7185, 918 | 7187, 919 | 7189, 920 | 7190, 921 | 7197, 922 | 7198, 923 | 7201, 924 | 7205, 925 | 7211, 926 | 7212, 927 | 7217, 928 | 7219, 929 | 7226, 930 | 7234, 931 | 7236, 932 | 7239, 933 | 7244, 934 | 7245, 935 | 7249, 936 | 7270, 937 | 7271, 938 | 7277, 939 | 7284, 940 | 7290, 941 | 7294, 942 | 7296, 943 | 7301, 944 | 7302, 945 | 7304, 946 | 7314, 947 | 7326, 948 | 7332, 949 | 7339, 950 | 7348, 951 | 7353, 952 | 7368, 953 | 7372, 954 | 7384, 955 | 7393, 956 | 7399, 957 | 7406, 958 | 7414, 959 | 7415, 960 | 7420, 961 | 7426, 962 | 7432, 963 | 7436, 964 | 7449, 965 | 7450, 966 | 7464, 967 | 7473, 968 | 7483, 969 | 7486, 970 | 7489, 971 | 7490, 972 | 7500, 973 | 7505, 974 | 7511, 975 | 7531, 976 | 7538, 977 | 7543, 978 | 7550, 979 | 7553, 980 | 7557, 981 | 7570, 982 | 7576, 983 | 7585, 984 | 7586, 985 | 7606, 986 | 7622, 987 | 7625, 988 | 7626, 989 | 7627, 990 | 7629, 991 | 7630, 992 | 7631, 993 | 7634, 994 | 7645, 995 | 7649, 996 | 7657, 997 | 7658, 998 | 7663, 999 | 7665, 1000 | 7667, 1001 | 7674, 1002 | 7679, 1003 | 7682, 1004 | 7689, 1005 | 7692, 1006 | 7697, 1007 | 7700, 1008 | 7708, 1009 | 7709, 1010 | 7710, 1011 | 7713, 1012 | 7714, 1013 | 7716, 1014 | 7721, 1015 | 7725, 1016 | 7739, 1017 | 7740, 1018 | 7753, 1019 | 7763, 1020 | 7765, 1021 | 7770, 1022 | 7775, 1023 | 7778, 1024 | 7788, 1025 | 7789, 1026 | 7794, 1027 | 7800, 1028 | 7806, 1029 | 7822, 1030 | 7828, 1031 | 7839, 1032 | 7844, 1033 | 7861, 1034 | 7862, 1035 | 7864, 1036 | 7878, 1037 | 7881, 1038 | 7882, 1039 | 7884, 1040 | 7896, 1041 | 7898, 1042 | 7902, 1043 | 7904, 1044 | 7912, 1045 | 7917, 1046 | 7921, 1047 | 7923, 1048 | 7926, 1049 | 7929, 1050 | 7933, 1051 | 7940, 1052 | 7967, 1053 | 7975, 1054 | 7978, 1055 | 7981, 1056 | 7986, 1057 | 7999, 1058 | 8007, 1059 | 8019, 1060 | 8023, 1061 | 8027, 1062 | 8032, 1063 | 8036, 1064 | 8037, 1065 | 8040, 1066 | 8041, 1067 | 8047, 1068 | 8050, 1069 | 8056, 1070 | 8057, 1071 | 8060, 1072 | 8065, 1073 | 8071, 1074 | 8072, 1075 | 8080, 1076 | 8086, 1077 | 8105, 1078 | 8112, 1079 | 8120, 1080 | 8157, 1081 | 8158, 1082 | 8163, 1083 | 8171, 1084 | 8172, 1085 | 8173, 1086 | 8179, 1087 | 8195, 1088 | 8197, 1089 | 8200, 1090 | 8201, 1091 | 8217, 1092 | 8220, 1093 | 8229, 1094 | 8234, 1095 | 8236, 1096 | 8239, 1097 | 8241, 1098 | 8242, 1099 | 8247, 1100 | 8256, 1101 | 8258, 1102 | 8265, 1103 | 8280, 1104 | 8287, 1105 | 8289, 1106 | 8298, 1107 | 8302, 1108 | 8305, 1109 | 8323, 1110 | 8326, 1111 | 8343, 1112 | 8344, 1113 | 8346, 1114 | 8357, 1115 | 8374, 1116 | 8376, 1117 | 8385, 1118 | 8387, 1119 | 8390, 1120 | 8399, 1121 | 8400, 1122 | 8407, 1123 | 8412, 1124 | 8416, 1125 | 8422, 1126 | 8425, 1127 | 8427, 1128 | 8438, 1129 | 8440, 1130 | 8443, 1131 | 8445, 1132 | 8446, 1133 | 8448, 1134 | 8463, 1135 | 8469, 1136 | 8479, 1137 | 8483, 1138 | 8485, 1139 | 8495, 1140 | 8539, 1141 | 8547, 1142 | 8557, 1143 | 8563, 1144 | 8567, 1145 | 8571, 1146 | 8574, 1147 | 8580, 1148 | 8584, 1149 | 8596, 1150 | 8610, 1151 | 8614, 1152 | 8617, 1153 | 8621, 1154 | 8627, 1155 | 8651, 1156 | 8658, 1157 | 8663, 1158 | 8670, 1159 | 8671, 1160 | 8674, 1161 | 8682, 1162 | 8684, 1163 | 8699, 1164 | 8701, 1165 | 8702, 1166 | 8707, 1167 | 8709, 1168 | 8712, 1169 | 8715, 1170 | 8717, 1171 | 8719, 1172 | 8741, 1173 | 8761, 1174 | 8767, 1175 | 8771, 1176 | 8772, 1177 | 8773, 1178 | 8774, 1179 | 8781, 1180 | 8783, 1181 | 8784, 1182 | 8786, 1183 | 8790, 1184 | 8792, 1185 | 8797, 1186 | 8802, 1187 | 8825, 1188 | 8830, 1189 | 8839, 1190 | 8853, 1191 | 8861, 1192 | 8862, 1193 | 8866, 1194 | 8867, 1195 | 8871, 1196 | 8872, 1197 | 8882, 1198 | 8884, 1199 | 8886, 1200 | 8887, 1201 | 8896, 1202 | 8904, 1203 | 8908, 1204 | 8918, 1205 | 8920, 1206 | 8921, 1207 | 8925, 1208 | 8931, 1209 | 8932, 1210 | 8949, 1211 | 8950, 1212 | 8963, 1213 | 8965, 1214 | 8969, 1215 | 8971, 1216 | 8977, 1217 | 8980, 1218 | 8982, 1219 | 8985, 1220 | 8992, 1221 | 8999, 1222 | 9008, 1223 | 9011, 1224 | 9014, 1225 | 9016, 1226 | 9018, 1227 | 9019, 1228 | 9021, 1229 | 9024, 1230 | 9026, 1231 | 9034, 1232 | 9042, 1233 | 9044, 1234 | 9046, 1235 | 9049, 1236 | 9050, 1237 | 9053, 1238 | 9054, 1239 | 9056, 1240 | 9061, 1241 | 9065, 1242 | 9066, 1243 | 9068, 1244 | 9080, 1245 | 9084, 1246 | 9090, 1247 | 9093, 1248 | 9094, 1249 | 9096, 1250 | 9099, 1251 | 9101, 1252 | 9106, 1253 | 9110, 1254 | 9113, 1255 | 9121, 1256 | 9124, 1257 | 9126, 1258 | 9128, 1259 | 9139, 1260 | 9142, 1261 | 9147, 1262 | 9156, 1263 | 9157, 1264 | 9163, 1265 | 9168, 1266 | 9178, 1267 | 9180, 1268 | 9188, 1269 | 9203, 1270 | 9205, 1271 | 9206, 1272 | 9207, 1273 | 9210, 1274 | 9211, 1275 | 9213, 1276 | 9235, 1277 | 9237, 1278 | 9241, 1279 | 9244, 1280 | 9245, 1281 | 9250, 1282 | 9262, 1283 | 9264, 1284 | 9266, 1285 | 9270, 1286 | 9271, 1287 | 9279, 1288 | 9287, 1289 | 9288, 1290 | 9289, 1291 | 9290, 1292 | 9302, 1293 | 9305, 1294 | 9324, 1295 | 9326, 1296 | 9328, 1297 | 9329, 1298 | 9333, 1299 | 9336, 1300 | 9342, 1301 | 9346, 1302 | 9355, 1303 | 9359, 1304 | 9362, 1305 | 9366, 1306 | 9375, 1307 | 9377, 1308 | 9382, 1309 | 9383, 1310 | 9390, 1311 | 9395, 1312 | 9396, 1313 | 9400, 1314 | 9404, 1315 | 9412, 1316 | 9414, 1317 | 9421, 1318 | 9430, 1319 | 9432, 1320 | 9433, 1321 | 9438, 1322 | 9440, 1323 | 9442, 1324 | 9445, 1325 | 9469, 1326 | 9482, 1327 | 9483, 1328 | 9485, 1329 | 9492, 1330 | 9499, 1331 | 9501, 1332 | 9502, 1333 | 9512, 1334 | 9516, 1335 | 9519, 1336 | 9525, 1337 | 9532, 1338 | 9537, 1339 | 9540, 1340 | 9547, 1341 | 9550, 1342 | 9559, 1343 | 9560, 1344 | 9562, 1345 | 9564, 1346 | 9570, 1347 | 9575, 1348 | 9579, 1349 | 9584, 1350 | 9585, 1351 | 9586, 1352 | 9587, 1353 | 9589, 1354 | 9594, 1355 | 9600, 1356 | 9608, 1357 | 9609, 1358 | 9611, 1359 | 9618, 1360 | 9627, 1361 | 9628, 1362 | 9637, 1363 | 9645, 1364 | 9651, 1365 | 9660, 1366 | 9663, 1367 | 9684, 1368 | 9698, 1369 | 9704, 1370 | 9709, 1371 | 9712, 1372 | 9717, 1373 | 9723, 1374 | 9728, 1375 | 9732, 1376 | 9734, 1377 | 9748, 1378 | 9770, 1379 | 9774, 1380 | 9776, 1381 | 9780, 1382 | 9791, 1383 | 9793, 1384 | 9804, 1385 | 9811, 1386 | 9814, 1387 | 9824, 1388 | 9828, 1389 | 9833, 1390 | 9835, 1391 | 9836, 1392 | 9848, 1393 | 9850, 1394 | 9857, 1395 | 9860, 1396 | 9861, 1397 | 9878, 1398 | 9880, 1399 | 9881, 1400 | 9882, 1401 | 9887, 1402 | 9888, 1403 | 9890, 1404 | 9896, 1405 | 9915, 1406 | 9921, 1407 | 9923, 1408 | 9947, 1409 | 9953, 1410 | 9958, 1411 | 9959, 1412 | 9961, 1413 | 9962, 1414 | 9963, 1415 | 9964, 1416 | 9966, 1417 | 9979, 1418 | 9980, 1419 | 9984, 1420 | 9991, 1421 | 9996 1422 | ] -------------------------------------------------------------------------------- /data/punk/punkAttr5.json: -------------------------------------------------------------------------------- 1 | [ 2 | 35, 3 | 173, 4 | 339, 5 | 377, 6 | 460, 7 | 588, 8 | 605, 9 | 621, 10 | 686, 11 | 920, 12 | 925, 13 | 959, 14 | 1183, 15 | 1271, 16 | 1358, 17 | 1493, 18 | 1605, 19 | 1606, 20 | 1636, 21 | 1679, 22 | 1968, 23 | 2033, 24 | 2049, 25 | 2096, 26 | 2183, 27 | 2209, 28 | 2281, 29 | 2319, 30 | 2619, 31 | 2639, 32 | 2657, 33 | 2843, 34 | 2885, 35 | 2894, 36 | 2940, 37 | 3009, 38 | 3112, 39 | 3127, 40 | 3130, 41 | 3196, 42 | 3296, 43 | 3361, 44 | 3437, 45 | 3438, 46 | 3491, 47 | 3555, 48 | 3603, 49 | 3685, 50 | 3700, 51 | 3706, 52 | 3733, 53 | 3852, 54 | 3858, 55 | 3871, 56 | 3961, 57 | 3999, 58 | 4019, 59 | 4122, 60 | 4176, 61 | 4231, 62 | 4316, 63 | 4324, 64 | 4387, 65 | 4415, 66 | 4424, 67 | 4435, 68 | 4590, 69 | 4669, 70 | 4678, 71 | 4716, 72 | 4874, 73 | 4971, 74 | 5057, 75 | 5101, 76 | 5108, 77 | 5149, 78 | 5204, 79 | 5208, 80 | 5211, 81 | 5251, 82 | 5261, 83 | 5362, 84 | 5486, 85 | 5545, 86 | 5647, 87 | 5746, 88 | 5971, 89 | 5978, 90 | 6062, 91 | 6229, 92 | 6262, 93 | 6295, 94 | 6367, 95 | 6466, 96 | 6498, 97 | 6667, 98 | 6795, 99 | 6852, 100 | 6943, 101 | 6949, 102 | 6957, 103 | 6971, 104 | 7007, 105 | 7015, 106 | 7134, 107 | 7182, 108 | 7237, 109 | 7257, 110 | 7291, 111 | 7295, 112 | 7331, 113 | 7334, 114 | 7363, 115 | 7496, 116 | 7602, 117 | 7618, 118 | 7623, 119 | 7661, 120 | 7671, 121 | 7785, 122 | 7866, 123 | 7944, 124 | 7945, 125 | 7953, 126 | 7956, 127 | 8000, 128 | 8035, 129 | 8079, 130 | 8101, 131 | 8187, 132 | 8191, 133 | 8194, 134 | 8222, 135 | 8248, 136 | 8432, 137 | 8618, 138 | 8700, 139 | 8753, 140 | 8770, 141 | 8844, 142 | 8870, 143 | 8889, 144 | 8929, 145 | 9032, 146 | 9036, 147 | 9037, 148 | 9038, 149 | 9097, 150 | 9316, 151 | 9378, 152 | 9385, 153 | 9413, 154 | 9454, 155 | 9536, 156 | 9573, 157 | 9583, 158 | 9674, 159 | 9692, 160 | 9760, 161 | 9785, 162 | 9819, 163 | 9912, 164 | 9925, 165 | 9943, 166 | 9957, 167 | 9960 168 | ] -------------------------------------------------------------------------------- /data/punk/punkZombie.json: -------------------------------------------------------------------------------- 1 | [ 2 | 117, 3 | 987, 4 | 1119, 5 | 1190, 6 | 1374, 7 | 1478, 8 | 1526, 9 | 1658, 10 | 1748, 11 | 1886, 12 | 1935, 13 | 2066, 14 | 2132, 15 | 2249, 16 | 2306, 17 | 2329, 18 | 2338, 19 | 2424, 20 | 2484, 21 | 2560, 22 | 2566, 23 | 2681, 24 | 2708, 25 | 2938, 26 | 2967, 27 | 3211, 28 | 3328, 29 | 3393, 30 | 3489, 31 | 3493, 32 | 3609, 33 | 3636, 34 | 3831, 35 | 4472, 36 | 4513, 37 | 4559, 38 | 4747, 39 | 4830, 40 | 4850, 41 | 4874, 42 | 5066, 43 | 5234, 44 | 5253, 45 | 5299, 46 | 5312, 47 | 5336, 48 | 5412, 49 | 5489, 50 | 5573, 51 | 5742, 52 | 5761, 53 | 5944, 54 | 6275, 55 | 6297, 56 | 6304, 57 | 6491, 58 | 6515, 59 | 6586, 60 | 6649, 61 | 6704, 62 | 6784, 63 | 7014, 64 | 7121, 65 | 7127, 66 | 7252, 67 | 7337, 68 | 7458, 69 | 7660, 70 | 7756, 71 | 7914, 72 | 8127, 73 | 8307, 74 | 8386, 75 | 8472, 76 | 8531, 77 | 8553, 78 | 8780, 79 | 8857, 80 | 8909, 81 | 8957, 82 | 9203, 83 | 9368, 84 | 9474, 85 | 9804, 86 | 9838, 87 | 9909, 88 | 9955, 89 | 9997 90 | ] 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xvault-contracts", 3 | "version": "0.0.1", 4 | "description": "XVault smart contracts", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "test" 8 | }, 9 | "author": "alexgausman", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@nomiclabs/buidler": "^1.4.6", 13 | "@nomiclabs/buidler-ethers": "^2.0.0", 14 | "@nomiclabs/buidler-waffle": "^2.1.0", 15 | "buidler-contract-sizer": "^1.0.2", 16 | "chai": "^4.2.0", 17 | "ethereum-waffle": "^3.1.0", 18 | "ethers": "^5.0.13" 19 | }, 20 | "dependencies": { 21 | "@nomiclabs/buidler-web3": "^1.3.4", 22 | "@openzeppelin/buidler-upgrades": "^1.2.1", 23 | "@openzeppelin/contracts": "^3.2.0", 24 | "dotenv": "^8.2.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/deployCounter.js: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const [deployer] = await ethers.getSigners(); 3 | 4 | console.log( 5 | "Deploying contracts with the account:", 6 | await deployer.getAddress() 7 | ); 8 | 9 | console.log("Account balance:", (await deployer.getBalance()).toString()); 10 | 11 | const Counter = await ethers.getContractFactory("Counter"); 12 | 13 | const counter = await Counter.deploy(); 14 | await counter.deployed(); 15 | 16 | console.log("Counter address:", counter.address); 17 | } 18 | 19 | main() 20 | .then(() => process.exit(0)) 21 | .catch((error) => { 22 | console.error(error); 23 | process.exit(1); 24 | }); 25 | -------------------------------------------------------------------------------- /scripts/deployTAC.js: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const [deployer] = await ethers.getSigners(); 3 | 4 | console.log( 5 | "Deploying contracts with the account:", 6 | await deployer.getAddress() 7 | ); 8 | 9 | console.log("Account balance:", (await deployer.getBalance()).toString()); 10 | 11 | const tokenManagerAddress = "0xbc43db7dddabd068732025107a0026fe758770d2"; 12 | const xDaoAgent = "0xeddb1b92b9ad55a5bb1dcc22c23e7839cd3dc99c"; 13 | 14 | const TAC = await ethers.getContractFactory("TokenAppController"); 15 | 16 | const tac = await TAC.deploy(); 17 | await tac.deployed(); 18 | 19 | await tac.setTokenManager(tokenManagerAddress); 20 | await tac.transferOwnership(xDaoAgent); 21 | 22 | console.log("TAC address:", tac.address); 23 | } 24 | 25 | main() 26 | .then(() => process.exit(0)) 27 | .catch((error) => { 28 | console.error(error); 29 | process.exit(1); 30 | }); 31 | -------------------------------------------------------------------------------- /scripts/deployToMainnet.js: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const [deployer] = await ethers.getSigners(); 3 | 4 | console.log( 5 | "Deploying contracts with the account:", 6 | await deployer.getAddress() 7 | ); 8 | 9 | console.log("Account balance:", (await deployer.getBalance()).toString()); 10 | 11 | const cryptoXsAddress = "0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"; 12 | 13 | const XToken = await ethers.getContractFactory("XToken"); 14 | const XVault = await ethers.getContractFactory("XVault"); 15 | 16 | const xToken = await XToken.deploy("X", "PUNK"); 17 | await xToken.deployed(); 18 | 19 | const xVault = await XVault.deploy(xToken.address, cryptoXsAddress); 20 | await xVault.deployed(); 21 | 22 | await xToken.transferOwnership(xVault.address); 23 | await xVault.transferOwnership("0x8F217D5cCCd08fD9dCe24D6d42AbA2BB4fF4785B"); 24 | // await xVault.setReverseLink(); 25 | // await xVault.increaseSecurityLevel(); 26 | 27 | console.log("XToken address:", xToken.address); 28 | console.log("XVault address:", xVault.address); 29 | } 30 | 31 | main() 32 | .then(() => process.exit(0)) 33 | .catch((error) => { 34 | console.error(error); 35 | process.exit(1); 36 | }); 37 | -------------------------------------------------------------------------------- /scripts/rinkeby/createVaults.js: -------------------------------------------------------------------------------- 1 | const { ethers, upgrades } = require("@nomiclabs/buidler"); 2 | 3 | const addresses = require("../../addresses/rinkeby.json"); 4 | 5 | async function main() { 6 | const [deployer] = await ethers.getSigners(); 7 | 8 | console.log( 9 | "Deploying contracts with the account:", 10 | await deployer.getAddress() 11 | ); 12 | 13 | console.log("Account balance:", (await deployer.getBalance()).toString()); 14 | 15 | const XToken = await ethers.getContractFactory("XToken"); 16 | const xStore = await ethers.getContractAt("XStore", addresses.xStore); 17 | 18 | const nftx = await ethers.getContractAt("NFTX", addresses.nftx); 19 | 20 | const funds = [ 21 | { 22 | ticker: "PUNK-BASIC", 23 | name: "Punk-Basic", 24 | asset: "cryptopunks", 25 | negateElig: true, 26 | }, 27 | { 28 | ticker: "PUNK-ATTR-4", 29 | name: "Punk-Attr-4", 30 | asset: "cryptopunks", 31 | negateElig: false, 32 | }, 33 | { 34 | ticker: "PUNK-ATTR-5", 35 | name: "Punk-Attr-5", 36 | asset: "cryptopunks", 37 | negateElig: false, 38 | }, 39 | { 40 | ticker: "PUNK-ZOMBIE", 41 | name: "Punk-Zombie", 42 | asset: "cryptopunks", 43 | negateElig: false, 44 | }, 45 | { 46 | ticker: "KITTY-GEN-0", 47 | name: "Kitty-Gen-0", 48 | asset: "cryptokitties", 49 | negateElig: false, 50 | }, 51 | { 52 | ticker: "KITTY-GEN-0-F", 53 | name: "Kitty-Gen-0-Fast", 54 | asset: "cryptokitties", 55 | negateElig: false, 56 | flipEligOnRedeem: true, 57 | }, 58 | { 59 | ticker: "KITTY-FANCY", 60 | name: "Kitty-Fancy", 61 | asset: "cryptokitties", 62 | negateElig: false, 63 | }, 64 | { 65 | ticker: "KITTY-FOUNDER", 66 | name: "Kitty-Founder", 67 | asset: "cryptokitties", 68 | negateElig: false, 69 | }, 70 | { 71 | ticker: "AXIE-ORIGIN", 72 | name: "Axie-Origin", 73 | asset: "axies", 74 | negateElig: false, 75 | }, 76 | { 77 | ticker: "AXIE-MYSTIC-1", 78 | name: "Axie-Mystic-1", 79 | asset: "axies", 80 | negateElig: false, 81 | }, 82 | { 83 | ticker: "AXIE-MYSTIC-2", 84 | name: "Axie-Mystic-2", 85 | asset: "axies", 86 | negateElig: false, 87 | }, 88 | { 89 | ticker: "AVASTR-RANK-25", 90 | name: "Avastar-Rank-25", 91 | asset: "avastars", 92 | negateElig: false, 93 | }, 94 | { 95 | ticker: "AVASTR-RANK-50", 96 | name: "Avastar-Rank-50", 97 | asset: "avastars", 98 | negateElig: false, 99 | }, 100 | { 101 | ticker: "AVASTR-RANK-75", 102 | name: "Avastar-Rank-75", 103 | asset: "avastars", 104 | negateElig: false, 105 | }, 106 | { ticker: "GLYPH", name: "Glyph", asset: "autoglyphs", negateElig: true }, 107 | { ticker: "JOY", name: "Joy", asset: "joys", negateElig: false }, 108 | ]; 109 | 110 | for (let i = 0; i < funds.length; i++) { 111 | const fund = funds[i]; 112 | const fundToken = await XToken.deploy( 113 | fund.name, 114 | fund.ticker, 115 | addresses.nftx 116 | ); 117 | await fundToken.deployed(); 118 | console.log(`${fund.ticker} deployed to ${fundToken.address}`); 119 | funds[i].tokenAddress = fundToken.address; 120 | 121 | await nftx.createVault(fund.tokenAddress, addresses[fund.asset], false); 122 | console.log(`Vault created: ${fund.ticker}`); 123 | 124 | await new Promise((resolve) => setTimeout(() => resolve(), 5000)); 125 | console.log('continuing...'); 126 | 127 | if (fund.flipEligOnRedeem) { 128 | await nftx.setFlipEligOnRedeem(i, true, { 129 | gasLimit: "9500000", 130 | }); 131 | console.log(`${fund.ticker} flipEligOnRedeem set to true`); 132 | 133 | await new Promise((resolve) => setTimeout(() => resolve(), 5000)); 134 | console.log('continuing...'); 135 | } 136 | if (fund.negateElig == false) { 137 | await nftx.setNegateEligibility(i, false, { 138 | gasLimit: "9500000", 139 | }); 140 | console.log(`${fund.ticker} negateEligibility set to false`); 141 | 142 | await new Promise((resolve) => setTimeout(() => resolve(), 5000)); 143 | console.log('continuing...'); 144 | } else { 145 | await nftx.finalizeVault(i, { 146 | gasLimit: "9500000", 147 | }); 148 | console.log(`${fund.ticker} finalized`); 149 | 150 | await new Promise((resolve) => setTimeout(() => resolve(), 5000)); 151 | console.log('continuing...'); 152 | } 153 | console.log(""); 154 | } 155 | 156 | console.log("-- DONE --"); 157 | } 158 | 159 | main() 160 | .then(() => process.exit(0)) 161 | .catch((error) => { 162 | console.error(error); 163 | process.exit(1); 164 | }); 165 | -------------------------------------------------------------------------------- /scripts/rinkeby/deploy.js: -------------------------------------------------------------------------------- 1 | const { ethers, upgrades } = require("@nomiclabs/buidler"); 2 | 3 | const addresses = require("../../addresses/rinkeby.json"); 4 | 5 | async function main() { 6 | const [deployer] = await ethers.getSigners(); 7 | 8 | console.log( 9 | "Deploying contracts with the account:", 10 | await deployer.getAddress() 11 | ); 12 | 13 | console.log("Account balance:", (await deployer.getBalance()).toString()); 14 | 15 | const XStore = await ethers.getContractFactory("XStore"); 16 | const xStore = await XStore.deploy(); 17 | await xStore.deployed(); 18 | console.log("XStore address:", xStore.address); 19 | 20 | const Nftx = await ethers.getContractFactory("NFTX"); 21 | let nftx = await upgrades.deployProxy(Nftx, [xStore.address], { 22 | initializer: "initialize", 23 | }); 24 | await nftx.deployed(); 25 | console.log("NFTX proxy address:", nftx.address); 26 | 27 | const ProxyController = await ethers.getContractFactory("ProxyController"); 28 | const proxyController = await ProxyController.deploy(nftx.address); 29 | await proxyController.deployed(); 30 | console.log("ProxyController address:", proxyController.address); 31 | 32 | await upgrades.admin.changeProxyAdmin(nftx.address, proxyController.address); 33 | console.log("Updated NFTX proxy admin"); 34 | 35 | await proxyController.transferOwnership(addresses.dao); 36 | console.log("Updated ProxyController owner"); 37 | 38 | await xStore.transferOwnership(nftx.address); 39 | console.log("Updated XStore owner"); 40 | 41 | await nftx.transferOwnership(addresses.dao); 42 | console.log("Updated NFTX owner"); 43 | 44 | console.log("-- DONE --"); 45 | } 46 | 47 | main() 48 | .then(() => process.exit(0)) 49 | .catch((error) => { 50 | console.error(error); 51 | process.exit(1); 52 | }); 53 | -------------------------------------------------------------------------------- /scripts/rinkeby/setupPunkElig.js: -------------------------------------------------------------------------------- 1 | const { ethers, upgrades } = require("@nomiclabs/buidler"); 2 | 3 | const addresses = require("../../addresses/rinkeby.json"); 4 | 5 | const punkAttr4Ids = require("../../data/punk/punkAttr4.json"); 6 | const punkAttr5Ids = require("../../data/punk/punkAttr5.json"); 7 | const punkZombieIds = require("../../data/punk/punkZombie.json"); 8 | 9 | async function main() { 10 | const [deployer] = await ethers.getSigners(); 11 | 12 | console.log( 13 | "Deploying contracts with the account:", 14 | await deployer.getAddress() 15 | ); 16 | 17 | console.log("Account balance:", (await deployer.getBalance()).toString()); 18 | 19 | const xStore = await ethers.getContractAt("XStore", addresses.xStore); 20 | const nftx = await ethers.getContractAt("NFTX", addresses.nftx); 21 | 22 | const data = [ 23 | { 24 | vaultId: 1, 25 | ids: punkAttr4Ids 26 | }, 27 | { 28 | vaultId: 2, 29 | ids: punkAttr5Ids 30 | }, 31 | { 32 | vaultId: 3, 33 | ids: punkZombieIds 34 | } 35 | ]; 36 | 37 | for (let i = 0; i < data.length; i++) { 38 | const {vaultId, ids} = data[i]; 39 | let j = 0; 40 | while (j < ids.length) { 41 | let k = Math.min(j+500, ids.length); 42 | const nftIds = punkAttr5Ids.slice(j, k); 43 | console.log(`i: ${i}, j: ${j}, k: ${k}\n`); 44 | await nftx.setIsEligible(vaultId, nftIds, true); 45 | j = k; 46 | } 47 | await nftx.finalizeVault(vaultId, { 48 | gasLimit: "9500000", 49 | }); 50 | console.log(`Vault ${vaultId} finalized\n`); 51 | } 52 | 53 | console.log("-- DONE --"); 54 | } 55 | 56 | main() 57 | .then(() => process.exit(0)) 58 | .catch((error) => { 59 | console.error(error); 60 | process.exit(1); 61 | }); 62 | -------------------------------------------------------------------------------- /scripts/temp.js: -------------------------------------------------------------------------------- 1 | // import zombidIds from "../data/zombies"; 2 | 3 | const { ethers, upgrades } = require("@nomiclabs/buidler"); 4 | 5 | const ProxyController = require("../artifacts/ProxyController.json"); 6 | 7 | // const zeroAddress = "0x0000000000000000000000000000000000000000"; 8 | 9 | const rinkebyDaoAddress = "0xeddb1b92b9ad55a5bb1dcc22c23e7839cd3dc99c"; 10 | 11 | const proxyControllerAddr = "0x49706a576bb823cdE3180C930F9947d59e2deD4D"; 12 | 13 | async function main() { 14 | const [deployer] = await ethers.getSigners(); 15 | 16 | console.log("Using the account:", await deployer.getAddress()); 17 | 18 | console.log("Account balance:", (await deployer.getBalance()).toString()); 19 | 20 | const proxyController = await ethers.getContractAt( 21 | "ProxyController", 22 | proxyControllerAddr 23 | ); 24 | 25 | await proxyController.updateImplAddress(); 26 | } 27 | 28 | main() 29 | .then(() => process.exit(0)) 30 | .catch((error) => { 31 | console.error(error); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /test/_helpers.js: -------------------------------------------------------------------------------- 1 | const { BigNumber } = require("ethers"); 2 | 3 | const BASE = BigNumber.from(10).pow(18); 4 | const UNIT = BASE.div(100); 5 | const zeroAddress = "0x0000000000000000000000000000000000000000"; 6 | 7 | const getIntArray = (firstElem, firstNonElem) => { 8 | const arr = []; 9 | for (let i = firstElem; i < firstNonElem; i++) { 10 | arr.push(i); 11 | } 12 | return arr; 13 | }; 14 | 15 | const initializeAssetTokenVault = async ( 16 | nftx, 17 | signers, 18 | assetNameOrExistingContract, 19 | xTokenName, 20 | idsToMint, 21 | isD2 22 | ) => { 23 | const [owner, misc, alice, bob, carol, dave, eve] = signers; 24 | 25 | const XToken = await ethers.getContractFactory("XToken"); 26 | const xToken = await XToken.deploy( 27 | xTokenName, 28 | xTokenName.toUpperCase(), 29 | nftx.address 30 | ); 31 | await xToken.deployed(); 32 | 33 | let asset; 34 | if (typeof assetNameOrExistingContract == "string") { 35 | let name = assetNameOrExistingContract; 36 | if (isD2) { 37 | const Erc20 = await ethers.getContractFactory("D2Token"); 38 | asset = await Erc20.deploy(name, name.toUpperCase()); 39 | } else { 40 | const Erc721 = await ethers.getContractFactory("ERC721"); 41 | asset = await Erc721.deploy(name, name.toUpperCase()); 42 | } 43 | await asset.deployed(); 44 | } else { 45 | asset = assetNameOrExistingContract; 46 | } 47 | const response = await nftx 48 | .connect(owner) 49 | .createVault(xToken.address, asset.address, isD2); 50 | const receipt = await response.wait(0); 51 | 52 | const vaultId = receipt.events 53 | .find((elem) => elem.event === "NewVault") 54 | .args[0].toString(); 55 | await nftx.connect(owner).finalizeVault(vaultId); 56 | if (isD2) { 57 | if (typeof assetNameOrExistingContract == "string") { 58 | await asset.mint(misc._address, BASE.mul(1000)); 59 | } 60 | } else { 61 | await checkMintNFTs(asset, idsToMint, misc); 62 | } 63 | return { asset, xToken, vaultId }; 64 | }; 65 | 66 | const checkMintNFTs = async (nft, nftIds, to) => { 67 | for (let i = 0; i < nftIds.length; i++) { 68 | try { 69 | await nft.ownerOf(nftIds[i]); 70 | } catch (err) { 71 | await nft.safeMint(to._address, nftIds[i]); 72 | } 73 | } 74 | }; 75 | 76 | const transferNFTs = async (nftx, nft, nftIds, sender, recipient) => { 77 | for (let i = 0; i < nftIds.length; i++) { 78 | await nft 79 | .connect(sender) 80 | .transferFrom(sender._address, recipient._address, nftIds[i]); 81 | } 82 | }; 83 | 84 | const setup = async (nftx, nft, signers, eligIds) => { 85 | const [owner, misc, alice, bob, carol, dave, eve] = signers; 86 | await transferNFTs(nftx, nft, eligIds.slice(0, 8), misc, alice); 87 | await transferNFTs(nftx, nft, eligIds.slice(8, 16), misc, bob); 88 | await transferNFTs(nftx, nft, eligIds.slice(16, 19), misc, carol); 89 | await transferNFTs(nftx, nft, eligIds.slice(19, 20), misc, dave); 90 | }; 91 | 92 | const setupD2 = async (nftx, asset, signers) => { 93 | const [owner, misc, alice, bob, carol, dave, eve] = signers; 94 | await asset.connect(misc).transfer(alice._address, BASE.mul(8)); 95 | await asset.connect(misc).transfer(bob._address, BASE.mul(8)); 96 | await asset.connect(misc).transfer(carol._address, BASE.mul(3)); 97 | await asset.connect(misc).transfer(dave._address, BASE.mul(1)); 98 | }; 99 | 100 | const cleanup = async (nftx, nft, token, signers, vaultId, eligIds) => { 101 | const [owner, misc, alice, bob, carol, dave, eve] = signers; 102 | for (let i = 2; i < 7; i++) { 103 | const signer = signers[i]; 104 | const bal = (await token.balanceOf(signer._address)).div(BASE).toNumber(); 105 | if (bal > 0) { 106 | await approveAndRedeem(nftx, token, bal, signer, vaultId); 107 | } 108 | } 109 | for (let i = 0; i < 40; i++) { 110 | try { 111 | const nftId = eligIds[i]; 112 | let addr; 113 | addr = await nft.ownerOf(nftId); 114 | if (addr == misc._address) { 115 | // console.log(`owner of ${nftId} is misc (${addr})`); 116 | continue; 117 | } 118 | const signerIndex = signers.findIndex((s) => s._address == addr); 119 | const signer = signers[signerIndex]; 120 | /* console.log( 121 | `owner of ${nftId} is signer ${signerIndex} (${signer._address})` 122 | ); */ 123 | await nft 124 | .connect(signer) 125 | .transferFrom(signer._address, misc._address, nftId); 126 | } catch (err) { 127 | // console.log("catch:", i, "continuing..."); 128 | break; 129 | } 130 | } 131 | }; 132 | 133 | const cleanupD2 = async (nftx, asset, xToken, signers, vaultId) => { 134 | const [owner, misc, alice, bob, carol, dave, eve] = signers; 135 | for (let i = 2; i < 7; i++) { 136 | const signer = signers[i]; 137 | const xBal = await xToken.balanceOf(signer._address); 138 | if (xBal.gt(0)) { 139 | await approveAndRedeemD2(nftx, xToken, bal, signer, vaultId, 0); 140 | } 141 | const assetBal = await asset.balanceOf(signer._address); 142 | if (assetBal.gt(0)) { 143 | await asset.connect(signer).transfer(misc._address, assetBal); 144 | } 145 | } 146 | }; 147 | 148 | const holdingsOf = async (nft, nftIds, accounts, isD2) => { 149 | const lists = []; 150 | for (let i = 0; i < accounts.length; i++) { 151 | const account = accounts[i]; 152 | const list = []; 153 | for (let _i = 0; _i < nftIds.length; _i++) { 154 | const id = nftIds[_i]; 155 | const nftOwner = await nft.ownerOf(id); 156 | if (nftOwner === account._address) { 157 | list.push(id); 158 | } 159 | } 160 | lists.push(list); 161 | } 162 | return lists; 163 | }; 164 | 165 | const balancesOf = async (token, accounts) => { 166 | const balances = []; 167 | for (let i = 0; i < accounts.length; i++) { 168 | balances.push(await token.balanceOf(accounts[i]._address)); 169 | } 170 | return balances; 171 | }; 172 | 173 | const checkBalances = async (nftx, nft, xToken, users) => { 174 | let tokenAmount = BigNumber.from(0); 175 | for (let i = 0; i < users.length; i++) { 176 | const user = users[i]; 177 | const bal = await xToken.balanceOf(user._address); 178 | tokenAmount = tokenAmount.add(bal); 179 | } 180 | const nftAmount = await nft.balanceOf(nftx.address); 181 | if (!nftAmount.mul(BASE).eq(tokenAmount)) { 182 | throw "Balances do not match up"; 183 | } 184 | }; 185 | 186 | const checkBalancesD2 = async (nftx, asset, xToken, accounts) => { 187 | let tokenAmount = BigNumber.from(0); 188 | const balances = await balancesOf(xToken, accounts); 189 | balances.forEach((balance) => { 190 | tokenAmount = tokenAmount.add(balance); 191 | }); 192 | const contractBal = await asset.balanceOf(nftx.address); 193 | if (tokenAmount.toString() !== contractBal.toString()) { 194 | throw "Balances do not match up (D2)"; 195 | } 196 | }; 197 | 198 | const approveEach = async (nft, nftIds, signer, to) => { 199 | for (let i = 0; i < nftIds.length; i++) { 200 | const nftId = nftIds[i]; 201 | await nft.connect(signer).approve(to, nftId); 202 | } 203 | }; 204 | 205 | const approveAndMint = async (nftx, nft, nftIds, signer, vaultId, value) => { 206 | await approveEach(nft, nftIds, signer, nftx.address); 207 | await nftx.connect(signer).mint(vaultId, nftIds, 0, { value: value }); 208 | }; 209 | 210 | const approveAndMintD2 = async ( 211 | nftx, 212 | asset, 213 | amount, 214 | signer, 215 | vaultId, 216 | value 217 | ) => { 218 | await asset.connect(signer).approve(nftx.address, amount); 219 | await nftx.connect(signer).mint(vaultId, [], amount, { value: value }); 220 | }; 221 | 222 | const approveAndRedeem = async ( 223 | nftx, 224 | xToken, 225 | amount, 226 | signer, 227 | vaultId, 228 | value = 0 229 | ) => { 230 | await xToken 231 | .connect(signer) 232 | .approve(nftx.address, BASE.mul(amount).toString()); 233 | await nftx.connect(signer).redeem(vaultId, amount, { value: value }); 234 | }; 235 | 236 | const approveAndRedeemD2 = async ( 237 | nftx, 238 | xToken, 239 | amount, 240 | signer, 241 | vaultId, 242 | value 243 | ) => { 244 | await xToken.connect(signer).approve(nftx.address, amount); 245 | await nftx.connect(signer).redeem(vaultId, amount, { value: value }); 246 | }; 247 | 248 | exports.getIntArray = getIntArray; 249 | exports.initializeAssetTokenVault = initializeAssetTokenVault; 250 | exports.checkMintNFTs = checkMintNFTs; 251 | exports.transferNFTs = transferNFTs; 252 | exports.setup = setup; 253 | exports.setupD2 = setupD2; 254 | exports.cleanup = cleanup; 255 | exports.cleanupD2 = cleanupD2; 256 | exports.holdingsOf = holdingsOf; 257 | exports.balancesOf = balancesOf; 258 | exports.checkBalances = checkBalances; 259 | exports.checkBalancesD2 = checkBalancesD2; 260 | exports.approveEach = approveEach; 261 | exports.approveAndMint = approveAndMint; 262 | exports.approveAndMintD2 = approveAndMintD2; 263 | exports.approveAndRedeem = approveAndRedeem; 264 | exports.approveAndRedeemD2 = approveAndRedeemD2; 265 | -------------------------------------------------------------------------------- /test/_runVaultTests.js: -------------------------------------------------------------------------------- 1 | const { BigNumber } = require("ethers"); 2 | const { expect } = require("chai"); 3 | const { expectRevert } = require("../utils/expectRevert"); 4 | 5 | const BASE = BigNumber.from(10).pow(18); 6 | const UNIT = BASE.div(100); 7 | 8 | const { 9 | transferNFTs, 10 | setup, 11 | cleanup, 12 | holdingsOf, 13 | checkBalances, 14 | approveEach, 15 | approveAndMint, 16 | approveAndRedeem, 17 | setupD2, 18 | balancesOf, 19 | approveAndMintD2, 20 | checkBalancesD2, 21 | approveAndRedeemD2, 22 | cleanupD2, 23 | } = require("./_helpers"); 24 | 25 | const runVaultTests = async ( 26 | nftx, 27 | asset, 28 | xToken, 29 | signers, 30 | vaultId, 31 | allNftIds, 32 | eligIds, 33 | isD2 34 | ) => { 35 | const [owner, misc, alice, bob, carol, dave, eve] = signers; 36 | 37 | const notEligIds = allNftIds.filter((elem) => !eligIds.includes(elem)); 38 | 39 | ////////////////// 40 | // mint, redeem // 41 | ////////////////// 42 | 43 | const runMintRedeem = async () => { 44 | console.log("Testing: mint, redeem...\n"); 45 | await setup(nftx, asset, signers, eligIds); 46 | let [aliceNFTs, bobNFTs] = await holdingsOf(asset, eligIds, [alice, bob]); 47 | 48 | await approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, 0); 49 | await approveAndMint(nftx, asset, bobNFTs, bob, vaultId, 0); 50 | await checkBalances(nftx, asset, xToken, signers.slice(2)); 51 | await approveAndRedeem(nftx, xToken, aliceNFTs.length, alice, vaultId); 52 | await approveAndRedeem(nftx, xToken, bobNFTs.length, bob, vaultId); 53 | 54 | [aliceNFTs, bobNFTs] = await holdingsOf(asset, eligIds, [alice, bob]); 55 | console.log(aliceNFTs); 56 | console.log(bobNFTs, "\n"); 57 | await checkBalances(nftx, asset, xToken, signers.slice(2)); 58 | await cleanup(nftx, asset, xToken, signers, vaultId, eligIds); 59 | }; 60 | 61 | const runMintRedeemD2 = async () => { 62 | console.log("Testing (D2): mint, redeem...\n"); 63 | await setupD2(nftx, asset, signers); 64 | const [aliceBal, bobBal] = await balancesOf(asset, [alice, bob]); 65 | await approveAndMintD2(nftx, asset, aliceBal, alice, vaultId, 0); 66 | await approveAndMintD2(nftx, asset, bobBal, bob, vaultId, 0); 67 | await checkBalancesD2(nftx, asset, xToken, [alice, bob]); 68 | await approveAndRedeemD2(nftx, xToken, aliceBal, alice, vaultId, 0); 69 | await approveAndRedeemD2(nftx, xToken, bobBal, bob, vaultId, 0); 70 | 71 | await checkBalancesD2(nftx, asset, xToken, [alice, bob]); 72 | await cleanupD2(nftx, asset, xToken, signers, vaultId); 73 | }; 74 | 75 | /////////////////// 76 | // mintAndRedeem // 77 | /////////////////// 78 | 79 | const runMintAndRedeem = async () => { 80 | console.log("Testing: mintAndRedeem...\n"); 81 | await setup(nftx, asset, signers, eligIds); 82 | let [aliceNFTs, bobNFTs] = await holdingsOf(asset, eligIds, [alice, bob]); 83 | await approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, 0); 84 | 85 | await approveEach(asset, bobNFTs, bob, nftx.address); 86 | await nftx.connect(bob).mintAndRedeem(vaultId, bobNFTs); 87 | await checkBalances(nftx, asset, xToken, signers.slice(2)); 88 | 89 | [bobNFTs] = await holdingsOf(asset, eligIds, [bob]); 90 | console.log(bobNFTs, "\n"); 91 | await cleanup(nftx, asset, xToken, signers, vaultId, eligIds); 92 | }; 93 | 94 | //////////////////////// 95 | // mintFees, burnFees // 96 | //////////////////////// 97 | 98 | const runMintFeesBurnFees = async () => { 99 | console.log("Testing: mintFees, burnFees...\n"); 100 | await setup(nftx, asset, signers, eligIds); 101 | let [aliceNFTs, bobNFTs] = await holdingsOf(asset, eligIds, [alice, bob]); 102 | await nftx.connect(owner).setMintFees(vaultId, UNIT.mul(5), UNIT); 103 | await nftx.connect(owner).setBurnFees(vaultId, UNIT.mul(5), UNIT); 104 | 105 | const n = aliceNFTs.length; 106 | let amount = UNIT.mul(5).add(UNIT.mul(n - 1)); 107 | await expectRevert( 108 | approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, amount.sub(1)) 109 | ); 110 | await approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, amount); 111 | await checkBalances(nftx, asset, xToken, signers.slice(2)); 112 | await expectRevert( 113 | approveAndRedeem(nftx, xToken, n, alice, vaultId, amount.sub(1)) 114 | ); 115 | await approveAndRedeem(nftx, xToken, n, alice, vaultId, amount); 116 | await checkBalances(nftx, asset, xToken, signers.slice(2)); 117 | 118 | await nftx.connect(owner).setMintFees(vaultId, 0, 0); 119 | await nftx.connect(owner).setBurnFees(vaultId, 0, 0); 120 | await cleanup(nftx, asset, xToken, signers, vaultId, eligIds); 121 | }; 122 | 123 | const runMintFeesBurnFeesD2 = async () => { 124 | console.log("Testing (D2): mintFees, burnFees...\n"); 125 | await setupD2(nftx, asset, signers); 126 | const [aliceBal] = await balancesOf(asset, [alice]); 127 | await nftx.connect(owner).setMintFees(vaultId, UNIT.mul(5), UNIT); 128 | await nftx.connect(owner).setBurnFees(vaultId, UNIT.mul(5), UNIT); 129 | 130 | const n = aliceBal.div(BASE); 131 | const amount = UNIT.mul(5).add(UNIT.mul(n - 1)); 132 | await expectRevert( 133 | approveAndMintD2(nftx, asset, aliceBal, alice, vaultId, amount.sub(1)) 134 | ); 135 | await approveAndMintD2(nftx, asset, aliceBal, alice, vaultId, amount); 136 | await checkBalancesD2(nftx, asset, xToken, [alice]); 137 | await expectRevert( 138 | approveAndRedeemD2(nftx, xToken, aliceBal, alice, vaultId, amount.sub(1)) 139 | ); 140 | await approveAndRedeemD2(nftx, xToken, aliceBal, alice, vaultId, amount); 141 | await nftx.connect(owner).setMintFees(vaultId, 0, 0); 142 | await nftx.connect(owner).setBurnFees(vaultId, 0, 0); 143 | await checkBalancesD2(nftx, asset, xToken, [alice]); 144 | await cleanupD2(nftx, asset, xToken, signers, vaultId); 145 | }; 146 | 147 | ////////////// 148 | // dualFees // 149 | ////////////// 150 | 151 | const runDualFees = async () => { 152 | console.log("Testing: dualFees...\n"); 153 | await setup(nftx, asset, signers, eligIds); 154 | let [aliceNFTs, bobNFTs] = await holdingsOf(asset, eligIds, [alice, bob]); 155 | await nftx.connect(owner).setDualFees(vaultId, UNIT.mul(5), UNIT); 156 | await approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, 0); 157 | 158 | await approveEach(asset, bobNFTs, bob, nftx.address); 159 | let amount = UNIT.mul(5).add(UNIT.mul(bobNFTs.length - 1)); 160 | await expectRevert( 161 | nftx 162 | .connect(bob) 163 | .mintAndRedeem(vaultId, bobNFTs, { value: amount.sub(1) }) 164 | ); 165 | await nftx.connect(bob).mintAndRedeem(vaultId, bobNFTs, { value: amount }); 166 | 167 | await nftx.connect(owner).setDualFees(vaultId, 0, 0); 168 | await cleanup(nftx, asset, xToken, signers, vaultId, eligIds); 169 | }; 170 | 171 | //////////////////// 172 | // supplierBounty // 173 | //////////////////// 174 | 175 | const runSupplierBounty = async () => { 176 | console.log("Testing: supplierBounty...\n"); 177 | await setup(nftx, asset, signers, eligIds); 178 | let [aliceNFTs] = await holdingsOf(asset, eligIds, [alice]); 179 | await nftx.connect(owner).depositETH(vaultId, { value: UNIT.mul(100) }); 180 | await nftx.connect(owner).setSupplierBounty(vaultId, UNIT.mul(10), 5); 181 | 182 | let nftxBal1 = BigNumber.from(await web3.eth.getBalance(nftx.address)); 183 | let aliceBal1 = BigNumber.from(await web3.eth.getBalance(alice._address)); 184 | await approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, 0); 185 | let nftxBal2 = BigNumber.from(await web3.eth.getBalance(nftx.address)); 186 | let aliceBal2 = BigNumber.from(await web3.eth.getBalance(alice._address)); 187 | expect(nftxBal2.toString()).to.equal( 188 | nftxBal1.sub(UNIT.mul(10 + 8 + 6 + 4 + 2)).toString() 189 | ); 190 | expect(aliceBal2.gt(aliceBal1)).to.equal(true); 191 | await approveAndRedeem( 192 | nftx, 193 | xToken, 194 | aliceNFTs.length, 195 | alice, 196 | vaultId, 197 | UNIT.mul(10 + 8 + 6 + 4 + 2).toString() 198 | ); 199 | let nftxBal3 = BigNumber.from(await web3.eth.getBalance(nftx.address)); 200 | let aliceBal3 = BigNumber.from(await web3.eth.getBalance(alice._address)); 201 | expect(nftxBal3.toString()).to.equal(nftxBal1.toString()); 202 | expect(aliceBal3.lt(aliceBal2)).to.equal(true); 203 | 204 | await nftx.connect(owner).setSupplierBounty(vaultId, 0, 0); 205 | await cleanup(nftx, asset, xToken, signers, vaultId, eligIds); 206 | }; 207 | 208 | const runSupplierBountyD2 = async () => { 209 | console.log("Testing (D2): supplierBounty...\n"); 210 | await setupD2(nftx, asset, signers); 211 | const [aliceBal] = await balancesOf(asset, [alice]); 212 | await nftx.connect(owner).depositETH(vaultId, { value: UNIT.mul(100) }); 213 | await nftx 214 | .connect(owner) 215 | .setSupplierBounty(vaultId, UNIT.mul(10), BASE.mul(5)); 216 | 217 | let nftxBal1 = BigNumber.from(await web3.eth.getBalance(nftx.address)); 218 | let aliceBal1 = BigNumber.from(await web3.eth.getBalance(alice._address)); 219 | await approveAndMintD2(nftx, asset, aliceBal, alice, vaultId, 0); 220 | await checkBalancesD2(nftx, asset, xToken, [alice]); 221 | let nftxBal2 = BigNumber.from(await web3.eth.getBalance(nftx.address)); 222 | let aliceBal2 = BigNumber.from(await web3.eth.getBalance(alice._address)); 223 | expect(nftxBal1.sub(nftxBal2).toString()).to.equal( 224 | UNIT.mul(10).mul(5).div(2).toString() 225 | ); 226 | expect(aliceBal2.gt(aliceBal1)).to.equal(true); 227 | await approveAndRedeemD2( 228 | nftx, 229 | xToken, 230 | aliceBal, 231 | alice, 232 | vaultId, 233 | UNIT.mul(10).mul(5).div(2) 234 | ); 235 | let nftxBal3 = BigNumber.from(await web3.eth.getBalance(nftx.address)); 236 | let aliceBal3 = BigNumber.from(await web3.eth.getBalance(alice._address)); 237 | expect(nftxBal3.toString()).to.equal(nftxBal1.toString()); 238 | expect(aliceBal3.lt(aliceBal2)).to.equal(true); 239 | await checkBalancesD2(nftx, asset, xToken, [alice]); 240 | 241 | await nftx.connect(owner).setSupplierBounty(vaultId, 0, 0); 242 | await cleanupD2(nftx, asset, xToken, signers, vaultId); 243 | }; 244 | 245 | //////////////// 246 | // isEligible // 247 | //////////////// 248 | 249 | const runIsEligible = async () => { 250 | console.log("Testing: isEligible...\n"); 251 | await setup(nftx, asset, signers, eligIds); 252 | let [aliceNFTs] = await holdingsOf(asset, eligIds, [alice]); 253 | let nftIds = notEligIds.slice(0, 2); 254 | await transferNFTs(nftx, asset, nftIds, misc, alice); 255 | await expectRevert(approveAndMint(nftx, asset, nftIds, alice, vaultId, 0)); 256 | await approveAndMint(nftx, asset, eligIds.slice(0, 2), alice, vaultId, 0); 257 | 258 | await cleanup(nftx, asset, xToken, signers, vaultId, allNftIds); 259 | }; 260 | 261 | //////////////////// 262 | // isEligibleFlip // 263 | //////////////////// 264 | 265 | const runIsEligibleFlip = async () => { 266 | console.log("Testing: isEligibleFlip...\n"); 267 | await setup(nftx, asset, signers, eligIds); 268 | await nftx.connect(owner).setFlipEligOnRedeem(vaultId, true); 269 | let [aliceNFTs] = await holdingsOf(asset, eligIds, [alice]); 270 | let nftIds = aliceNFTs.slice(0, 1); 271 | await approveAndMint(nftx, asset, nftIds, alice, vaultId, 0); 272 | await approveAndRedeem(nftx, xToken, nftIds.length, alice, vaultId, 0); 273 | let [newAliceNFTs] = await holdingsOf(asset, eligIds, [alice]); 274 | await expect(JSON.stringify(aliceNFTs)).to.equal( 275 | JSON.stringify(newAliceNFTs) 276 | ); 277 | await expectRevert(approveAndMint(nftx, asset, nftIds, alice, vaultId, 0)); 278 | await nftx.connect(owner).setFlipEligOnRedeem(vaultId, false); 279 | await cleanup(nftx, asset, xToken, signers, vaultId, allNftIds); 280 | }; 281 | 282 | ///////////////// 283 | // requestMint // 284 | ///////////////// 285 | 286 | const runRequestMint = async () => { 287 | console.log("Testing requestMint...\n"); 288 | await setup(nftx, asset, signers, allNftIds); 289 | let [aliceNFTs] = await holdingsOf(asset, notEligIds, [alice]); 290 | let nftIds = aliceNFTs.slice(0, 2); 291 | await expectRevert(approveAndMint(nftx, asset, nftIds, alice, vaultId, 0)); 292 | await approveEach(asset, nftIds, alice, nftx.address); 293 | await nftx.connect(alice).requestMint(vaultId, nftIds); 294 | await nftx.connect(alice).revokeMintRequests(vaultId, nftIds); 295 | await expectRevert(nftx.connect(owner).approveMintRequest(vaultId, nftIds)); 296 | let [newAliceNFTs] = await holdingsOf(asset, notEligIds, [alice]); 297 | await expect(JSON.stringify(aliceNFTs)).to.equal( 298 | JSON.stringify(newAliceNFTs) 299 | ); 300 | await approveEach(asset, nftIds, alice, nftx.address); 301 | await nftx.connect(alice).requestMint(vaultId, nftIds); 302 | await expectRevert(nftx.connect(alice).approveMintRequest(vaultId, nftIds)); 303 | await nftx.connect(owner).approveMintRequest(vaultId, nftIds); 304 | await expectRevert(nftx.connect(alice).revokeMintRequests(vaultId, nftIds)); 305 | await approveAndRedeem(nftx, xToken, nftIds.length, alice, vaultId, 0); 306 | await approveAndMint(nftx, asset, nftIds, alice, vaultId, 0); 307 | await approveAndRedeem(nftx, xToken, nftIds.length, alice, vaultId, 0); 308 | await cleanup(nftx, asset, xToken, signers, vaultId, allNftIds); 309 | }; 310 | 311 | ////////////////////////// 312 | // Run Feature Tests... // 313 | ////////////////////////// 314 | 315 | if (isD2) { 316 | await runMintRedeemD2(); 317 | await runMintFeesBurnFeesD2(); 318 | await runSupplierBountyD2(); 319 | } else { 320 | await runMintRedeem(); 321 | // await runMintAndRedeem(); 322 | await runMintFeesBurnFees(); 323 | // await runDualFees(); 324 | await runSupplierBounty(); 325 | eligIds[1] - eligIds[0] > 1 && (await runIsEligible()); 326 | eligIds[1] - eligIds[0] > 1 && (await runIsEligibleFlip()); 327 | eligIds[1] - eligIds[0] > 1 && (await runRequestMint()); 328 | } 329 | 330 | console.log("\n-- Vault tests complete --\n\n"); 331 | }; 332 | 333 | exports.runVaultTests = runVaultTests; 334 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | const { expectRevert } = require("../utils/expectRevert"); 2 | const { runVaultTests } = require("./_runVaultTests"); 3 | const { 4 | getIntArray, 5 | initializeAssetTokenVault, 6 | setup, 7 | holdingsOf, 8 | approveAndMint, 9 | checkBalances, 10 | approveAndRedeem, 11 | cleanup, 12 | } = require("./_helpers"); 13 | 14 | const bre = require("@nomiclabs/buidler"); 15 | const { ethers, upgrades } = bre; 16 | 17 | const { 18 | getProxyFactory, 19 | getProxyAdminFactory, 20 | } = require("@openzeppelin/buidler-upgrades/dist/proxy-factory"); 21 | 22 | describe("NFTX", function () { 23 | this.timeout(0); 24 | it("Should run as expected", async function () { 25 | console.log(""); 26 | 27 | /////////////////////////////////////////////////////////////// 28 | // Initialize XStore + NFTX /////////////////////////////////// 29 | /////////////////////////////////////////////////////////////// 30 | 31 | const XStore = await ethers.getContractFactory("XStore"); 32 | const xStore = await XStore.deploy(); 33 | await xStore.deployed(); 34 | 35 | const Nftx = await ethers.getContractFactory("NFTX"); 36 | let nftx = await upgrades.deployProxy(Nftx, [xStore.address], { 37 | initializer: "initialize", 38 | }); 39 | await nftx.deployed(); 40 | await xStore.transferOwnership(nftx.address); 41 | 42 | const signers = await ethers.getSigners(); 43 | const [owner, misc, alice, bob, carol, dave, eve, proxyAdmin] = signers; 44 | 45 | const allNftIds = getIntArray(0, 40); 46 | 47 | /////////////// 48 | // NFT-basic // 49 | /////////////// 50 | 51 | const runNftBasic = async () => { 52 | const { asset, xToken, vaultId } = await initializeAssetTokenVault( 53 | nftx, 54 | signers, 55 | "NFT", 56 | "XToken", 57 | allNftIds, 58 | false 59 | ); 60 | const eligIds = getIntArray(0, 20); 61 | await runVaultTests( 62 | nftx, 63 | asset, 64 | xToken, 65 | signers, 66 | vaultId, 67 | allNftIds, 68 | eligIds, 69 | false 70 | ); 71 | }; 72 | 73 | ///////////////// 74 | // NFT-special // 75 | ///////////////// 76 | 77 | let _asset; 78 | const runNftSpecial = async () => { 79 | const { asset, xToken, vaultId } = await initializeAssetTokenVault( 80 | nftx, 81 | signers, 82 | "NFT", 83 | "XToken", 84 | allNftIds, 85 | false 86 | ); 87 | const eligIds = getIntArray(0, 20).map((n) => n * 2); 88 | nftx.connect(owner).setNegateEligibility(vaultId, false); 89 | nftx.connect(owner).setIsEligible(vaultId, eligIds, true); 90 | await runVaultTests( 91 | nftx, 92 | asset, 93 | xToken, 94 | signers, 95 | vaultId, 96 | allNftIds, 97 | eligIds, 98 | false 99 | ); 100 | _asset = asset; 101 | }; 102 | 103 | /////////////////// 104 | // NFT-special-2 // 105 | /////////////////// 106 | 107 | const runNftSpecial2 = async () => { 108 | if (!_asset) { 109 | console.log("No _asset object..."); 110 | return; 111 | } 112 | const { asset, xToken, vaultId } = await initializeAssetTokenVault( 113 | nftx, 114 | signers, 115 | _asset, 116 | "XToken", 117 | allNftIds, 118 | false 119 | ); 120 | const eligIds = getIntArray(0, 20).map((n) => n * 2 + 1); 121 | const notEligIds = allNftIds.filter((elem) => !eligIds.includes(elem)); 122 | nftx.connect(owner).setNegateEligibility(vaultId, false); 123 | nftx.connect(owner).setIsEligible(vaultId, eligIds, true); 124 | nftx.connect(owner).setIsEligible(vaultId, notEligIds, false); 125 | await runVaultTests( 126 | nftx, 127 | asset, 128 | xToken, 129 | signers, 130 | vaultId, 131 | allNftIds, 132 | eligIds, 133 | false 134 | ); 135 | }; 136 | 137 | ////////////// 138 | // D2 Vault // 139 | ////////////// 140 | 141 | const runD2Vault = async () => { 142 | const { asset, xToken, vaultId } = await initializeAssetTokenVault( 143 | nftx, 144 | signers, 145 | "Punk-BPT", 146 | "Punk", 147 | allNftIds, 148 | true 149 | ); 150 | await runVaultTests(nftx, asset, xToken, signers, vaultId, [], [], true); 151 | }; 152 | 153 | /////////////////////////// 154 | // NFTX contract upgrade // 155 | /////////////////////////// 156 | 157 | const runContractUpgrade = async () => { 158 | console.log("Testing: Contract upgrade...\n"); 159 | 160 | const { asset, xToken, vaultId } = await initializeAssetTokenVault( 161 | nftx, 162 | signers, 163 | "NFT", 164 | "XToken", 165 | allNftIds, 166 | false 167 | ); 168 | const eligIds = getIntArray(0, 20); 169 | await setup(nftx, asset, signers, eligIds); 170 | const [aliceNFTs] = await holdingsOf(asset, eligIds, [alice], false); 171 | await approveAndMint(nftx, asset, aliceNFTs, alice, vaultId, 0); 172 | await checkBalances(nftx, asset, xToken, [alice]); 173 | 174 | const NFTXv2 = await ethers.getContractFactory("NFTXv2"); 175 | // nftx = await upgrades.upgradeProxy(nftx.address, NFTXv2); 176 | const nftxV2Address = await upgrades.prepareUpgrade(nftx.address, NFTXv2); 177 | 178 | const ProxyController = await ethers.getContractFactory("ProxyController"); 179 | const pc = await ProxyController.deploy(nftx.address); 180 | await pc.deployed(); 181 | await upgrades.admin.changeProxyAdmin(nftx.address, pc.address); 182 | 183 | await pc.connect(owner).upgradeProxyTo(nftxV2Address); 184 | nftx = NFTXv2.attach(nftx.address); 185 | 186 | const nftId = aliceNFTs[0]; 187 | await nftx.transferERC721(vaultId, nftId, bob._address); 188 | await expectRevert( 189 | approveAndRedeem(nftx, xToken, aliceNFTs.length, alice, vaultId) 190 | ); 191 | await asset.connect(bob).transferFrom(bob._address, nftx.address, nftId); 192 | await approveAndRedeem(nftx, xToken, aliceNFTs.length, alice, vaultId); 193 | 194 | await checkBalances(nftx, asset, xToken, [alice]); 195 | await pc.connect(owner).changeProxyAdmin(proxyAdmin._address); 196 | await cleanup(nftx, asset, xToken, signers, vaultId, eligIds); 197 | }; 198 | 199 | //////////////////////////////////////////////////////////////////// 200 | // Run Vault Tests... ////////////////////////////////////////////// 201 | //////////////////////////////////////////////////////////////////// 202 | 203 | await runNftBasic(); 204 | await runNftSpecial(); 205 | await runNftSpecial2(); 206 | await runD2Vault(); 207 | await runContractUpgrade(); 208 | 209 | //////////////////////////////////////////////////////////////////// 210 | // Initialize XController... /////////////////////////////////////// 211 | //////////////////////////////////////////////////////////////////// 212 | 213 | const XController = await ethers.getContractFactory("XController"); 214 | let xController = await upgrades.deployProxy(XController, [nftx.address], { 215 | initializer: "initXController", 216 | }); 217 | await xController.deployed(); 218 | 219 | await nftx.transferOwnership(xController.address); 220 | 221 | // TODO - Test timelocks and controller interface 222 | 223 | ////////////////////////////////// 224 | // XController contract upgrade // 225 | ////////////////////////////////// 226 | 227 | // TODO 228 | 229 | //////////////////////////////////////////////////////////////////// 230 | // Initialize UpgradeController... ///////////////////////////////// 231 | //////////////////////////////////////////////////////////////////// 232 | 233 | // TODO 234 | 235 | /////////////////////////////////// 236 | // NFTX proxied contract upgrade // 237 | /////////////////////////////////// 238 | 239 | // TODO 240 | 241 | ////////////////////////////////////////// 242 | // XController proxied contract upgrade // 243 | ////////////////////////////////////////// 244 | 245 | // TODO 246 | 247 | //////////////////////////////////////////////////////////////////// 248 | }); 249 | }); 250 | -------------------------------------------------------------------------------- /utils/expectRevert.js: -------------------------------------------------------------------------------- 1 | const { fail } = require("assert"); 2 | 3 | const expectException = async (promise, expectedError) => { 4 | try { 5 | await promise; 6 | } catch (error) { 7 | if (error.message.indexOf(expectedError) === -1) { 8 | const actualError = error.message.replace( 9 | "Returned error: VM Exception while processing transaction: ", 10 | "" 11 | ); 12 | fail(actualError); // , expectedError, 'Wrong kind of exception received'); 13 | } 14 | return; 15 | } 16 | 17 | fail("Expected an exception but none was received"); 18 | }; 19 | 20 | const expectRevert = async (promise) => { 21 | await expectException(promise, "revert"); 22 | }; 23 | 24 | expectRevert.assertion = (promise) => 25 | expectException(promise, "invalid opcode"); 26 | expectRevert.outOfGas = (promise) => expectException(promise, "out of gas"); 27 | expectRevert.unspecified = (promise) => expectException(promise, "revert"); 28 | 29 | exports.expectException = expectException; 30 | exports.expectRevert = expectRevert; 31 | --------------------------------------------------------------------------------