From 7c2de0af81f6d1a6cf8970e43d6a5fcd1ce654e6 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 5 Apr 2021 12:00:40 +0800 Subject: [PATCH] add initializableERC721 --- config/kovan-config.js | 1 + contracts/Factory/ERC721Factory.sol | 2 +- .../SmartRoute/proxies/DODOUpCpProxy.sol | 77 ++++++ .../external/ERC721/InitializableERC721.sol | 232 ++++++++++++++++-- contracts/intf/IERC721Metadata.sol | 28 +++ contracts/lib/Address.sol | 190 ++++++++++++++ contracts/lib/Strings.sol | 68 +++++ migrations/4_deploy_periphery.js | 1 + 8 files changed, 583 insertions(+), 16 deletions(-) create mode 100644 contracts/SmartRoute/proxies/DODOUpCpProxy.sol create mode 100644 contracts/intf/IERC721Metadata.sol create mode 100644 contracts/lib/Address.sol create mode 100644 contracts/lib/Strings.sol diff --git a/config/kovan-config.js b/config/kovan-config.js index 6f31fec..fc15bff 100644 --- a/config/kovan-config.js +++ b/config/kovan-config.js @@ -49,6 +49,7 @@ module.exports = { DSPProxy: "0xC5fF477667E29df8887D258CaE593e04A1961A69", CpProxy: "0x0e3DAAa1eCfBF2fA4cb990dC34D760c38279C5a8", + //vDODO DODOCirculationHelper: "", Governance: "", diff --git a/contracts/Factory/ERC721Factory.sol b/contracts/Factory/ERC721Factory.sol index 74e6e63..f01c8ec 100644 --- a/contracts/Factory/ERC721Factory.sol +++ b/contracts/Factory/ERC721Factory.sol @@ -45,7 +45,7 @@ contract ERC721Facotry { string memory baseUrl ) external returns (address newERC721) { newERC721 = ICloneFactory(_CLONE_FACTORY_).clone(_ERC721_TEMPLATE_); - InitializableERC721(newERC721).init(msg.sender, name, symbol, baseUrl); + InitializableERC721(newERC721).init(name, symbol, baseUrl); _USER_REGISTRY_[msg.sender].push(newERC721); emit NewERC721(newERC721, msg.sender); } diff --git a/contracts/SmartRoute/proxies/DODOUpCpProxy.sol b/contracts/SmartRoute/proxies/DODOUpCpProxy.sol new file mode 100644 index 0000000..258ca4d --- /dev/null +++ b/contracts/SmartRoute/proxies/DODOUpCpProxy.sol @@ -0,0 +1,77 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IDODOApproveProxy} from "../DODOApproveProxy.sol"; +import {IDODOV2} from "./../intf/IDODOV2.sol"; +import {IERC20} from "../../intf/IERC20.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; +import {IWETH} from "../../intf/IWETH.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; +import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol"; + +/** + * @title DODOUpCpProxy + * @author DODO Breeder + * + * @notice UpCrowdPooling Proxy (temporary) + */ +contract DODOUpCpProxy is ReentrancyGuard { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + address public immutable _UPCP_FACTORY_; + + fallback() external payable {} + + receive() external payable {} + + constructor( + address upCpFactory, + address payable weth + ) public { + _UPCP_FACTORY_ = upCpFactory; + _WETH_ = weth; + } + + //============ UpCrowdPooling Functions (create) ============ + + function createUpCrowdPooling( + address creator, + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256[] memory timeLine, + uint256[] memory valueList, + bool isOpenTWAP + ) external payable preventReentrant returns (address payable newUpCrowdPooling) { + address _baseToken = baseToken; + address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + + newUpCrowdPooling = IDODOV2(_UPCP_FACTORY_).createCrowdPooling(); + + IERC20(_baseToken).safeTransferFrom(msg.sender, newUpCrowdPooling, baseInAmount); + + newUpCrowdPooling.transfer(msg.value); + + IDODOV2(_UPCP_FACTORY_).initCrowdPooling( + newUpCrowdPooling, + creator, + _baseToken, + _quoteToken, + timeLine, + valueList, + isOpenTWAP + ); + } +} diff --git a/contracts/external/ERC721/InitializableERC721.sol b/contracts/external/ERC721/InitializableERC721.sol index 519e798..a3825f4 100644 --- a/contracts/external/ERC721/InitializableERC721.sol +++ b/contracts/external/ERC721/InitializableERC721.sol @@ -7,29 +7,231 @@ pragma solidity 0.6.9; -// import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {IERC721} from "../../intf/IERC721.sol"; +import {IERC721Receiver} from "../../intf/IERC721Receiver.sol"; +import {IERC721Metadata} from "../../intf/IERC721Metadata.sol"; +import {IERC165} from "../../intf/IERC165.sol"; +import {Strings} from "../../lib/Strings.sol"; +import {Address} from "../../lib/Address.sol"; -//TODO: -contract InitializableERC721 is InitializableOwnable { + +contract InitializableERC721 is IERC165, IERC721, IERC721Metadata { + + using Address for address; + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + // Base URI + string private _baseURI; + + // Mapping from token ID to owner address + mapping (uint256 => address) private _owners; + + // Mapping owner address to token count + mapping (address => uint256) private _balances; + + // Mapping from token ID to approved address + mapping (uint256 => address) private _tokenApprovals; + + // Mapping from owner to operator approvals + mapping (address => mapping (address => bool)) private _operatorApprovals; function init( - address creator, string memory name, string memory symbol, - string memory baseUrl + string memory baseUrI ) public { - initOwner(creator); - // super(name,symbol); + _name = name; + _symbol = symbol; + _baseURI = baseUrI; + _safeMint(msg.sender, 0); } - // baseUrl overide - // function mint(string memory _name, address _to) public { - // require(msg.sender == gameOwner, "Only game owner can create new monsters"); - // uint id = monsters.length; - // monsters.push(Monster(_name, 1)); - // _safeMint(_to, id); - // } + function tokenURI(uint256 tokenId) public view override returns (string memory) { + require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); + + string memory baseURI = _baseURI; + return bytes(baseURI).length > 0 + ? string(abi.encodePacked(baseURI, tokenId.toString())) + : ''; + } + + + function supportsInterface(bytes4 interfaceId) public view override returns (bool) { + return interfaceId == type(IERC721).interfaceId + || interfaceId == type(IERC721Metadata).interfaceId; + } + + + function balanceOf(address owner) public view virtual override returns (uint256) { + require(owner != address(0), "ERC721: balance query for the zero address"); + return _balances[owner]; + } + + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + address owner = _owners[tokenId]; + require(owner != address(0), "ERC721: owner query for nonexistent token"); + return owner; + } + + + function name() public view virtual override returns (string memory) { + return _name; + } + + + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + + function approve(address to, uint256 tokenId) public virtual override { + address owner = ownerOf(tokenId); + require(to != owner, "ERC721: approval to current owner"); + + require(msg.sender == owner || isApprovedForAll(owner, msg.sender), + "ERC721: approve caller is not owner nor approved for all" + ); + + _approve(to, tokenId); + } + + function getApproved(uint256 tokenId) public view virtual override returns (address) { + require(_exists(tokenId), "ERC721: approved query for nonexistent token"); + + return _tokenApprovals[tokenId]; + } + + + function setApprovalForAll(address operator, bool approved) public virtual override { + require(operator != msg.sender, "ERC721: approve to caller"); + + _operatorApprovals[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { + return _operatorApprovals[owner][operator]; + } + + + function transferFrom(address from, address to, uint256 tokenId) public virtual override { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); + + _transfer(from, to, tokenId); + } + + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { + require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); + _safeTransfer(from, to, tokenId, _data); + } + + + // =========== internal ================== + + function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { + _transfer(from, to, tokenId); + require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); + } + + function _exists(uint256 tokenId) internal view virtual returns (bool) { + return _owners[tokenId] != address(0); + } + + function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { + require(_exists(tokenId), "ERC721: operator query for nonexistent token"); + address owner = ownerOf(tokenId); + return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); + } + + function _safeMint(address to, uint256 tokenId) internal virtual { + _safeMint(to, tokenId, ""); + } + + function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { + _mint(to, tokenId); + require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); + } + + function _mint(address to, uint256 tokenId) internal virtual { + require(to != address(0), "ERC721: mint to the zero address"); + require(!_exists(tokenId), "ERC721: token already minted"); + + _beforeTokenTransfer(address(0), to, tokenId); + + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + } + + function _burn(uint256 tokenId) internal virtual { + address owner = ownerOf(tokenId); + + _beforeTokenTransfer(owner, address(0), tokenId); + + // Clear approvals + _approve(address(0), tokenId); + + _balances[owner] -= 1; + delete _owners[tokenId]; + + emit Transfer(owner, address(0), tokenId); + } + + function _transfer(address from, address to, uint256 tokenId) internal virtual { + require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); + require(to != address(0), "ERC721: transfer to the zero address"); + + _beforeTokenTransfer(from, to, tokenId); + + // Clear approvals from the previous owner + _approve(address(0), tokenId); + + _balances[from] -= 1; + _balances[to] += 1; + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + } + + function _approve(address to, uint256 tokenId) internal virtual { + _tokenApprovals[tokenId] = to; + emit Approval(ownerOf(tokenId), to, tokenId); + } + + function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) + private returns (bool) + { + if (to.isContract()) { + try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) returns (bytes4 retval) { + return retval == IERC721Receiver(to).onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC721: transfer to non ERC721Receiver implementer"); + } else { + // solhint-disable-next-line no-inline-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } else { + return true; + } + } + + function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { } } diff --git a/contracts/intf/IERC721Metadata.sol b/contracts/intf/IERC721Metadata.sol new file mode 100644 index 0000000..f33aded --- /dev/null +++ b/contracts/intf/IERC721Metadata.sol @@ -0,0 +1,28 @@ +// This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/IERC721Metadata.sol +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.9; + +import "./IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} diff --git a/contracts/lib/Address.sol b/contracts/lib/Address.sol new file mode 100644 index 0000000..8c39033 --- /dev/null +++ b/contracts/lib/Address.sol @@ -0,0 +1,190 @@ +// This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.9; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/contracts/lib/Strings.sol b/contracts/lib/Strings.sol new file mode 100644 index 0000000..3739acb --- /dev/null +++ b/contracts/lib/Strings.sol @@ -0,0 +1,68 @@ +// This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.9; + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant alphabet = "0123456789abcdef"; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + // Inspired by OraclizeAPI's implementation - MIT licence + // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0x00"; + } + uint256 temp = value; + uint256 length = 0; + while (temp != 0) { + length++; + temp >>= 8; + } + return toHexString(value, length); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = alphabet[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } + +} diff --git a/migrations/4_deploy_periphery.js b/migrations/4_deploy_periphery.js index 47224c2..ea8c011 100644 --- a/migrations/4_deploy_periphery.js +++ b/migrations/4_deploy_periphery.js @@ -40,6 +40,7 @@ module.exports = async (deployer, network, accounts) => { let WETHAddress = CONFIG.WETH; let DODOTokenAddress = CONFIG.DODO; let DODOApproveProxyAddress = CONFIG.DODOApproveProxy; + let WETH = CONFIG.WETH; let DspTemplateAddress = CONFIG.DSP; let DspFactoryAddress = CONFIG.DSPFactory;