dodo-start && cpV2 ing
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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");
|
||||
_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
73
contracts/DODOStarter/impl/Storage.sol
Normal file
73
contracts/DODOStarter/impl/Storage.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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_);
|
||||
|
||||
17
contracts/DODOStarter/intf/IDODOStarter.sol
Normal file
17
contracts/DODOStarter/intf/IDODOStarter.sol
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
164
contracts/Factory/DODOStarterFactory.sol
Normal file
164
contracts/Factory/DODOStarterFactory.sol
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user