DODO starter first edition
This commit is contained in:
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"solidity.compileUsingRemoteVersion": "v0.6.9+commit.3e3065ac",
|
||||
"solidity.defaultCompiler": "remote"
|
||||
}
|
||||
159
contracts/DODOStarter/impl/FairFunding.sol
Normal file
159
contracts/DODOStarter/impl/FairFunding.sol
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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 {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";
|
||||
|
||||
contract FairFunding is InitializableOwnable, ReentrancyGuard {
|
||||
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_;
|
||||
|
||||
// ============ View Functions ============
|
||||
|
||||
function getCurrentPrice() public view returns (uint256) {
|
||||
return getPrice(_TOTAL_RAISED_FUNDS_);
|
||||
}
|
||||
|
||||
function getPrice(uint256 fundAmount) public view returns (uint256 price) {
|
||||
price = DecimalMath.divFloor(fundAmount, _TOTAL_TOKEN_AMOUNT_);
|
||||
if (price < _LOWER_LIMIT_PRICE_) {
|
||||
price = _LOWER_LIMIT_PRICE_;
|
||||
}
|
||||
if (price > _UPPER_LIMIT_PRICE_) {
|
||||
price = _UPPER_LIMIT_PRICE_;
|
||||
}
|
||||
}
|
||||
|
||||
function getUserTokenAllocation(address user) public view returns (uint256) {
|
||||
if (_FINAL_PRICE_ == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return
|
||||
DecimalMath.divFloor(
|
||||
DecimalMath.mulFloor(_FUNDS_DEPOSITED_[user], _USED_FUND_RATIO_),
|
||||
_FINAL_PRICE_
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getUserFundsUnused(address user) public view returns (uint256) {
|
||||
return
|
||||
DecimalMath.mulFloor(_FUNDS_DEPOSITED_[user], DecimalMath.ONE.sub(_USED_FUND_RATIO_));
|
||||
}
|
||||
|
||||
function getUserFundsUsed(address user) public view returns (uint256) {
|
||||
return DecimalMath.mulFloor(_FUNDS_DEPOSITED_[user], _USED_FUND_RATIO_);
|
||||
}
|
||||
|
||||
// ============ Settle Functions ============
|
||||
|
||||
function settle() public {
|
||||
require(_FINAL_PRICE_ == 0 && isFundingEnd(), "CAN_NOT_SETTLE");
|
||||
_FINAL_PRICE_ = getCurrentPrice();
|
||||
_USED_FUND_RATIO_ = DecimalMath.divFloor(
|
||||
DecimalMath.mulFloor(_TOTAL_TOKEN_AMOUNT_, _FINAL_PRICE_),
|
||||
_TOTAL_RAISED_FUNDS_
|
||||
);
|
||||
if (_USED_FUND_RATIO_ > DecimalMath.ONE) {
|
||||
_USED_FUND_RATIO_ = DecimalMath.ONE;
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 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 {
|
||||
require(isDepositOpen(), "DEPOSIT_NOT_OPEN");
|
||||
// input fund check
|
||||
uint256 inputFund = IERC20(_FUNDS_ADDRESS_).balanceOf(address(this)).sub(_FUNDS_RESERVE_);
|
||||
_FUNDS_RESERVE_ = _FUNDS_RESERVE_.add(inputFund);
|
||||
|
||||
if (_QUOTA_ != address(0)) {
|
||||
require(
|
||||
inputFund.add(_FUNDS_DEPOSITED_[to]) <= uint256(IQuota(_QUOTA_).getUserQuota(to)),
|
||||
"QUOTA_EXCEED"
|
||||
);
|
||||
}
|
||||
|
||||
_FUNDS_DEPOSITED_[to] = _FUNDS_DEPOSITED_[to].add(inputFund);
|
||||
_TOTAL_RAISED_FUNDS_ = _TOTAL_RAISED_FUNDS_.add(inputFund);
|
||||
}
|
||||
|
||||
function withdrawFunds(address to, uint256 amount) external preventReentrant {
|
||||
if (!isSettled()) {
|
||||
require(_FUNDS_DEPOSITED_[msg.sender] >= amount, "WITHDRAW_TOO_MUCH");
|
||||
_FUNDS_DEPOSITED_[msg.sender] = _FUNDS_DEPOSITED_[msg.sender].sub(amount);
|
||||
_TOTAL_RAISED_FUNDS_ = _TOTAL_RAISED_FUNDS_.sub(amount);
|
||||
IERC20(_FUNDS_ADDRESS_).safeTransfer(to, amount);
|
||||
} else {
|
||||
require(!_FUNDS_CLAIMED_[msg.sender], "ALREADY_CLAIMED");
|
||||
_FUNDS_CLAIMED_[msg.sender] = true;
|
||||
IERC20(_FUNDS_ADDRESS_).safeTransfer(to, getUserFundsUnused(msg.sender));
|
||||
}
|
||||
}
|
||||
|
||||
function withdrawUnallocatedToken(address to) external preventReentrant onlyOwner {
|
||||
require(_FINAL_PRICE_ == _LOWER_LIMIT_PRICE_, "NO_TOKEN_LEFT");
|
||||
uint256 allocatedToken = DecimalMath.divCeil(_TOTAL_RAISED_FUNDS_, _FINAL_PRICE_);
|
||||
IERC20(_TOKEN_ADDRESS_).safeTransfer(to, _TOTAL_TOKEN_AMOUNT_.sub(allocatedToken));
|
||||
_TOTAL_TOKEN_AMOUNT_ = allocatedToken;
|
||||
}
|
||||
|
||||
// ============ Timeline Control Functions ============
|
||||
|
||||
function isDepositOpen() public returns (bool) {
|
||||
return
|
||||
block.timestamp >= _START_TIME_ &&
|
||||
block.timestamp < _START_TIME_.add(_BIDDING_DURATION_);
|
||||
}
|
||||
|
||||
function isFundingEnd() public returns (bool) {
|
||||
return block.timestamp > _START_TIME_.add(_BIDDING_DURATION_).add(_COOLING_DURATION_);
|
||||
}
|
||||
|
||||
function isSettled() public returns (bool) {
|
||||
return _FINAL_PRICE_ != 0;
|
||||
}
|
||||
}
|
||||
124
contracts/DODOStarter/impl/InstantFunding.sol
Normal file
124
contracts/DODOStarter/impl/InstantFunding.sol
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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 {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";
|
||||
|
||||
contract InstantFunding is InitializableOwnable, ReentrancyGuard {
|
||||
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_;
|
||||
|
||||
// ============ 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_)) {
|
||||
price = _END_PRICE_;
|
||||
} else {
|
||||
uint256 timePast = block.timestamp.sub(_START_TIME_);
|
||||
price = _START_PRICE_.mul(timePast).div(_DURATION_).add(
|
||||
_END_PRICE_.mul(_DURATION_.sub(timePast)).div(_DURATION_)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getUserTokenAllocation(address user) public view returns (uint256) {
|
||||
return _TOKEN_ALLOCATION_[user];
|
||||
}
|
||||
|
||||
function getUserFundsUnused(address user) public view returns (uint256) {
|
||||
return _FUNDS_UNUSED_[user];
|
||||
}
|
||||
|
||||
function getUserFundsUsed(address user) public view returns (uint256) {
|
||||
return _FUNDS_USED_[user];
|
||||
}
|
||||
|
||||
// ============ 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
|
||||
returns (uint256 newTokenAllocation)
|
||||
{
|
||||
// input fund check
|
||||
uint256 inputFund = IERC20(_FUNDS_ADDRESS_).balanceOf(address(this)).sub(_FUNDS_RESERVE_);
|
||||
_FUNDS_RESERVE_ = _FUNDS_RESERVE_.add(inputFund);
|
||||
|
||||
if (_QUOTA_ != address(0)) {
|
||||
require(
|
||||
inputFund.add(_FUNDS_USED_[to]) <= uint256(IQuota(_QUOTA_).getUserQuota(to)),
|
||||
"QUOTA_EXCEED"
|
||||
);
|
||||
}
|
||||
|
||||
// allocation calculation
|
||||
uint256 currentPrice = getCurrentPrice();
|
||||
newTokenAllocation = DecimalMath.divFloor(inputFund, currentPrice);
|
||||
|
||||
if (newTokenAllocation.add(_TOTAL_ALLOCATED_TOKEN_) > _TOTAL_TOKEN_AMOUNT_) {
|
||||
newTokenAllocation = _TOTAL_TOKEN_AMOUNT_.sub(_TOTAL_ALLOCATED_TOKEN_);
|
||||
uint256 fundUsed = DecimalMath.mulFloor(newTokenAllocation, currentPrice);
|
||||
_FUNDS_USED_[to] = _FUNDS_USED_[to].add(fundUsed);
|
||||
_TOTAL_RAISED_FUNDS_ = _TOTAL_RAISED_FUNDS_.add(fundUsed);
|
||||
_FUNDS_UNUSED_[to] = _FUNDS_UNUSED_[to].add(inputFund.sub(fundUsed));
|
||||
} else {
|
||||
_FUNDS_USED_[to] = _FUNDS_USED_[to].add(inputFund);
|
||||
_TOTAL_RAISED_FUNDS_ = _TOTAL_RAISED_FUNDS_.add(inputFund);
|
||||
}
|
||||
|
||||
_TOKEN_ALLOCATION_[to] = _TOKEN_ALLOCATION_[to].add(newTokenAllocation);
|
||||
_TOTAL_ALLOCATED_TOKEN_ = _TOTAL_ALLOCATED_TOKEN_.add(newTokenAllocation);
|
||||
}
|
||||
|
||||
function withdrawFunds(address to, uint256 amount) external preventReentrant {
|
||||
require(_FUNDS_UNUSED_[msg.sender] >= amount, "UNUSED_FUND_NOT_ENOUGH");
|
||||
_FUNDS_UNUSED_[msg.sender] = _FUNDS_UNUSED_[msg.sender].sub(amount);
|
||||
IERC20(_FUNDS_ADDRESS_).safeTransfer(to, amount);
|
||||
_FUNDS_RESERVE_ = _FUNDS_RESERVE_.sub(amount);
|
||||
}
|
||||
|
||||
function withdrawUnallocatedToken(address to) external preventReentrant onlyOwner {
|
||||
IERC20(_TOKEN_ADDRESS_).safeTransfer(to, _TOTAL_TOKEN_AMOUNT_.sub(_TOTAL_ALLOCATED_TOKEN_));
|
||||
_TOTAL_TOKEN_AMOUNT_ = _TOTAL_ALLOCATED_TOKEN_;
|
||||
}
|
||||
}
|
||||
87
contracts/DODOStarter/impl/Vesting.sol
Normal file
87
contracts/DODOStarter/impl/Vesting.sol
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {InstantFunding} from "./InstantFunding.sol";
|
||||
import {IDVM} from "../../DODOVendingMachine/intf/IDVM.sol";
|
||||
import {IDVMFactory} from "../../Factory/DVMFactory.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";
|
||||
|
||||
contract Vesting is InstantFunding {
|
||||
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])
|
||||
);
|
||||
_CLAIMED_TOKEN_[msg.sender] = unlockedAllocation;
|
||||
}
|
||||
|
||||
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_
|
||||
);
|
||||
IERC20(_TOKEN_ADDRESS_).safeTransfer(to, unlockedFunds.sub(_CLAIMED_FUNDS_));
|
||||
_CLAIMED_FUNDS_ = unlockedFunds;
|
||||
}
|
||||
|
||||
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_
|
||||
);
|
||||
IERC20(_TOKEN_ADDRESS_).safeTransfer(to, unlockedLp.sub(_CLAIMED_LP_));
|
||||
_CLAIMED_LP_ = unlockedLp;
|
||||
}
|
||||
|
||||
function initializeLiquidity(uint256 initialTokenAmount) external preventReentrant onlyOwner {
|
||||
_INITIAL_POOL_ = IDVMFactory(_POOL_FACTORY_).createDODOVendingMachine(
|
||||
_TOKEN_ADDRESS_,
|
||||
_FUNDS_ADDRESS_,
|
||||
3e15, // 0.3% lp feeRate
|
||||
1,
|
||||
DecimalMath.ONE,
|
||||
true
|
||||
);
|
||||
IERC20(_TOKEN_ADDRESS_).transferFrom(msg.sender, _INITIAL_POOL_, initialTokenAmount);
|
||||
IERC20(_FUNDS_ADDRESS_).transfer(_INITIAL_POOL_, _INITIAL_FUND_LIQUIDITY_);
|
||||
(_TOTAL_LP_, , ) = IDVM(_INITIAL_POOL_).buyShares(address(this));
|
||||
}
|
||||
}
|
||||
14431
package-lock.json
generated
14431
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@
|
||||
"@types/ethereumjs-abi": "^0.6.3",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"assert": "^2.0.0",
|
||||
"axios": "^0.20.0",
|
||||
"axios": "^0.24.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
|
||||
Reference in New Issue
Block a user