From 26b5cf29715f71d27f3ea59bd33a035544622d99 Mon Sep 17 00:00:00 2001 From: mingda Date: Tue, 8 Dec 2020 19:52:32 +0800 Subject: [PATCH] CA main contract --- contracts/CallAuction/impl/CA.sol | 90 ++++++++++++++++++++++++ contracts/CallAuction/impl/CAFunding.sol | 39 +++++++--- contracts/CallAuction/impl/CAStorage.sol | 13 ++-- contracts/CallAuction/impl/CAVesting.sol | 13 ++-- 4 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 contracts/CallAuction/impl/CA.sol diff --git a/contracts/CallAuction/impl/CA.sol b/contracts/CallAuction/impl/CA.sol new file mode 100644 index 0000000..4b9e049 --- /dev/null +++ b/contracts/CallAuction/impl/CA.sol @@ -0,0 +1,90 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {CAVesting} from "./CAVesting.sol"; +import {IERC20} from "../../intf/IERC20.sol"; +import {IPermissionManager} from "../../lib/PermissionManager.sol"; + +contract CA is CAVesting { + function init( + address[] calldata addressList, + uint256[] calldata timeLine, + uint256[] calldata valueList, + bytes calldata basePayBackData, + bytes calldata quotePayBackData + ) external { + /* + Address List + 0. owner + 1. baseToken + 2. quoteToken + 3. basePayBack + 4. quotePayBack + 5. permissionManager + */ + + initOwner(addressList[0]); + _BASE_TOKEN_ = IERC20(addressList[1]); + _QUOTE_TOKEN_ = IERC20(addressList[2]); + _BASE_PAY_BACK_ = addressList[3]; + _QUOTE_PAY_BACK_ = addressList[4]; + _BIDDER_PERMISSION_ = IPermissionManager(addressList[5]); + + /* + Time Line + 0. phase bid starttime + 1. phase bid endtime + 2. phase calm endtime + 3. start vesting time + 4. vesting duration + */ + + require( + block.timestamp <= timeLine[0] && + timeLine[0] <= timeLine[1] && + timeLine[1] <= timeLine[2] && + timeLine[2] <= timeLine[3], + "TIMELINE_WRONG" + ); + + _PHASE_BID_STARTTIME_ = timeLine[0]; + _PHASE_BID_ENDTIME_ = timeLine[1]; + _PHASE_CALM_ENDTIME_ = timeLine[2]; + _START_VESTING_TIME_ = timeLine[3]; + + _VESTING_DURATION_ = timeLine[4]; + + /* + Value List + 0. quote cap + 1. cliff rate + 2. k + 3. i + */ + + require( + valueList[1] <= 10**18 && + valueList[2] <= 10**18 && + valueList[3] > 0 && + valueList[3] <= 10**36, + "VALUE_RANGE_WRONG" + ); + + _QUOTE_CAP_ = valueList[0]; + _CLIFF_RATE_ = valueList[1]; + _K_ = valueList[2]; + _I_ = valueList[3]; + + // ============ External Call Data ============ + + _BASE_PAY_BACK_CALL_DATA_ = basePayBackData; + _QUOTE_PAY_BACK_CALL_DATA_ = quotePayBackData; + } +} diff --git a/contracts/CallAuction/impl/CAFunding.sol b/contracts/CallAuction/impl/CAFunding.sol index 380586c..bf8d8ec 100644 --- a/contracts/CallAuction/impl/CAFunding.sol +++ b/contracts/CallAuction/impl/CAFunding.sol @@ -19,7 +19,14 @@ import {PMMPricing} from "../../lib/PMMPricing.sol"; contract CAFunding is CAStorage { using SafeERC20 for IERC20; - // ============ BID ============ + // ============ PRE BID PHASE ============ + + // in case deposit base amount too much + function withdrawBaseToken(address to, uint256 amount) external phasePreBid onlyOwner { + _transferQuoteOut(to, amount); + } + + // ============ BID & CALM PHASE ============ modifier isBidderAllow(address bidder) { require(_BIDDER_PERMISSION_.isAllowed(bidder), "BIDDER_NOT_ALLOWED"); @@ -33,8 +40,6 @@ contract CAFunding is CAStorage { _sync(); } - // ============ CALM ============ - function cancel(address to, uint256 amount) external phaseBidOrCalm preventReentrant { require(_QUOTE_SHARES_[msg.sender] >= amount, "SHARES_NOT_ENOUGH"); _QUOTE_SHARES_[msg.sender] = _QUOTE_SHARES_[msg.sender].sub(amount); @@ -61,20 +66,32 @@ contract CAFunding is CAStorage { uint256 usedQuote = _QUOTE_CAP_ <= quoteBalance ? _QUOTE_CAP_ : quoteBalance; _transferQuoteOut(_QUOTE_PAY_BACK_, usedQuote); - // 4. unused quote token + // 4. leave unused quote token in contract _TOTAL_UNUSED_QUOTE_ = quoteBalance.sub(usedQuote); // 5. external call - if (_BASE_PAY_BACK_CALL_DATA_.length > 0) { - (bool success, ) = _BASE_PAY_BACK_.call(_BASE_PAY_BACK_CALL_DATA_); - require(success, "BASE_PAY_BACK_CALL_FAILED"); - } - if (_QUOTE_PAY_BACK_CALL_DATA_.length > 0) { - (bool success, ) = _QUOTE_PAY_BACK_.call(_QUOTE_PAY_BACK_CALL_DATA_); - require(success, "QUOTE_PAY_BACK_CALL_FAILED"); + if (block.timestamp < _PHASE_CALM_ENDTIME_.add(_SETTLEMENT_EXPIRED_TIME_)) { + if (_BASE_PAY_BACK_CALL_DATA_.length > 0) { + (bool success, ) = _BASE_PAY_BACK_.call(_BASE_PAY_BACK_CALL_DATA_); + require(success, "BASE_PAY_BACK_CALL_FAILED"); + } + if (_QUOTE_PAY_BACK_CALL_DATA_.length > 0) { + (bool success, ) = _QUOTE_PAY_BACK_.call(_QUOTE_PAY_BACK_CALL_DATA_); + require(success, "QUOTE_PAY_BACK_CALL_FAILED"); + } } } + function emergencySettle() external phaseSettlement preventReentrant { + require(!_SETTLED_, "ALREADY_SETTLED"); + require( + block.timestamp > _PHASE_CALM_ENDTIME_.add(_SETTLEMENT_EXPIRED_TIME_), + "NOT_EMERGENCY" + ); + _SETTLED_ = true; + _TOTAL_UNUSED_QUOTE_ = _QUOTE_TOKEN_.balanceOf(address(this)); + } + // ============ Pricing ============ function getAvgPrice() public view returns (uint256 avgPrice) { diff --git a/contracts/CallAuction/impl/CAStorage.sol b/contracts/CallAuction/impl/CAStorage.sol index d7b064e..c458c39 100644 --- a/contracts/CallAuction/impl/CAStorage.sol +++ b/contracts/CallAuction/impl/CAStorage.sol @@ -17,9 +17,11 @@ import {IERC20} from "../../intf/IERC20.sol"; contract CAStorage is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; + uint256 internal constant _SETTLEMENT_EXPIRED_TIME_ = 86400 * 7; + // ============ Timeline ============ - uint256 _PAHSE_SETTING_ENDTIME_; + uint256 _PHASE_BID_STARTTIME_; uint256 _PHASE_BID_ENDTIME_; uint256 _PHASE_CALM_ENDTIME_; bool _SETTLED_; @@ -40,7 +42,6 @@ contract CAStorage is InitializableOwnable, ReentrancyGuard { // ============ Balances ============ uint256 public _QUOTE_RESERVE_; - uint256 public _BASE_RESERVE_; uint256 public _TOTAL_SOLD_BASE_; uint256 public _TOTAL_UNUSED_QUOTE_; uint256 public _TOTAL_QUOTE_SHARES_; @@ -62,14 +63,14 @@ contract CAStorage is InitializableOwnable, ReentrancyGuard { // ============ Modifiers ============ - modifier phaseSetting() { - require(block.timestamp <= _PAHSE_SETTING_ENDTIME_, "NOT_PHASE_SETTING"); + modifier phasePreBid() { + require(block.timestamp <= _PHASE_BID_STARTTIME_, "NOT_PHASE_PREBID"); _; } modifier phaseBid() { require( - block.timestamp > _PAHSE_SETTING_ENDTIME_ && block.timestamp <= _PHASE_BID_ENDTIME_, + block.timestamp > _PHASE_BID_STARTTIME_ && block.timestamp <= _PHASE_BID_ENDTIME_, "NOT_PHASE_BID" ); _; @@ -85,7 +86,7 @@ contract CAStorage is InitializableOwnable, ReentrancyGuard { modifier phaseBidOrCalm() { require( - block.timestamp > _PAHSE_SETTING_ENDTIME_ && block.timestamp <= _PHASE_CALM_ENDTIME_, + block.timestamp > _PHASE_BID_STARTTIME_ && block.timestamp <= _PHASE_CALM_ENDTIME_, "NOT_PHASE_BID_OR_CALM" ); _; diff --git a/contracts/CallAuction/impl/CAVesting.sol b/contracts/CallAuction/impl/CAVesting.sol index 5c01111..816f1b9 100644 --- a/contracts/CallAuction/impl/CAVesting.sol +++ b/contracts/CallAuction/impl/CAVesting.sol @@ -16,25 +16,30 @@ import {IERC20} from "../../intf/IERC20.sol"; import {CAFunding} from "./CAFunding.sol"; /** - * @title LockedTokenVault + * @title CAVesting * @author DODO Breeder * * @notice Lock Token and release it linearly */ -contract LockedTokenVault is CAFunding { +contract CAVesting is CAFunding { using SafeMath for uint256; using SafeERC20 for IERC20; + modifier afterSettlement() { + require(_SETTLED_, "NOT_SETTLED"); + _; + } + // ============ Functions ============ - function claimBase() external { + function claimBase() external afterSettlement { uint256 claimableToken = getClaimableBaseBalance(msg.sender); _transferBaseOut(msg.sender, claimableToken); _CLAIMED_BASE_[msg.sender] = _CLAIMED_BASE_[msg.sender].add(claimableToken); } - function claimQuote() external { + function claimQuote() external afterSettlement { require(!_QUOTE_CLAIMED_[msg.sender], "QUOTE_CLAIMED"); _QUOTE_CLAIMED_[msg.sender] = true; _transferQuoteOut(msg.sender, getClaimableQuoteBalance(msg.sender));