diff --git a/README.md b/README.md index 07f6e66..9c87322 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,13 @@ -# DODO V2: Help 1 Trillion People Issue Token +## DODO CrowdPoolingV2 Audit Scope -## Audit Report +- contracts/CrowdPooling/impl/CP.sol +- contracts/CrowdPooling/impl/CPFunding.sol +- contracts/CrowdPooling/impl/CPStorage.sol +- contracts/CrowdPooling/impl/CPVesting.sol -[Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) +*Note: CrowdPoolingV2 && CrowdPoolingV1 code diff* -## Bug Bounty 💰 +- [https://github.com/DODOEX/contractV2/blob/starter/cpV2&&cpV1-diff.html](https://github.com/DODOEX/contractV2/blob/starter/cpV2&&cpV1-diff.html) -### Rewards +## DODO Starter Audit Scope -Severity of bugs will be assessed under the [CVSS Risk Rating](https://www.first.org/cvss/calculator/3.0) scale, as follows: - - - Critical (9.0-10.0): Up to $100,000 - - High (7.0-8.9): Up to $10,000 - - Medium (4.0-6.9): Up to $5,000 - - Low (0.1-3.9): Up to $1,000 - -In addition to assessing severity, rewards will be considered based on the impact of the discovered vulnerability as well as the level of difficulty in discovering such vulnerability. - -### Disclosure - -Any vulnerability or bug discovered must be reported only to the following email: contact@dodoex.io; must not be disclosed publicly; must not be disclosed to any other person, entity or email address prior to disclosure to the contact@dodoex.io email; and must not be disclosed in any way other than to the contact@dodoex.io email. In addition, disclosure to contact@dodoex.io must be made promptly following discovery of the vulnerability. Please include as much information about the vulnerability as possible, including: - - - The conditions on which reproducing the bug is contingent. - - The steps needed to reproduce the bug or, preferably, a proof of concept. - - The potential implications of the vulnerability being abused. - -A detailed report of a vulnerability increases the likelihood of a reward and may increase the reward amount. - -Anyone who reports a unique, previously-unreported vulnerability that results in a change to the code or a configuration change and who keeps such vulnerability confidential until it has been resolved by our engineers will be recognized publicly for their contribution, if agreed. - -## Contact Us - -Send E-mail to contact@dodoex.io \ No newline at end of file diff --git a/contracts/CrowdPooling/impl/CP.sol b/contracts/CrowdPooling/impl/CP.sol index 65ed870..bdcbdb8 100644 --- a/contracts/CrowdPooling/impl/CP.sol +++ b/contracts/CrowdPooling/impl/CP.sol @@ -23,7 +23,9 @@ import {SafeMath} from "../../lib/SafeMath.sol"; contract CP is CPVesting { using SafeMath for uint256; - receive() external payable {} + receive() external payable { + require(_INITIALIZED_ == false, "WE_NOT_SAVE_ETH_AFTER_INIT"); + } function init( address[] calldata addressList, @@ -110,6 +112,6 @@ contract CP is CPVesting { // ============ Version Control ============ function version() virtual external pure returns (string memory) { - return "CP 1.0.1"; + return "CP 2.0.0"; } } diff --git a/contracts/Factory/CrowdPoolingFactory.sol b/contracts/Factory/CrowdPoolingFactory.sol index 6850bc7..d56cfdd 100644 --- a/contracts/Factory/CrowdPoolingFactory.sol +++ b/contracts/Factory/CrowdPoolingFactory.sol @@ -51,7 +51,6 @@ contract CrowdPoolingFactory is InitializableOwnable { // ============ modifiers =========== modifier valueCheck( - address creator, address cpAddress, address baseToken, uint256[] memory timeLine, @@ -64,6 +63,7 @@ contract CrowdPoolingFactory is InitializableOwnable { uint256 baseTokenBalance = IERC20(baseToken).balanceOf(cpAddress); require(valueList[0].mul(100) <= baseTokenBalance.mul(valueList[2]).div(10**18).mul(_CAP_RATIO_),"CP_FACTORY : QUOTE_CAP_INVALID"); + require(timeLine[3]>= _FREEZE_DURATION_, "CP_FACTORY : FREEZE_DURATION_INVALID"); _; } @@ -107,7 +107,7 @@ contract CrowdPoolingFactory is InitializableOwnable { uint256[] memory timeLine, uint256[] memory valueList, bool[] memory switches - ) external valueCheck(creator,cpAddress,tokens[0],timeLine,valueList) { + ) external valueCheck(cpAddress,tokens[0],timeLine,valueList) { { address[] memory addressList = new address[](7); addressList[0] = creator; diff --git a/contracts/SmartRoute/DODOV2Proxy02.sol b/contracts/SmartRoute/DODOV2Proxy02.sol index 4b7a1c2..bc0b91e 100644 --- a/contracts/SmartRoute/DODOV2Proxy02.sol +++ b/contracts/SmartRoute/DODOV2Proxy02.sol @@ -467,15 +467,15 @@ contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable } //============ CrowdPooling Functions (bid) ============ - function bid( - address cpAddress, - uint256 quoteAmount, - uint8 flag, // 0 - ERC20, 1 - quoteInETH - uint256 deadLine - ) external override payable preventReentrant judgeExpired(deadLine) { - _deposit(msg.sender, cpAddress, IDODOV2(cpAddress)._QUOTE_TOKEN_(), quoteAmount, flag == 1); - IDODOV2(cpAddress).bid(msg.sender); - } + // function bid( + // address cpAddress, + // uint256 quoteAmount, + // uint8 flag, // 0 - ERC20, 1 - quoteInETH + // uint256 deadLine + // ) external override payable preventReentrant judgeExpired(deadLine) { + // _deposit(msg.sender, cpAddress, IDODOV2(cpAddress)._QUOTE_TOKEN_(), quoteAmount, flag == 1); + // IDODOV2(cpAddress).bid(msg.sender); + // } function addLiquidityToV1( diff --git a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol index 806b34f..17b513e 100644 --- a/contracts/SmartRoute/intf/IDODOV2Proxy01.sol +++ b/contracts/SmartRoute/intf/IDODOV2Proxy01.sol @@ -92,12 +92,12 @@ interface IDODOV2Proxy01 { // ) external payable; - function bid( - address cpAddress, - uint256 quoteAmount, - uint8 flag, // 0 - ERC20, 1 - quoteInETH - uint256 deadLine - ) external payable; + // function bid( + // address cpAddress, + // uint256 quoteAmount, + // uint8 flag, // 0 - ERC20, 1 - quoteInETH + // uint256 deadLine + // ) external payable; function addLiquidityToV1( address pair, diff --git a/contracts/SmartRoute/proxies/DODOCpProxy.sol b/contracts/SmartRoute/proxies/DODOCpProxy.sol index d67fb28..b89d43e 100644 --- a/contracts/SmartRoute/proxies/DODOCpProxy.sol +++ b/contracts/SmartRoute/proxies/DODOCpProxy.sol @@ -137,6 +137,16 @@ contract DODOCpProxy is ReentrancyGuard { ); } + function bid( + address cpAddress, + uint256 quoteAmount, + uint8 flag, // 0 - ERC20, 1 - quoteInETH + uint256 deadLine + ) external payable preventReentrant judgeExpired(deadLine) { + _deposit(msg.sender, cpAddress, IDODOV2(cpAddress)._QUOTE_TOKEN_(), quoteAmount, flag == 1); + IDODOV2(cpAddress).bid(msg.sender); + } + //====================== internal ======================= function _deposit( diff --git a/cpV2&&cpV1-diff.html b/cpV2&&cpV1-diff.html new file mode 100644 index 0000000..69cfa73 --- /dev/null +++ b/cpV2&&cpV1-diff.html @@ -0,0 +1,8765 @@ + + +
+ + +| + | -+ | +/** | +
| + | + | *Submitted for verification at BscScan.com on 2021-03-14 | +
| + | + | */ | +
| + | + | + |
| // File: contracts/lib/SafeMath.sol | += | +// File: contracts/lib/SafeMath.sol | +
| + | + | + |
| /* | ++ | /* | +
| + | + | + |
| Copyright 2020 DODO ZOO. | ++ | Copyright 2020 DODO ZOO. | +
| SPDX-License-Identifier: Apache-2.0 | ++ | SPDX-License-Identifier: Apache-2.0 | +
| + | + | + |
| */ | ++ | */ | +
| + | + | + |
| pragma solidity 0.6.9; | ++ | pragma solidity 0.6.9; | +
| pragma experimental ABIEncoderV2; | ++ | pragma experimental ABIEncoderV2; | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title SafeMath | ++ | * @title SafeMath | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Math operations with safety checks that revert on error | ++ | * @notice Math operations with safety checks that revert on error | +
| */ | ++ | */ | +
| library SafeMath { | ++ | library SafeMath { | +
| function mul(uint256 a, uint256 b) internal pure returns (uint256) { | ++ | function mul(uint256 a, uint256 b) internal pure returns (uint256) { | +
| if (a == 0) { | ++ | if (a == 0) { | +
| return 0; | ++ | return 0; | +
| } | ++ | } | +
| + | + | + |
| uint256 c = a * b; | ++ | uint256 c = a * b; | +
| require(c / a == b, "MUL_ERROR"); | ++ | require(c / a == b, "MUL_ERROR"); | +
| + | + | + |
| return c; | ++ | return c; | +
| } | ++ | } | +
| + | + | + |
| function div(uint256 a, uint256 b) internal pure returns (uint256) { | ++ | function div(uint256 a, uint256 b) internal pure returns (uint256) { | +
| require(b > 0, "DIVIDING_ERROR"); | ++ | require(b > 0, "DIVIDING_ERROR"); | +
| return a / b; | ++ | return a / b; | +
| } | ++ | } | +
| + | + | + |
| function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { | ++ | function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { | +
| uint256 quotient = div(a, b); | ++ | uint256 quotient = div(a, b); | +
| uint256 remainder = a - quotient * b; | ++ | uint256 remainder = a - quotient * b; | +
| if (remainder > 0) { | ++ | if (remainder > 0) { | +
| return quotient + 1; | ++ | return quotient + 1; | +
| } else { | ++ | } else { | +
| return quotient; | ++ | return quotient; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function sub(uint256 a, uint256 b) internal pure returns (uint256) { | ++ | function sub(uint256 a, uint256 b) internal pure returns (uint256) { | +
| require(b <= a, "SUB_ERROR"); | ++ | require(b <= a, "SUB_ERROR"); | +
| return a - b; | ++ | return a - b; | +
| } | ++ | } | +
| + | + | + |
| function add(uint256 a, uint256 b) internal pure returns (uint256) { | ++ | function add(uint256 a, uint256 b) internal pure returns (uint256) { | +
| uint256 c = a + b; | ++ | uint256 c = a + b; | +
| require(c >= a, "ADD_ERROR"); | ++ | require(c >= a, "ADD_ERROR"); | +
| return c; | ++ | return c; | +
| } | ++ | } | +
| + | + | + |
| function sqrt(uint256 x) internal pure returns (uint256 y) { | ++ | function sqrt(uint256 x) internal pure returns (uint256 y) { | +
| uint256 z = x / 2 + 1; | ++ | uint256 z = x / 2 + 1; | +
| y = x; | ++ | y = x; | +
| while (z < y) { | ++ | while (z < y) { | +
| y = z; | ++ | y = z; | +
| z = (x / z + z) / 2; | ++ | z = (x / z + z) / 2; | +
| } | ++ | } | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/DecimalMath.sol | ++ | // File: contracts/lib/DecimalMath.sol | +
| + | + | + |
| + | -+ | ++ |
| /** | += | +/** | +
| * @title DecimalMath | ++ | * @title DecimalMath | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Functions for fixed point number with 18 decimals | ++ | * @notice Functions for fixed point number with 18 decimals | +
| */ | ++ | */ | +
| library DecimalMath { | ++ | library DecimalMath { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| + | + | + |
| uint256 internal constant ONE = 10**18; | ++ | uint256 internal constant ONE = 10**18; | +
| uint256 internal constant ONE2 = 10**36; | ++ | uint256 internal constant ONE2 = 10**36; | +
| + | + | + |
| function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { | ++ | function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { | +
| return target.mul(d) / (10**18); | ++ | return target.mul(d) / (10**18); | +
| } | ++ | } | +
| + | + | + |
| function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { | ++ | function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { | +
| return target.mul(d).divCeil(10**18); | ++ | return target.mul(d).divCeil(10**18); | +
| } | ++ | } | +
| + | + | + |
| function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { | ++ | function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { | +
| return target.mul(10**18).div(d); | ++ | return target.mul(10**18).div(d); | +
| } | ++ | } | +
| + | + | + |
| function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { | ++ | function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { | +
| return target.mul(10**18).divCeil(d); | ++ | return target.mul(10**18).divCeil(d); | +
| } | ++ | } | +
| + | + | + |
| function reciprocalFloor(uint256 target) internal pure returns (uint256) { | ++ | function reciprocalFloor(uint256 target) internal pure returns (uint256) { | +
| return uint256(10**36).div(target); | ++ | return uint256(10**36).div(target); | +
| } | ++ | } | +
| + | + | + |
| function reciprocalCeil(uint256 target) internal pure returns (uint256) { | ++ | function reciprocalCeil(uint256 target) internal pure returns (uint256) { | +
| return uint256(10**36).divCeil(target); | ++ | return uint256(10**36).divCeil(target); | +
| } | ++ | } | +
| + | +- | ++ |
| function powFloor(uint256 target, uint256 e) internal pure returns (uint256) { | ++ | + |
| if (e == 0) { | ++ | + |
| return 10 ** 18; | ++ | + |
| } else if (e == 1) { | ++ | + |
| return target; | ++ | + |
| } else { | ++ | + |
| uint p = powFloor(target, e.div(2)); | ++ | + |
| p = p.mul(p) / (10**18); | ++ | + |
| if (e % 2 == 1) { | ++ | + |
| p = p.mul(target) / (10**18); | ++ | + |
| } | ++ | + |
| return p; | ++ | + |
| } | ++ | + |
| } | ++ | + |
| } | += | +} | +
| + | + | + |
| // File: contracts/lib/Ownable.sol | ++ | // File: contracts/lib/Ownable.sol | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title Ownable | ++ | * @title Ownable | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Ownership related functions | ++ | * @notice Ownership related functions | +
| */ | ++ | */ | +
| contract Ownable { | ++ | contract Ownable { | +
| address public _OWNER_; | ++ | address public _OWNER_; | +
| address public _NEW_OWNER_; | ++ | address public _NEW_OWNER_; | +
| + | + | + |
| // ============ Events ============ | ++ | // ============ Events ============ | +
| + | + | + |
| event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); | ++ | event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); | +
| + | + | + |
| event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | ++ | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | +
| + | + | + |
| // ============ Modifiers ============ | ++ | // ============ Modifiers ============ | +
| + | + | + |
| modifier onlyOwner() { | ++ | modifier onlyOwner() { | +
| require(msg.sender == _OWNER_, "NOT_OWNER"); | ++ | require(msg.sender == _OWNER_, "NOT_OWNER"); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| // ============ Functions ============ | ++ | // ============ Functions ============ | +
| + | + | + |
| constructor() internal { | ++ | constructor() internal { | +
| _OWNER_ = msg.sender; | ++ | _OWNER_ = msg.sender; | +
| emit OwnershipTransferred(address(0), _OWNER_); | ++ | emit OwnershipTransferred(address(0), _OWNER_); | +
| } | ++ | } | +
| + | + | + |
| function transferOwnership(address newOwner) external virtual onlyOwner { | +<> | +function transferOwnership(address newOwner) external onlyOwner { | +
| emit OwnershipTransferPrepared(_OWNER_, newOwner); | += | +emit OwnershipTransferPrepared(_OWNER_, newOwner); | +
| _NEW_OWNER_ = newOwner; | ++ | _NEW_OWNER_ = newOwner; | +
| } | ++ | } | +
| + | + | + |
| function claimOwnership() external { | ++ | function claimOwnership() external { | +
| require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); | ++ | require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); | +
| emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); | ++ | emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); | +
| _OWNER_ = _NEW_OWNER_; | ++ | _OWNER_ = _NEW_OWNER_; | +
| _NEW_OWNER_ = address(0); | ++ | _NEW_OWNER_ = address(0); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/intf/IERC20.sol | ++ | // File: contracts/intf/IERC20.sol | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @dev Interface of the ERC20 standard as defined in the EIP. | ++ | * @dev Interface of the ERC20 standard as defined in the EIP. | +
| */ | ++ | */ | +
| interface IERC20 { | ++ | interface IERC20 { | +
| /** | ++ | /** | +
| * @dev Returns the amount of tokens in existence. | ++ | * @dev Returns the amount of tokens in existence. | +
| */ | ++ | */ | +
| function totalSupply() external view returns (uint256); | ++ | function totalSupply() external view returns (uint256); | +
| + | + | + |
| function decimals() external view returns (uint8); | ++ | function decimals() external view returns (uint8); | +
| + | + | + |
| function name() external view returns (string memory); | ++ | function name() external view returns (string memory); | +
| + | + | + |
| function symbol() external view returns (string memory); | ++ | function symbol() external view returns (string memory); | +
| + | + | + |
| /** | ++ | /** | +
| * @dev Returns the amount of tokens owned by `account`. | ++ | * @dev Returns the amount of tokens owned by `account`. | +
| */ | ++ | */ | +
| function balanceOf(address account) external view returns (uint256); | ++ | function balanceOf(address account) external view returns (uint256); | +
| + | + | + |
| /** | ++ | /** | +
| * @dev Moves `amount` tokens from the caller's account to `recipient`. | ++ | * @dev Moves `amount` tokens from the caller's account to `recipient`. | +
| * | ++ | * | +
| * Returns a boolean value indicating whether the operation succeeded. | ++ | * Returns a boolean value indicating whether the operation succeeded. | +
| * | ++ | * | +
| * Emits a {Transfer} event. | ++ | * Emits a {Transfer} event. | +
| */ | ++ | */ | +
| function transfer(address recipient, uint256 amount) external returns (bool); | ++ | function transfer(address recipient, uint256 amount) external returns (bool); | +
| + | + | + |
| /** | ++ | /** | +
| * @dev Returns the remaining number of tokens that `spender` will be | ++ | * @dev Returns the remaining number of tokens that `spender` will be | +
| * allowed to spend on behalf of `owner` through {transferFrom}. This is | ++ | * allowed to spend on behalf of `owner` through {transferFrom}. This is | +
| * zero by default. | ++ | * zero by default. | +
| * | ++ | * | +
| * This value changes when {approve} or {transferFrom} are called. | ++ | * This value changes when {approve} or {transferFrom} are called. | +
| */ | ++ | */ | +
| function allowance(address owner, address spender) external view returns (uint256); | ++ | function allowance(address owner, address spender) external view returns (uint256); | +
| + | + | + |
| /** | ++ | /** | +
| * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. | ++ | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. | +
| * | ++ | * | +
| * Returns a boolean value indicating whether the operation succeeded. | ++ | * Returns a boolean value indicating whether the operation succeeded. | +
| * | ++ | * | +
| * IMPORTANT: Beware that changing an allowance with this method brings the risk | ++ | * IMPORTANT: Beware that changing an allowance with this method brings the risk | +
| * that someone may use both the old and the new allowance by unfortunate | ++ | * that someone may use both the old and the new allowance by unfortunate | +
| * transaction ordering. One possible solution to mitigate this race | ++ | * transaction ordering. One possible solution to mitigate this race | +
| * condition is to first reduce the spender's allowance to 0 and set the | ++ | * condition is to first reduce the spender's allowance to 0 and set the | +
| * desired value afterwards: | ++ | * desired value afterwards: | +
| * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | ++ | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | +
| * | ++ | * | +
| * Emits an {Approval} event. | ++ | * Emits an {Approval} event. | +
| */ | ++ | */ | +
| function approve(address spender, uint256 amount) external returns (bool); | ++ | function approve(address spender, uint256 amount) external returns (bool); | +
| + | + | + |
| /** | ++ | /** | +
| * @dev Moves `amount` tokens from `sender` to `recipient` using the | ++ | * @dev Moves `amount` tokens from `sender` to `recipient` using the | +
| * allowance mechanism. `amount` is then deducted from the caller's | ++ | * allowance mechanism. `amount` is then deducted from the caller's | +
| * allowance. | ++ | * allowance. | +
| * | ++ | * | +
| * Returns a boolean value indicating whether the operation succeeded. | ++ | * Returns a boolean value indicating whether the operation succeeded. | +
| * | ++ | * | +
| * Emits a {Transfer} event. | ++ | * Emits a {Transfer} event. | +
| */ | ++ | */ | +
| function transferFrom( | ++ | function transferFrom( | +
| address sender, | ++ | address sender, | +
| address recipient, | ++ | address recipient, | +
| uint256 amount | ++ | uint256 amount | +
| ) external returns (bool); | ++ | ) external returns (bool); | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/SafeERC20.sol | ++ | // File: contracts/lib/SafeERC20.sol | +
| + | + | + |
| + | +- | ++ |
| /** | += | +/** | +
| * @title SafeERC20 | ++ | * @title SafeERC20 | +
| * @dev Wrappers around ERC20 operations that throw on failure (when the token | ++ | * @dev Wrappers around ERC20 operations that throw on failure (when the token | +
| * contract returns false). Tokens that return no value (and instead revert or | ++ | * contract returns false). Tokens that return no value (and instead revert or | +
| * throw on failure) are also supported, non-reverting calls are assumed to be | ++ | * throw on failure) are also supported, non-reverting calls are assumed to be | +
| * successful. | ++ | * successful. | +
| * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, | ++ | * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, | +
| * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. | ++ | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. | +
| */ | ++ | */ | +
| library SafeERC20 { | ++ | library SafeERC20 { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| + | + | + |
| function safeTransfer( | ++ | function safeTransfer( | +
| IERC20 token, | ++ | IERC20 token, | +
| address to, | ++ | address to, | +
| uint256 value | ++ | uint256 value | +
| ) internal { | ++ | ) internal { | +
| _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | ++ | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | +
| } | ++ | } | +
| + | + | + |
| function safeTransferFrom( | ++ | function safeTransferFrom( | +
| IERC20 token, | ++ | IERC20 token, | +
| address from, | ++ | address from, | +
| address to, | ++ | address to, | +
| uint256 value | ++ | uint256 value | +
| ) internal { | ++ | ) internal { | +
| _callOptionalReturn( | ++ | _callOptionalReturn( | +
| token, | ++ | token, | +
| abi.encodeWithSelector(token.transferFrom.selector, from, to, value) | ++ | abi.encodeWithSelector(token.transferFrom.selector, from, to, value) | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| function safeApprove( | ++ | function safeApprove( | +
| IERC20 token, | ++ | IERC20 token, | +
| address spender, | ++ | address spender, | +
| uint256 value | ++ | uint256 value | +
| ) internal { | ++ | ) internal { | +
| // safeApprove should only be called when setting an initial allowance, | ++ | // safeApprove should only be called when setting an initial allowance, | +
| // or when resetting it to zero. To increase and decrease it, use | ++ | // or when resetting it to zero. To increase and decrease it, use | +
| // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' | ++ | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' | +
| // solhint-disable-next-line max-line-length | ++ | // solhint-disable-next-line max-line-length | +
| require( | ++ | require( | +
| (value == 0) || (token.allowance(address(this), spender) == 0), | ++ | (value == 0) || (token.allowance(address(this), spender) == 0), | +
| "SafeERC20: approve from non-zero to non-zero allowance" | ++ | "SafeERC20: approve from non-zero to non-zero allowance" | +
| ); | ++ | ); | +
| _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | ++ | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | +
| } | ++ | } | +
| + | + | + |
| /** | ++ | /** | +
| * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement | ++ | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement | +
| * on the return value: the return value is optional (but if data is returned, it must not be false). | ++ | * on the return value: the return value is optional (but if data is returned, it must not be false). | +
| * @param token The token targeted by the call. | ++ | * @param token The token targeted by the call. | +
| * @param data The call data (encoded using abi.encode or one of its variants). | ++ | * @param data The call data (encoded using abi.encode or one of its variants). | +
| */ | ++ | */ | +
| function _callOptionalReturn(IERC20 token, bytes memory data) private { | ++ | function _callOptionalReturn(IERC20 token, bytes memory data) private { | +
| // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since | ++ | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since | +
| // we're implementing it ourselves. | ++ | // we're implementing it ourselves. | +
| + | + | + |
| // A Solidity high level call has three parts: | ++ | // A Solidity high level call has three parts: | +
| // 1. The target address is checked to verify it contains contract code | ++ | // 1. The target address is checked to verify it contains contract code | +
| // 2. The call itself is made, and success asserted | ++ | // 2. The call itself is made, and success asserted | +
| // 3. The return value is decoded, which in turn checks the size of the returned data. | ++ | // 3. The return value is decoded, which in turn checks the size of the returned data. | +
| // solhint-disable-next-line max-line-length | ++ | // solhint-disable-next-line max-line-length | +
| + | + | + |
| // solhint-disable-next-line avoid-low-level-calls | ++ | // solhint-disable-next-line avoid-low-level-calls | +
| (bool success, bytes memory returndata) = address(token).call(data); | ++ | (bool success, bytes memory returndata) = address(token).call(data); | +
| require(success, "SafeERC20: low-level call failed"); | ++ | require(success, "SafeERC20: low-level call failed"); | +
| + | + | + |
| if (returndata.length > 0) { | ++ | if (returndata.length > 0) { | +
| // Return data is optional | ++ | // Return data is optional | +
| // solhint-disable-next-line max-line-length | ++ | // solhint-disable-next-line max-line-length | +
| require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | ++ | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | +
| } | ++ | } | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/DODOVendingMachine/intf/IDVM.sol | ++ | // File: contracts/DODOVendingMachine/intf/IDVM.sol | +
| + | + | + |
| + | + | + |
| interface IDVM { | ++ | interface IDVM { | +
| function init( | ++ | function init( | +
| address maintainer, | ++ | address maintainer, | +
| address baseTokenAddress, | ++ | address baseTokenAddress, | +
| address quoteTokenAddress, | ++ | address quoteTokenAddress, | +
| uint256 lpFeeRate, | ++ | uint256 lpFeeRate, | +
| address mtFeeRateModel, | ++ | address mtFeeRateModel, | +
| uint256 i, | ++ | uint256 i, | +
| uint256 k, | ++ | uint256 k, | +
| bool isOpenTWAP | ++ | bool isOpenTWAP | +
| ) external; | ++ | ) external; | +
| + | + | + |
| function _BASE_TOKEN_() external returns (address); | ++ | function _BASE_TOKEN_() external returns (address); | +
| + | + | + |
| function _QUOTE_TOKEN_() external returns (address); | ++ | function _QUOTE_TOKEN_() external returns (address); | +
| + | + | + |
| function _MT_FEE_RATE_MODEL_() external returns (address); | ++ | function _MT_FEE_RATE_MODEL_() external returns (address); | +
| + | + | + |
| function getVaultReserve() external returns (uint256 baseReserve, uint256 quoteReserve); | ++ | function getVaultReserve() external returns (uint256 baseReserve, uint256 quoteReserve); | +
| + | + | + |
| function sellBase(address to) external returns (uint256); | ++ | function sellBase(address to) external returns (uint256); | +
| + | + | + |
| function sellQuote(address to) external returns (uint256); | ++ | function sellQuote(address to) external returns (uint256); | +
| + | + | + |
| function buyShares(address to) external returns (uint256,uint256,uint256); | ++ | function buyShares(address to) external returns (uint256,uint256,uint256); | +
| + | + | + |
| function addressToShortString(address _addr) external pure returns (string memory); | ++- | ++ |
| + | + | + |
| function getMidPrice() external view returns (uint256 midPrice); | ++ | + |
| + | + | + |
| function sellShares( | ++ | + |
| uint256 shareAmount, | ++ | + |
| address to, | ++ | + |
| uint256 baseMinAmount, | ++ | + |
| uint256 quoteMinAmount, | ++ | + |
| bytes calldata data, | ++ | + |
| uint256 deadline | ++ | + |
| ) external returns (uint256 baseAmount, uint256 quoteAmount); | ++ | + |
| + | + | + |
| } | += | +} | +
| + | + | + |
| // File: contracts/lib/InitializableOwnable.sol | ++ | // File: contracts/lib/InitializableOwnable.sol | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title Ownable | ++ | * @title Ownable | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Ownership related functions | ++ | * @notice Ownership related functions | +
| */ | ++ | */ | +
| contract InitializableOwnable { | ++ | contract InitializableOwnable { | +
| address public _OWNER_; | ++ | address public _OWNER_; | +
| address public _NEW_OWNER_; | ++ | address public _NEW_OWNER_; | +
| bool internal _INITIALIZED_; | ++ | bool internal _INITIALIZED_; | +
| + | + | + |
| // ============ Events ============ | ++ | // ============ Events ============ | +
| + | + | + |
| event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); | ++ | event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); | +
| + | + | + |
| event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | ++ | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | +
| + | + | + |
| // ============ Modifiers ============ | ++ | // ============ Modifiers ============ | +
| + | + | + |
| modifier notInitialized() { | ++ | modifier notInitialized() { | +
| require(!_INITIALIZED_, "DODO_INITIALIZED"); | ++ | require(!_INITIALIZED_, "DODO_INITIALIZED"); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier onlyOwner() { | ++ | modifier onlyOwner() { | +
| require(msg.sender == _OWNER_, "NOT_OWNER"); | ++ | require(msg.sender == _OWNER_, "NOT_OWNER"); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| // ============ Functions ============ | ++ | // ============ Functions ============ | +
| + | + | + |
| function initOwner(address newOwner) public notInitialized { | ++ | function initOwner(address newOwner) public notInitialized { | +
| _INITIALIZED_ = true; | ++ | _INITIALIZED_ = true; | +
| _OWNER_ = newOwner; | ++ | _OWNER_ = newOwner; | +
| } | ++ | } | +
| + | + | + |
| function transferOwnership(address newOwner) public onlyOwner { | ++ | function transferOwnership(address newOwner) public onlyOwner { | +
| emit OwnershipTransferPrepared(_OWNER_, newOwner); | ++ | emit OwnershipTransferPrepared(_OWNER_, newOwner); | +
| _NEW_OWNER_ = newOwner; | ++ | _NEW_OWNER_ = newOwner; | +
| } | ++ | } | +
| + | + | + |
| function claimOwnership() public { | ++ | function claimOwnership() public { | +
| require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); | ++ | require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); | +
| emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); | ++ | emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); | +
| _OWNER_ = _NEW_OWNER_; | ++ | _OWNER_ = _NEW_OWNER_; | +
| _NEW_OWNER_ = address(0); | ++ | _NEW_OWNER_ = address(0); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/CloneFactory.sol | ++ | // File: contracts/lib/CloneFactory.sol | +
| + | + | + |
| + | + | + |
| interface ICloneFactory { | ++ | interface ICloneFactory { | +
| function clone(address prototype) external returns (address proxy); | ++ | function clone(address prototype) external returns (address proxy); | +
| } | ++ | } | +
| + | + | + |
| // introduction of proxy mode design: https://docs.openzeppelin.com/upgrades/2.8/ | ++ | // introduction of proxy mode design: https://docs.openzeppelin.com/upgrades/2.8/ | +
| // minimum implementation of transparent proxy: https://eips.ethereum.org/EIPS/eip-1167 | ++ | // minimum implementation of transparent proxy: https://eips.ethereum.org/EIPS/eip-1167 | +
| + | + | + |
| contract CloneFactory is ICloneFactory { | ++ | contract CloneFactory is ICloneFactory { | +
| function clone(address prototype) external override returns (address proxy) { | ++ | function clone(address prototype) external override returns (address proxy) { | +
| bytes20 targetBytes = bytes20(prototype); | ++ | bytes20 targetBytes = bytes20(prototype); | +
| assembly { | ++ | assembly { | +
| let clone := mload(0x40) | ++ | let clone := mload(0x40) | +
| mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) | ++ | mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) | +
| mstore(add(clone, 0x14), targetBytes) | ++ | mstore(add(clone, 0x14), targetBytes) | +
| mstore( | ++ | mstore( | +
| add(clone, 0x28), | ++ | add(clone, 0x28), | +
| 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 | ++ | 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 | +
| ) | ++ | ) | +
| proxy := create(0, clone, 0x37) | ++ | proxy := create(0, clone, 0x37) | +
| } | ++ | } | +
| return proxy; | ++ | return proxy; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/Factory/DVMFactory.sol | ++ | // File: contracts/Factory/DVMFactory.sol | +
| + | + | + |
| + | + | + |
| + | -+ | ++ |
| + | + | + |
| interface IDVMFactory { | += | +interface IDVMFactory { | +
| function createDODOVendingMachine( | ++ | function createDODOVendingMachine( | +
| address baseToken, | ++ | address baseToken, | +
| address quoteToken, | ++ | address quoteToken, | +
| uint256 lpFeeRate, | ++ | uint256 lpFeeRate, | +
| uint256 i, | ++ | uint256 i, | +
| uint256 k, | ++ | uint256 k, | +
| bool isOpenTWAP | ++ | bool isOpenTWAP | +
| ) external returns (address newVendingMachine); | ++ | ) external returns (address newVendingMachine); | +
| } | ++ | } | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title DODO VendingMachine Factory | ++ | * @title DODO VendingMachine Factory | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Create And Register DVM Pools | ++ | * @notice Create And Register DVM Pools | +
| */ | ++ | */ | +
| contract DVMFactory is InitializableOwnable { | ++ | contract DVMFactory is InitializableOwnable { | +
| // ============ Templates ============ | ++ | // ============ Templates ============ | +
| + | + | + |
| address public immutable _CLONE_FACTORY_; | ++ | address public immutable _CLONE_FACTORY_; | +
| + | -+ | +address public immutable _DEFAULT_MAINTAINER_; | +
| address public immutable _DEFAULT_MT_FEE_RATE_MODEL_; | += | +address public immutable _DEFAULT_MT_FEE_RATE_MODEL_; | +
| address public _DEFAULT_MAINTAINER_; | ++- | ++ |
| address public _DVM_TEMPLATE_; | += | +address public _DVM_TEMPLATE_; | +
| + | + | + |
| // ============ Registry ============ | ++ | // ============ Registry ============ | +
| + | + | + |
| // base -> quote -> DVM address list | ++ | // base -> quote -> DVM address list | +
| mapping(address => mapping(address => address[])) public _REGISTRY_; | ++ | mapping(address => mapping(address => address[])) public _REGISTRY_; | +
| // creator -> DVM address list | ++ | // creator -> DVM address list | +
| mapping(address => address[]) public _USER_REGISTRY_; | ++ | mapping(address => address[]) public _USER_REGISTRY_; | +
| + | + | + |
| // ============ Events ============ | ++ | // ============ Events ============ | +
| + | + | + |
| event NewDVM( | ++ | event NewDVM( | +
| address baseToken, | ++ | address baseToken, | +
| address quoteToken, | ++ | address quoteToken, | +
| address creator, | ++ | address creator, | +
| address dvm | ++ | address dvm | +
| ); | ++ | ); | +
| + | + | + |
| event RemoveDVM(address dvm); | ++ | event RemoveDVM(address dvm); | +
| + | + | + |
| // ============ Functions ============ | ++ | // ============ Functions ============ | +
| + | + | + |
| constructor( | ++ | constructor( | +
| address cloneFactory, | ++ | address cloneFactory, | +
| address dvmTemplate, | ++ | address dvmTemplate, | +
| address defaultMaintainer, | ++ | address defaultMaintainer, | +
| address defaultMtFeeRateModel | ++ | address defaultMtFeeRateModel | +
| ) public { | ++ | ) public { | +
| _CLONE_FACTORY_ = cloneFactory; | ++ | _CLONE_FACTORY_ = cloneFactory; | +
| _DVM_TEMPLATE_ = dvmTemplate; | ++ | _DVM_TEMPLATE_ = dvmTemplate; | +
| _DEFAULT_MAINTAINER_ = defaultMaintainer; | ++ | _DEFAULT_MAINTAINER_ = defaultMaintainer; | +
| _DEFAULT_MT_FEE_RATE_MODEL_ = defaultMtFeeRateModel; | ++ | _DEFAULT_MT_FEE_RATE_MODEL_ = defaultMtFeeRateModel; | +
| } | ++ | } | +
| + | + | + |
| function createDODOVendingMachine( | ++ | function createDODOVendingMachine( | +
| address baseToken, | ++ | address baseToken, | +
| address quoteToken, | ++ | address quoteToken, | +
| uint256 lpFeeRate, | ++ | uint256 lpFeeRate, | +
| uint256 i, | ++ | uint256 i, | +
| uint256 k, | ++ | uint256 k, | +
| bool isOpenTWAP | ++ | bool isOpenTWAP | +
| ) external returns (address newVendingMachine) { | ++ | ) external returns (address newVendingMachine) { | +
| newVendingMachine = ICloneFactory(_CLONE_FACTORY_).clone(_DVM_TEMPLATE_); | ++ | newVendingMachine = ICloneFactory(_CLONE_FACTORY_).clone(_DVM_TEMPLATE_); | +
| { | ++ | { | +
| IDVM(newVendingMachine).init( | ++ | IDVM(newVendingMachine).init( | +
| _DEFAULT_MAINTAINER_, | ++ | _DEFAULT_MAINTAINER_, | +
| baseToken, | ++ | baseToken, | +
| quoteToken, | ++ | quoteToken, | +
| lpFeeRate, | ++ | lpFeeRate, | +
| _DEFAULT_MT_FEE_RATE_MODEL_, | ++ | _DEFAULT_MT_FEE_RATE_MODEL_, | +
| i, | ++ | i, | +
| k, | ++ | k, | +
| isOpenTWAP | ++ | isOpenTWAP | +
| ); | ++ | ); | +
| } | ++ | } | +
| _REGISTRY_[baseToken][quoteToken].push(newVendingMachine); | ++ | _REGISTRY_[baseToken][quoteToken].push(newVendingMachine); | +
| _USER_REGISTRY_[tx.origin].push(newVendingMachine); | ++ | _USER_REGISTRY_[tx.origin].push(newVendingMachine); | +
| emit NewDVM(baseToken, quoteToken, tx.origin, newVendingMachine); | ++ | emit NewDVM(baseToken, quoteToken, tx.origin, newVendingMachine); | +
| } | ++ | } | +
| + | + | + |
| // ============ Admin Operation Functions ============ | ++ | // ============ Admin Operation Functions ============ | +
| + | + | + |
| function updateDvmTemplate(address _newDVMTemplate) external onlyOwner { | ++ | function updateDvmTemplate(address _newDVMTemplate) external onlyOwner { | +
| _DVM_TEMPLATE_ = _newDVMTemplate; | ++ | _DVM_TEMPLATE_ = _newDVMTemplate; | +
| } | ++- | ++ |
| + | + | + |
| function updateDefaultMaintainer(address _newMaintainer) external onlyOwner { | ++ | + |
| _DEFAULT_MAINTAINER_ = _newMaintainer; | ++ | + |
| } | += | +} | +
| + | + | + |
| function addPoolByAdmin( | ++ | function addPoolByAdmin( | +
| address creator, | ++ | address creator, | +
| address baseToken, | ++ | address baseToken, | +
| address quoteToken, | ++ | address quoteToken, | +
| address pool | ++ | address pool | +
| ) external onlyOwner { | ++ | ) external onlyOwner { | +
| _REGISTRY_[baseToken][quoteToken].push(pool); | ++ | _REGISTRY_[baseToken][quoteToken].push(pool); | +
| _USER_REGISTRY_[creator].push(pool); | ++ | _USER_REGISTRY_[creator].push(pool); | +
| emit NewDVM(baseToken, quoteToken, creator, pool); | ++ | emit NewDVM(baseToken, quoteToken, creator, pool); | +
| } | ++ | } | +
| + | + | + |
| function removePoolByAdmin( | ++ | function removePoolByAdmin( | +
| address creator, | ++ | address creator, | +
| address baseToken, | ++ | address baseToken, | +
| address quoteToken, | ++ | address quoteToken, | +
| address pool | ++ | address pool | +
| ) external onlyOwner { | ++ | ) external onlyOwner { | +
| address[] memory registryList = _REGISTRY_[baseToken][quoteToken]; | ++ | address[] memory registryList = _REGISTRY_[baseToken][quoteToken]; | +
| for (uint256 i = 0; i < registryList.length; i++) { | ++ | for (uint256 i = 0; i < registryList.length; i++) { | +
| if (registryList[i] == pool) { | ++ | if (registryList[i] == pool) { | +
| registryList[i] = registryList[registryList.length - 1]; | ++ | registryList[i] = registryList[registryList.length - 1]; | +
| break; | ++ | break; | +
| } | ++ | } | +
| } | ++ | } | +
| _REGISTRY_[baseToken][quoteToken] = registryList; | ++ | _REGISTRY_[baseToken][quoteToken] = registryList; | +
| _REGISTRY_[baseToken][quoteToken].pop(); | ++ | _REGISTRY_[baseToken][quoteToken].pop(); | +
| address[] memory userRegistryList = _USER_REGISTRY_[creator]; | ++ | address[] memory userRegistryList = _USER_REGISTRY_[creator]; | +
| for (uint256 i = 0; i < userRegistryList.length; i++) { | ++ | for (uint256 i = 0; i < userRegistryList.length; i++) { | +
| if (userRegistryList[i] == pool) { | ++ | if (userRegistryList[i] == pool) { | +
| userRegistryList[i] = userRegistryList[userRegistryList.length - 1]; | ++ | userRegistryList[i] = userRegistryList[userRegistryList.length - 1]; | +
| break; | ++ | break; | +
| } | ++ | } | +
| } | ++ | } | +
| _USER_REGISTRY_[creator] = userRegistryList; | ++ | _USER_REGISTRY_[creator] = userRegistryList; | +
| _USER_REGISTRY_[creator].pop(); | ++ | _USER_REGISTRY_[creator].pop(); | +
| emit RemoveDVM(pool); | ++ | emit RemoveDVM(pool); | +
| } | ++ | } | +
| + | + | + |
| // ============ View Functions ============ | ++ | // ============ View Functions ============ | +
| + | + | + |
| function getDODOPool(address baseToken, address quoteToken) | ++ | function getDODOPool(address baseToken, address quoteToken) | +
| external | ++ | external | +
| view | ++ | view | +
| returns (address[] memory machines) | ++ | returns (address[] memory machines) | +
| { | ++ | { | +
| return _REGISTRY_[baseToken][quoteToken]; | ++ | return _REGISTRY_[baseToken][quoteToken]; | +
| } | ++ | } | +
| + | + | + |
| function getDODOPoolBidirection(address token0, address token1) | ++ | function getDODOPoolBidirection(address token0, address token1) | +
| external | ++ | external | +
| view | ++ | view | +
| returns (address[] memory baseToken0Machines, address[] memory baseToken1Machines) | ++ | returns (address[] memory baseToken0Machines, address[] memory baseToken1Machines) | +
| { | ++ | { | +
| return (_REGISTRY_[token0][token1], _REGISTRY_[token1][token0]); | ++ | return (_REGISTRY_[token0][token1], _REGISTRY_[token1][token0]); | +
| } | ++ | } | +
| + | + | + |
| function getDODOPoolByUser(address user) | ++ | function getDODOPoolByUser(address user) | +
| external | ++ | external | +
| view | ++ | view | +
| returns (address[] memory machines) | ++ | returns (address[] memory machines) | +
| { | ++ | { | +
| return _USER_REGISTRY_[user]; | ++ | return _USER_REGISTRY_[user]; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/ReentrancyGuard.sol | ++ | // File: contracts/lib/ReentrancyGuard.sol | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title ReentrancyGuard | ++ | * @title ReentrancyGuard | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Protect functions from Reentrancy Attack | ++ | * @notice Protect functions from Reentrancy Attack | +
| */ | ++ | */ | +
| contract ReentrancyGuard { | ++ | contract ReentrancyGuard { | +
| // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations | ++ | // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations | +
| // zero-state of _ENTERED_ is false | ++ | // zero-state of _ENTERED_ is false | +
| bool private _ENTERED_; | ++ | bool private _ENTERED_; | +
| + | + | + |
| modifier preventReentrant() { | ++ | modifier preventReentrant() { | +
| require(!_ENTERED_, "REENTRANT"); | ++ | require(!_ENTERED_, "REENTRANT"); | +
| _ENTERED_ = true; | ++ | _ENTERED_ = true; | +
| _; | ++ | _; | +
| _ENTERED_ = false; | ++ | _ENTERED_ = false; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/PermissionManager.sol | ++ | // File: contracts/lib/PermissionManager.sol | +
| + | + | + |
| + | + | + |
| + | + | + |
| interface IPermissionManager { | ++ | interface IPermissionManager { | +
| function initOwner(address) external; | ++ | function initOwner(address) external; | +
| + | + | + |
| function isAllowed(address) external view returns (bool); | ++ | function isAllowed(address) external view returns (bool); | +
| } | ++ | } | +
| + | + | + |
| contract PermissionManager is InitializableOwnable { | ++ | contract PermissionManager is InitializableOwnable { | +
| bool public _WHITELIST_MODE_ON_; | ++ | bool public _WHITELIST_MODE_ON_; | +
| + | + | + |
| mapping(address => bool) internal _whitelist_; | ++ | mapping(address => bool) internal _whitelist_; | +
| mapping(address => bool) internal _blacklist_; | ++ | mapping(address => bool) internal _blacklist_; | +
| + | + | + |
| function isAllowed(address account) external view returns (bool) { | ++ | function isAllowed(address account) external view returns (bool) { | +
| if (_WHITELIST_MODE_ON_) { | ++ | if (_WHITELIST_MODE_ON_) { | +
| return _whitelist_[account]; | ++ | return _whitelist_[account]; | +
| } else { | ++ | } else { | +
| return !_blacklist_[account]; | ++ | return !_blacklist_[account]; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function openBlacklistMode() external onlyOwner { | ++ | function openBlacklistMode() external onlyOwner { | +
| _WHITELIST_MODE_ON_ = false; | ++ | _WHITELIST_MODE_ON_ = false; | +
| } | ++ | } | +
| + | + | + |
| function openWhitelistMode() external onlyOwner { | ++ | function openWhitelistMode() external onlyOwner { | +
| _WHITELIST_MODE_ON_ = true; | ++ | _WHITELIST_MODE_ON_ = true; | +
| } | ++ | } | +
| + | + | + |
| function addToWhitelist(address account) external onlyOwner { | ++ | function addToWhitelist(address account) external onlyOwner { | +
| _whitelist_[account] = true; | ++ | _whitelist_[account] = true; | +
| } | ++ | } | +
| + | + | + |
| function removeFromWhitelist(address account) external onlyOwner { | ++ | function removeFromWhitelist(address account) external onlyOwner { | +
| _whitelist_[account] = false; | ++ | _whitelist_[account] = false; | +
| } | ++ | } | +
| + | + | + |
| function addToBlacklist(address account) external onlyOwner { | ++ | function addToBlacklist(address account) external onlyOwner { | +
| _blacklist_[account] = true; | ++ | _blacklist_[account] = true; | +
| } | ++ | } | +
| + | + | + |
| function removeFromBlacklist(address account) external onlyOwner { | ++ | function removeFromBlacklist(address account) external onlyOwner { | +
| _blacklist_[account] = false; | ++ | _blacklist_[account] = false; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/FeeRateModel.sol | ++ | // File: contracts/lib/FeeRateModel.sol | +
| + | + | + |
| + | + | + |
| + | +- | ++ |
| interface IFeeRateImpl { | += | +interface IFeeRateImpl { | +
| function getFeeRate(address pool, address trader) external view returns (uint256); | ++ | function getFeeRate(address pool, address trader) external view returns (uint256); | +
| } | ++ | } | +
| + | + | + |
| interface IFeeRateModel { | ++ | interface IFeeRateModel { | +
| function getFeeRate(address trader) external view returns (uint256); | ++ | function getFeeRate(address trader) external view returns (uint256); | +
| } | ++ | } | +
| + | + | + |
| contract FeeRateModel is InitializableOwnable { | ++ | contract FeeRateModel is InitializableOwnable { | +
| address public feeRateImpl; | ++ | address public feeRateImpl; | +
| + | + | + |
| function setFeeProxy(address _feeRateImpl) public onlyOwner { | ++ | function setFeeProxy(address _feeRateImpl) public onlyOwner { | +
| feeRateImpl = _feeRateImpl; | ++ | feeRateImpl = _feeRateImpl; | +
| } | ++ | } | +
| + | + | + |
| function getFeeRate(address trader) external view returns (uint256) { | ++ | function getFeeRate(address trader) external view returns (uint256) { | +
| if(feeRateImpl == address(0)) | ++ | if(feeRateImpl == address(0)) | +
| return 0; | ++ | return 0; | +
| return IFeeRateImpl(feeRateImpl).getFeeRate(msg.sender,trader); | ++ | return IFeeRateImpl(feeRateImpl).getFeeRate(msg.sender,trader); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/CrowdPooling/impl/CPStorage.sol | ++ | // File: contracts/CrowdPooling/impl/CPStorage.sol | +
| + | + | + |
| + | +- | ++ |
| + | + | + |
| contract CPStorage is InitializableOwnable, ReentrancyGuard { | += | +contract CPStorage is InitializableOwnable, ReentrancyGuard { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| + | + | + |
| // ============ Constant ============ | ++ | // ============ Constant ============ | +
| + | + | + |
| uint256 internal constant _SETTLEMENT_EXPIRE_ = 86400 * 7; | ++ | uint256 internal constant _SETTLEMENT_EXPIRE_ = 86400 * 7; | +
| uint256 internal constant _SETTEL_FUND_ = 200 finney; | ++ | uint256 internal constant _SETTEL_FUND_ = 200 finney; | +
| bool public _IS_OPEN_TWAP_ = false; | ++ | bool public _IS_OPEN_TWAP_ = false; | +
| bool public _IS_OVERCAP_STOP = false; | ++- | ++ |
| + | + | + |
| bool public _FORCE_STOP_ = false; | ++ | + |
| + | = | ++ |
| // ============ Timeline ============ | ++ | // ============ Timeline ============ | +
| + | + | + |
| uint256 public _PHASE_BID_STARTTIME_; | ++ | uint256 public _PHASE_BID_STARTTIME_; | +
| uint256 public _PHASE_BID_ENDTIME_; | ++ | uint256 public _PHASE_BID_ENDTIME_; | +
| uint256 public _PHASE_CALM_ENDTIME_; | ++ | uint256 public _PHASE_CALM_ENDTIME_; | +
| uint256 public _SETTLED_TIME_; | ++ | uint256 public _SETTLED_TIME_; | +
| bool public _SETTLED_; | ++ | bool public _SETTLED_; | +
| + | + | + |
| // ============ Core Address ============ | ++ | // ============ Core Address ============ | +
| + | + | + |
| IERC20 public _BASE_TOKEN_; | ++ | IERC20 public _BASE_TOKEN_; | +
| IERC20 public _QUOTE_TOKEN_; | ++ | IERC20 public _QUOTE_TOKEN_; | +
| + | + | + |
| // ============ Distribution Parameters ============ | ++ | // ============ Distribution Parameters ============ | +
| + | + | + |
| uint256 public _TOTAL_BASE_; | ++ | uint256 public _TOTAL_BASE_; | +
| uint256 public _POOL_QUOTE_CAP_; | ++ | uint256 public _POOL_QUOTE_CAP_; | +
| + | + | + |
| // ============ Settlement ============ | ++ | // ============ Settlement ============ | +
| + | + | + |
| uint256 public _QUOTE_RESERVE_; | ++ | uint256 public _QUOTE_RESERVE_; | +
| + | + | + |
| uint256 public _UNUSED_BASE_; | ++ | uint256 public _UNUSED_BASE_; | +
| uint256 public _UNUSED_QUOTE_; | ++ | uint256 public _UNUSED_QUOTE_; | +
| + | + | + |
| uint256 public _TOTAL_SHARES_; | ++ | uint256 public _TOTAL_SHARES_; | +
| mapping(address => uint256) internal _SHARES_; | ++ | mapping(address => uint256) internal _SHARES_; | +
| mapping(address => bool) public _CLAIMED_QUOTE_; | +<> | +mapping(address => bool) public _CLAIMED_; | +
| + | = | ++ |
| address public _POOL_FACTORY_; | ++ | address public _POOL_FACTORY_; | +
| address public _POOL_; | ++ | address public _POOL_; | +
| uint256 public _POOL_FEE_RATE_; | ++- | ++ |
| uint256 public _AVG_SETTLED_PRICE_; | += | +uint256 public _AVG_SETTLED_PRICE_; | +
| + | + | + |
| // ============ Advanced Control ============ | ++ | // ============ Advanced Control ============ | +
| + | + | + |
| address public _MAINTAINER_; | ++ | address public _MAINTAINER_; | +
| IFeeRateModel public _MT_FEE_RATE_MODEL_; | ++ | IFeeRateModel public _MT_FEE_RATE_MODEL_; | +
| IPermissionManager public _BIDDER_PERMISSION_; | ++ | IPermissionManager public _BIDDER_PERMISSION_; | +
| + | + | + |
| // ============ PMM Parameters ============ | ++ | // ============ PMM Parameters ============ | +
| + | + | + |
| uint256 public _K_; | ++ | uint256 public _K_; | +
| uint256 public _I_; | ++ | uint256 public _I_; | +
| + | + | + |
| // ============ LP Token Vesting && Claim Params ============ | +<> | +// ============ LP Token Vesting ============ | +
| + | = | ++ |
| uint256 public _TOTAL_LP_AMOUNT_; | ++ | uint256 public _TOTAL_LP_AMOUNT_; | +
| uint256 public _FREEZE_DURATION_; | ++ | uint256 public _FREEZE_DURATION_; | +
| uint256 public _VESTING_DURATION_; | ++ | uint256 public _VESTING_DURATION_; | +
| uint256 public _CLIFF_RATE_; | ++ | uint256 public _CLIFF_RATE_; | +
| + | + | + |
| uint256 public _TOKEN_CLAIM_DURATION_; | ++- | ++ |
| uint256 public _TOKEN_VESTING_DURATION_; | ++ | + |
| uint256 public _TOKEN_CLIFF_RATE_; | ++ | + |
| mapping(address => uint256) _CLAIMED_BASE_TOKEN_; | ++ | + |
| + | + | + |
| // ============ Modifiers ============ | += | +// ============ Modifiers ============ | +
| modifier isForceStop() { | ++- | ++ |
| require(!_FORCE_STOP_, "FORCE_STOP"); | ++ | + |
| _; | ++ | + |
| } | ++ | + |
| + | = | ++ |
| modifier phaseBid() { | ++ | modifier phaseBid() { | +
| require( | ++ | require( | +
| block.timestamp >= _PHASE_BID_STARTTIME_ && block.timestamp < _PHASE_BID_ENDTIME_, | ++ | block.timestamp >= _PHASE_BID_STARTTIME_ && block.timestamp < _PHASE_BID_ENDTIME_, | +
| "NOT_PHASE_BID" | ++ | "NOT_PHASE_BID" | +
| ); | ++ | ); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier phaseCalm() { | ++ | modifier phaseCalm() { | +
| require( | ++ | require( | +
| block.timestamp >= _PHASE_BID_ENDTIME_ && block.timestamp < _PHASE_CALM_ENDTIME_, | ++ | block.timestamp >= _PHASE_BID_ENDTIME_ && block.timestamp < _PHASE_CALM_ENDTIME_, | +
| "NOT_PHASE_CALM" | ++ | "NOT_PHASE_CALM" | +
| ); | ++ | ); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier phaseBidOrCalm() { | ++ | modifier phaseBidOrCalm() { | +
| require( | ++ | require( | +
| block.timestamp >= _PHASE_BID_STARTTIME_ && block.timestamp < _PHASE_CALM_ENDTIME_, | ++ | block.timestamp >= _PHASE_BID_STARTTIME_ && block.timestamp < _PHASE_CALM_ENDTIME_, | +
| "NOT_PHASE_BID_OR_CALM" | ++ | "NOT_PHASE_BID_OR_CALM" | +
| ); | ++ | ); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier phaseSettlement() { | ++ | modifier phaseSettlement() { | +
| require(block.timestamp >= _PHASE_CALM_ENDTIME_, "NOT_PHASE_EXE"); | ++ | require(block.timestamp >= _PHASE_CALM_ENDTIME_, "NOT_PHASE_EXE"); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier phaseVesting() { | ++ | modifier phaseVesting() { | +
| require(_SETTLED_, "NOT_VESTING"); | ++ | require(_SETTLED_, "NOT_VESTING"); | +
| _; | ++ | _; | +
| } | ++- | ++ |
| + | + | + |
| function forceStop() external onlyOwner { | ++ | + |
| require(block.timestamp < _PHASE_BID_STARTTIME_, "CP_ALREADY_STARTED"); | ++ | + |
| _FORCE_STOP_ = true; | ++ | + |
| _TOTAL_BASE_ = 0; | ++ | + |
| uint256 baseAmount = _BASE_TOKEN_.balanceOf(address(this)); | ++ | + |
| _BASE_TOKEN_.transfer(_OWNER_, baseAmount); | ++ | + |
| } | += | +} | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/DODOMath.sol | ++ | // File: contracts/lib/DODOMath.sol | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title DODOMath | ++ | * @title DODOMath | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions | ++ | * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions | +
| */ | ++ | */ | +
| library DODOMath { | ++ | library DODOMath { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| + | + | + |
| /* | ++ | /* | +
| Integrate dodo curve from V1 to V2 | ++ | Integrate dodo curve from V1 to V2 | +
| require V0>=V1>=V2>0 | ++ | require V0>=V1>=V2>0 | +
| res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1) | ++ | res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1) | +
| let V1-V2=delta | ++ | let V1-V2=delta | +
| res = i*delta*(1-k+k(V0^2/V1/V2)) | ++ | res = i*delta*(1-k+k(V0^2/V1/V2)) | +
| + | + | + |
| i is the price of V-res trading pair | ++ | i is the price of V-res trading pair | +
| + | + | + |
| support k=1 & k=0 case | ++ | support k=1 & k=0 case | +
| + | + | + |
| [round down] | ++ | [round down] | +
| */ | ++ | */ | +
| function _GeneralIntegrate( | ++ | function _GeneralIntegrate( | +
| uint256 V0, | ++ | uint256 V0, | +
| uint256 V1, | ++ | uint256 V1, | +
| uint256 V2, | ++ | uint256 V2, | +
| uint256 i, | ++ | uint256 i, | +
| uint256 k | ++ | uint256 k | +
| ) internal pure returns (uint256) { | ++ | ) internal pure returns (uint256) { | +
| require(V0 > 0, "TARGET_IS_ZERO"); | ++ | require(V0 > 0, "TARGET_IS_ZERO"); | +
| uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta | ++ | uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta | +
| if (k == 0) { | ++ | if (k == 0) { | +
| return fairAmount.div(DecimalMath.ONE); | ++ | return fairAmount.div(DecimalMath.ONE); | +
| } | ++ | } | +
| uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2); | ++ | uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2); | +
| uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2) | ++ | uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2) | +
| return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2); | ++ | return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2); | +
| } | ++ | } | +
| + | + | + |
| /* | ++ | /* | +
| Follow the integration function above | ++ | Follow the integration function above | +
| i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) | ++ | i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) | +
| Assume Q2=Q0, Given Q1 and deltaB, solve Q0 | ++ | Assume Q2=Q0, Given Q1 and deltaB, solve Q0 | +
| + | + | + |
| i is the price of delta-V trading pair | ++ | i is the price of delta-V trading pair | +
| give out target of V | ++ | give out target of V | +
| + | + | + |
| support k=1 & k=0 case | ++ | support k=1 & k=0 case | +
| + | + | + |
| [round down] | ++ | [round down] | +
| */ | ++ | */ | +
| function _SolveQuadraticFunctionForTarget( | ++ | function _SolveQuadraticFunctionForTarget( | +
| uint256 V1, | ++ | uint256 V1, | +
| uint256 delta, | ++ | uint256 delta, | +
| uint256 i, | ++ | uint256 i, | +
| uint256 k | ++ | uint256 k | +
| ) internal pure returns (uint256) { | ++ | ) internal pure returns (uint256) { | +
| if (V1 == 0) { | ++ | if (V1 == 0) { | +
| return 0; | ++ | return 0; | +
| } | ++ | } | +
| if (k == 0) { | ++ | if (k == 0) { | +
| return V1.add(DecimalMath.mulFloor(i, delta)); | ++ | return V1.add(DecimalMath.mulFloor(i, delta)); | +
| } | ++ | } | +
| // V0 = V1*(1+(sqrt-1)/2k) | ++ | // V0 = V1*(1+(sqrt-1)/2k) | +
| // sqrt = √(1+4kidelta/V1) | ++ | // sqrt = √(1+4kidelta/V1) | +
| // premium = 1+(sqrt-1)/2k | ++ | // premium = 1+(sqrt-1)/2k | +
| // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt(); | ++ | // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt(); | +
| uint256 sqrt; | ++ | uint256 sqrt; | +
| uint256 ki = (4 * k).mul(i); | ++ | uint256 ki = (4 * k).mul(i); | +
| if (ki == 0) { | ++ | if (ki == 0) { | +
| sqrt = DecimalMath.ONE; | ++ | sqrt = DecimalMath.ONE; | +
| } else if ((ki * delta) / ki == delta) { | ++ | } else if ((ki * delta) / ki == delta) { | +
| sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt(); | ++ | sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt(); | +
| } else { | ++ | } else { | +
| sqrt = ki.div(V1).mul(delta).add(DecimalMath.ONE2).sqrt(); | ++ | sqrt = ki.div(V1).mul(delta).add(DecimalMath.ONE2).sqrt(); | +
| } | ++ | } | +
| uint256 premium = | ++ | uint256 premium = | +
| DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add(DecimalMath.ONE); | ++ | DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add(DecimalMath.ONE); | +
| // V0 is greater than or equal to V1 according to the solution | ++ | // V0 is greater than or equal to V1 according to the solution | +
| return DecimalMath.mulFloor(V1, premium); | ++ | return DecimalMath.mulFloor(V1, premium); | +
| } | ++ | } | +
| + | + | + |
| /* | ++ | /* | +
| Follow the integration expression above, we have: | ++ | Follow the integration expression above, we have: | +
| i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) | ++ | i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) | +
| Given Q1 and deltaB, solve Q2 | ++ | Given Q1 and deltaB, solve Q2 | +
| This is a quadratic function and the standard version is | ++ | This is a quadratic function and the standard version is | +
| aQ2^2 + bQ2 + c = 0, where | ++ | aQ2^2 + bQ2 + c = 0, where | +
| a=1-k | ++ | a=1-k | +
| -b=(1-k)Q1-kQ0^2/Q1+i*deltaB | ++ | -b=(1-k)Q1-kQ0^2/Q1+i*deltaB | +
| c=-kQ0^2 | ++ | c=-kQ0^2 | +
| and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k) | ++ | and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k) | +
| note: another root is negative, abondan | ++ | note: another root is negative, abondan | +
| + | + | + |
| if deltaBSig=true, then Q2>Q1, user sell Q and receive B | ++ | if deltaBSig=true, then Q2>Q1, user sell Q and receive B | +
| if deltaBSig=false, then Q2<Q1, user sell B and receive Q | ++ | if deltaBSig=false, then Q2<Q1, user sell B and receive Q | +
| return |Q1-Q2| | ++ | return |Q1-Q2| | +
| + | + | + |
| as we only support sell amount as delta, the deltaB is always negative | ++ | as we only support sell amount as delta, the deltaB is always negative | +
| the input ideltaB is actually -ideltaB in the equation | ++ | the input ideltaB is actually -ideltaB in the equation | +
| + | + | + |
| i is the price of delta-V trading pair | ++ | i is the price of delta-V trading pair | +
| + | + | + |
| support k=1 & k=0 case | ++ | support k=1 & k=0 case | +
| + | + | + |
| [round down] | ++ | [round down] | +
| */ | ++ | */ | +
| function _SolveQuadraticFunctionForTrade( | ++ | function _SolveQuadraticFunctionForTrade( | +
| uint256 V0, | ++ | uint256 V0, | +
| uint256 V1, | ++ | uint256 V1, | +
| uint256 delta, | ++ | uint256 delta, | +
| uint256 i, | ++ | uint256 i, | +
| uint256 k | ++ | uint256 k | +
| ) internal pure returns (uint256) { | ++ | ) internal pure returns (uint256) { | +
| require(V0 > 0, "TARGET_IS_ZERO"); | ++ | require(V0 > 0, "TARGET_IS_ZERO"); | +
| if (delta == 0) { | ++ | if (delta == 0) { | +
| return 0; | ++ | return 0; | +
| } | ++ | } | +
| + | + | + |
| if (k == 0) { | ++ | if (k == 0) { | +
| return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta); | ++ | return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta); | +
| } | ++ | } | +
| + | + | + |
| if (k == DecimalMath.ONE) { | ++ | if (k == DecimalMath.ONE) { | +
| // if k==1 | ++ | // if k==1 | +
| // Q2=Q1/(1+ideltaBQ1/Q0/Q0) | ++ | // Q2=Q1/(1+ideltaBQ1/Q0/Q0) | +
| // temp = ideltaBQ1/Q0/Q0 | ++ | // temp = ideltaBQ1/Q0/Q0 | +
| // Q2 = Q1/(1+temp) | ++ | // Q2 = Q1/(1+temp) | +
| // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp)) | ++ | // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp)) | +
| // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0)); | ++ | // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0)); | +
| uint256 temp; | ++ | uint256 temp; | +
| uint256 idelta = i.mul(delta); | ++ | uint256 idelta = i.mul(delta); | +
| if (idelta == 0) { | ++ | if (idelta == 0) { | +
| temp = 0; | ++ | temp = 0; | +
| } else if ((idelta * V1) / idelta == V1) { | ++ | } else if ((idelta * V1) / idelta == V1) { | +
| temp = (idelta * V1).div(V0.mul(V0)); | ++ | temp = (idelta * V1).div(V0.mul(V0)); | +
| } else { | ++ | } else { | +
| temp = delta.mul(V1).div(V0).mul(i).div(V0); | ++ | temp = delta.mul(V1).div(V0).mul(i).div(V0); | +
| } | ++ | } | +
| return V1.mul(temp).div(temp.add(DecimalMath.ONE)); | ++ | return V1.mul(temp).div(temp.add(DecimalMath.ONE)); | +
| } | ++ | } | +
| + | + | + |
| // calculate -b value and sig | ++ | // calculate -b value and sig | +
| // b = kQ0^2/Q1-i*deltaB-(1-k)Q1 | ++ | // b = kQ0^2/Q1-i*deltaB-(1-k)Q1 | +
| // part1 = (1-k)Q1 >=0 | ++ | // part1 = (1-k)Q1 >=0 | +
| // part2 = kQ0^2/Q1-i*deltaB >=0 | ++ | // part2 = kQ0^2/Q1-i*deltaB >=0 | +
| // bAbs = abs(part1-part2) | ++ | // bAbs = abs(part1-part2) | +
| // if part1>part2 => b is negative => bSig is false | ++ | // if part1>part2 => b is negative => bSig is false | +
| // if part2>part1 => b is positive => bSig is true | ++ | // if part2>part1 => b is positive => bSig is true | +
| uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB | ++ | uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB | +
| uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1 | ++ | uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1 | +
| + | + | + |
| bool bSig; | ++ | bool bSig; | +
| if (bAbs >= part2) { | ++ | if (bAbs >= part2) { | +
| bAbs = bAbs - part2; | ++ | bAbs = bAbs - part2; | +
| bSig = false; | ++ | bSig = false; | +
| } else { | ++ | } else { | +
| bAbs = part2 - bAbs; | ++ | bAbs = part2 - bAbs; | +
| bSig = true; | ++ | bSig = true; | +
| } | ++ | } | +
| bAbs = bAbs.div(DecimalMath.ONE); | ++ | bAbs = bAbs.div(DecimalMath.ONE); | +
| + | + | + |
| // calculate sqrt | ++ | // calculate sqrt | +
| uint256 squareRoot = | ++ | uint256 squareRoot = | +
| DecimalMath.mulFloor( | ++ | DecimalMath.mulFloor( | +
| DecimalMath.ONE.sub(k).mul(4), | ++ | DecimalMath.ONE.sub(k).mul(4), | +
| DecimalMath.mulFloor(k, V0).mul(V0) | ++ | DecimalMath.mulFloor(k, V0).mul(V0) | +
| ); // 4(1-k)kQ0^2 | ++ | ); // 4(1-k)kQ0^2 | +
| squareRoot = bAbs.mul(bAbs).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0) | ++ | squareRoot = bAbs.mul(bAbs).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0) | +
| + | + | + |
| // final res | ++ | // final res | +
| uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k) | ++ | uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k) | +
| uint256 numerator; | ++ | uint256 numerator; | +
| if (bSig) { | ++ | if (bSig) { | +
| numerator = squareRoot.sub(bAbs); | ++ | numerator = squareRoot.sub(bAbs); | +
| } else { | ++ | } else { | +
| numerator = bAbs.add(squareRoot); | ++ | numerator = bAbs.add(squareRoot); | +
| } | ++ | } | +
| + | + | + |
| uint256 V2 = DecimalMath.divCeil(numerator, denominator); | ++ | uint256 V2 = DecimalMath.divCeil(numerator, denominator); | +
| if (V2 > V1) { | ++ | if (V2 > V1) { | +
| return 0; | ++ | return 0; | +
| } else { | ++ | } else { | +
| return V1 - V2; | ++ | return V1 - V2; | +
| } | ++ | } | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/lib/PMMPricing.sol | ++ | // File: contracts/lib/PMMPricing.sol | +
| + | + | + |
| + | + | + |
| /** | ++ | /** | +
| * @title Pricing | ++ | * @title Pricing | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice DODO Pricing model | ++ | * @notice DODO Pricing model | +
| */ | ++ | */ | +
| + | + | + |
| library PMMPricing { | ++ | library PMMPricing { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| + | + | + |
| enum RState {ONE, ABOVE_ONE, BELOW_ONE} | ++ | enum RState {ONE, ABOVE_ONE, BELOW_ONE} | +
| + | + | + |
| struct PMMState { | ++ | struct PMMState { | +
| uint256 i; | ++ | uint256 i; | +
| uint256 K; | ++ | uint256 K; | +
| uint256 B; | ++ | uint256 B; | +
| uint256 Q; | ++ | uint256 Q; | +
| uint256 B0; | ++ | uint256 B0; | +
| uint256 Q0; | ++ | uint256 Q0; | +
| RState R; | ++ | RState R; | +
| } | ++ | } | +
| + | + | + |
| // ============ buy & sell ============ | ++ | // ============ buy & sell ============ | +
| + | + | + |
| function sellBaseToken(PMMState memory state, uint256 payBaseAmount) | ++ | function sellBaseToken(PMMState memory state, uint256 payBaseAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns (uint256 receiveQuoteAmount, RState newR) | ++ | returns (uint256 receiveQuoteAmount, RState newR) | +
| { | ++ | { | +
| if (state.R == RState.ONE) { | ++ | if (state.R == RState.ONE) { | +
| // case 1: R=1 | ++ | // case 1: R=1 | +
| // R falls below one | ++ | // R falls below one | +
| receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount); | ++ | receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount); | +
| newR = RState.BELOW_ONE; | ++ | newR = RState.BELOW_ONE; | +
| } else if (state.R == RState.ABOVE_ONE) { | ++ | } else if (state.R == RState.ABOVE_ONE) { | +
| uint256 backToOnePayBase = state.B0.sub(state.B); | ++ | uint256 backToOnePayBase = state.B0.sub(state.B); | +
| uint256 backToOneReceiveQuote = state.Q.sub(state.Q0); | ++ | uint256 backToOneReceiveQuote = state.Q.sub(state.Q0); | +
| // case 2: R>1 | ++ | // case 2: R>1 | +
| // complex case, R status depends on trading amount | ++ | // complex case, R status depends on trading amount | +
| if (payBaseAmount < backToOnePayBase) { | ++ | if (payBaseAmount < backToOnePayBase) { | +
| // case 2.1: R status do not change | ++ | // case 2.1: R status do not change | +
| receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount); | ++ | receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount); | +
| newR = RState.ABOVE_ONE; | ++ | newR = RState.ABOVE_ONE; | +
| if (receiveQuoteAmount > backToOneReceiveQuote) { | ++ | if (receiveQuoteAmount > backToOneReceiveQuote) { | +
| // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount | ++ | // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount | +
| // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote | ++ | // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote | +
| receiveQuoteAmount = backToOneReceiveQuote; | ++ | receiveQuoteAmount = backToOneReceiveQuote; | +
| } | ++ | } | +
| } else if (payBaseAmount == backToOnePayBase) { | ++ | } else if (payBaseAmount == backToOnePayBase) { | +
| // case 2.2: R status changes to ONE | ++ | // case 2.2: R status changes to ONE | +
| receiveQuoteAmount = backToOneReceiveQuote; | ++ | receiveQuoteAmount = backToOneReceiveQuote; | +
| newR = RState.ONE; | ++ | newR = RState.ONE; | +
| } else { | ++ | } else { | +
| // case 2.3: R status changes to BELOW_ONE | ++ | // case 2.3: R status changes to BELOW_ONE | +
| receiveQuoteAmount = backToOneReceiveQuote.add( | ++ | receiveQuoteAmount = backToOneReceiveQuote.add( | +
| _ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase)) | ++ | _ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase)) | +
| ); | ++ | ); | +
| newR = RState.BELOW_ONE; | ++ | newR = RState.BELOW_ONE; | +
| } | ++ | } | +
| } else { | ++ | } else { | +
| // state.R == RState.BELOW_ONE | ++ | // state.R == RState.BELOW_ONE | +
| // case 3: R<1 | ++ | // case 3: R<1 | +
| receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount); | ++ | receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount); | +
| newR = RState.BELOW_ONE; | ++ | newR = RState.BELOW_ONE; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | ++ | function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns (uint256 receiveBaseAmount, RState newR) | ++ | returns (uint256 receiveBaseAmount, RState newR) | +
| { | ++ | { | +
| if (state.R == RState.ONE) { | ++ | if (state.R == RState.ONE) { | +
| receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount); | ++ | receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount); | +
| newR = RState.ABOVE_ONE; | ++ | newR = RState.ABOVE_ONE; | +
| } else if (state.R == RState.ABOVE_ONE) { | ++ | } else if (state.R == RState.ABOVE_ONE) { | +
| receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount); | ++ | receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount); | +
| newR = RState.ABOVE_ONE; | ++ | newR = RState.ABOVE_ONE; | +
| } else { | ++ | } else { | +
| uint256 backToOnePayQuote = state.Q0.sub(state.Q); | ++ | uint256 backToOnePayQuote = state.Q0.sub(state.Q); | +
| uint256 backToOneReceiveBase = state.B.sub(state.B0); | ++ | uint256 backToOneReceiveBase = state.B.sub(state.B0); | +
| if (payQuoteAmount < backToOnePayQuote) { | ++ | if (payQuoteAmount < backToOnePayQuote) { | +
| receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount); | ++ | receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount); | +
| newR = RState.BELOW_ONE; | ++ | newR = RState.BELOW_ONE; | +
| if (receiveBaseAmount > backToOneReceiveBase) { | ++ | if (receiveBaseAmount > backToOneReceiveBase) { | +
| receiveBaseAmount = backToOneReceiveBase; | ++ | receiveBaseAmount = backToOneReceiveBase; | +
| } | ++ | } | +
| } else if (payQuoteAmount == backToOnePayQuote) { | ++ | } else if (payQuoteAmount == backToOnePayQuote) { | +
| receiveBaseAmount = backToOneReceiveBase; | ++ | receiveBaseAmount = backToOneReceiveBase; | +
| newR = RState.ONE; | ++ | newR = RState.ONE; | +
| } else { | ++ | } else { | +
| receiveBaseAmount = backToOneReceiveBase.add( | ++ | receiveBaseAmount = backToOneReceiveBase.add( | +
| _ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote)) | ++ | _ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote)) | +
| ); | ++ | ); | +
| newR = RState.ABOVE_ONE; | ++ | newR = RState.ABOVE_ONE; | +
| } | ++ | } | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // ============ R = 1 cases ============ | ++ | // ============ R = 1 cases ============ | +
| + | + | + |
| function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount) | ++ | function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns ( | ++ | returns ( | +
| uint256 // receiveQuoteToken | ++ | uint256 // receiveQuoteToken | +
| ) | ++ | ) | +
| { | ++ | { | +
| // in theory Q2 <= targetQuoteTokenAmount | ++ | // in theory Q2 <= targetQuoteTokenAmount | +
| // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount | ++ | // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount | +
| return | ++ | return | +
| DODOMath._SolveQuadraticFunctionForTrade( | ++ | DODOMath._SolveQuadraticFunctionForTrade( | +
| state.Q0, | ++ | state.Q0, | +
| state.Q0, | ++ | state.Q0, | +
| payBaseAmount, | ++ | payBaseAmount, | +
| state.i, | ++ | state.i, | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | ++ | function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns ( | ++ | returns ( | +
| uint256 // receiveBaseToken | ++ | uint256 // receiveBaseToken | +
| ) | ++ | ) | +
| { | ++ | { | +
| return | ++ | return | +
| DODOMath._SolveQuadraticFunctionForTrade( | ++ | DODOMath._SolveQuadraticFunctionForTrade( | +
| state.B0, | ++ | state.B0, | +
| state.B0, | ++ | state.B0, | +
| payQuoteAmount, | ++ | payQuoteAmount, | +
| DecimalMath.reciprocalFloor(state.i), | ++ | DecimalMath.reciprocalFloor(state.i), | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| // ============ R < 1 cases ============ | ++ | // ============ R < 1 cases ============ | +
| + | + | + |
| function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | ++ | function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns ( | ++ | returns ( | +
| uint256 // receiveBaseToken | ++ | uint256 // receiveBaseToken | +
| ) | ++ | ) | +
| { | ++ | { | +
| return | ++ | return | +
| DODOMath._GeneralIntegrate( | ++ | DODOMath._GeneralIntegrate( | +
| state.Q0, | ++ | state.Q0, | +
| state.Q.add(payQuoteAmount), | ++ | state.Q.add(payQuoteAmount), | +
| state.Q, | ++ | state.Q, | +
| DecimalMath.reciprocalFloor(state.i), | ++ | DecimalMath.reciprocalFloor(state.i), | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount) | ++ | function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns ( | ++ | returns ( | +
| uint256 // receiveQuoteToken | ++ | uint256 // receiveQuoteToken | +
| ) | ++ | ) | +
| { | ++ | { | +
| return | ++ | return | +
| DODOMath._SolveQuadraticFunctionForTrade( | ++ | DODOMath._SolveQuadraticFunctionForTrade( | +
| state.Q0, | ++ | state.Q0, | +
| state.Q, | ++ | state.Q, | +
| payBaseAmount, | ++ | payBaseAmount, | +
| state.i, | ++ | state.i, | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| // ============ R > 1 cases ============ | ++ | // ============ R > 1 cases ============ | +
| + | + | + |
| function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount) | ++ | function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns ( | ++ | returns ( | +
| uint256 // receiveQuoteToken | ++ | uint256 // receiveQuoteToken | +
| ) | ++ | ) | +
| { | ++ | { | +
| return | ++ | return | +
| DODOMath._GeneralIntegrate( | ++ | DODOMath._GeneralIntegrate( | +
| state.B0, | ++ | state.B0, | +
| state.B.add(payBaseAmount), | ++ | state.B.add(payBaseAmount), | +
| state.B, | ++ | state.B, | +
| state.i, | ++ | state.i, | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | ++ | function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount) | +
| internal | ++ | internal | +
| pure | ++ | pure | +
| returns ( | ++ | returns ( | +
| uint256 // receiveBaseToken | ++ | uint256 // receiveBaseToken | +
| ) | ++ | ) | +
| { | ++ | { | +
| return | ++ | return | +
| DODOMath._SolveQuadraticFunctionForTrade( | ++ | DODOMath._SolveQuadraticFunctionForTrade( | +
| state.B0, | ++ | state.B0, | +
| state.B, | ++ | state.B, | +
| payQuoteAmount, | ++ | payQuoteAmount, | +
| DecimalMath.reciprocalFloor(state.i), | ++ | DecimalMath.reciprocalFloor(state.i), | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| + | + | + |
| // ============ Helper functions ============ | ++ | // ============ Helper functions ============ | +
| + | + | + |
| function adjustedTarget(PMMState memory state) internal pure { | ++ | function adjustedTarget(PMMState memory state) internal pure { | +
| if (state.R == RState.BELOW_ONE) { | ++ | if (state.R == RState.BELOW_ONE) { | +
| state.Q0 = DODOMath._SolveQuadraticFunctionForTarget( | ++ | state.Q0 = DODOMath._SolveQuadraticFunctionForTarget( | +
| state.Q, | ++ | state.Q, | +
| state.B.sub(state.B0), | ++ | state.B.sub(state.B0), | +
| state.i, | ++ | state.i, | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } else if (state.R == RState.ABOVE_ONE) { | ++ | } else if (state.R == RState.ABOVE_ONE) { | +
| state.B0 = DODOMath._SolveQuadraticFunctionForTarget( | ++ | state.B0 = DODOMath._SolveQuadraticFunctionForTarget( | +
| state.B, | ++ | state.B, | +
| state.Q.sub(state.Q0), | ++ | state.Q.sub(state.Q0), | +
| DecimalMath.reciprocalFloor(state.i), | ++ | DecimalMath.reciprocalFloor(state.i), | +
| state.K | ++ | state.K | +
| ); | ++ | ); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function getMidPrice(PMMState memory state) internal pure returns (uint256) { | ++ | function getMidPrice(PMMState memory state) internal pure returns (uint256) { | +
| if (state.R == RState.BELOW_ONE) { | ++ | if (state.R == RState.BELOW_ONE) { | +
| uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q); | ++ | uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q); | +
| R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R)); | ++ | R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R)); | +
| return DecimalMath.divFloor(state.i, R); | ++ | return DecimalMath.divFloor(state.i, R); | +
| } else { | ++ | } else { | +
| uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B); | ++ | uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B); | +
| R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R)); | ++ | R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R)); | +
| return DecimalMath.mulFloor(state.i, R); | ++ | return DecimalMath.mulFloor(state.i, R); | +
| } | ++ | } | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/intf/IDODOCallee.sol | ++ | // File: contracts/intf/IDODOCallee.sol | +
| + | + | + |
| + | + | + |
| interface IDODOCallee { | ++ | interface IDODOCallee { | +
| function DVMSellShareCall( | ++ | function DVMSellShareCall( | +
| address sender, | ++ | address sender, | +
| uint256 burnShareAmount, | ++ | uint256 burnShareAmount, | +
| uint256 baseAmount, | ++ | uint256 baseAmount, | +
| uint256 quoteAmount, | ++ | uint256 quoteAmount, | +
| bytes calldata data | ++ | bytes calldata data | +
| ) external; | ++ | ) external; | +
| + | + | + |
| function DVMFlashLoanCall( | ++ | function DVMFlashLoanCall( | +
| address sender, | ++ | address sender, | +
| uint256 baseAmount, | ++ | uint256 baseAmount, | +
| uint256 quoteAmount, | ++ | uint256 quoteAmount, | +
| bytes calldata data | ++ | bytes calldata data | +
| ) external; | ++ | ) external; | +
| + | + | + |
| function DPPFlashLoanCall( | ++ | function DPPFlashLoanCall( | +
| address sender, | ++ | address sender, | +
| uint256 baseAmount, | ++ | uint256 baseAmount, | +
| uint256 quoteAmount, | ++ | uint256 quoteAmount, | +
| bytes calldata data | ++ | bytes calldata data | +
| ) external; | ++ | ) external; | +
| + | + | + |
| function DSPFlashLoanCall( | ++- | ++ |
| address sender, | ++ | + |
| uint256 baseAmount, | ++ | + |
| uint256 quoteAmount, | ++ | + |
| bytes calldata data | ++ | + |
| ) external; | ++ | + |
| + | + | + |
| function CPCancelCall( | += | +function CPCancelCall( | +
| address sender, | ++ | address sender, | +
| uint256 amount, | ++ | uint256 amount, | +
| bytes calldata data | ++ | bytes calldata data | +
| ) external; | ++ | ) external; | +
| + | + | + |
| function CPClaimBidCall( | ++ | function CPClaimBidCall( | +
| address sender, | ++ | address sender, | +
| uint256 baseAmount, | ++ | uint256 baseAmount, | +
| uint256 quoteAmount, | ++ | uint256 quoteAmount, | +
| bytes calldata data | ++ | bytes calldata data | +
| ) external; | ++ | ) external; | +
| + | +- | ++ |
| function NFTRedeemCall( | ++ | + |
| address payable assetTo, | ++ | + |
| uint256 quoteAmount, | ++ | + |
| bytes calldata | ++ | + |
| ) external; | ++ | + |
| } | += | +} | +
| + | + | + |
| // File: contracts/CrowdPooling/impl/CPFunding.sol | ++ | // File: contracts/CrowdPooling/impl/CPFunding.sol | +
| + | -+ | ++ |
| + | + | + |
| + | = | ++ |
| + | + | + |
| + | + | + |
| contract CPFunding is CPStorage { | ++ | contract CPFunding is CPStorage { | +
| using SafeERC20 for IERC20; | ++ | using SafeERC20 for IERC20; | +
| + | + | + |
| // ============ Events ============ | ++ | // ============ Events ============ | +
| + | + | + |
| event Bid(address to, uint256 amount, uint256 fee); | ++ | event Bid(address to, uint256 amount, uint256 fee); | +
| event Cancel(address to,uint256 amount); | ++ | event Cancel(address to,uint256 amount); | +
| event Settle(); | ++ | event Settle(); | +
| + | + | + |
| // ============ BID & CALM PHASE ============ | ++ | // ============ BID & CALM PHASE ============ | +
| + | + | + |
| modifier isBidderAllow(address bidder) { | ++ | modifier isBidderAllow(address bidder) { | +
| require(_BIDDER_PERMISSION_.isAllowed(bidder), "BIDDER_NOT_ALLOWED"); | ++ | require(_BIDDER_PERMISSION_.isAllowed(bidder), "BIDDER_NOT_ALLOWED"); | +
| if(_IS_OVERCAP_STOP) { | ++- | ++ |
| require(_QUOTE_TOKEN_.balanceOf(address(this)) < _POOL_QUOTE_CAP_, "ALREADY_OVER_CAP"); | ++ | + |
| } | ++ | + |
| _; | += | +_; | +
| } | ++ | } | +
| + | + | + |
| function bid(address to) external isForceStop phaseBid preventReentrant isBidderAllow(to) { | +<> | +function bid(address to) external phaseBid preventReentrant isBidderAllow(to) { | +
| uint256 input = _getQuoteInput(); | += | +uint256 input = _getQuoteInput(); | +
| uint256 mtFee = DecimalMath.mulFloor(input, _MT_FEE_RATE_MODEL_.getFeeRate(to)); | ++ | uint256 mtFee = DecimalMath.mulFloor(input, _MT_FEE_RATE_MODEL_.getFeeRate(to)); | +
| _transferQuoteOut(_MAINTAINER_, mtFee); | ++ | _transferQuoteOut(_MAINTAINER_, mtFee); | +
| _mintShares(to, input.sub(mtFee)); | ++ | _mintShares(to, input.sub(mtFee)); | +
| _sync(); | ++ | _sync(); | +
| emit Bid(to, input, mtFee); | ++ | emit Bid(to, input, mtFee); | +
| } | ++ | } | +
| + | + | + |
| function cancel(address to, uint256 amount, bytes calldata data) external phaseBidOrCalm preventReentrant { | ++ | function cancel(address to, uint256 amount, bytes calldata data) external phaseBidOrCalm preventReentrant { | +
| require(_SHARES_[msg.sender] >= amount, "SHARES_NOT_ENOUGH"); | ++ | require(_SHARES_[msg.sender] >= amount, "SHARES_NOT_ENOUGH"); | +
| _burnShares(msg.sender, amount); | ++ | _burnShares(msg.sender, amount); | +
| _transferQuoteOut(to, amount); | ++ | _transferQuoteOut(to, amount); | +
| _sync(); | ++ | _sync(); | +
| + | + | + |
| if(data.length > 0){ | ++ | if(data.length > 0){ | +
| IDODOCallee(to).CPCancelCall(msg.sender,amount,data); | ++ | IDODOCallee(to).CPCancelCall(msg.sender,amount,data); | +
| } | ++ | } | +
| + | + | + |
| emit Cancel(msg.sender,amount); | ++ | emit Cancel(msg.sender,amount); | +
| } | ++ | } | +
| + | + | + |
| function _mintShares(address to, uint256 amount) internal { | ++ | function _mintShares(address to, uint256 amount) internal { | +
| _SHARES_[to] = _SHARES_[to].add(amount); | ++ | _SHARES_[to] = _SHARES_[to].add(amount); | +
| _TOTAL_SHARES_ = _TOTAL_SHARES_.add(amount); | ++ | _TOTAL_SHARES_ = _TOTAL_SHARES_.add(amount); | +
| } | ++ | } | +
| + | + | + |
| function _burnShares(address from, uint256 amount) internal { | ++ | function _burnShares(address from, uint256 amount) internal { | +
| _SHARES_[from] = _SHARES_[from].sub(amount); | ++ | _SHARES_[from] = _SHARES_[from].sub(amount); | +
| _TOTAL_SHARES_ = _TOTAL_SHARES_.sub(amount); | ++ | _TOTAL_SHARES_ = _TOTAL_SHARES_.sub(amount); | +
| } | ++ | } | +
| + | + | + |
| // ============ SETTLEMENT ============ | ++ | // ============ SETTLEMENT ============ | +
| + | + | + |
| function settle() external isForceStop phaseSettlement preventReentrant { | +<> | +function settle() external phaseSettlement preventReentrant { | +
| _settle(); | += | +_settle(); | +
| + | + | + |
| (uint256 poolBase, uint256 poolQuote, uint256 poolI, uint256 unUsedBase, uint256 unUsedQuote) = getSettleResult(); | ++ | (uint256 poolBase, uint256 poolQuote, uint256 poolI, uint256 unUsedBase, uint256 unUsedQuote) = getSettleResult(); | +
| _UNUSED_BASE_ = unUsedBase; | ++ | _UNUSED_BASE_ = unUsedBase; | +
| _UNUSED_QUOTE_ = unUsedQuote; | ++ | _UNUSED_QUOTE_ = unUsedQuote; | +
| + | + | + |
| address _poolBaseToken; | ++ | address _poolBaseToken; | +
| address _poolQuoteToken; | ++ | address _poolQuoteToken; | +
| + | + | + |
| if (_UNUSED_BASE_ > poolBase) { | ++ | if (_UNUSED_BASE_ > poolBase) { | +
| _poolBaseToken = address(_QUOTE_TOKEN_); | ++ | _poolBaseToken = address(_QUOTE_TOKEN_); | +
| _poolQuoteToken = address(_BASE_TOKEN_); | ++ | _poolQuoteToken = address(_BASE_TOKEN_); | +
| } else { | ++ | } else { | +
| _poolBaseToken = address(_BASE_TOKEN_); | ++ | _poolBaseToken = address(_BASE_TOKEN_); | +
| _poolQuoteToken = address(_QUOTE_TOKEN_); | ++ | _poolQuoteToken = address(_QUOTE_TOKEN_); | +
| } | ++ | } | +
| + | + | + |
| _POOL_ = IDVMFactory(_POOL_FACTORY_).createDODOVendingMachine( | ++ | _POOL_ = IDVMFactory(_POOL_FACTORY_).createDODOVendingMachine( | +
| _poolBaseToken, | ++ | _poolBaseToken, | +
| _poolQuoteToken, | ++ | _poolQuoteToken, | +
| _POOL_FEE_RATE_, | +<> | +3e15, // 0.3% lp feeRate | +
| poolI, | += | +poolI, | +
| DecimalMath.ONE, | ++ | DecimalMath.ONE, | +
| _IS_OPEN_TWAP_ | ++ | _IS_OPEN_TWAP_ | +
| ); | ++ | ); | +
| + | + | + |
| uint256 avgPrice = unUsedBase == 0 ? _I_ : DecimalMath.divCeil(poolQuote, unUsedBase); | ++ | uint256 avgPrice = unUsedBase == 0 ? _I_ : DecimalMath.divCeil(poolQuote, unUsedBase); | +
| _AVG_SETTLED_PRICE_ = avgPrice; | ++ | _AVG_SETTLED_PRICE_ = avgPrice; | +
| + | + | + |
| _transferBaseOut(_POOL_, poolBase); | ++ | _transferBaseOut(_POOL_, poolBase); | +
| _transferQuoteOut(_POOL_, poolQuote); | ++ | _transferQuoteOut(_POOL_, poolQuote); | +
| + | + | + |
| (_TOTAL_LP_AMOUNT_, ,) = IDVM(_POOL_).buyShares(address(this)); | ++ | (_TOTAL_LP_AMOUNT_, ,) = IDVM(_POOL_).buyShares(address(this)); | +
| + | + | + |
| msg.sender.transfer(_SETTEL_FUND_); | ++ | msg.sender.transfer(_SETTEL_FUND_); | +
| + | + | + |
| emit Settle(); | ++ | emit Settle(); | +
| } | ++ | } | +
| + | + | + |
| // in case something wrong with base token contract | ++ | // in case something wrong with base token contract | +
| function emergencySettle() external isForceStop phaseSettlement preventReentrant { | +<> | +function emergencySettle() external phaseSettlement preventReentrant { | +
| require(block.timestamp >= _PHASE_CALM_ENDTIME_.add(_SETTLEMENT_EXPIRE_), "NOT_EMERGENCY"); | += | +require(block.timestamp >= _PHASE_CALM_ENDTIME_.add(_SETTLEMENT_EXPIRE_), "NOT_EMERGENCY"); | +
| _settle(); | ++ | _settle(); | +
| _UNUSED_QUOTE_ = _QUOTE_TOKEN_.balanceOf(address(this)); | ++ | _UNUSED_QUOTE_ = _QUOTE_TOKEN_.balanceOf(address(this)); | +
| } | ++ | } | +
| + | + | + |
| function _settle() internal { | ++ | function _settle() internal { | +
| require(!_SETTLED_, "ALREADY_SETTLED"); | ++ | require(!_SETTLED_, "ALREADY_SETTLED"); | +
| _SETTLED_ = true; | ++ | _SETTLED_ = true; | +
| _SETTLED_TIME_ = block.timestamp; | ++ | _SETTLED_TIME_ = block.timestamp; | +
| } | ++ | } | +
| + | + | + |
| // ============ Pricing ============ | ++ | // ============ Pricing ============ | +
| + | + | + |
| function getSettleResult() public view returns (uint256 poolBase, uint256 poolQuote, uint256 poolI, uint256 unUsedBase, uint256 unUsedQuote) { | ++ | function getSettleResult() public view returns (uint256 poolBase, uint256 poolQuote, uint256 poolI, uint256 unUsedBase, uint256 unUsedQuote) { | +
| poolQuote = _QUOTE_TOKEN_.balanceOf(address(this)); | ++ | poolQuote = _QUOTE_TOKEN_.balanceOf(address(this)); | +
| if (poolQuote > _POOL_QUOTE_CAP_) { | ++ | if (poolQuote > _POOL_QUOTE_CAP_) { | +
| poolQuote = _POOL_QUOTE_CAP_; | ++ | poolQuote = _POOL_QUOTE_CAP_; | +
| } | ++ | } | +
| (uint256 soldBase,) = PMMPricing.sellQuoteToken(_getPMMState(), poolQuote); | ++ | (uint256 soldBase,) = PMMPricing.sellQuoteToken(_getPMMState(), poolQuote); | +
| poolBase = _TOTAL_BASE_.sub(soldBase); | ++ | poolBase = _TOTAL_BASE_.sub(soldBase); | +
| + | + | + |
| unUsedQuote = _QUOTE_TOKEN_.balanceOf(address(this)).sub(poolQuote); | ++ | unUsedQuote = _QUOTE_TOKEN_.balanceOf(address(this)).sub(poolQuote); | +
| unUsedBase = _BASE_TOKEN_.balanceOf(address(this)).sub(poolBase); | ++ | unUsedBase = _BASE_TOKEN_.balanceOf(address(this)).sub(poolBase); | +
| + | + | + |
| // Try to make midPrice equal to avgPrice | ++ | // Try to make midPrice equal to avgPrice | +
| // k=1, If quote and base are not balanced, one side must be cut off | ++ | // k=1, If quote and base are not balanced, one side must be cut off | +
| // DVM truncated quote, but if more quote than base entering the pool, we need set the quote to the base | ++ | // DVM truncated quote, but if more quote than base entering the pool, we need set the quote to the base | +
| + | + | + |
| // m = avgPrice | ++ | // m = avgPrice | +
| // i = m (1-quote/(m*base)) | ++ | // i = m (1-quote/(m*base)) | +
| // if quote = m*base i = 1 | ++ | // if quote = m*base i = 1 | +
| // if quote > m*base reverse | ++ | // if quote > m*base reverse | +
| uint256 avgPrice = unUsedBase == 0 ? _I_ : DecimalMath.divCeil(poolQuote, unUsedBase); | ++ | uint256 avgPrice = unUsedBase == 0 ? _I_ : DecimalMath.divCeil(poolQuote, unUsedBase); | +
| uint256 baseDepth = DecimalMath.mulFloor(avgPrice, poolBase); | ++ | uint256 baseDepth = DecimalMath.mulFloor(avgPrice, poolBase); | +
| + | + | + |
| if (poolQuote == 0) { | ++ | if (poolQuote == 0) { | +
| // ask side only DVM | ++ | // ask side only DVM | +
| poolI = _I_; | ++ | poolI = _I_; | +
| } else if (unUsedBase== poolBase) { | ++ | } else if (unUsedBase== poolBase) { | +
| // standard bonding curve | ++ | // standard bonding curve | +
| poolI = 1; | ++ | poolI = 1; | +
| } else if (unUsedBase < poolBase) { | ++ | } else if (unUsedBase < poolBase) { | +
| // poolI up round | ++ | // poolI up round | +
| uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divFloor(poolQuote, baseDepth)); | ++ | uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divFloor(poolQuote, baseDepth)); | +
| poolI = avgPrice.mul(ratio).mul(ratio).divCeil(DecimalMath.ONE2); | ++ | poolI = avgPrice.mul(ratio).mul(ratio).divCeil(DecimalMath.ONE2); | +
| } else if (unUsedBase > poolBase) { | ++ | } else if (unUsedBase > poolBase) { | +
| // poolI down round | ++ | // poolI down round | +
| uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divCeil(baseDepth, poolQuote)); | ++ | uint256 ratio = DecimalMath.ONE.sub(DecimalMath.divCeil(baseDepth, poolQuote)); | +
| poolI = ratio.mul(ratio).div(avgPrice); | ++ | poolI = ratio.mul(ratio).div(avgPrice); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function _getPMMState() internal view returns (PMMPricing.PMMState memory state) { | ++ | function _getPMMState() internal view returns (PMMPricing.PMMState memory state) { | +
| state.i = _I_; | ++ | state.i = _I_; | +
| state.K = _K_; | ++ | state.K = _K_; | +
| state.B = _TOTAL_BASE_; | ++ | state.B = _TOTAL_BASE_; | +
| state.Q = 0; | ++ | state.Q = 0; | +
| state.B0 = state.B; | ++ | state.B0 = state.B; | +
| state.Q0 = 0; | ++ | state.Q0 = 0; | +
| state.R = PMMPricing.RState.ONE; | ++ | state.R = PMMPricing.RState.ONE; | +
| } | ++ | } | +
| + | + | + |
| function getExpectedAvgPrice() external view returns (uint256) { | ++ | function getExpectedAvgPrice() external view returns (uint256) { | +
| require(!_SETTLED_, "ALREADY_SETTLED"); | ++ | require(!_SETTLED_, "ALREADY_SETTLED"); | +
| (uint256 poolBase, uint256 poolQuote, , , ) = getSettleResult(); | ++ | (uint256 poolBase, uint256 poolQuote, , , ) = getSettleResult(); | +
| return DecimalMath.divCeil(poolQuote, _BASE_TOKEN_.balanceOf(address(this)).sub(poolBase)); | ++ | return DecimalMath.divCeil(poolQuote, _BASE_TOKEN_.balanceOf(address(this)).sub(poolBase)); | +
| } | ++ | } | +
| + | + | + |
| // ============ Asset In ============ | ++ | // ============ Asset In ============ | +
| + | + | + |
| function _getQuoteInput() internal view returns (uint256 input) { | ++ | function _getQuoteInput() internal view returns (uint256 input) { | +
| return _QUOTE_TOKEN_.balanceOf(address(this)).sub(_QUOTE_RESERVE_); | ++ | return _QUOTE_TOKEN_.balanceOf(address(this)).sub(_QUOTE_RESERVE_); | +
| } | ++ | } | +
| + | + | + |
| // ============ Set States ============ | ++ | // ============ Set States ============ | +
| + | + | + |
| function _sync() internal { | ++ | function _sync() internal { | +
| uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); | ++ | uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); | +
| if (quoteBalance != _QUOTE_RESERVE_) { | ++ | if (quoteBalance != _QUOTE_RESERVE_) { | +
| _QUOTE_RESERVE_ = quoteBalance; | ++ | _QUOTE_RESERVE_ = quoteBalance; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // ============ Asset Out ============ | ++ | // ============ Asset Out ============ | +
| + | + | + |
| function _transferBaseOut(address to, uint256 amount) internal { | ++ | function _transferBaseOut(address to, uint256 amount) internal { | +
| if (amount > 0) { | ++ | if (amount > 0) { | +
| _BASE_TOKEN_.safeTransfer(to, amount); | ++ | _BASE_TOKEN_.safeTransfer(to, amount); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function _transferQuoteOut(address to, uint256 amount) internal { | ++ | function _transferQuoteOut(address to, uint256 amount) internal { | +
| if (amount > 0) { | ++ | if (amount > 0) { | +
| _QUOTE_TOKEN_.safeTransfer(to, amount); | ++ | _QUOTE_TOKEN_.safeTransfer(to, amount); | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| function getShares(address user) external view returns (uint256) { | ++ | function getShares(address user) external view returns (uint256) { | +
| return _SHARES_[user]; | ++ | return _SHARES_[user]; | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/CrowdPooling/impl/CPVesting.sol | ++ | // File: contracts/CrowdPooling/impl/CPVesting.sol | +
| + | + | + |
| + | + | + |
| + | -+ | ++ |
| + | + | + |
| /** | += | +/** | +
| * @title CPVesting | ++ | * @title CPVesting | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice Lock Token and release it linearly | ++ | * @notice Lock Token and release it linearly | +
| */ | ++ | */ | +
| + | + | + |
| contract CPVesting is CPFunding { | ++ | contract CPVesting is CPFunding { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| using SafeERC20 for IERC20; | ++ | using SafeERC20 for IERC20; | +
| + | + | + |
| // ============ Events ============ | ++ | // ============ Events ============ | +
| + | + | + |
| event ClaimBaseToken(address user, uint256 baseAmount); | +<> | ++ |
| event ClaimQuoteToken(address user, uint256 quoteAmount); | ++ | event Claim(address user, uint256 baseAmount, uint256 quoteAmount); | +
| event ClaimLP(uint256 amount); | += | +event ClaimLP(uint256 amount); | +
| + | + | + |
| + | + | + |
| // ================ Modifiers ================ | ++ | // ================ Modifiers ================ | +
| + | + | + |
| modifier afterSettlement() { | ++ | modifier afterSettlement() { | +
| require(_SETTLED_, "NOT_SETTLED"); | ++ | require(_SETTLED_, "NOT_SETTLED"); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier afterFreeze() { | ++ | modifier afterFreeze() { | +
| require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_FREEZE_DURATION_), "FREEZED"); | ++ | require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_FREEZE_DURATION_), "FREEZED"); | +
| _; | ++ | _; | +
| } | ++ | } | +
| + | + | + |
| modifier afterClaimFreeze() { | ++- | ++ |
| require(_SETTLED_ && block.timestamp >= _SETTLED_TIME_.add(_TOKEN_CLAIM_DURATION_), "CLAIM_FREEZED"); | ++ | + |
| _; | ++ | + |
| } | ++ | + |
| + | + | + |
| // ============ Bidder Functions ============ | += | +// ============ Bidder Functions ============ | +
| + | + | + |
| function claimQuoteToken(address to,bytes calldata data) external afterSettlement { | +<> | +function bidderClaim(address to,bytes calldata data) external afterSettlement { | +
| require(!_CLAIMED_QUOTE_[msg.sender], "ALREADY_CLAIMED_FUND"); | ++ | require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED"); | +
| _CLAIMED_QUOTE_[msg.sender] = true; | ++ | _CLAIMED_[msg.sender] = true; | +
| + | = | ++ |
| + | -+ | +uint256 baseAmount = _UNUSED_BASE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); | +
| uint256 quoteAmount = _UNUSED_QUOTE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); | += | +uint256 quoteAmount = _UNUSED_QUOTE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); | +
| + | + | + |
| + | -+ | +_transferBaseOut(to, baseAmount); | +
| _transferQuoteOut(to, quoteAmount); | += | +_transferQuoteOut(to, quoteAmount); | +
| + | + | + |
| if(data.length>0){ | ++ | if(data.length>0){ | +
| IDODOCallee(to).CPClaimBidCall(msg.sender,0,quoteAmount,data); | +<> | +IDODOCallee(to).CPClaimBidCall(msg.sender,baseAmount,quoteAmount,data); | +
| } | += | +} | +
| + | + | + |
| emit ClaimQuoteToken(msg.sender, quoteAmount); | +<> | +emit Claim(msg.sender, baseAmount, quoteAmount); | +
| } | ++ | + |
| + | + | + |
| + | + | + |
| function claimBaseToken() external afterClaimFreeze { | ++ | + |
| uint256 claimableBaseAmount = getClaimableBaseToken(msg.sender); | ++ | + |
| _transferBaseOut(msg.sender, claimableBaseAmount); | ++ | + |
| _CLAIMED_BASE_TOKEN_[msg.sender] = _CLAIMED_BASE_TOKEN_[msg.sender].add(claimableBaseAmount); | ++ | + |
| emit ClaimBaseToken(msg.sender, claimableBaseAmount); | ++ | + |
| } | ++ | + |
| + | + | + |
| function getClaimableBaseToken(address user) public view afterClaimFreeze returns (uint256) { | ++ | + |
| uint256 baseTotalAmount = _UNUSED_BASE_.mul(_SHARES_[user]).div(_TOTAL_SHARES_); | ++ | + |
| + | + | + |
| uint256 remainingBaseToken = DecimalMath.mulFloor( | ++ | + |
| getRemainingBaseTokenRatio(block.timestamp), | ++ | + |
| baseTotalAmount | ++ | + |
| ); | ++ | + |
| return baseTotalAmount.sub(remainingBaseToken).sub(_CLAIMED_BASE_TOKEN_[user]); | ++ | + |
| } | ++ | + |
| + | + | + |
| function getRemainingBaseTokenRatio(uint256 timestamp) public view afterClaimFreeze returns (uint256) { | ++ | + |
| uint256 timePast = timestamp.sub(_SETTLED_TIME_.add(_TOKEN_CLAIM_DURATION_)); | ++ | + |
| if (timePast < _TOKEN_VESTING_DURATION_) { | ++ | + |
| uint256 remainingTime = _TOKEN_VESTING_DURATION_.sub(timePast); | ++ | + |
| return DecimalMath.ONE.sub(_TOKEN_CLIFF_RATE_).mul(remainingTime).div(_TOKEN_VESTING_DURATION_); | ++ | + |
| } else { | ++ | + |
| return 0; | ++ | + |
| } | ++ | + |
| } | += | +} | +
| + | + | + |
| // ============ Owner Functions ============ | ++ | // ============ Owner Functions ============ | +
| + | + | + |
| function claimLPToken() external onlyOwner afterFreeze { | ++ | function claimLPToken() external onlyOwner afterFreeze { | +
| uint256 lpAmount = getClaimableLPToken(); | ++ | uint256 lpAmount = getClaimableLPToken(); | +
| IERC20(_POOL_).safeTransfer(_OWNER_, lpAmount); | ++ | IERC20(_POOL_).safeTransfer(_OWNER_, lpAmount); | +
| emit ClaimLP(lpAmount); | ++ | emit ClaimLP(lpAmount); | +
| } | ++ | } | +
| + | + | + |
| function getClaimableLPToken() public view afterFreeze returns (uint256) { | ++ | function getClaimableLPToken() public view afterFreeze returns (uint256) { | +
| uint256 remainingLPToken = DecimalMath.mulFloor( | ++ | uint256 remainingLPToken = DecimalMath.mulFloor( | +
| getRemainingLPRatio(block.timestamp), | ++ | getRemainingLPRatio(block.timestamp), | +
| _TOTAL_LP_AMOUNT_ | ++ | _TOTAL_LP_AMOUNT_ | +
| ); | ++ | ); | +
| return IERC20(_POOL_).balanceOf(address(this)).sub(remainingLPToken); | ++ | return IERC20(_POOL_).balanceOf(address(this)).sub(remainingLPToken); | +
| } | ++ | } | +
| + | + | + |
| function getRemainingLPRatio(uint256 timestamp) public view afterFreeze returns (uint256) { | ++ | function getRemainingLPRatio(uint256 timestamp) public view afterFreeze returns (uint256) { | +
| uint256 timePast = timestamp.sub(_SETTLED_TIME_.add(_FREEZE_DURATION_)); | ++ | uint256 timePast = timestamp.sub(_SETTLED_TIME_.add(_FREEZE_DURATION_)); | +
| if (timePast < _VESTING_DURATION_) { | ++ | if (timePast < _VESTING_DURATION_) { | +
| uint256 remainingTime = _VESTING_DURATION_.sub(timePast); | ++ | uint256 remainingTime = _VESTING_DURATION_.sub(timePast); | +
| return DecimalMath.ONE.sub(_CLIFF_RATE_).mul(remainingTime).div(_VESTING_DURATION_); | ++ | return DecimalMath.ONE.sub(_CLIFF_RATE_).mul(remainingTime).div(_VESTING_DURATION_); | +
| } else { | ++ | } else { | +
| return 0; | ++ | return 0; | +
| } | ++ | } | +
| } | ++ | } | +
| } | ++ | } | +
| + | + | + |
| // File: contracts/CrowdPooling/impl/CP.sol | ++ | // File: contracts/CrowdPooling/impl/CP.sol | +
| + | + | + |
| + | + | + |
| + | + | + |
| + | -+ | ++ |
| /** | += | +/** | +
| * @title DODO CrowdPooling | ++ | * @title DODO CrowdPooling | +
| * @author DODO Breeder | ++ | * @author DODO Breeder | +
| * | ++ | * | +
| * @notice CrowdPooling initialization | ++ | * @notice CrowdPooling initialization | +
| */ | ++ | */ | +
| contract CP is CPVesting { | ++ | contract CP is CPVesting { | +
| using SafeMath for uint256; | ++ | using SafeMath for uint256; | +
| + | + | + |
| receive() external payable { | ++ | receive() external payable { | +
| require(_INITIALIZED_ == false, "WE_NOT_SAVE_ETH_AFTER_INIT"); | ++ | require(_INITIALIZED_ == false, "WE_NOT_SAVE_ETH_AFTER_INIT"); | +
| } | ++ | } | +
| + | + | + |
| function init( | ++ | function init( | +
| address[] calldata addressList, | ++ | address[] calldata addressList, | +
| uint256[] calldata timeLine, | ++ | uint256[] calldata timeLine, | +
| uint256[] calldata valueList, | ++ | uint256[] calldata valueList, | +
| bool[] calldata switches //0 isOverCapStop 1 isOpenTWAP | +<> | +bool isOpenTWAP | +
| ) external { | += | +) external { | +
| /* | ++ | /* | +
| Address List | ++ | Address List | +
| 0. owner | ++ | 0. owner | +
| 1. maintainer | ++ | 1. maintainer | +
| 2. baseToken | ++ | 2. baseToken | +
| 3. quoteToken | ++ | 3. quoteToken | +
| 4. permissionManager | ++ | 4. permissionManager | +
| 5. feeRateModel | ++ | 5. feeRateModel | +
| 6. poolFactory | ++ | 6. poolFactory | +
| */ | ++ | */ | +
| + | + | + |
| require(addressList.length == 7, "LIST_LENGTH_WRONG"); | ++ | require(addressList.length == 7, "LIST_LENGTH_WRONG"); | +
| + | + | + |
| initOwner(addressList[0]); | ++ | initOwner(addressList[0]); | +
| _MAINTAINER_ = addressList[1]; | ++ | _MAINTAINER_ = addressList[1]; | +
| _BASE_TOKEN_ = IERC20(addressList[2]); | ++ | _BASE_TOKEN_ = IERC20(addressList[2]); | +
| _QUOTE_TOKEN_ = IERC20(addressList[3]); | ++ | _QUOTE_TOKEN_ = IERC20(addressList[3]); | +
| _BIDDER_PERMISSION_ = IPermissionManager(addressList[4]); | ++ | _BIDDER_PERMISSION_ = IPermissionManager(addressList[4]); | +
| _MT_FEE_RATE_MODEL_ = IFeeRateModel(addressList[5]); | ++ | _MT_FEE_RATE_MODEL_ = IFeeRateModel(addressList[5]); | +
| _POOL_FACTORY_ = addressList[6]; | ++ | _POOL_FACTORY_ = addressList[6]; | +
| + | + | + |
| /* | ++ | /* | +
| Time Line | ++ | Time Line | +
| 0. phase bid starttime | ++ | 0. phase bid starttime | +
| 1. phase bid duration | ++ | 1. phase bid duration | +
| 2. phase calm duration | ++ | 2. phase calm duration | +
| 3. freeze duration | ++ | 3. freeze duration | +
| 4. vesting duration | ++ | 4. vesting duration | +
| 5. claim freeze duration | ++- | ++ |
| 6. claim vesting duration | ++ | + |
| */ | += | +*/ | +
| + | + | + |
| require(timeLine.length == 7, "LIST_LENGTH_WRONG"); | +<> | +require(timeLine.length == 5, "LIST_LENGTH_WRONG"); | +
| + | = | ++ |
| _PHASE_BID_STARTTIME_ = timeLine[0]; | ++ | _PHASE_BID_STARTTIME_ = timeLine[0]; | +
| _PHASE_BID_ENDTIME_ = _PHASE_BID_STARTTIME_.add(timeLine[1]); | ++ | _PHASE_BID_ENDTIME_ = _PHASE_BID_STARTTIME_.add(timeLine[1]); | +
| _PHASE_CALM_ENDTIME_ = _PHASE_BID_ENDTIME_.add(timeLine[2]); | ++ | _PHASE_CALM_ENDTIME_ = _PHASE_BID_ENDTIME_.add(timeLine[2]); | +
| + | + | + |
| _FREEZE_DURATION_ = timeLine[3]; | ++ | _FREEZE_DURATION_ = timeLine[3]; | +
| _VESTING_DURATION_ = timeLine[4]; | ++ | _VESTING_DURATION_ = timeLine[4]; | +
| _TOKEN_CLAIM_DURATION_ = timeLine[5]; | +<> | ++ |
| _TOKEN_VESTING_DURATION_ = timeLine[6]; | ++ | + |
| require(block.timestamp <= _PHASE_BID_STARTTIME_, "TIMELINE_WRONG"); | += | +require(block.timestamp <= _PHASE_BID_STARTTIME_, "TIMELINE_WRONG"); | +
| + | + | + |
| /* | ++ | /* | +
| Value List | ++ | Value List | +
| 0. pool quote cap | ++ | 0. pool quote cap | +
| 1. k | ++ | 1. k | +
| 2. i | ++ | 2. i | +
| 3. lp cliff rate | +<> | +3. cliff rate | +
| 4. base token cliff rate | ++ | + |
| 5. lp fee rate | ++ | + |
| */ | += | +*/ | +
| + | + | + |
| require(valueList.length == 6, "LIST_LENGTH_WRONG"); | +<> | +require(valueList.length == 4, "LIST_LENGTH_WRONG"); | +
| + | = | ++ |
| _POOL_QUOTE_CAP_ = valueList[0]; | ++ | _POOL_QUOTE_CAP_ = valueList[0]; | +
| _K_ = valueList[1]; | ++ | _K_ = valueList[1]; | +
| _I_ = valueList[2]; | ++ | _I_ = valueList[2]; | +
| _CLIFF_RATE_ = valueList[3]; | ++ | _CLIFF_RATE_ = valueList[3]; | +
| _TOKEN_CLIFF_RATE_ = valueList[4]; | ++- | ++ |
| _POOL_FEE_RATE_ = valueList[5]; | ++ | + |
| + | = | ++ |
| require(_I_ > 0 && _I_ <= 1e36, "I_VALUE_WRONG"); | ++ | require(_I_ > 0 && _I_ <= 1e36, "I_VALUE_WRONG"); | +
| require(_K_ <= 1e18, "K_VALUE_WRONG"); | ++ | require(_K_ <= 1e18, "K_VALUE_WRONG"); | +
| require(_CLIFF_RATE_ <= 1e18, "CLIFF_RATE_WRONG"); | ++ | require(_CLIFF_RATE_ <= 1e18, "CLIFF_RATE_WRONG"); | +
| require(_TOKEN_CLIFF_RATE_ <= 1e18, "TOKEN_CLIFF_RATE_WRONG"); | ++- | ++ |
| + | = | ++ |
| _TOTAL_BASE_ = _BASE_TOKEN_.balanceOf(address(this)); | ++ | _TOTAL_BASE_ = _BASE_TOKEN_.balanceOf(address(this)); | +
| + | + | + |
| _IS_OVERCAP_STOP = switches[0]; | +<> | ++ |
| _IS_OPEN_TWAP_ = switches[1]; | ++ | _IS_OPEN_TWAP_ = isOpenTWAP; | +
| + | = | ++ |
| require(address(this).balance == _SETTEL_FUND_, "SETTLE_FUND_NOT_MATCH"); | ++ | require(address(this).balance == _SETTEL_FUND_, "SETTLE_FUND_NOT_MATCH"); | +
| } | ++ | } | +
| + | +- | ++ |
| // ============ Version Control ============ | ++ | + |
| + | + | + |
| function version() virtual external pure returns (string memory) { | ++ | + |
| return "CP 2.0.0"; | ++ | + |
| } | ++ | + |
| } | += | +} | +