dodo-start && cpV2 ing

This commit is contained in:
owen05
2021-12-02 16:15:22 +08:00
parent 63c1d9c70c
commit 4471274053
13 changed files with 555 additions and 111 deletions

View File

@@ -82,15 +82,17 @@ contract CP is CPVesting {
2. i
3. lp cliff rate
4. base token cliff rate
5. lp fee rate
*/
require(valueList.length == 5, "LIST_LENGTH_WRONG");
require(valueList.length == 6, "LIST_LENGTH_WRONG");
_POOL_QUOTE_CAP_ = valueList[0];
_K_ = valueList[1];
_I_ = valueList[2];
_CLIFF_RATE_ = valueList[3];
_TOKEN_CLIFF_RATE_ = valueList[4];
_POOL_FEE_RATE_ = valueList[5];
require(_I_ > 0 && _I_ <= 1e36, "I_VALUE_WRONG");
require(_K_ <= 1e18, "K_VALUE_WRONG");

View File

@@ -37,7 +37,7 @@ contract CPFunding is CPStorage {
_;
}
function bid(address to) external phaseBid preventReentrant isBidderAllow(to) {
function bid(address to) external isForceStop phaseBid preventReentrant isBidderAllow(to) {
uint256 input = _getQuoteInput();
uint256 mtFee = DecimalMath.mulFloor(input, _MT_FEE_RATE_MODEL_.getFeeRate(to));
_transferQuoteOut(_MAINTAINER_, mtFee);
@@ -71,7 +71,7 @@ contract CPFunding is CPStorage {
// ============ SETTLEMENT ============
function settle() external phaseSettlement preventReentrant {
function settle() external isForceStop phaseSettlement preventReentrant {
_settle();
(uint256 poolBase, uint256 poolQuote, uint256 poolI, uint256 unUsedBase, uint256 unUsedQuote) = getSettleResult();
@@ -92,7 +92,7 @@ contract CPFunding is CPStorage {
_POOL_ = IDVMFactory(_POOL_FACTORY_).createDODOVendingMachine(
_poolBaseToken,
_poolQuoteToken,
3e15, // 0.3% lp feeRate
_POOL_FEE_RATE_,
poolI,
DecimalMath.ONE,
_IS_OPEN_TWAP_
@@ -112,7 +112,7 @@ contract CPFunding is CPStorage {
}
// in case something wrong with base token contract
function emergencySettle() external phaseSettlement preventReentrant {
function emergencySettle() external isForceStop phaseSettlement preventReentrant {
require(block.timestamp >= _PHASE_CALM_ENDTIME_.add(_SETTLEMENT_EXPIRE_), "NOT_EMERGENCY");
_settle();
_UNUSED_QUOTE_ = _QUOTE_TOKEN_.balanceOf(address(this));

View File

@@ -25,6 +25,8 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard {
bool public _IS_OPEN_TWAP_ = false;
bool public _IS_OVERCAP_STOP = false;
bool public _FORCE_STOP_ = false;
// ============ Timeline ============
uint256 public _PHASE_BID_STARTTIME_;
@@ -56,6 +58,7 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard {
address public _POOL_FACTORY_;
address public _POOL_;
uint256 public _POOL_FEE_RATE_;
uint256 public _AVG_SETTLED_PRICE_;
// ============ Advanced Control ============
@@ -82,6 +85,10 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard {
mapping(address => uint256) _CLAIMED_BASE_TOKEN_;
// ============ Modifiers ============
modifier isForceStop() {
require(!_FORCE_STOP_, "FORCE_STOP");
_;
}
modifier phaseBid() {
require(
@@ -116,4 +123,12 @@ contract CPStorage is InitializableOwnable, ReentrancyGuard {
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);
}
}

View File

@@ -50,7 +50,7 @@ contract DPPStorage is InitializableOwnable, ReentrancyGuard {
// ============ Helper Functions ============
function getPMMState() public view returns (PMMPricing.PMMState memory state) {
(state.i,,,) = IOracle(_I_).getPrice(address(_BASE_TOKEN_));
state.i = IOracle(_I_).prices(address(_BASE_TOKEN_));
state.K = _K_;
state.B = _BASE_RESERVE_;
state.Q = _QUOTE_RESERVE_;

View File

@@ -39,9 +39,8 @@ contract DPPTrader is DPPVault {
event RChange(PMMPricing.RState newRState);
modifier isOraclePriceValid() {
(, bool isValid, bool isStale,) = IOracle(_I_).getPrice(address(_BASE_TOKEN_));
require(isValid, "ORACLE_PRICE_INVALID");
require(!isStale, "ORACLE_PRICE_STALE");
bool isFeasible = IOracle(_I_).isFeasible(address(_BASE_TOKEN_));
require(isFeasible, "ORACLE_PRICE_INVALID");
_;
}

View File

@@ -9,4 +9,8 @@ pragma solidity 0.6.9;
interface IOracle {
function getPrice(address base) external view returns (uint256 latestPrice,bool isValid,bool isStale,uint256 timestamp);
function prices(address base) external view returns (uint256);
function isFeasible(address base) external view returns (bool);
}

View File

@@ -8,43 +8,112 @@
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {InitializableOwnable} from "../../lib/InitializableOwnable.sol";
import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol";
import {IQuota} from "../../DODOFee/UserQuota.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {IERC20} from "../../intf/IERC20.sol";
import {SafeERC20} from "../../lib/SafeERC20.sol";
import {Vesting} from "./Vesting.sol";
contract FairFunding is InitializableOwnable, ReentrancyGuard {
contract FairFunding is Vesting {
using SafeMath for uint256;
using SafeERC20 for IERC20;
// ============ Token & Balance ============
uint256 public _FUNDS_RESERVE_;
address public _FUNDS_ADDRESS_;
address public _TOKEN_ADDRESS_;
uint256 public _TOTAL_TOKEN_AMOUNT_;
// ============ Fair Mode ============
uint256 public _START_TIME_;
uint256 public _BIDDING_DURATION_;
uint256 public _COOLING_DURATION_;
address _QUOTA_;
mapping(address => uint256) _FUNDS_DEPOSITED_;
mapping(address => bool) _FUNDS_CLAIMED_;
uint256 public _TOTAL_RAISED_FUNDS_;
uint256 public _USED_FUND_RATIO_;
uint256 public _FINAL_PRICE_;
// ============ Parameters ============
uint256 public _LOWER_LIMIT_PRICE_;
uint256 public _UPPER_LIMIT_PRICE_;
// ============ Init ============
function init(
address[] calldata addressList,
uint256[] calldata timeLine,
uint256[] calldata valueList
) external {
/*
Address List
0. owner
1. sellToken
2. fundToken
3. quotaManager
4. poolFactory
*/
require(addressList.length == 5, "ADDR_LENGTH_WRONG");
initOwner(addressList[0]);
_TOKEN_ADDRESS_ = addressList[1];
_FUNDS_ADDRESS_ = addressList[2];
_QUOTA_ = addressList[3];
_POOL_FACTORY_ = addressList[4];
/*
Time Line
0. starttime
1. bid duration
2. calm duration
3. token vesting starttime
4. token vesting duration
5. fund vesting starttime
6. fund vesting duration
7. lp vesting starttime
8. lp vesting duration
*/
require(timeLine.length == 9, "TIME_LENGTH_WRONG");
_START_TIME_ = timeLine[0];
_BIDDING_DURATION_ = timeLine[1];
_COOLING_DURATION_ = timeLine[2];
_TOKEN_VESTING_START_ = timeLine[3];
_TOKEN_VESTING_DURATION_ = timeLine[4];
_FUNDS_VESTING_START_ = timeLine[5];
_FUNDS_VESTING_DURATION_ = timeLine[6];
_LP_VESTING_START_ = timeLine[7];
_LP_VESTING_DURATION_ = timeLine[8];
require(block.timestamp <= _START_TIME_, "START_TIME_WRONG");
require(_START_TIME_.add(_BIDDING_DURATION_).add(_COOLING_DURATION_) <= _TOKEN_VESTING_START_, "TOKEN_VESTING_TIME_WRONG");
require(_START_TIME_.add(_BIDDING_DURATION_).add(_COOLING_DURATION_) <= _FUNDS_VESTING_START_, "FUND_VESTING_TIME_WRONG");
/*
Value List
0. lower price
1. upper price
2. token cliffRate
3. fund cliffRate
4. lp cliffRate
5. initial liquidity
*/
require(valueList.length == 6, "VALUE_LENGTH_WRONG");
_LOWER_LIMIT_PRICE_ = valueList[0];
_UPPER_LIMIT_PRICE_ = valueList[1];
_TOKEN_CLIFF_RATE_ = valueList[2];
_FUNDS_CLIFF_RATE_ = valueList[3];
_LP_CLIFF_RATE_ = valueList[4];
_INITIAL_FUND_LIQUIDITY_ = valueList[5];
require(_LOWER_LIMIT_PRICE_ <= _UPPER_LIMIT_PRICE_, "PRICE_WRONG");
require(_TOKEN_CLIFF_RATE_ <= 1e18, "TOKEN_CLIFF_RATE_WRONG");
require(_FUNDS_CLIFF_RATE_ <= 1e18, "FUND_CLIFF_RATE_WRONG");
require(_LP_CLIFF_RATE_ <= 1e18, "LP_CLIFF_RATE_WRONG");
_TOTAL_TOKEN_AMOUNT_ = IERC20(_TOKEN_ADDRESS_).balanceOf(address(this));
}
// ============ View Functions ============
function getCurrentPrice() public view returns (uint256) {
@@ -84,7 +153,7 @@ contract FairFunding is InitializableOwnable, ReentrancyGuard {
// ============ Settle Functions ============
function settle() public {
function settle() public isForceStop {
require(_FINAL_PRICE_ == 0 && isFundingEnd(), "CAN_NOT_SETTLE");
_FINAL_PRICE_ = getCurrentPrice();
_USED_FUND_RATIO_ = DecimalMath.divFloor(
@@ -98,13 +167,7 @@ contract FairFunding is InitializableOwnable, ReentrancyGuard {
// ============ Funding Functions ============
function depositToken(uint256 amount) external preventReentrant onlyOwner {
require(block.timestamp < _START_TIME_, "FUNDING_ALREADY_STARTED");
IERC20(_TOKEN_ADDRESS_).safeTransferFrom(msg.sender, address(this), amount);
_TOTAL_TOKEN_AMOUNT_ = _TOTAL_TOKEN_AMOUNT_.add(amount);
}
function depositFunds(address to) external preventReentrant {
function depositFunds(address to) external preventReentrant isForceStop {
require(isDepositOpen(), "DEPOSIT_NOT_OPEN");
// input fund check
uint256 inputFund = IERC20(_FUNDS_ADDRESS_).balanceOf(address(this)).sub(_FUNDS_RESERVE_);
@@ -141,6 +204,11 @@ contract FairFunding is InitializableOwnable, ReentrancyGuard {
_TOTAL_TOKEN_AMOUNT_ = allocatedToken;
}
function claimToken(address to) external {
uint256 totalAllocation = getUserTokenAllocation(msg.sender);
_claimToken(to, totalAllocation);
}
// ============ Timeline Control Functions ============
function isDepositOpen() public view returns (bool) {

View File

@@ -8,49 +8,118 @@
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {InitializableOwnable} from "../../lib/InitializableOwnable.sol";
import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol";
import {IQuota} from "../../DODOFee/UserQuota.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {IERC20} from "../../intf/IERC20.sol";
import {SafeERC20} from "../../lib/SafeERC20.sol";
import {Vesting} from "./Vesting.sol";
contract InstantFunding is InitializableOwnable, ReentrancyGuard {
contract InstantFunding is Vesting {
using SafeMath for uint256;
using SafeERC20 for IERC20;
// ============ Token & Balance ============
uint256 public _FUNDS_RESERVE_;
address public _FUNDS_ADDRESS_;
address public _TOKEN_ADDRESS_;
uint256 public _TOTAL_TOKEN_AMOUNT_;
// ============ Instant Commit Mode ============
uint256 public _START_TIME_;
uint256 public _DURATION_;
uint256 public _START_PRICE_;
uint256 public _END_PRICE_;
address _QUOTA_;
mapping(address => uint256) _FUNDS_USED_;
mapping(address => uint256) _FUNDS_UNUSED_;
mapping(address => uint256) _TOKEN_ALLOCATION_;
uint256 public _TOTAL_ALLOCATED_TOKEN_;
uint256 public _TOTAL_RAISED_FUNDS_;
// ============ Init ============
function init(
address[] calldata addressList,
uint256[] calldata timeLine,
uint256[] calldata valueList
) external {
/*
Address List
0. owner
1. sellToken
2. fundToken
3. quotaManager
4. poolFactory
*/
require(addressList.length == 5, "ADDR_LENGTH_WRONG");
initOwner(addressList[0]);
_TOKEN_ADDRESS_ = addressList[1];
_FUNDS_ADDRESS_ = addressList[2];
_QUOTA_ = addressList[3];
_POOL_FACTORY_ = addressList[4];
/*
Time Line
0. starttime
1. bid duration
2. token vesting starttime
3. token vesting duration
4. fund vesting starttime
5. fund vesting duration
6. lp vesting starttime
7. lp vesting duration
*/
require(timeLine.length == 8, "TIME_LENGTH_WRONG");
_START_TIME_ = timeLine[0];
_BIDDING_DURATION_ = timeLine[1];
_TOKEN_VESTING_START_ = timeLine[2];
_TOKEN_VESTING_DURATION_ = timeLine[3];
_FUNDS_VESTING_START_ = timeLine[4];
_FUNDS_VESTING_DURATION_ = timeLine[5];
_LP_VESTING_START_ = timeLine[6];
_LP_VESTING_DURATION_ = timeLine[7];
require(block.timestamp <= _START_TIME_, "START_TIME_WRONG");
require(_START_TIME_.add(_BIDDING_DURATION_) <= _TOKEN_VESTING_START_, "TOKEN_VESTING_TIME_WRONG");
require(_START_TIME_.add(_BIDDING_DURATION_) <= _FUNDS_VESTING_START_, "FUND_VESTING_TIME_WRONG");
/*
Value List
0. start price
1. end price
2. token cliffRate
3. fund cliffRate
4. lp cliffRate
5. initial liquidity
*/
require(valueList.length == 6, "VALUE_LENGTH_WRONG");
_START_PRICE_ = valueList[0];
_END_PRICE_ = valueList[1];
_TOKEN_CLIFF_RATE_ = valueList[2];
_FUNDS_CLIFF_RATE_ = valueList[3];
_LP_CLIFF_RATE_ = valueList[4];
_INITIAL_FUND_LIQUIDITY_ = valueList[5];
require(_TOKEN_CLIFF_RATE_ <= 1e18, "TOKEN_CLIFF_RATE_WRONG");
require(_FUNDS_CLIFF_RATE_ <= 1e18, "FUND_CLIFF_RATE_WRONG");
require(_LP_CLIFF_RATE_ <= 1e18, "LP_CLIFF_RATE_WRONG");
_TOTAL_TOKEN_AMOUNT_ = IERC20(_TOKEN_ADDRESS_).balanceOf(address(this));
}
// ============ View Functions ============
function getCurrentPrice() public view returns (uint256 price) {
if (block.timestamp <= _START_TIME_) {
price = _START_PRICE_;
} else if (block.timestamp >= _START_TIME_.add(_DURATION_)) {
} else if (block.timestamp >= _START_TIME_.add(_BIDDING_DURATION_)) {
price = _END_PRICE_;
} else {
uint256 timePast = block.timestamp.sub(_START_TIME_);
price = _START_PRICE_.mul(_DURATION_.sub(timePast)).div(_DURATION_).add(
_END_PRICE_.mul(timePast).div(_DURATION_)
price = _START_PRICE_.mul(_BIDDING_DURATION_.sub(timePast)).div(_BIDDING_DURATION_).add(
_END_PRICE_.mul(timePast).div(_BIDDING_DURATION_)
);
}
}
@@ -68,16 +137,11 @@ contract InstantFunding is InitializableOwnable, ReentrancyGuard {
}
// ============ Funding Functions ============
//TODO:强制转入,适配通缩代币
function depositToken(uint256 amount) external preventReentrant onlyOwner {
require(block.timestamp < _START_TIME_, "FUNDING_ALREADY_STARTED");
IERC20(_TOKEN_ADDRESS_).safeTransferFrom(msg.sender, address(this), amount);
_TOTAL_TOKEN_AMOUNT_ = _TOTAL_TOKEN_AMOUNT_.add(amount);
}
function depositFunds(address to)
external
preventReentrant
isForceStop
returns (uint256 newTokenAllocation)
{
require(isDepositOpen(), "DEPOSIT_NOT_OPEN");
@@ -124,15 +188,20 @@ contract InstantFunding is InitializableOwnable, ReentrancyGuard {
_TOTAL_TOKEN_AMOUNT_ = _TOTAL_ALLOCATED_TOKEN_;
}
function claimToken(address to) external {
uint256 totalAllocation = getUserTokenAllocation(msg.sender);
_claimToken(to, totalAllocation);
}
// ============ Timeline Control Functions ============
function isDepositOpen() public view returns (bool) {
return
block.timestamp >= _START_TIME_ &&
block.timestamp < _START_TIME_.add(_DURATION_);
block.timestamp < _START_TIME_.add(_BIDDING_DURATION_);
}
function isFundingEnd() public view returns (bool) {
return block.timestamp > _START_TIME_.add(_DURATION_);
return block.timestamp > _START_TIME_.add(_BIDDING_DURATION_);
}
}

View File

@@ -0,0 +1,73 @@
/*
Copyright 2021 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {InitializableOwnable} from "../../lib/InitializableOwnable.sol";
import {ReentrancyGuard} from "../../lib/ReentrancyGuard.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
import {IERC20} from "../../intf/IERC20.sol";
contract Storage is InitializableOwnable, ReentrancyGuard {
using SafeMath for uint256;
bool public _FORCE_STOP_ = false;
address public _QUOTA_;
// ============ Token & Balance ============
uint256 public _FUNDS_RESERVE_;
address public _FUNDS_ADDRESS_;
address public _TOKEN_ADDRESS_;
uint256 public _TOTAL_TOKEN_AMOUNT_;
uint256 public _TOTAL_RAISED_FUNDS_;
// ============ Vesting Timeline ============
uint256 public _TOKEN_VESTING_START_;
uint256 public _TOKEN_VESTING_DURATION_;
uint256 public _TOKEN_CLIFF_RATE_;
mapping(address => uint256) _CLAIMED_TOKEN_;
uint256 public _FUNDS_VESTING_START_;
uint256 public _FUNDS_VESTING_DURATION_;
uint256 public _FUNDS_CLIFF_RATE_;
uint256 _CLAIMED_FUNDS_;
uint256 public _LP_VESTING_START_;
uint256 public _LP_VESTING_DURATION_;
uint256 public _LP_CLIFF_RATE_;
uint256 _CLAIMED_LP_;
// ============ Liquidity Params ============
address public _POOL_FACTORY_;
address public _INITIAL_POOL_;
uint256 public _INITIAL_FUND_LIQUIDITY_;
uint256 public _TOTAL_LP_;
// ============ Timeline ==============
uint256 public _START_TIME_;
uint256 public _BIDDING_DURATION_;
// ============ Modifiers ============
modifier isForceStop() {
require(!_FORCE_STOP_, "FORCE_STOP");
_;
}
function forceStop() external onlyOwner {
require(block.timestamp < _START_TIME_, "FUNDING_ALREADY_STARTED");
_FORCE_STOP_ = true;
_TOTAL_TOKEN_AMOUNT_ = 0;
uint256 tokenAmount = IERC20(_TOKEN_ADDRESS_).balanceOf(address(this));
IERC20(_TOKEN_ADDRESS_).transfer(_OWNER_, tokenAmount);
}
}

View File

@@ -8,7 +8,7 @@
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {InstantFunding} from "./InstantFunding.sol";
import {Storage} from "./Storage.sol";
import {IDVM} from "../../DODOVendingMachine/intf/IDVM.sol";
import {IDVMFactory} from "../../Factory/DVMFactory.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
@@ -16,69 +16,82 @@ import {DecimalMath} from "../../lib/DecimalMath.sol";
import {IERC20} from "../../intf/IERC20.sol";
import {SafeERC20} from "../../lib/SafeERC20.sol";
contract Vesting is InstantFunding {
contract Vesting is Storage {
using SafeMath for uint256;
using SafeERC20 for IERC20;
// ============ Timeline ============
uint256 public _TOKEN_VESTING_START_;
uint256 public _TOKEN_VESTING_DURATION_;
mapping(address => uint256) _CLAIMED_TOKEN_;
uint256 public _FUNDS_VESTING_START_;
uint256 public _FUNDS_VESTING_DURATION_;
uint256 _CLAIMED_FUNDS_;
uint256 public _LP_VESTING_START_;
uint256 public _LP_VESTING_DURATION_;
uint256 _CLAIMED_LP_;
// ============ Liquidity Params ============
address public _POOL_FACTORY_;
address public _INITIAL_POOL_;
uint256 public _INITIAL_FUND_LIQUIDITY_;
uint256 public _TOTAL_LP_;
function claimToken(address to) external {
uint256 totalAllocation = getUserTokenAllocation(msg.sender);
uint256 unlockedAllocation = totalAllocation
.mul(block.timestamp.sub(_TOKEN_VESTING_START_))
.div(_TOKEN_VESTING_DURATION_);
IERC20(_TOKEN_ADDRESS_).safeTransfer(
to,
unlockedAllocation.sub(_CLAIMED_TOKEN_[msg.sender])
function _claimToken(address to, uint256 totalAllocation) internal {
uint256 remainingToken = DecimalMath.mulFloor(
getRemainingRatio(block.timestamp,0),
totalAllocation
);
_CLAIMED_TOKEN_[msg.sender] = unlockedAllocation;
uint256 claimableTokenAmount = totalAllocation.sub(remainingToken).sub(_CLAIMED_TOKEN_[msg.sender]);
IERC20(_TOKEN_ADDRESS_).safeTransfer(to,claimableTokenAmount);
_CLAIMED_TOKEN_[msg.sender] = _CLAIMED_TOKEN_[msg.sender].add(claimableTokenAmount);
}
function claimFunds(address to) external preventReentrant onlyOwner {
uint256 vestingFunds = _TOTAL_RAISED_FUNDS_.sub(_INITIAL_FUND_LIQUIDITY_);
uint256 unlockedFunds = vestingFunds.mul(block.timestamp.sub(_FUNDS_VESTING_START_)).div(
_FUNDS_VESTING_DURATION_
uint256 remainingFund = DecimalMath.mulFloor(
getRemainingRatio(block.timestamp,1),
vestingFunds
);
IERC20(_TOKEN_ADDRESS_).safeTransfer(to, unlockedFunds.sub(_CLAIMED_FUNDS_));
_CLAIMED_FUNDS_ = unlockedFunds;
uint256 claimableFund = vestingFunds.sub(remainingFund).sub(_CLAIMED_FUNDS_);
IERC20(_FUNDS_ADDRESS_).safeTransfer(to, claimableFund);
_CLAIMED_FUNDS_ = _CLAIMED_FUNDS_.add(claimableFund);
}
function claimLp(address to) external preventReentrant onlyOwner {
require(_INITIAL_POOL_ != address(0), "LIQUIDITY_NOT_ESTABLISHED");
uint256 unlockedLp = _TOTAL_LP_.mul(block.timestamp.sub(_LP_VESTING_START_)).div(
_LP_VESTING_DURATION_
uint256 remainingLp = DecimalMath.mulFloor(
getRemainingRatio(block.timestamp,2),
_TOTAL_LP_
);
IERC20(_TOKEN_ADDRESS_).safeTransfer(to, unlockedLp.sub(_CLAIMED_LP_));
_CLAIMED_LP_ = unlockedLp;
uint256 claimableLp = _TOTAL_LP_.sub(remainingLp).sub(_CLAIMED_LP_);
IERC20(_INITIAL_POOL_).safeTransfer(to, claimableLp);
_CLAIMED_LP_ = _CLAIMED_LP_.add(claimableLp);
}
function initializeLiquidity(uint256 initialTokenAmount) external preventReentrant onlyOwner {
//tokenType 0: BaseToken, 1: Fund, 2: LpToken
function getRemainingRatio(uint256 timestamp, uint256 tokenType) public view returns (uint256) {
uint256 vestingStart;
uint256 vestingDuration;
uint256 cliffRate;
if(tokenType == 0) {
vestingStart = _TOKEN_VESTING_START_;
vestingDuration = _TOKEN_VESTING_DURATION_;
cliffRate = _TOKEN_CLIFF_RATE_;
} else if(tokenType == 1) {
vestingStart = _FUNDS_VESTING_START_;
vestingDuration = _FUNDS_VESTING_DURATION_;
cliffRate = _FUNDS_CLIFF_RATE_;
} else {
vestingStart = _LP_VESTING_START_;
vestingDuration = _LP_VESTING_DURATION_;
cliffRate = _LP_CLIFF_RATE_;
}
uint256 timePast = timestamp.sub(vestingStart);
if (timePast < vestingDuration) {
uint256 remainingTime = vestingDuration.sub(timePast);
return DecimalMath.ONE.sub(cliffRate).mul(remainingTime).div(vestingDuration);
} else {
return 0;
}
}
function initializeLiquidity(uint256 initialTokenAmount, uint256 lpFeeRate, bool isOpenTWAP) external preventReentrant onlyOwner {
_INITIAL_POOL_ = IDVMFactory(_POOL_FACTORY_).createDODOVendingMachine(
_TOKEN_ADDRESS_,
_FUNDS_ADDRESS_,
3e15, // 0.3% lp feeRate DIP3
lpFeeRate,
1,
DecimalMath.ONE,
true //TODO:是否开启
isOpenTWAP
);
IERC20(_TOKEN_ADDRESS_).transferFrom(msg.sender, _INITIAL_POOL_, initialTokenAmount);
IERC20(_FUNDS_ADDRESS_).transfer(_INITIAL_POOL_, _INITIAL_FUND_LIQUIDITY_);

View File

@@ -0,0 +1,17 @@
/*
Copyright 2021 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
interface IDODOStarter {
function init(
address[] calldata addressList,
uint256[] calldata timeLine,
uint256[] calldata valueList
) external;
}

View File

@@ -41,8 +41,6 @@ contract CrowdPoolingFactory is InitializableOwnable {
uint256 public _K_ = 0;
uint256 public _CLIFF_RATE_ = 10**18;
mapping(address => address) liquidityProtectWhitelist;
// ============ Registry ============
// base -> quote -> CP address list
@@ -66,9 +64,6 @@ 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");
if(liquidityProtectWhitelist[creator] != baseToken) {
require(timeLine[3]>= _FREEZE_DURATION_, "CP_FACTORY : FREEZE_DURATION_INVALID");
}
_;
}
@@ -81,6 +76,8 @@ contract CrowdPoolingFactory is InitializableOwnable {
address cp
);
event RemoveCP(address cp);
constructor(
address cloneFactory,
address cpTemplate,
@@ -162,10 +159,6 @@ contract CrowdPoolingFactory is InitializableOwnable {
}
// ============ Owner Functions ============
function setLiquidityProtectWhitelist(address creator, address baseToken) external onlyOwner {
liquidityProtectWhitelist[creator] = baseToken;
}
function updateCPTemplate(address _newCPTemplate) external onlyOwner {
_CP_TEMPLATE_ = _newCPTemplate;
@@ -201,4 +194,31 @@ contract CrowdPoolingFactory is InitializableOwnable {
require(_newCliffRate <= 10**18, "CP_FACTORY : INVALID");
_CLIFF_RATE_ = _newCliffRate;
}
function removePoolByAdmin(
address creator,
address baseToken,
address quoteToken,
address pool
) external onlyOwner {
address[] memory registryList = _REGISTRY_[baseToken][quoteToken];
for (uint256 i = 0; i < registryList.length; i++) {
if (registryList[i] == pool) {
registryList[i] = registryList[registryList.length - 1];
break;
}
}
_REGISTRY_[baseToken][quoteToken] = registryList;
_REGISTRY_[baseToken][quoteToken].pop();
address[] memory userRegistryList = _USER_REGISTRY_[creator];
for (uint256 i = 0; i < userRegistryList.length; i++) {
if (userRegistryList[i] == pool) {
userRegistryList[i] = userRegistryList[userRegistryList.length - 1];
break;
}
}
_USER_REGISTRY_[creator] = userRegistryList;
_USER_REGISTRY_[creator].pop();
emit RemoveCP(pool);
}
}

View File

@@ -0,0 +1,164 @@
/*
Copyright 2021 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {InitializableOwnable} from "../lib/InitializableOwnable.sol";
import {ICloneFactory} from "../lib/CloneFactory.sol";
import {SafeMath} from "../lib/SafeMath.sol";
import {IERC20} from "../intf/IERC20.sol";
import {DecimalMath} from "../lib/DecimalMath.sol";
import {IDODOStarter} from "../DODOStarter/intf/IDODOStarter.sol";
/**
* @title DODOStarterFactory
* @author DODO Breeder
*
* @notice Create And Register DODOStarter Pools
*/
contract DODOStarterFactory is InitializableOwnable {
using SafeMath for uint256;
// ============ Templates ============
address public immutable _CLONE_FACTORY_;
address public _FAIR_FUND_TEMPLATE_;
address public _INSTANT_FUND_TEMPLATE_;
mapping(address => address) fundingWhitelist;
// ============ Registry ============
// baseToken -> fundToken -> fair Pool list
mapping(address => mapping(address => address[])) public _FAIR_REGISTRY_;
// baseToken -> fundToken -> Instant Pool list
mapping(address => mapping(address => address[])) public _INSTANT_REGISTRY_;
// ============ Events ============
event NewFairFund(
address baseToken,
address fundToken,
address creator,
address fairFundPool
);
event NewInstantFund(
address baseToken,
address fundToken,
address creator,
address instantFundPool
);
// ============ modifiers ===========
modifier permissionCheck(address creator, address baseToken) {
require(fundingWhitelist[creator] == baseToken || msg.sender == _OWNER_, "NO_PERMISSION");
_;
}
constructor(
address cloneFactory,
address fairFundTemplate,
address instantFundTemplate
) public {
_CLONE_FACTORY_ = cloneFactory;
_FAIR_FUND_TEMPLATE_ = fairFundTemplate;
_INSTANT_FUND_TEMPLATE_ = instantFundTemplate;
}
// ============ Functions ============
function createFairFund(
address[] memory addressList,
uint256[] memory timeLine,
uint256[] memory valueList,
uint256 sellTokenAmount
) external permissionCheck(addressList[0],addressList[1]) returns(address newFairFundPool){
newFairFundPool = ICloneFactory(_CLONE_FACTORY_).clone(_FAIR_FUND_TEMPLATE_);
IERC20(addressList[1]).transferFrom(msg.sender, newFairFundPool,sellTokenAmount);
IDODOStarter(newFairFundPool).init(
addressList,
timeLine,
valueList
);
_FAIR_REGISTRY_[addressList[1]][addressList[2]].push(newFairFundPool);
emit NewFairFund(addressList[1], addressList[2], addressList[0], newFairFundPool);
}
function createInstantFund(
address[] memory addressList,
uint256[] memory timeLine,
uint256[] memory valueList,
uint256 sellTokenAmount
) external permissionCheck(addressList[0],addressList[1]) returns(address newInstantFundPool){
newInstantFundPool = ICloneFactory(_CLONE_FACTORY_).clone(_INSTANT_FUND_TEMPLATE_);
IERC20(addressList[1]).transferFrom(msg.sender, newInstantFundPool,sellTokenAmount);
IDODOStarter(newInstantFundPool).init(
addressList,
timeLine,
valueList
);
_INSTANT_REGISTRY_[addressList[1]][addressList[2]].push(newInstantFundPool);
emit NewInstantFund(addressList[1], addressList[2], addressList[0], newInstantFundPool);
}
// ============ View Functions ============
function getFairFundPools(address baseToken, address fundToken)
external
view
returns (address[] memory pools)
{
return _FAIR_REGISTRY_[baseToken][fundToken];
}
function getFairFundPoolsBidirection(address token0, address token1)
external
view
returns (address[] memory baseToken0Pools, address[] memory baseToken1Pools)
{
return (_FAIR_REGISTRY_[token0][token1], _FAIR_REGISTRY_[token1][token0]);
}
function getInstantFundPools(address baseToken, address fundToken)
external
view
returns (address[] memory pools)
{
return _INSTANT_REGISTRY_[baseToken][fundToken];
}
function getInstantFundPoolsBidirection(address token0, address token1)
external
view
returns (address[] memory baseToken0Pools, address[] memory baseToken1Pools)
{
return (_INSTANT_REGISTRY_[token0][token1], _INSTANT_REGISTRY_[token1][token0]);
}
// ============ Owner Functions ============
function setWhitelist(address creator, address baseToken) external onlyOwner {
fundingWhitelist[creator] = baseToken;
}
function updateFairFundTemplate(address _newFairFundTemplate) external onlyOwner {
_FAIR_FUND_TEMPLATE_ = _newFairFundTemplate;
}
function updateInstantFundTemplate(address _newInstantFundTemplate) external onlyOwner {
_INSTANT_FUND_TEMPLATE_ = _newInstantFundTemplate;
}
}