第一次走查
This commit is contained in:
@@ -38,6 +38,8 @@ contract DPP is DPPTrader {
|
||||
_GAS_PRICE_LIMIT_ = IExternalValue(gasPriceSource);
|
||||
_TRADE_PERMISSION_ = IPermissionManager(tradePermissionManager);
|
||||
_resetTargetAndReserve();
|
||||
_checkIK();
|
||||
require(_BASE_TOKEN_ != _QUOTE_TOKEN_, "BASE_QUOTE_CAN_NOT_BE_SAME");
|
||||
}
|
||||
|
||||
// ============ Version Control ============
|
||||
|
||||
@@ -9,16 +9,20 @@ pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {IDPP} from "../intf/IDPP.sol";
|
||||
import {IDODOApprove} from '../../intf/IDODOApprove.sol';
|
||||
import {IDODOApprove} from "../../intf/IDODOApprove.sol";
|
||||
import {InitializableOwnable} from "../../lib/InitializableOwnable.sol";
|
||||
|
||||
contract DPPAdmin is InitializableOwnable {
|
||||
|
||||
address public _DPP_;
|
||||
address public _OPERATOR_;
|
||||
address public _OPERATOR_;
|
||||
address public _DODO_APPROVE_;
|
||||
|
||||
function init(address owner, address dpp,address operator, address dodoApprove) external {
|
||||
function init(
|
||||
address owner,
|
||||
address dpp,
|
||||
address operator,
|
||||
address dodoApprove
|
||||
) external {
|
||||
initOwner(owner);
|
||||
_DPP_ = dpp;
|
||||
_OPERATOR_ = operator;
|
||||
@@ -29,33 +33,33 @@ contract DPPAdmin is InitializableOwnable {
|
||||
_OPERATOR_ = newOperator;
|
||||
}
|
||||
|
||||
function setLpFeeRateModel(address newLpFeeRateModel) external onlyOwner {
|
||||
IDPP(_DPP_).setLpFeeRateModel(newLpFeeRateModel);
|
||||
}
|
||||
// function setLpFeeRateModel(address newLpFeeRateModel) external onlyOwner {
|
||||
// IDPP(_DPP_).setLpFeeRateModel(newLpFeeRateModel);
|
||||
// }
|
||||
|
||||
function setMtFeeRateModel(address newMtFeeRateModel) external onlyOwner {
|
||||
IDPP(_DPP_).setMtFeeRateModel(newMtFeeRateModel);
|
||||
}
|
||||
// function setMtFeeRateModel(address newMtFeeRateModel) external onlyOwner {
|
||||
// IDPP(_DPP_).setMtFeeRateModel(newMtFeeRateModel);
|
||||
// }
|
||||
|
||||
function setTradePermissionManager(address newTradePermissionManager) external onlyOwner {
|
||||
IDPP(_DPP_).setTradePermissionManager(newTradePermissionManager);
|
||||
}
|
||||
// function setTradePermissionManager(address newTradePermissionManager) external onlyOwner {
|
||||
// IDPP(_DPP_).setTradePermissionManager(newTradePermissionManager);
|
||||
// }
|
||||
|
||||
function setMaintainer(address newMaintainer) external onlyOwner {
|
||||
IDPP(_DPP_).setMaintainer(newMaintainer);
|
||||
}
|
||||
|
||||
function setGasPriceSource(address newGasPriceLimitSource) external onlyOwner {
|
||||
IDPP(_DPP_).setGasPriceSource(newGasPriceLimitSource);
|
||||
}
|
||||
// function setGasPriceSource(address newGasPriceLimitSource) external onlyOwner {
|
||||
// IDPP(_DPP_).setGasPriceSource(newGasPriceLimitSource);
|
||||
// }
|
||||
|
||||
function setISource(address newISource) external onlyOwner {
|
||||
IDPP(_DPP_).setISource(newISource);
|
||||
}
|
||||
// function setISource(address newISource) external onlyOwner {
|
||||
// IDPP(_DPP_).setISource(newISource);
|
||||
// }
|
||||
|
||||
function setKSource(address newKSource) external onlyOwner {
|
||||
IDPP(_DPP_).setKSource(newKSource);
|
||||
}
|
||||
// function setKSource(address newKSource) external onlyOwner {
|
||||
// IDPP(_DPP_).setKSource(newKSource);
|
||||
// }
|
||||
|
||||
function setBuy(bool open) external onlyOwner {
|
||||
IDPP(_DPP_).setBuy(open);
|
||||
@@ -65,12 +69,16 @@ contract DPPAdmin is InitializableOwnable {
|
||||
IDPP(_DPP_).setSell(open);
|
||||
}
|
||||
|
||||
function retrieve(address payable to,address token,uint256 amount) external onlyOwner {
|
||||
IDPP(_DPP_).retrieve(to,token,amount);
|
||||
function retrieve(
|
||||
address payable to,
|
||||
address token,
|
||||
uint256 amount
|
||||
) external onlyOwner {
|
||||
IDPP(_DPP_).retrieve(to, token, amount);
|
||||
}
|
||||
|
||||
function reset(
|
||||
address assetTo,
|
||||
address operator,
|
||||
uint256 newLpFeeRate,
|
||||
uint256 newMtFeeRate,
|
||||
uint256 newI,
|
||||
@@ -78,28 +86,12 @@ contract DPPAdmin is InitializableOwnable {
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount
|
||||
) external {
|
||||
require(msg.sender == _OWNER_ || (msg.sender == IDODOApprove(_DODO_APPROVE_).getDODOProxy() && assetTo == _OPERATOR_), "RESET FORBIDDEN!");
|
||||
IDPP(_DPP_).reset(
|
||||
assetTo,
|
||||
newLpFeeRate,
|
||||
newMtFeeRate,
|
||||
newI,
|
||||
newK,
|
||||
baseOutAmount,
|
||||
quoteOutAmount
|
||||
require(
|
||||
msg.sender == _OWNER_ ||
|
||||
(msg.sender == IDODOApprove(_DODO_APPROVE_).getDODOProxy() &&
|
||||
operator == _OPERATOR_),
|
||||
"RESET FORBIDDEN!"
|
||||
);
|
||||
}
|
||||
|
||||
function resetETH(
|
||||
address from,
|
||||
uint256 newLpFeeRate,
|
||||
uint256 newMtFeeRate,
|
||||
uint256 newI,
|
||||
uint256 newK,
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount
|
||||
) external {
|
||||
require(msg.sender == _OWNER_ || (msg.sender == IDODOApprove(_DODO_APPROVE_).getDODOProxy() && from == _OPERATOR_), "RESET FORBIDDEN!");
|
||||
IDPP(_DPP_).reset(
|
||||
msg.sender,
|
||||
newLpFeeRate,
|
||||
|
||||
@@ -96,6 +96,13 @@ contract DPPStorage is InitializableOwnable, ReentrancyGuard {
|
||||
_SELLING_CLOSE_ = !open;
|
||||
}
|
||||
|
||||
function _checkIK() internal view {
|
||||
uint256 k = _K_.get();
|
||||
uint256 i = _I_.get();
|
||||
require(k > 0 && k <= 1e18, "K_OUT_OF_RANGE");
|
||||
require(i > 0 && i <= 1e36, "I_OUT_OF_RANGE");
|
||||
}
|
||||
|
||||
// ============ View Functions ============
|
||||
|
||||
function getLpFeeRate(address trader) external view returns (uint256 feeRate) {
|
||||
@@ -105,5 +112,4 @@ contract DPPStorage is InitializableOwnable, ReentrancyGuard {
|
||||
function getMtFeeRate(address trader) external view returns (uint256 feeRate) {
|
||||
return _MT_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -117,9 +117,6 @@ contract DPPTrader is DPPVault {
|
||||
"FLASH_LOAN_FAILED"
|
||||
);
|
||||
|
||||
// no output -> pure profit
|
||||
if (baseBalance >= _BASE_RESERVE_ && quoteBalance >= _QUOTE_RESERVE_) return;
|
||||
|
||||
// sell quote case
|
||||
// quote input + base output
|
||||
if (baseBalance < _BASE_RESERVE_) {
|
||||
@@ -137,8 +134,6 @@ contract DPPTrader is DPPVault {
|
||||
_RState_ = newRState;
|
||||
_QUOTE_TARGET_ = newQuoteTarget;
|
||||
}
|
||||
|
||||
_syncReserve();
|
||||
}
|
||||
|
||||
// sell base case
|
||||
@@ -158,9 +153,9 @@ contract DPPTrader is DPPVault {
|
||||
_RState_ = newRState;
|
||||
_BASE_TARGET_ = newBaseTarget;
|
||||
}
|
||||
|
||||
_syncReserve();
|
||||
}
|
||||
|
||||
_syncReserve();
|
||||
}
|
||||
|
||||
// ============ Query Functions ============
|
||||
@@ -180,11 +175,10 @@ contract DPPTrader is DPPVault {
|
||||
|
||||
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
mtFee = DecimalMath.mulCeil(receiveQuoteAmount, mtFeeRate);
|
||||
receiveQuoteAmount = DecimalMath.mulFloor(
|
||||
receiveQuoteAmount,
|
||||
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
|
||||
);
|
||||
mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
|
||||
receiveQuoteAmount = receiveQuoteAmount
|
||||
.sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate))
|
||||
.sub(mtFee);
|
||||
|
||||
return (receiveQuoteAmount, mtFee, newRState, state.B0);
|
||||
}
|
||||
@@ -204,11 +198,10 @@ contract DPPTrader is DPPVault {
|
||||
|
||||
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
mtFee = DecimalMath.mulCeil(receiveBaseAmount, mtFeeRate);
|
||||
receiveBaseAmount = DecimalMath.mulFloor(
|
||||
receiveBaseAmount,
|
||||
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
|
||||
);
|
||||
mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
|
||||
receiveBaseAmount = receiveBaseAmount
|
||||
.sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate))
|
||||
.sub(mtFee);
|
||||
return (receiveBaseAmount, mtFee, newRState, state.Q0);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ contract DPPVault is DPPStorage {
|
||||
return (_BASE_RESERVE_, _QUOTE_RESERVE_);
|
||||
}
|
||||
|
||||
// ============ Set Status ============
|
||||
// ============ Set States ============
|
||||
|
||||
function setTarget(uint256 baseTarget, uint256 quoteTarget) public preventReentrant onlyOwner {
|
||||
_BASE_TARGET_ = baseTarget;
|
||||
@@ -68,8 +68,6 @@ contract DPPVault is DPPStorage {
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount
|
||||
) public preventReentrant onlyOwner {
|
||||
require(newK >= 1e12 && newK <= 1e18, "K_OUT_OF_RANGE");
|
||||
require(newI > 0 && newI <= 1e36, "I_OUT_OF_RANGE");
|
||||
_LP_FEE_RATE_MODEL_.setFeeRate(newLpFeeRate);
|
||||
_MT_FEE_RATE_MODEL_.setFeeRate(newMtFeeRate);
|
||||
_I_.set(newI);
|
||||
@@ -77,6 +75,7 @@ contract DPPVault is DPPStorage {
|
||||
_transferBaseOut(assetTo, baseOutAmount);
|
||||
_transferQuoteOut(assetTo, quoteOutAmount);
|
||||
_resetTargetAndReserve();
|
||||
_checkIK();
|
||||
}
|
||||
|
||||
function _setRState() internal {
|
||||
|
||||
@@ -22,6 +22,10 @@ interface IDPP {
|
||||
address tradePermissionManager
|
||||
) external;
|
||||
|
||||
function _LP_FEE_RATE_MODEL_() external returns (address);
|
||||
|
||||
function _MT_FEE_RATE_MODEL_() external returns (address);
|
||||
|
||||
//=========== admin ==========
|
||||
function setLpFeeRateModel(address newLpFeeRateModel) external;
|
||||
|
||||
@@ -40,9 +44,14 @@ interface IDPP {
|
||||
function setBuy(bool open) external;
|
||||
|
||||
function setSell(bool open) external;
|
||||
//==============================
|
||||
|
||||
function retrieve(address payable to,address token,uint256 amount) external;
|
||||
//==============================
|
||||
|
||||
function retrieve(
|
||||
address payable to,
|
||||
address token,
|
||||
uint256 amount
|
||||
) external;
|
||||
|
||||
function reset(
|
||||
address assetTo,
|
||||
@@ -53,6 +62,4 @@ interface IDPP {
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount
|
||||
) external;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import {SafeMath} from "../lib/SafeMath.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {IDODORewardVault, DODORewardVault} from "./DODORewardVault.sol";
|
||||
|
||||
|
||||
contract DODOMine is Ownable {
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
@@ -157,7 +156,7 @@ contract DODOMine is Ownable {
|
||||
.div(totalAllocPoint);
|
||||
accDODOPerShare = accDODOPerShare.add(DecimalMath.divFloor(DODOReward, lpSupply));
|
||||
}
|
||||
return DecimalMath.mul(user.amount, accDODOPerShare).sub(user.rewardDebt);
|
||||
return DecimalMath.mulFloor(user.amount, accDODOPerShare).sub(user.rewardDebt);
|
||||
}
|
||||
|
||||
function getAllPendingReward(address _user) external view returns (uint256) {
|
||||
@@ -181,7 +180,7 @@ contract DODOMine is Ownable {
|
||||
accDODOPerShare = accDODOPerShare.add(DecimalMath.divFloor(DODOReward, lpSupply));
|
||||
}
|
||||
totalReward = totalReward.add(
|
||||
DecimalMath.mul(user.amount, accDODOPerShare).sub(user.rewardDebt)
|
||||
DecimalMath.mulFloor(user.amount, accDODOPerShare).sub(user.rewardDebt)
|
||||
);
|
||||
}
|
||||
return totalReward;
|
||||
@@ -237,14 +236,14 @@ contract DODOMine is Ownable {
|
||||
UserInfo storage user = userInfo[pid][msg.sender];
|
||||
updatePool(pid);
|
||||
if (user.amount > 0) {
|
||||
uint256 pending = DecimalMath.mul(user.amount, pool.accDODOPerShare).sub(
|
||||
uint256 pending = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare).sub(
|
||||
user.rewardDebt
|
||||
);
|
||||
safeDODOTransfer(msg.sender, pending);
|
||||
}
|
||||
IERC20(pool.lpToken).safeTransferFrom(address(msg.sender), address(this), _amount);
|
||||
user.amount = user.amount.add(_amount);
|
||||
user.rewardDebt = DecimalMath.mul(user.amount, pool.accDODOPerShare);
|
||||
user.rewardDebt = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare);
|
||||
emit Deposit(msg.sender, pid, _amount);
|
||||
}
|
||||
|
||||
@@ -254,10 +253,12 @@ contract DODOMine is Ownable {
|
||||
UserInfo storage user = userInfo[pid][msg.sender];
|
||||
require(user.amount >= _amount, "withdraw too much");
|
||||
updatePool(pid);
|
||||
uint256 pending = DecimalMath.mul(user.amount, pool.accDODOPerShare).sub(user.rewardDebt);
|
||||
uint256 pending = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare).sub(
|
||||
user.rewardDebt
|
||||
);
|
||||
safeDODOTransfer(msg.sender, pending);
|
||||
user.amount = user.amount.sub(_amount);
|
||||
user.rewardDebt = DecimalMath.mul(user.amount, pool.accDODOPerShare);
|
||||
user.rewardDebt = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare);
|
||||
IERC20(pool.lpToken).safeTransfer(address(msg.sender), _amount);
|
||||
emit Withdraw(msg.sender, pid, _amount);
|
||||
}
|
||||
@@ -285,8 +286,10 @@ contract DODOMine is Ownable {
|
||||
PoolInfo storage pool = poolInfos[pid];
|
||||
UserInfo storage user = userInfo[pid][msg.sender];
|
||||
updatePool(pid);
|
||||
uint256 pending = DecimalMath.mul(user.amount, pool.accDODOPerShare).sub(user.rewardDebt);
|
||||
user.rewardDebt = DecimalMath.mul(user.amount, pool.accDODOPerShare);
|
||||
uint256 pending = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare).sub(
|
||||
user.rewardDebt
|
||||
);
|
||||
user.rewardDebt = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare);
|
||||
safeDODOTransfer(msg.sender, pending);
|
||||
}
|
||||
|
||||
@@ -301,9 +304,9 @@ contract DODOMine is Ownable {
|
||||
UserInfo storage user = userInfo[pid][msg.sender];
|
||||
updatePool(pid);
|
||||
pending = pending.add(
|
||||
DecimalMath.mul(user.amount, pool.accDODOPerShare).sub(user.rewardDebt)
|
||||
DecimalMath.mulFloor(user.amount, pool.accDODOPerShare).sub(user.rewardDebt)
|
||||
);
|
||||
user.rewardDebt = DecimalMath.mul(user.amount, pool.accDODOPerShare);
|
||||
user.rewardDebt = DecimalMath.mulFloor(user.amount, pool.accDODOPerShare);
|
||||
}
|
||||
safeDODOTransfer(msg.sender, pending);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {Ownable} from "../lib/Ownable.sol";
|
||||
import {SafeERC20} from "../lib/SafeERC20.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title LockedTokenVault
|
||||
* @author DODO Breeder
|
||||
@@ -154,7 +153,7 @@ contract LockedTokenVault is Ownable {
|
||||
|
||||
function getRemainingBalance(address holder) public view returns (uint256) {
|
||||
uint256 remainingRatio = getRemainingRatio(block.timestamp);
|
||||
return DecimalMath.mul(originBalances[holder], remainingRatio);
|
||||
return DecimalMath.mulFloor(originBalances[holder], remainingRatio);
|
||||
}
|
||||
|
||||
function getRemainingRatio(uint256 timestamp) public view returns (uint256) {
|
||||
|
||||
@@ -38,7 +38,9 @@ contract DVM is DVMTrader, DVMFunding {
|
||||
_GAS_PRICE_LIMIT_ = IExternalValue(gasPriceSource);
|
||||
_MAINTAINER_ = maintainer;
|
||||
|
||||
require(i > 0 && i < 10**36);
|
||||
require(_BASE_TOKEN_ != _QUOTE_TOKEN_, "BASE_QUOTE_CAN_NOT_BE_SAME");
|
||||
|
||||
require(i > 0 && i <= 10**36);
|
||||
_I_ = i;
|
||||
|
||||
require(k > 0 && k <= 10**18);
|
||||
@@ -46,7 +48,13 @@ contract DVM is DVMTrader, DVMFunding {
|
||||
|
||||
string memory connect = "_";
|
||||
string memory suffix = "DLP";
|
||||
string memory uid = string(abi.encodePacked(address(this)));
|
||||
uint32 uid = uint32(address(this));
|
||||
bytes memory id = new bytes(4);
|
||||
id[0] = bytes1(uint8(48 + (uid % 10)));
|
||||
id[1] = bytes1(uint8(48 + ((uid / 10) % 10)));
|
||||
id[2] = bytes1(uint8(48 + ((uid / 100) % 10)));
|
||||
id[3] = bytes1(uint8(48 + ((uid / 1000) % 10)));
|
||||
|
||||
name = string(
|
||||
abi.encodePacked(
|
||||
suffix,
|
||||
@@ -55,7 +63,7 @@ contract DVM is DVMTrader, DVMFunding {
|
||||
connect,
|
||||
_QUOTE_TOKEN_.symbol(),
|
||||
connect,
|
||||
uid
|
||||
string(id)
|
||||
)
|
||||
);
|
||||
symbol = "DLP";
|
||||
|
||||
@@ -10,9 +10,9 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import {IDVM} from "../intf/IDVM.sol";
|
||||
import {InitializableOwnable} from "../../lib/InitializableOwnable.sol";
|
||||
import {IExternalValue} from "../../lib/ExternalValue.sol";
|
||||
|
||||
contract DVMAdmin is InitializableOwnable {
|
||||
|
||||
address public _DVM_;
|
||||
|
||||
function init(address owner, address dvm) external {
|
||||
@@ -20,25 +20,33 @@ contract DVMAdmin is InitializableOwnable {
|
||||
_DVM_ = dvm;
|
||||
}
|
||||
|
||||
function setLpFeeRateModel(address newLpFeeRateModel) external onlyOwner {
|
||||
IDVM(_DVM_).setLpFeeRateModel(newLpFeeRateModel);
|
||||
// function setLpFeeRateModel(address newLpFeeRateModel) external onlyOwner {
|
||||
// IDVM(_DVM_).setLpFeeRateModel(newLpFeeRateModel);
|
||||
// }
|
||||
|
||||
function setLpFeeRateValue(uint256 newLpFeeRate) external onlyOwner {
|
||||
IExternalValue(IDVM(_DVM_)._LP_FEE_RATE_MODEL_()).set(newLpFeeRate);
|
||||
}
|
||||
|
||||
function setMtFeeRateModel(address newMtFeeRateModel) external onlyOwner {
|
||||
IDVM(_DVM_).setMtFeeRateModel(newMtFeeRateModel);
|
||||
// function setMtFeeRateModel(address newMtFeeRateModel) external onlyOwner {
|
||||
// IDVM(_DVM_).setMtFeeRateModel(newMtFeeRateModel);
|
||||
// }
|
||||
|
||||
function setMtFeeRateValue(uint256 newMtFeeRate) external onlyOwner {
|
||||
IExternalValue(IDVM(_DVM_)._MT_FEE_RATE_MODEL_()).set(newMtFeeRate);
|
||||
}
|
||||
|
||||
function setTradePermissionManager(address newTradePermissionManager) external onlyOwner {
|
||||
IDVM(_DVM_).setTradePermissionManager(newTradePermissionManager);
|
||||
}
|
||||
// function setTradePermissionManager(address newTradePermissionManager) external onlyOwner {
|
||||
// IDVM(_DVM_).setTradePermissionManager(newTradePermissionManager);
|
||||
// }
|
||||
|
||||
function setMaintainer(address newMaintainer) external onlyOwner {
|
||||
IDVM(_DVM_).setMaintainer(newMaintainer);
|
||||
}
|
||||
|
||||
function setGasPriceSource(address newGasPriceLimitSource) external onlyOwner {
|
||||
IDVM(_DVM_).setGasPriceSource(newGasPriceLimitSource);
|
||||
}
|
||||
// function setGasPriceSource(address newGasPriceLimitSource) external onlyOwner {
|
||||
// IDVM(_DVM_).setGasPriceSource(newGasPriceLimitSource);
|
||||
// }
|
||||
|
||||
function setBuy(bool open) external onlyOwner {
|
||||
IDVM(_DVM_).setBuy(open);
|
||||
|
||||
@@ -44,11 +44,10 @@ contract DVMTrader is DVMVault {
|
||||
external
|
||||
preventReentrant
|
||||
limitGasPrice
|
||||
isSellAllow(to)
|
||||
isSellAllow(to) // set DVM address in trade permission
|
||||
returns (uint256 receiveQuoteAmount)
|
||||
{
|
||||
uint256 baseInput = getBaseInput();
|
||||
require(baseInput > 0, "INSUFFICIENT_BASE_INPUT");
|
||||
uint256 mtFee;
|
||||
(receiveQuoteAmount, mtFee) = querySellBase(tx.origin, baseInput);
|
||||
_transferQuoteOut(to, receiveQuoteAmount);
|
||||
@@ -65,7 +64,6 @@ contract DVMTrader is DVMVault {
|
||||
returns (uint256 receiveBaseAmount)
|
||||
{
|
||||
uint256 quoteInput = getQuoteInput();
|
||||
require(quoteInput > 0, "INSUFFICIENT_QUOTE_INPUT");
|
||||
uint256 mtFee;
|
||||
(receiveBaseAmount, mtFee) = querySellQuote(tx.origin, quoteInput);
|
||||
_transferBaseOut(to, receiveBaseAmount);
|
||||
@@ -74,8 +72,6 @@ contract DVMTrader is DVMVault {
|
||||
return receiveBaseAmount;
|
||||
}
|
||||
|
||||
// 这是一个试验性质的函数
|
||||
// 没有走标准库,需要仔细考虑下
|
||||
function flashLoan(
|
||||
uint256 baseAmount,
|
||||
uint256 quoteAmount,
|
||||
@@ -96,9 +92,6 @@ contract DVMTrader is DVMVault {
|
||||
"FLASH_LOAN_FAILED"
|
||||
);
|
||||
|
||||
// no output -> pure profit
|
||||
if (baseBalance >= _BASE_RESERVE_ && quoteBalance >= _QUOTE_RESERVE_) return;
|
||||
|
||||
if (baseBalance < _BASE_RESERVE_) {
|
||||
(uint256 receiveBaseAmount, uint256 mtFee) = querySellQuote(
|
||||
tx.origin,
|
||||
@@ -120,6 +113,8 @@ contract DVMTrader is DVMVault {
|
||||
_sync();
|
||||
}
|
||||
|
||||
// ============ View Functions ============
|
||||
|
||||
function querySellBase(address trader, uint256 payBaseAmount)
|
||||
public
|
||||
view
|
||||
@@ -129,11 +124,10 @@ contract DVMTrader is DVMVault {
|
||||
|
||||
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
mtFee = DecimalMath.mulCeil(receiveQuoteAmount, mtFeeRate);
|
||||
receiveQuoteAmount = DecimalMath.mulFloor(
|
||||
receiveQuoteAmount,
|
||||
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
|
||||
);
|
||||
mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
|
||||
receiveQuoteAmount = receiveQuoteAmount
|
||||
.sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate))
|
||||
.sub(mtFee);
|
||||
|
||||
return (receiveQuoteAmount, mtFee);
|
||||
}
|
||||
@@ -147,18 +141,13 @@ contract DVMTrader is DVMVault {
|
||||
|
||||
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
|
||||
mtFee = DecimalMath.mulCeil(receiveBaseAmount, mtFeeRate);
|
||||
receiveBaseAmount = DecimalMath.mulFloor(
|
||||
receiveBaseAmount,
|
||||
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
|
||||
);
|
||||
mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
|
||||
receiveBaseAmount = receiveBaseAmount
|
||||
.sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate))
|
||||
.sub(mtFee);
|
||||
return (receiveBaseAmount, mtFee);
|
||||
}
|
||||
|
||||
function getMidPrice() public view returns (uint256 midPrice) {
|
||||
return PMMPricing.getMidPrice(getPMMState());
|
||||
}
|
||||
|
||||
// ============ Helper Functions ============
|
||||
|
||||
function getPMMState() public view returns (PMMPricing.PMMState memory state) {
|
||||
@@ -166,30 +155,14 @@ contract DVMTrader is DVMVault {
|
||||
state.K = _K_;
|
||||
state.B = _BASE_RESERVE_;
|
||||
state.Q = _QUOTE_RESERVE_;
|
||||
state.B0 = calculateBase0(state.B, state.Q);
|
||||
state.B0 = 0; // recalculate in adjustedTarget
|
||||
state.Q0 = 0;
|
||||
state.R = PMMPricing.RState.ABOVE_ONE;
|
||||
PMMPricing.adjustedTarget(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
function calculateBase0(uint256 baseAmount, uint256 quoteAmount) public view returns (uint256) {
|
||||
return
|
||||
DODOMath._SolveQuadraticFunctionForTarget(
|
||||
baseAmount,
|
||||
quoteAmount,
|
||||
DecimalMath.reciprocalFloor(_I_),
|
||||
_K_
|
||||
);
|
||||
}
|
||||
|
||||
function getBase0() public view returns (uint256) {
|
||||
(uint256 baseAmount, uint256 quoteAmount) = getVaultReserve();
|
||||
return
|
||||
DODOMath._SolveQuadraticFunctionForTarget(
|
||||
baseAmount,
|
||||
quoteAmount,
|
||||
DecimalMath.reciprocalFloor(_I_),
|
||||
_K_
|
||||
);
|
||||
function getMidPrice() public view returns (uint256 midPrice) {
|
||||
return PMMPricing.getMidPrice(getPMMState());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ interface IDVM {
|
||||
|
||||
function _QUOTE_TOKEN_() external returns (address);
|
||||
|
||||
function _LP_FEE_RATE_MODEL_() external returns (address);
|
||||
|
||||
function _MT_FEE_RATE_MODEL_() external returns (address);
|
||||
|
||||
function getVaultReserve() external returns (uint256 baseReserve, uint256 quoteReserve);
|
||||
|
||||
function sellBase(address to) external returns (uint256);
|
||||
@@ -34,7 +38,6 @@ interface IDVM {
|
||||
|
||||
function buyShares(address to) external returns (uint256);
|
||||
|
||||
|
||||
//=========== admin ==========
|
||||
function setLpFeeRateModel(address newLpFeeRateModel) external;
|
||||
|
||||
@@ -49,5 +52,5 @@ interface IDVM {
|
||||
function setBuy(bool open) external;
|
||||
|
||||
function setSell(bool open) external;
|
||||
//==============================
|
||||
//==============================
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {Ownable} from "./lib/Ownable.sol";
|
||||
import {IDODO} from "./intf/IDODO.sol";
|
||||
import {ICloneFactory} from "./lib/CloneFactory.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title DODOZoo
|
||||
* @author DODO Breeder
|
||||
*
|
||||
* @notice Register of All DODO
|
||||
*/
|
||||
contract DODOZoo is Ownable {
|
||||
address public _DODO_LOGIC_;
|
||||
address public _CLONE_FACTORY_;
|
||||
|
||||
address public _DEFAULT_SUPERVISOR_;
|
||||
|
||||
mapping(address => mapping(address => address)) internal _DODO_REGISTER_;
|
||||
address[] public _DODOs;
|
||||
|
||||
// ============ Events ============
|
||||
|
||||
event DODOBirth(address newBorn, address baseToken, address quoteToken);
|
||||
|
||||
// ============ Constructor Function ============
|
||||
|
||||
constructor(
|
||||
address _dodoLogic,
|
||||
address _cloneFactory,
|
||||
address _defaultSupervisor
|
||||
) public {
|
||||
_DODO_LOGIC_ = _dodoLogic;
|
||||
_CLONE_FACTORY_ = _cloneFactory;
|
||||
_DEFAULT_SUPERVISOR_ = _defaultSupervisor;
|
||||
}
|
||||
|
||||
// ============ Admin Function ============
|
||||
|
||||
function setDODOLogic(address _dodoLogic) external onlyOwner {
|
||||
_DODO_LOGIC_ = _dodoLogic;
|
||||
}
|
||||
|
||||
function setCloneFactory(address _cloneFactory) external onlyOwner {
|
||||
_CLONE_FACTORY_ = _cloneFactory;
|
||||
}
|
||||
|
||||
function setDefaultSupervisor(address _defaultSupervisor) external onlyOwner {
|
||||
_DEFAULT_SUPERVISOR_ = _defaultSupervisor;
|
||||
}
|
||||
|
||||
function removeDODO(address dodo) external onlyOwner {
|
||||
address baseToken = IDODO(dodo)._BASE_TOKEN_();
|
||||
address quoteToken = IDODO(dodo)._QUOTE_TOKEN_();
|
||||
require(isDODORegistered(baseToken, quoteToken), "DODO_NOT_REGISTERED");
|
||||
_DODO_REGISTER_[baseToken][quoteToken] = address(0);
|
||||
for (uint256 i = 0; i <= _DODOs.length - 1; i++) {
|
||||
if (_DODOs[i] == dodo) {
|
||||
_DODOs[i] = _DODOs[_DODOs.length - 1];
|
||||
_DODOs.pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addDODO(address dodo) public onlyOwner {
|
||||
address baseToken = IDODO(dodo)._BASE_TOKEN_();
|
||||
address quoteToken = IDODO(dodo)._QUOTE_TOKEN_();
|
||||
require(!isDODORegistered(baseToken, quoteToken), "DODO_REGISTERED");
|
||||
_DODO_REGISTER_[baseToken][quoteToken] = dodo;
|
||||
_DODOs.push(dodo);
|
||||
}
|
||||
|
||||
// ============ Breed DODO Function ============
|
||||
|
||||
function breedDODO(
|
||||
address maintainer,
|
||||
address baseToken,
|
||||
address quoteToken,
|
||||
address oracle,
|
||||
uint256 lpFeeRate,
|
||||
uint256 mtFeeRate,
|
||||
uint256 k,
|
||||
uint256 gasPriceLimit
|
||||
) external onlyOwner returns (address newBornDODO) {
|
||||
require(!isDODORegistered(baseToken, quoteToken), "DODO_REGISTERED");
|
||||
newBornDODO = ICloneFactory(_CLONE_FACTORY_).clone(_DODO_LOGIC_);
|
||||
IDODO(newBornDODO).init(
|
||||
_OWNER_,
|
||||
_DEFAULT_SUPERVISOR_,
|
||||
maintainer,
|
||||
baseToken,
|
||||
quoteToken,
|
||||
oracle,
|
||||
lpFeeRate,
|
||||
mtFeeRate,
|
||||
k,
|
||||
gasPriceLimit
|
||||
);
|
||||
addDODO(newBornDODO);
|
||||
emit DODOBirth(newBornDODO, baseToken, quoteToken);
|
||||
return newBornDODO;
|
||||
}
|
||||
|
||||
// ============ View Functions ============
|
||||
|
||||
function isDODORegistered(address baseToken, address quoteToken) public view returns (bool) {
|
||||
if (
|
||||
_DODO_REGISTER_[baseToken][quoteToken] == address(0) &&
|
||||
_DODO_REGISTER_[quoteToken][baseToken] == address(0)
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function getDODO(address baseToken, address quoteToken) external view returns (address) {
|
||||
return _DODO_REGISTER_[baseToken][quoteToken];
|
||||
}
|
||||
|
||||
function getDODOs() external view returns (address[] memory) {
|
||||
return _DODOs;
|
||||
}
|
||||
}
|
||||
@@ -26,20 +26,10 @@ contract DPPFactory is Ownable {
|
||||
address public _VALUE_SOURCE_;
|
||||
address public _DODO_SMART_APPROVE_;
|
||||
|
||||
//TODO: 平台修改tag的权限 && 池子标签类型
|
||||
struct DPPInfo {
|
||||
address creator;
|
||||
uint256 createTimeStamp;
|
||||
}
|
||||
|
||||
// base -> quote -> DPP address list
|
||||
mapping(address => mapping(address => address[])) public _REGISTRY_;
|
||||
// token0 -> token1 -> DPP address list
|
||||
mapping(address => mapping(address => address[])) public _SORT_REGISTRY_;
|
||||
// creator -> DPP address list
|
||||
mapping(address => address[]) public _USER_REGISTRY_;
|
||||
// DPP address -> info
|
||||
mapping(address => DPPInfo) public _DPP_INFO_;
|
||||
|
||||
constructor(
|
||||
address cloneFactory,
|
||||
@@ -61,13 +51,13 @@ contract DPPFactory is Ownable {
|
||||
_DEFAULT_GAS_PRICE_SOURCE_ = defaultGasPriceSource;
|
||||
}
|
||||
|
||||
function createDODOPrivatePool() external returns(address newPrivatePool) {
|
||||
function createDODOPrivatePool() external returns (address newPrivatePool) {
|
||||
newPrivatePool = ICloneFactory(_CLONE_FACTORY_).clone(_DPP_TEMPLATE_);
|
||||
}
|
||||
|
||||
function initDODOPrivatePool(
|
||||
address dppAddress,
|
||||
address from,
|
||||
address creator,
|
||||
address baseToken,
|
||||
address quoteToken,
|
||||
uint256 lpFeeRate,
|
||||
@@ -75,38 +65,36 @@ contract DPPFactory is Ownable {
|
||||
uint256 k,
|
||||
uint256 i
|
||||
) external {
|
||||
{
|
||||
address _dppAddress = dppAddress;
|
||||
address adminModel = _createDPPAdminModel(from,_dppAddress,from,_DODO_SMART_APPROVE_);
|
||||
IDPP(_dppAddress).init(
|
||||
adminModel,
|
||||
from,
|
||||
baseToken,
|
||||
quoteToken,
|
||||
_createFeeRateModel(_dppAddress, lpFeeRate),
|
||||
_createFeeRateModel(_dppAddress, mtFeeRate),
|
||||
_createExternalValueModel(_dppAddress, k),
|
||||
_createExternalValueModel(_dppAddress, i),
|
||||
_DEFAULT_GAS_PRICE_SOURCE_,
|
||||
_createPermissionManager(adminModel)
|
||||
);
|
||||
{
|
||||
address _dppAddress = dppAddress;
|
||||
address adminModel = _createDPPAdminModel(
|
||||
creator,
|
||||
_dppAddress,
|
||||
creator,
|
||||
_DODO_SMART_APPROVE_
|
||||
);
|
||||
IDPP(_dppAddress).init(
|
||||
adminModel,
|
||||
creator,
|
||||
baseToken,
|
||||
quoteToken,
|
||||
_createFeeRateModel(_dppAddress, lpFeeRate),
|
||||
_createFeeRateModel(_dppAddress, mtFeeRate),
|
||||
_createExternalValueModel(_dppAddress, k),
|
||||
_createExternalValueModel(_dppAddress, i),
|
||||
_DEFAULT_GAS_PRICE_SOURCE_,
|
||||
_createPermissionManager(adminModel)
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
(address token0, address token1) = baseToken < quoteToken ? (baseToken, quoteToken) : (quoteToken, baseToken);
|
||||
_SORT_REGISTRY_[token0][token1].push(dppAddress);
|
||||
}
|
||||
_REGISTRY_[baseToken][quoteToken].push(dppAddress);
|
||||
_USER_REGISTRY_[from].push(dppAddress);
|
||||
_DPP_INFO_[dppAddress] = (
|
||||
DPPInfo({
|
||||
creator: from,
|
||||
createTimeStamp: block.timestamp
|
||||
})
|
||||
);
|
||||
_USER_REGISTRY_[creator].push(dppAddress);
|
||||
}
|
||||
|
||||
function _createFeeRateModel(address owner, uint256 feeRate) internal returns (address feeRateModel){
|
||||
function _createFeeRateModel(address owner, uint256 feeRate)
|
||||
internal
|
||||
returns (address feeRateModel)
|
||||
{
|
||||
feeRateModel = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_RATE_MODEL_TEMPLATE_);
|
||||
IFeeRateModel(feeRateModel).init(owner, feeRate);
|
||||
}
|
||||
@@ -116,14 +104,22 @@ contract DPPFactory is Ownable {
|
||||
IPermissionManager(permissionManager).initOwner(owner);
|
||||
}
|
||||
|
||||
function _createExternalValueModel(address owner, uint256 value) internal returns (address valueModel) {
|
||||
function _createExternalValueModel(address owner, uint256 value)
|
||||
internal
|
||||
returns (address valueModel)
|
||||
{
|
||||
valueModel = ICloneFactory(_CLONE_FACTORY_).clone(_VALUE_SOURCE_);
|
||||
IExternalValue(valueModel).init(owner, value);
|
||||
}
|
||||
|
||||
function _createDPPAdminModel(address owner, address dpp,address operator, address dodoSmartApprove) internal returns (address adminModel) {
|
||||
function _createDPPAdminModel(
|
||||
address owner,
|
||||
address dpp,
|
||||
address operator,
|
||||
address dodoSmartApprove
|
||||
) internal returns (address adminModel) {
|
||||
adminModel = ICloneFactory(_CLONE_FACTORY_).clone(_DPP_ADMIN_TEMPLATE_);
|
||||
IDPPAdmin(adminModel).init(owner,dpp,operator,dodoSmartApprove);
|
||||
IDPPAdmin(adminModel).init(owner, dpp, operator, dodoSmartApprove);
|
||||
}
|
||||
|
||||
function updateAdminTemplate(address _newDPPAdminTemplate) external onlyOwner {
|
||||
@@ -137,4 +133,12 @@ contract DPPFactory is Ownable {
|
||||
{
|
||||
return _REGISTRY_[baseToken][quoteToken];
|
||||
}
|
||||
|
||||
function getPrivatePoolBidirection(address token0, address token1)
|
||||
external
|
||||
view
|
||||
returns (address[] memory baseToken0Pool, address[] memory baseToken1Pool)
|
||||
{
|
||||
return (_REGISTRY_[token0][token1], _REGISTRY_[token1][token0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,10 @@ contract DVMFactory is Ownable {
|
||||
address public _PERMISSION_MANAGER_TEMPLATE_;
|
||||
address public _DEFAULT_GAS_PRICE_SOURCE_;
|
||||
|
||||
//TODO: 平台修改tag的权限 && 池子标签类型
|
||||
struct DVMInfo {
|
||||
address creator;
|
||||
uint256 createTimeStamp;
|
||||
}
|
||||
|
||||
// base -> quote -> DVM address list
|
||||
mapping(address => mapping(address => address[])) public _REGISTRY_;
|
||||
// token0 -> token1 -> DVM address list
|
||||
mapping(address => mapping(address => address[])) public _SORT_REGISTRY_;
|
||||
// creator -> DVM address list
|
||||
mapping(address => address[]) public _USER_REGISTRY_;
|
||||
// DVM address -> info
|
||||
mapping(address => DVMInfo) public _DVM_INFO_;
|
||||
|
||||
constructor(
|
||||
address cloneFactory,
|
||||
@@ -55,7 +45,7 @@ contract DVMFactory is Ownable {
|
||||
}
|
||||
|
||||
function createDODOVendingMachine(
|
||||
address from,
|
||||
address creator,
|
||||
address baseToken,
|
||||
address quoteToken,
|
||||
uint256 lpFeeRate,
|
||||
@@ -65,35 +55,28 @@ contract DVMFactory is Ownable {
|
||||
) external returns (address newVendingMachine) {
|
||||
newVendingMachine = ICloneFactory(_CLONE_FACTORY_).clone(_DVM_TEMPLATE_);
|
||||
{
|
||||
address adminModel = _createDVMAdminModel(from,newVendingMachine);
|
||||
IDVM(newVendingMachine).init(
|
||||
adminModel,
|
||||
from,
|
||||
baseToken,
|
||||
quoteToken,
|
||||
_createFeeRateModel(newVendingMachine, lpFeeRate),
|
||||
_createFeeRateModel(newVendingMachine, mtFeeRate),
|
||||
_createPermissionManager(adminModel),
|
||||
_DEFAULT_GAS_PRICE_SOURCE_,
|
||||
i,
|
||||
k
|
||||
);
|
||||
address adminModel = _createDVMAdminModel(creator, newVendingMachine);
|
||||
IDVM(newVendingMachine).init(
|
||||
adminModel,
|
||||
creator,
|
||||
baseToken,
|
||||
quoteToken,
|
||||
_createFeeRateModel(adminModel, lpFeeRate),
|
||||
_createFeeRateModel(adminModel, mtFeeRate),
|
||||
_createPermissionManager(adminModel),
|
||||
_DEFAULT_GAS_PRICE_SOURCE_,
|
||||
i,
|
||||
k
|
||||
);
|
||||
}
|
||||
_REGISTRY_[baseToken][quoteToken].push(newVendingMachine);
|
||||
{
|
||||
(address token0, address token1) = baseToken < quoteToken ? (baseToken, quoteToken) : (quoteToken, baseToken);
|
||||
_SORT_REGISTRY_[token0][token1].push(newVendingMachine);
|
||||
}
|
||||
_USER_REGISTRY_[from].push(newVendingMachine);
|
||||
_DVM_INFO_[newVendingMachine] = (
|
||||
DVMInfo({
|
||||
creator: from,
|
||||
createTimeStamp: block.timestamp
|
||||
})
|
||||
);
|
||||
_USER_REGISTRY_[creator].push(newVendingMachine);
|
||||
}
|
||||
|
||||
function _createFeeRateModel(address owner, uint256 feeRate) internal returns (address feeRateModel) {
|
||||
function _createFeeRateModel(address owner, uint256 feeRate)
|
||||
internal
|
||||
returns (address feeRateModel)
|
||||
{
|
||||
feeRateModel = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_RATE_MODEL_TEMPLATE_);
|
||||
IConstFeeRateModel(feeRateModel).init(owner, feeRate);
|
||||
}
|
||||
@@ -103,9 +86,12 @@ contract DVMFactory is Ownable {
|
||||
IPermissionManager(permissionManager).initOwner(owner);
|
||||
}
|
||||
|
||||
function _createDVMAdminModel(address owner, address dvm) internal returns (address adminModel) {
|
||||
function _createDVMAdminModel(address owner, address dvm)
|
||||
internal
|
||||
returns (address adminModel)
|
||||
{
|
||||
adminModel = ICloneFactory(_CLONE_FACTORY_).clone(_DVM_ADMIN_TEMPLATE_);
|
||||
IDVMAdmin(adminModel).init(owner,dvm);
|
||||
IDVMAdmin(adminModel).init(owner, dvm);
|
||||
}
|
||||
|
||||
function updateAdminTemplate(address _newDVMAdminTemplate) external onlyOwner {
|
||||
@@ -119,4 +105,12 @@ contract DVMFactory is Ownable {
|
||||
{
|
||||
return _REGISTRY_[baseToken][quoteToken];
|
||||
}
|
||||
|
||||
function getVendingMachineBidirection(address token0, address token1)
|
||||
external
|
||||
view
|
||||
returns (address[] memory baseToken0Machines, address[] memory baseToken1Machines)
|
||||
{
|
||||
return (_REGISTRY_[token0][token1], _REGISTRY_[token1][token0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ contract DODOApprove is Ownable {
|
||||
address dest,
|
||||
uint256 amount
|
||||
) external {
|
||||
require(msg.sender == dodoProxy, 'DODOApprove:Access restricted');
|
||||
IERC20(token).safeTransferFrom(who, dest, amount);
|
||||
require(msg.sender == dodoProxy, "DODOApprove:Access restricted");
|
||||
if (amount > 0) {
|
||||
IERC20(token).safeTransferFrom(who, dest, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,13 @@ pragma solidity 0.6.9;
|
||||
|
||||
import {Ownable} from "../lib/Ownable.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {UniversalERC20} from "../lib/UniversalERC20.sol";
|
||||
import {UniversalERC20} from "./UniversalERC20.sol";
|
||||
import {SafeMath} from "../lib/SafeMath.sol";
|
||||
import {IDODOSellHelper} from "../intf/IDODOSellHelper.sol";
|
||||
import {IDODOApprove} from "../intf/IDODOApprove.sol";
|
||||
import {IDODO} from "../intf/IDODO.sol";
|
||||
import {IWETH} from "../intf/IWETH.sol";
|
||||
|
||||
|
||||
contract DODOV1Proxy01 is Ownable {
|
||||
using SafeMath for uint256;
|
||||
using UniversalERC20 for IERC20;
|
||||
@@ -27,7 +26,7 @@ contract DODOV1Proxy01 is Ownable {
|
||||
address payable public _WETH_;
|
||||
|
||||
modifier judgeExpired(uint256 deadline) {
|
||||
require(deadline >= block.timestamp, 'DODOV1Proxy01: EXPIRED');
|
||||
require(deadline >= block.timestamp, "DODOV1Proxy01: EXPIRED");
|
||||
_;
|
||||
}
|
||||
|
||||
@@ -63,12 +62,15 @@ contract DODOV1Proxy01 is Ownable {
|
||||
uint256[] memory directions,
|
||||
uint256 deadline
|
||||
) public payable judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, 'DODOV1Proxy01: Min return should be bigger then 0.');
|
||||
|
||||
if (fromToken != ETH_ADDRESS) {
|
||||
IDODOApprove(dodoApprove).claimTokens(fromToken, msg.sender, address(this), fromTokenAmount);
|
||||
IDODOApprove(dodoApprove).claimTokens(
|
||||
fromToken,
|
||||
msg.sender,
|
||||
address(this),
|
||||
fromTokenAmount
|
||||
);
|
||||
} else {
|
||||
require(msg.value == fromTokenAmount, 'DODOV1Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
require(msg.value == fromTokenAmount, "DODOV1Proxy01: ETH_AMOUNT_NOT_MATCH");
|
||||
IWETH(_WETH_).deposit{value: fromTokenAmount}();
|
||||
}
|
||||
|
||||
@@ -98,9 +100,16 @@ contract DODOV1Proxy01 is Ownable {
|
||||
|
||||
returnAmount = IERC20(toToken).universalBalanceOf(address(this));
|
||||
|
||||
require(returnAmount >= minReturnAmount, 'DODOV1Proxy01: Return amount is not enough');
|
||||
require(returnAmount >= minReturnAmount, "DODOV1Proxy01: Return amount is not enough");
|
||||
IERC20(toToken).universalTransfer(msg.sender, returnAmount);
|
||||
emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp);
|
||||
emit OrderHistory(
|
||||
fromToken,
|
||||
toToken,
|
||||
msg.sender,
|
||||
fromTokenAmount,
|
||||
returnAmount,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
function externalSwap(
|
||||
@@ -112,24 +121,37 @@ contract DODOV1Proxy01 is Ownable {
|
||||
uint256 minReturnAmount,
|
||||
bytes memory callDataConcat,
|
||||
uint256 deadline
|
||||
) public payable judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, 'DODOV1Proxy01: Min return should be bigger then 0.');
|
||||
|
||||
) public payable judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
if (fromToken != ETH_ADDRESS) {
|
||||
IDODOApprove(dodoApprove).claimTokens(fromToken, msg.sender, address(this), fromTokenAmount);
|
||||
IERC20(fromToken).universalApprove(approveTarget, fromTokenAmount);
|
||||
IDODOApprove(dodoApprove).claimTokens(
|
||||
fromToken,
|
||||
msg.sender,
|
||||
address(this),
|
||||
fromTokenAmount
|
||||
);
|
||||
IERC20(fromToken).universalApproveMax(approveTarget, fromTokenAmount);
|
||||
}
|
||||
|
||||
(bool success, ) = to.call{value: fromToken == ETH_ADDRESS ? msg.value : 0}(
|
||||
callDataConcat
|
||||
(bool success, ) = to.call{value: fromToken == ETH_ADDRESS ? msg.value : 0}(callDataConcat);
|
||||
|
||||
require(success, "DODOV1Proxy01: Contract Swap execution Failed");
|
||||
|
||||
IERC20(fromToken).universalTransfer(
|
||||
msg.sender,
|
||||
IERC20(fromToken).universalBalanceOf(address(this))
|
||||
);
|
||||
|
||||
require(success, 'DODOV1Proxy01: Contract Swap execution Failed');
|
||||
|
||||
returnAmount = IERC20(toToken).universalBalanceOf(address(this));
|
||||
|
||||
require(returnAmount >= minReturnAmount, 'DODOV1Proxy01: Return amount is not enough');
|
||||
require(returnAmount >= minReturnAmount, "DODOV1Proxy01: Return amount is not enough");
|
||||
IERC20(toToken).universalTransfer(msg.sender, returnAmount);
|
||||
emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp);
|
||||
|
||||
emit OrderHistory(
|
||||
fromToken,
|
||||
toToken,
|
||||
msg.sender,
|
||||
fromTokenAmount,
|
||||
returnAmount,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import {IDODOV2} from "../intf/IDODOV2.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {IWETH} from "../intf/IWETH.sol";
|
||||
import {SafeMath} from "../lib/SafeMath.sol";
|
||||
import {UniversalERC20} from "../lib/UniversalERC20.sol";
|
||||
import {UniversalERC20} from "./UniversalERC20.sol";
|
||||
import {SafeERC20} from "../lib/SafeERC20.sol";
|
||||
import {DecimalMath} from "../lib/DecimalMath.sol";
|
||||
|
||||
contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
@@ -22,12 +23,11 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
address payable public _WETH_;
|
||||
address public dodoApprove;
|
||||
address public dodoSellHelper;
|
||||
address public dvmFactory;
|
||||
address public dppFactory;
|
||||
|
||||
modifier judgeExpired(uint256 deadline) {
|
||||
require(deadline >= block.timestamp, 'DODOV2Proxy01: EXPIRED');
|
||||
require(deadline >= block.timestamp, "DODOV2Proxy01: EXPIRED");
|
||||
_;
|
||||
}
|
||||
|
||||
@@ -44,22 +44,21 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256 returnAmount,
|
||||
uint256 timeStamp
|
||||
);
|
||||
|
||||
//========================================================================
|
||||
|
||||
constructor(
|
||||
address _dvmFactory,
|
||||
address _dppFactory,
|
||||
address payable _weth,
|
||||
address _dodoApprove,
|
||||
address _dodoSellHelper
|
||||
address _dodoApprove
|
||||
) public {
|
||||
dvmFactory = _dvmFactory;
|
||||
dppFactory = _dppFactory;
|
||||
_WETH_ = _weth;
|
||||
dodoApprove = _dodoApprove;
|
||||
dodoSellHelper = _dodoSellHelper;
|
||||
}
|
||||
|
||||
|
||||
function createDODOVendingMachine(
|
||||
address assetTo,
|
||||
address baseToken,
|
||||
@@ -71,39 +70,55 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256 i,
|
||||
uint256 k,
|
||||
uint256 deadline
|
||||
) external virtual override payable judgeExpired(deadline) returns (address newVendingMachine,uint256 shares) {
|
||||
)
|
||||
external
|
||||
virtual
|
||||
override
|
||||
payable
|
||||
judgeExpired(deadline)
|
||||
returns (address newVendingMachine, uint256 shares)
|
||||
{
|
||||
{
|
||||
address _baseToken = baseToken == ETH_ADDRESS ? _WETH_ : baseToken;
|
||||
address _quoteToken = quoteToken == ETH_ADDRESS ? _WETH_ : quoteToken;
|
||||
newVendingMachine = IDODOV2(dvmFactory).createDODOVendingMachine(msg.sender, _baseToken, _quoteToken, lpFeeRate, mtFeeRate, i, k);
|
||||
address _baseToken = baseToken == ETH_ADDRESS ? _WETH_ : baseToken;
|
||||
address _quoteToken = quoteToken == ETH_ADDRESS ? _WETH_ : quoteToken;
|
||||
newVendingMachine = IDODOV2(dvmFactory).createDODOVendingMachine(
|
||||
msg.sender,
|
||||
_baseToken,
|
||||
_quoteToken,
|
||||
lpFeeRate,
|
||||
mtFeeRate,
|
||||
i,
|
||||
k
|
||||
);
|
||||
}
|
||||
if(baseInAmount > 0){
|
||||
if(baseToken != ETH_ADDRESS){
|
||||
IDODOV2(dodoApprove).claimTokens(baseToken, msg.sender, newVendingMachine, baseInAmount);
|
||||
}else {
|
||||
require(msg.value == baseInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: baseInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(newVendingMachine, baseInAmount));
|
||||
}
|
||||
}
|
||||
if(quoteInAmount > 0){
|
||||
if(quoteToken != ETH_ADDRESS){
|
||||
IDODOV2(dodoApprove).claimTokens(quoteToken, msg.sender, newVendingMachine, quoteInAmount);
|
||||
}else {
|
||||
require(msg.value == quoteInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: quoteInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(newVendingMachine, quoteInAmount));
|
||||
}
|
||||
}
|
||||
(shares,,) = IDODOV2(newVendingMachine).buyShares(assetTo);
|
||||
}
|
||||
|
||||
{
|
||||
address _baseToken = baseToken;
|
||||
address _quoteToken = quoteToken;
|
||||
_deposit(
|
||||
msg.sender,
|
||||
newVendingMachine,
|
||||
_baseToken,
|
||||
baseInAmount,
|
||||
_baseToken == ETH_ADDRESS
|
||||
);
|
||||
_deposit(
|
||||
msg.sender,
|
||||
newVendingMachine,
|
||||
_quoteToken,
|
||||
quoteInAmount,
|
||||
_quoteToken == ETH_ADDRESS
|
||||
);
|
||||
}
|
||||
|
||||
(shares, , ) = IDODOV2(newVendingMachine).buyShares(assetTo);
|
||||
}
|
||||
|
||||
function _addDVMLiquidity(
|
||||
address DVMAddress,
|
||||
uint256 baseInAmount,
|
||||
uint256 quoteInAmount
|
||||
) internal virtual view returns (uint baseAdjustedInAmount, uint quoteAdjustedInAmount) {
|
||||
) internal virtual view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount) {
|
||||
(uint256 baseReserve, uint256 quoteReserve) = IDODOV2(DVMAddress).getVaultReserve();
|
||||
if (quoteReserve == 0 && baseReserve == 0) {
|
||||
baseAdjustedInAmount = baseInAmount;
|
||||
@@ -133,59 +148,35 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseMinAmount,
|
||||
uint256 quoteMinAmount,
|
||||
uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH
|
||||
uint256 deadline
|
||||
) external virtual override judgeExpired(deadline) returns (uint256 shares,uint256 baseAdjustedInAmount,uint256 quoteAdjustedInAmount) {
|
||||
(baseAdjustedInAmount, quoteAdjustedInAmount) = _addDVMLiquidity(DVMAddress,baseInAmount,quoteInAmount);
|
||||
require(baseAdjustedInAmount >= baseMinAmount && quoteAdjustedInAmount >= quoteMinAmount, 'DODOV2Proxy01: deposit amount is not enough');
|
||||
)
|
||||
external
|
||||
virtual
|
||||
override
|
||||
payable
|
||||
judgeExpired(deadline)
|
||||
returns (
|
||||
uint256 shares,
|
||||
uint256 baseAdjustedInAmount,
|
||||
uint256 quoteAdjustedInAmount
|
||||
)
|
||||
{
|
||||
(baseAdjustedInAmount, quoteAdjustedInAmount) = _addDVMLiquidity(
|
||||
DVMAddress,
|
||||
baseInAmount,
|
||||
quoteInAmount
|
||||
);
|
||||
require(
|
||||
baseAdjustedInAmount >= baseMinAmount && quoteAdjustedInAmount >= quoteMinAmount,
|
||||
"DODOV2Proxy01: deposit amount is not enough"
|
||||
);
|
||||
address _dvm = DVMAddress;
|
||||
if(baseAdjustedInAmount > 0) {
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(_dvm)._BASE_TOKEN_(), msg.sender, _dvm, baseAdjustedInAmount);
|
||||
}
|
||||
if(quoteAdjustedInAmount > 0)
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(_dvm)._QUOTE_TOKEN_(), msg.sender, _dvm, quoteAdjustedInAmount);
|
||||
(shares,,) = IDODOV2(_dvm).buyShares(to);
|
||||
}
|
||||
|
||||
function addDVMLiquidityETH(
|
||||
address DVMAddress,
|
||||
address to,
|
||||
uint256 baseInAmount,
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseMinAmount,
|
||||
uint256 quoteMinAmount,
|
||||
uint8 flag, // 1 - baseInETH, 2 - quoteInETH
|
||||
uint256 deadline
|
||||
) external virtual override payable judgeExpired(deadline) returns (uint256 shares,uint256 baseAdjustedInAmount,uint256 quoteAdjustedInAmount) {
|
||||
(baseAdjustedInAmount, quoteAdjustedInAmount) = _addDVMLiquidity(DVMAddress,baseInAmount,quoteInAmount);
|
||||
require(baseAdjustedInAmount >= baseMinAmount && quoteAdjustedInAmount >= quoteMinAmount, 'DODOV2Proxy01: deposit amount is not enough');
|
||||
address _dvm = DVMAddress;
|
||||
if(baseAdjustedInAmount > 0) {
|
||||
if(flag == 1) {
|
||||
require(msg.value >= baseAdjustedInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: baseAdjustedInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(_dvm, baseAdjustedInAmount));
|
||||
if (msg.value > baseAdjustedInAmount) {
|
||||
(bool success,) = msg.sender.call{value:msg.value - baseAdjustedInAmount}(new bytes(0));
|
||||
require(success, 'DODOV2Proxy01: ETH_TRANSFER_FAILED');
|
||||
}
|
||||
}else {
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(_dvm)._BASE_TOKEN_(), msg.sender, _dvm, baseAdjustedInAmount);
|
||||
}
|
||||
}
|
||||
if(quoteAdjustedInAmount > 0){
|
||||
if(flag == 2) {
|
||||
require(msg.value >= quoteAdjustedInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: quoteAdjustedInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(_dvm, quoteAdjustedInAmount));
|
||||
if (msg.value > quoteAdjustedInAmount) {
|
||||
(bool success,) = msg.sender.call{value:msg.value - quoteAdjustedInAmount}(new bytes(0));
|
||||
require(success, 'DODOV2Proxy01: ETH_TRANSFER_FAILED');
|
||||
}
|
||||
}else {
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(_dvm)._QUOTE_TOKEN_(), msg.sender, _dvm, quoteAdjustedInAmount);
|
||||
}
|
||||
}
|
||||
(shares,,) = IDODOV2(_dvm).buyShares(to);
|
||||
_deposit(msg.sender, _dvm, IDODOV2(_dvm)._BASE_TOKEN_(), baseAdjustedInAmount, flag == 1);
|
||||
_deposit(msg.sender, _dvm, IDODOV2(_dvm)._QUOTE_TOKEN_(), quoteAdjustedInAmount, flag == 2);
|
||||
|
||||
(shares, , ) = IDODOV2(_dvm).buyShares(to);
|
||||
}
|
||||
|
||||
function createDODOPrivatePool(
|
||||
@@ -200,31 +191,26 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256 deadline
|
||||
) external virtual override payable judgeExpired(deadline) returns (address newPrivatePool) {
|
||||
newPrivatePool = IDODOV2(dppFactory).createDODOPrivatePool();
|
||||
if(baseInAmount > 0){
|
||||
if(baseToken != ETH_ADDRESS){
|
||||
IDODOV2(dodoApprove).claimTokens(baseToken, msg.sender, newPrivatePool, baseInAmount);
|
||||
}else {
|
||||
require(msg.value == baseInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: baseInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(newPrivatePool, baseInAmount));
|
||||
baseToken = _WETH_;
|
||||
}
|
||||
}
|
||||
if(quoteInAmount > 0){
|
||||
if(quoteToken != ETH_ADDRESS){
|
||||
IDODOV2(dodoApprove).claimTokens(quoteToken, msg.sender, newPrivatePool, quoteInAmount);
|
||||
}else {
|
||||
require(msg.value == quoteInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: quoteInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(newPrivatePool, quoteInAmount));
|
||||
quoteToken = _WETH_;
|
||||
}
|
||||
}
|
||||
|
||||
address _baseToken = baseToken;
|
||||
address _quoteToken = quoteToken;
|
||||
_deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == ETH_ADDRESS);
|
||||
_deposit(
|
||||
msg.sender,
|
||||
newPrivatePool,
|
||||
_quoteToken,
|
||||
quoteInAmount,
|
||||
_quoteToken == ETH_ADDRESS
|
||||
);
|
||||
|
||||
if (_baseToken == ETH_ADDRESS) _baseToken = _WETH_;
|
||||
if (_quoteToken == ETH_ADDRESS) _quoteToken = _WETH_;
|
||||
|
||||
IDODOV2(dppFactory).initDODOPrivatePool(
|
||||
newPrivatePool,
|
||||
msg.sender,
|
||||
baseToken,
|
||||
quoteToken,
|
||||
_baseToken,
|
||||
_quoteToken,
|
||||
lpFeeRate,
|
||||
mtFeeRate,
|
||||
k,
|
||||
@@ -242,12 +228,24 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount,
|
||||
uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH
|
||||
uint256 deadline
|
||||
) external virtual override judgeExpired(deadline) {
|
||||
if(baseInAmount > 0)
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(DPPAddress)._BASE_TOKEN_(), msg.sender, DPPAddress, baseInAmount);
|
||||
if(quoteInAmount > 0)
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(DPPAddress)._QUOTE_TOKEN_(), msg.sender, DPPAddress, quoteInAmount);
|
||||
) external virtual override payable judgeExpired(deadline) {
|
||||
_deposit(
|
||||
msg.sender,
|
||||
DPPAddress,
|
||||
IDODOV2(DPPAddress)._BASE_TOKEN_(),
|
||||
baseInAmount,
|
||||
flag == 1
|
||||
);
|
||||
_deposit(
|
||||
msg.sender,
|
||||
DPPAddress,
|
||||
IDODOV2(DPPAddress)._QUOTE_TOKEN_(),
|
||||
quoteInAmount,
|
||||
flag == 2
|
||||
);
|
||||
|
||||
IDODOV2(IDODOV2(DPPAddress)._OWNER_()).reset(
|
||||
msg.sender,
|
||||
newLpFeeRate,
|
||||
@@ -257,81 +255,9 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
baseOutAmount,
|
||||
quoteOutAmount
|
||||
);
|
||||
}
|
||||
|
||||
function resetDODOPrivatePoolETH(
|
||||
address DPPAddress,
|
||||
uint256 newLpFeeRate,
|
||||
uint256 newMtFeeRate,
|
||||
uint256 newI,
|
||||
uint256 newK,
|
||||
uint256 baseInAmount,
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount,
|
||||
uint8 flag, // 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH
|
||||
uint256 deadline
|
||||
) external virtual override payable judgeExpired(deadline) {
|
||||
if(baseInAmount > 0){
|
||||
if(flag == 1){
|
||||
require(msg.value == baseInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: baseInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(DPPAddress, baseInAmount));
|
||||
}else {
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(DPPAddress)._BASE_TOKEN_(), msg.sender, DPPAddress, baseInAmount);
|
||||
}
|
||||
}
|
||||
if(quoteInAmount > 0){
|
||||
if(flag == 2){
|
||||
require(msg.value == quoteInAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
IWETH(_WETH_).deposit{value: quoteInAmount}();
|
||||
assert(IWETH(_WETH_).transfer(DPPAddress, quoteInAmount));
|
||||
}else {
|
||||
IDODOV2(dodoApprove).claimTokens(IDODOV2(DPPAddress)._QUOTE_TOKEN_(), msg.sender, DPPAddress, quoteInAmount);
|
||||
}
|
||||
}
|
||||
if( (flag == 3 && baseOutAmount > 0) || (flag == 4 && quoteOutAmount > 0) ) {
|
||||
IDODOV2(IDODOV2(DPPAddress)._OWNER_()).resetETH(
|
||||
msg.sender,
|
||||
newLpFeeRate,
|
||||
newMtFeeRate,
|
||||
newI,
|
||||
newK,
|
||||
baseOutAmount,
|
||||
quoteOutAmount
|
||||
);
|
||||
if(baseOutAmount > 0) {
|
||||
if(flag == 3) {
|
||||
uint256 wethAmount = IWETH(_WETH_).balanceOf(address(this));
|
||||
IWETH(_WETH_).withdraw(wethAmount);
|
||||
(bool success,) = msg.sender.call{value:wethAmount}(new bytes(0));
|
||||
require(success, 'DODOV2Proxy01: ETH_TRANSFER_FAILED');
|
||||
}else {
|
||||
IERC20(IDODOV2(DPPAddress)._BASE_TOKEN_()).universalTransfer(msg.sender, baseOutAmount);
|
||||
}
|
||||
}
|
||||
|
||||
if(quoteOutAmount > 0) {
|
||||
if(flag == 4) {
|
||||
uint256 wethAmount = IWETH(_WETH_).balanceOf(address(this));
|
||||
IWETH(_WETH_).withdraw(wethAmount);
|
||||
(bool success,) = msg.sender.call{value:wethAmount}(new bytes(0));
|
||||
require(success, 'DODOV2Proxy01: ETH_TRANSFER_FAILED');
|
||||
}else {
|
||||
IERC20(IDODOV2(DPPAddress)._QUOTE_TOKEN_()).universalTransfer(msg.sender, quoteOutAmount);
|
||||
}
|
||||
}
|
||||
}else {
|
||||
IDODOV2(IDODOV2(DPPAddress)._OWNER_()).reset(
|
||||
msg.sender,
|
||||
newLpFeeRate,
|
||||
newMtFeeRate,
|
||||
newI,
|
||||
newK,
|
||||
baseOutAmount,
|
||||
quoteOutAmount
|
||||
);
|
||||
}
|
||||
_withdraw(msg.sender, IDODOV2(DPPAddress)._BASE_TOKEN_(), baseOutAmount, flag == 3);
|
||||
_withdraw(msg.sender, IDODOV2(DPPAddress)._QUOTE_TOKEN_(), quoteOutAmount, flag == 4);
|
||||
}
|
||||
|
||||
function dodoSwapETHToToken(
|
||||
@@ -343,28 +269,38 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256[] memory directions,
|
||||
uint256 deadline
|
||||
) external virtual override payable judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, 'DODOV2Proxy01: Min return should be bigger than 0.');
|
||||
require(msg.value == fromTokenAmount, 'DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH');
|
||||
uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender);
|
||||
|
||||
require(msg.value == fromTokenAmount, "DODOV2Proxy01: ETH_AMOUNT_NOT_MATCH");
|
||||
IWETH(_WETH_).deposit{value: fromTokenAmount}();
|
||||
IWETH(_WETH_).transfer(dodoPairs[0],IWETH(_WETH_).balanceOf(address(this)));
|
||||
IWETH(_WETH_).transfer(dodoPairs[0], fromTokenAmount);
|
||||
|
||||
for (uint256 i = 0; i < dodoPairs.length; i++) {
|
||||
if(i == dodoPairs.length - 1){
|
||||
if (i == dodoPairs.length - 1) {
|
||||
if (directions[i] == 0) {
|
||||
returnAmount = IDODOV2(dodoPairs[i]).sellBase(assetTo);
|
||||
IDODOV2(dodoPairs[i]).sellBase(assetTo);
|
||||
} else {
|
||||
returnAmount = IDODOV2(dodoPairs[i]).sellQuote(assetTo);
|
||||
IDODOV2(dodoPairs[i]).sellQuote(assetTo);
|
||||
}
|
||||
} else {
|
||||
if (directions[i] == 0) {
|
||||
IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i+1]);
|
||||
IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
|
||||
} else {
|
||||
IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i+1]);
|
||||
IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
require(returnAmount >= minReturnAmount, 'DODOV2Proxy01: Return amount is not enough');
|
||||
emit OrderHistory(ETH_ADDRESS, toToken, assetTo, fromTokenAmount, returnAmount, block.timestamp);
|
||||
|
||||
returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance);
|
||||
require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough");
|
||||
emit OrderHistory(
|
||||
ETH_ADDRESS,
|
||||
toToken,
|
||||
assetTo,
|
||||
fromTokenAmount,
|
||||
returnAmount,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
function dodoSwapTokenToETH(
|
||||
@@ -376,11 +312,10 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256[] memory directions,
|
||||
uint256 deadline
|
||||
) external virtual override judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, 'DODOV2Proxy01: Min return should be bigger than 0.');
|
||||
IDODOV2(dodoApprove).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount);
|
||||
|
||||
for (uint256 i = 0; i < dodoPairs.length; i++) {
|
||||
if(i == dodoPairs.length - 1){
|
||||
if (i == dodoPairs.length - 1) {
|
||||
if (directions[i] == 0) {
|
||||
IDODOV2(dodoPairs[i]).sellBase(address(this));
|
||||
} else {
|
||||
@@ -388,20 +323,26 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
}
|
||||
} else {
|
||||
if (directions[i] == 0) {
|
||||
IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i+1]);
|
||||
IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
|
||||
} else {
|
||||
IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i+1]);
|
||||
IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
returnAmount = IWETH(_WETH_).balanceOf(address(this));
|
||||
require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough");
|
||||
IWETH(_WETH_).withdraw(returnAmount);
|
||||
require(returnAmount >= minReturnAmount, 'DODOV2Proxy01: Return amount is not enough');
|
||||
IERC20(ETH_ADDRESS).universalTransfer(assetTo, returnAmount);
|
||||
emit OrderHistory(fromToken, ETH_ADDRESS, assetTo, fromTokenAmount, returnAmount, block.timestamp);
|
||||
assetTo.transfer(returnAmount);
|
||||
emit OrderHistory(
|
||||
fromToken,
|
||||
ETH_ADDRESS,
|
||||
assetTo,
|
||||
fromTokenAmount,
|
||||
returnAmount,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function dodoSwapTokenToToken(
|
||||
address payable assetTo,
|
||||
address fromToken,
|
||||
@@ -412,26 +353,34 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
uint256[] memory directions,
|
||||
uint256 deadline
|
||||
) external virtual override judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, 'DODOV2Proxy01: Min return should be bigger than 0.');
|
||||
uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender);
|
||||
IDODOV2(dodoApprove).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount);
|
||||
|
||||
for (uint256 i = 0; i < dodoPairs.length; i++) {
|
||||
if(i == dodoPairs.length - 1){
|
||||
if (i == dodoPairs.length - 1) {
|
||||
if (directions[i] == 0) {
|
||||
returnAmount = IDODOV2(dodoPairs[i]).sellBase(assetTo);
|
||||
IDODOV2(dodoPairs[i]).sellBase(assetTo);
|
||||
} else {
|
||||
returnAmount = IDODOV2(dodoPairs[i]).sellQuote(assetTo);
|
||||
IDODOV2(dodoPairs[i]).sellQuote(assetTo);
|
||||
}
|
||||
} else {
|
||||
if (directions[i] == 0) {
|
||||
IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i+1]);
|
||||
IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
|
||||
} else {
|
||||
IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i+1]);
|
||||
IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
require(returnAmount >= minReturnAmount, 'DODOV2Proxy01: Return amount is not enough');
|
||||
emit OrderHistory(fromToken, toToken, assetTo, fromTokenAmount, returnAmount, block.timestamp);
|
||||
returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance);
|
||||
require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough");
|
||||
emit OrderHistory(
|
||||
fromToken,
|
||||
toToken,
|
||||
assetTo,
|
||||
fromTokenAmount,
|
||||
returnAmount,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
function externalSwap(
|
||||
@@ -444,21 +393,64 @@ contract DODOV2Proxy01 is IDODOV2Proxy01 {
|
||||
bytes memory callDataConcat,
|
||||
uint256 deadline
|
||||
) external virtual override payable judgeExpired(deadline) returns (uint256 returnAmount) {
|
||||
require(minReturnAmount > 0, 'DODOV2Proxy01: Min return should be bigger then 0.');
|
||||
if (fromToken != ETH_ADDRESS) {
|
||||
IDODOV2(dodoApprove).claimTokens(fromToken, msg.sender, address(this), fromTokenAmount);
|
||||
IERC20(fromToken).universalApprove(approveTarget, fromTokenAmount);
|
||||
IERC20(fromToken).universalApproveMax(approveTarget, fromTokenAmount);
|
||||
}
|
||||
|
||||
(bool success, ) = to.call{value: fromToken == ETH_ADDRESS ? msg.value : 0}(callDataConcat);
|
||||
|
||||
require(success, 'DODOV2Proxy01: Contract Swap execution Failed');
|
||||
require(success, "DODOV2Proxy01: Contract Swap execution Failed");
|
||||
|
||||
IERC20(fromToken).universalTransfer(
|
||||
msg.sender,
|
||||
IERC20(fromToken).universalBalanceOf(address(this))
|
||||
);
|
||||
|
||||
IERC20(fromToken).universalTransfer(msg.sender, IERC20(fromToken).universalBalanceOf(address(this)));
|
||||
returnAmount = IERC20(toToken).universalBalanceOf(address(this));
|
||||
|
||||
require(returnAmount >= minReturnAmount, 'DODOV2Proxy01: Return amount is not enough');
|
||||
require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough");
|
||||
IERC20(toToken).universalTransfer(msg.sender, returnAmount);
|
||||
emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount, block.timestamp);
|
||||
|
||||
emit OrderHistory(
|
||||
fromToken,
|
||||
toToken,
|
||||
msg.sender,
|
||||
fromTokenAmount,
|
||||
returnAmount,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
function _deposit(
|
||||
address from,
|
||||
address to,
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool isETH
|
||||
) internal {
|
||||
if (isETH) {
|
||||
if (amount > 0) {
|
||||
IWETH(_WETH_).deposit{value: amount}();
|
||||
SafeERC20.safeTransfer(IERC20(_WETH_), to, amount);
|
||||
}
|
||||
} else {
|
||||
IDODOV2(dodoApprove).claimTokens(token, from, to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
function _withdraw(
|
||||
address to,
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool isETH
|
||||
) internal {
|
||||
if (isETH) {
|
||||
if (amount > 0) {
|
||||
IWETH(_WETH_).withdraw(amount);
|
||||
msg.sender.transfer(amount);
|
||||
}
|
||||
} else {
|
||||
SafeERC20.safeTransfer(IERC20(token), to, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,25 @@
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
|
||||
import {SafeMath} from "./SafeMath.sol";
|
||||
import {SafeMath} from "../lib/SafeMath.sol";
|
||||
import {IERC20} from "../intf/IERC20.sol";
|
||||
import {SafeERC20} from "./SafeERC20.sol";
|
||||
import {SafeERC20} from "../lib/SafeERC20.sol";
|
||||
|
||||
library UniversalERC20 {
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
IERC20 private constant ZERO_ADDRESS = IERC20(0x0000000000000000000000000000000000000000);
|
||||
IERC20 private constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
|
||||
function isETH(IERC20 token) internal pure returns (bool) {
|
||||
return (token == ZERO_ADDRESS || token == ETH_ADDRESS);
|
||||
return token == ETH_ADDRESS;
|
||||
}
|
||||
|
||||
|
||||
function universalTransfer(IERC20 token, address payable to, uint256 amount) internal {
|
||||
function universalTransfer(
|
||||
IERC20 token,
|
||||
address payable to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
if (amount > 0) {
|
||||
if (isETH(token)) {
|
||||
to.transfer(amount);
|
||||
@@ -33,32 +35,20 @@ library UniversalERC20 {
|
||||
}
|
||||
}
|
||||
|
||||
function universalApprove(IERC20 token, address to, uint256 amount) internal {
|
||||
require(!isETH(token), "ETH Don't need approve");
|
||||
if (amount == 0) {
|
||||
token.safeApprove(to, 0);
|
||||
} else {
|
||||
uint256 allowance = token.allowance(address(this), to);
|
||||
if (allowance < amount) {
|
||||
if (allowance > 0) {
|
||||
token.safeApprove(to, 0);
|
||||
}
|
||||
token.safeApprove(to, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function universalApproveMax(IERC20 token, address to, uint256 amount) internal {
|
||||
function universalApproveMax(
|
||||
IERC20 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
uint256 allowance = token.allowance(address(this), to);
|
||||
if (allowance < amount) {
|
||||
if (allowance > 0) {
|
||||
token.safeApprove(to, 0);
|
||||
}
|
||||
token.safeApprove(to, uint(-1));
|
||||
token.safeApprove(to, uint256(-1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function universalBalanceOf(IERC20 token, address who) internal view returns (uint256) {
|
||||
if (isETH(token)) {
|
||||
return who.balance;
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
*Submitted for verification at Etherscan.io on 2020-10-10
|
||||
*/
|
||||
*/
|
||||
|
||||
// File: contracts/intf/IDODO.sol
|
||||
|
||||
@@ -17,8 +17,8 @@ pragma experimental ABIEncoderV2;
|
||||
import {IDODO} from "../intf/IDODO.sol";
|
||||
import {SafeMath} from "../lib/SafeMath.sol";
|
||||
import {DecimalMath} from "../lib/DecimalMath.sol";
|
||||
// import {DODOMath} from "../lib/DODOMath.sol";
|
||||
|
||||
// import {DODOMath} from "../lib/DODOMath.sol";
|
||||
|
||||
library DODOMath {
|
||||
using SafeMath for uint256;
|
||||
@@ -37,10 +37,10 @@ library DODOMath {
|
||||
uint256 i,
|
||||
uint256 k
|
||||
) internal pure returns (uint256) {
|
||||
uint256 fairAmount = DecimalMath.mul(i, V1.sub(V2)); // i*delta
|
||||
uint256 fairAmount = DecimalMath.mulFloor(i, V1.sub(V2)); // i*delta
|
||||
uint256 V0V0V1V2 = DecimalMath.divCeil(V0.mul(V0).div(V1), V2);
|
||||
uint256 penalty = DecimalMath.mul(k, V0V0V1V2); // k(V0^2/V1/V2)
|
||||
return DecimalMath.mul(fairAmount, DecimalMath.ONE.sub(k).add(penalty));
|
||||
uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
|
||||
return DecimalMath.mulFloor(fairAmount, DecimalMath.ONE.sub(k).add(penalty));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -66,8 +66,8 @@ library DODOMath {
|
||||
) internal pure returns (uint256) {
|
||||
// calculate -b value and sig
|
||||
// -b = (1-k)Q1-kQ0^2/Q1+i*deltaB
|
||||
uint256 kQ02Q1 = DecimalMath.mul(k, Q0).mul(Q0).div(Q1); // kQ0^2/Q1
|
||||
uint256 b = DecimalMath.mul(DecimalMath.ONE.sub(k), Q1); // (1-k)Q1
|
||||
uint256 kQ02Q1 = DecimalMath.mulFloor(k, Q0).mul(Q0).div(Q1); // kQ0^2/Q1
|
||||
uint256 b = DecimalMath.mulFloor(DecimalMath.ONE.sub(k), Q1); // (1-k)Q1
|
||||
bool minusbSig = true;
|
||||
if (deltaBSig) {
|
||||
b = b.add(ideltaB); // (1-k)Q1+i*deltaB
|
||||
@@ -83,9 +83,9 @@ library DODOMath {
|
||||
}
|
||||
|
||||
// calculate sqrt
|
||||
uint256 squareRoot = DecimalMath.mul(
|
||||
uint256 squareRoot = DecimalMath.mulFloor(
|
||||
DecimalMath.ONE.sub(k).mul(4),
|
||||
DecimalMath.mul(k, Q0).mul(Q0)
|
||||
DecimalMath.mulFloor(k, Q0).mul(Q0)
|
||||
); // 4(1-k)kQ0^2
|
||||
squareRoot = b.mul(b).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0)
|
||||
|
||||
@@ -117,15 +117,14 @@ library DODOMath {
|
||||
uint256 fairAmount
|
||||
) internal pure returns (uint256 V0) {
|
||||
// V0 = V1+V1*(sqrt-1)/2k
|
||||
uint256 sqrt = DecimalMath.divCeil(DecimalMath.mul(k, fairAmount).mul(4), V1);
|
||||
uint256 sqrt = DecimalMath.divCeil(DecimalMath.mulFloor(k, fairAmount).mul(4), V1);
|
||||
sqrt = sqrt.add(DecimalMath.ONE).mul(DecimalMath.ONE).sqrt();
|
||||
uint256 premium = DecimalMath.divCeil(sqrt.sub(DecimalMath.ONE), k.mul(2));
|
||||
// V0 is greater than or equal to V1 according to the solution
|
||||
return DecimalMath.mul(V1, DecimalMath.ONE.add(premium));
|
||||
return DecimalMath.mulFloor(V1, DecimalMath.ONE.add(premium));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract DODOSellHelper {
|
||||
using SafeMath for uint256;
|
||||
|
||||
@@ -191,7 +190,7 @@ contract DODOSellHelper {
|
||||
uint256 B2 = DODOMath._SolveQuadraticFunctionForTrade(
|
||||
state.baseTarget,
|
||||
state.baseTarget,
|
||||
DecimalMath.mul(i, amount),
|
||||
DecimalMath.mulFloor(i, amount),
|
||||
false,
|
||||
state.K
|
||||
);
|
||||
@@ -207,7 +206,7 @@ contract DODOSellHelper {
|
||||
uint256 B2 = DODOMath._SolveQuadraticFunctionForTrade(
|
||||
state.baseTarget,
|
||||
state.B,
|
||||
DecimalMath.mul(i, amount),
|
||||
DecimalMath.mulFloor(i, amount),
|
||||
false,
|
||||
state.K
|
||||
);
|
||||
@@ -223,4 +222,4 @@ contract DODOSellHelper {
|
||||
uint256 i = DecimalMath.divFloor(ONE, state.oraclePrice);
|
||||
return DODOMath._GeneralIntegrate(state.quoteTarget, Q1, state.Q, i, state.K);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IDODOV2Proxy01 {
|
||||
function dodoSwapETHToToken(
|
||||
function dodoSwapETHToToken(
|
||||
address payable assetTo,
|
||||
address toToken,
|
||||
uint256 fromTokenAmount,
|
||||
@@ -51,7 +51,6 @@ interface IDODOV2Proxy01 {
|
||||
uint256 deadline
|
||||
) external payable returns (uint256 returnAmount);
|
||||
|
||||
|
||||
function createDODOVendingMachine(
|
||||
address assetTo,
|
||||
address baseToken,
|
||||
@@ -63,29 +62,25 @@ interface IDODOV2Proxy01 {
|
||||
uint256 i,
|
||||
uint256 k,
|
||||
uint256 deadline
|
||||
) external payable returns (address newVendingMachine,uint256 shares);
|
||||
) external payable returns (address newVendingMachine, uint256 shares);
|
||||
|
||||
|
||||
function addDVMLiquidity(
|
||||
address DVMAddress,
|
||||
address to,
|
||||
uint256 baseInAmount,
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseMinAmount,
|
||||
uint256 quoteMinAmount,
|
||||
uint256 deadline
|
||||
) external returns (uint256 shares,uint256 baseAdjustedInAmount,uint256 quoteAdjustedInAmount);
|
||||
|
||||
function addDVMLiquidityETH(
|
||||
address DVMAddress,
|
||||
address to,
|
||||
uint256 baseInAmount,
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseMinAmount,
|
||||
uint256 quoteMinAmount,
|
||||
uint8 flag, // 1 - baseInETH, 2 - quoteInETH
|
||||
uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH
|
||||
uint256 deadline
|
||||
) external payable returns (uint256 shares,uint256 baseAdjustedInAmount,uint256 quoteAdjustedInAmount);
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (
|
||||
uint256 shares,
|
||||
uint256 baseAdjustedInAmount,
|
||||
uint256 quoteAdjustedInAmount
|
||||
);
|
||||
|
||||
function createDODOPrivatePool(
|
||||
address baseToken,
|
||||
@@ -100,20 +95,6 @@ interface IDODOV2Proxy01 {
|
||||
) external payable returns (address newPrivatePool);
|
||||
|
||||
function resetDODOPrivatePool(
|
||||
address DPPAddress,
|
||||
uint256 newLpFeeRate,
|
||||
uint256 newMtFeeRate,
|
||||
uint256 newI,
|
||||
uint256 newK,
|
||||
uint256 baseInAmount,
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount,
|
||||
uint256 deadline
|
||||
) external;
|
||||
|
||||
|
||||
function resetDODOPrivatePoolETH(
|
||||
address DPPAddress,
|
||||
uint256 newLpFeeRate,
|
||||
uint256 newMtFeeRate,
|
||||
@@ -123,12 +104,11 @@ interface IDODOV2Proxy01 {
|
||||
uint256 quoteInAmount,
|
||||
uint256 baseOutAmount,
|
||||
uint256 quoteOutAmount,
|
||||
uint8 flag, // 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH
|
||||
uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH
|
||||
uint256 deadline
|
||||
) external payable;
|
||||
|
||||
//TODO: addLiquidityToClassical
|
||||
|
||||
//TODO: removeLiquidityToClassical
|
||||
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ library DODOMath {
|
||||
DecimalMath.ONE
|
||||
);
|
||||
// V0 is greater than or equal to V1 according to the solution
|
||||
return DecimalMath.mul(V1, premium);
|
||||
return DecimalMath.mulFloor(V1, premium);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -103,6 +103,10 @@ library DODOMath {
|
||||
uint256 k
|
||||
) internal pure returns (uint256) {
|
||||
require(V0 > 0, "TARGET_IS_ZERO");
|
||||
if (delta == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (k == DecimalMath.ONE) {
|
||||
// if k==1
|
||||
// Q2=Q1/(1+ideltaBQ1/Q0/Q0)
|
||||
@@ -133,9 +137,9 @@ library DODOMath {
|
||||
bAbs = bAbs.div(DecimalMath.ONE);
|
||||
|
||||
// calculate sqrt
|
||||
uint256 squareRoot = DecimalMath.mul(
|
||||
uint256 squareRoot = DecimalMath.mulFloor(
|
||||
DecimalMath.ONE.sub(k).mul(4),
|
||||
DecimalMath.mul(k, V0).mul(V0)
|
||||
DecimalMath.mulFloor(k, V0).mul(V0)
|
||||
); // 4(1-k)kQ0^2
|
||||
squareRoot = bAbs.mul(bAbs).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0)
|
||||
|
||||
|
||||
@@ -22,10 +22,6 @@ library DecimalMath {
|
||||
uint256 constant ONE = 10**18;
|
||||
uint256 constant ONE2 = 10**36;
|
||||
|
||||
function mul(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return target.mul(d) / (10**18);
|
||||
}
|
||||
|
||||
function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
|
||||
return target.mul(d) / (10**18);
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 DODO ZOO.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.6.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
library ExternalCall {
|
||||
// Source: https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol
|
||||
// call has been separated into its own function in order to take advantage
|
||||
// of the Solidity's code generator to produce a loop that copies tx.data into memory.
|
||||
function externalCall(address destination, uint value, bytes memory data, uint dataOffset, uint dataLength, uint gasLimit) internal returns(bool result) {
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
if (gasLimit == 0) {
|
||||
gasLimit = gasleft() - 40000;
|
||||
}
|
||||
assembly {
|
||||
let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
|
||||
let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
|
||||
result := call(
|
||||
gasLimit,
|
||||
destination,
|
||||
value,
|
||||
add(d, dataOffset),
|
||||
dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
|
||||
x,
|
||||
0 // Output is ignored, therefore the output size is zero
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,12 +234,12 @@ library PMMPricing {
|
||||
function getMidPrice(PMMState memory state) internal pure returns (uint256 midPrice) {
|
||||
if (state.R == RState.BELOW_ONE) {
|
||||
uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q);
|
||||
R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mul(state.K, R));
|
||||
R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
|
||||
return DecimalMath.divFloor(state.i, R);
|
||||
} else {
|
||||
uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B);
|
||||
R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mul(state.K, R));
|
||||
return DecimalMath.mul(state.i, R);
|
||||
R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
|
||||
return DecimalMath.mulFloor(state.i, R);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,155 +2,155 @@ const fs = require("fs");
|
||||
const file = fs.createWriteStream("../deploy-detail.txt");
|
||||
let logger = new console.Console(file, file);
|
||||
|
||||
const SmartApprove = artifacts.require("SmartApprove");
|
||||
const SmartSwap = artifacts.require("SmartSwap");
|
||||
const DODOSellHelper = artifacts.require("DODOSellHelper");
|
||||
const TestERC20 = artifacts.require("TestERC20");
|
||||
const NaiveOracle = artifacts.require("NaiveOracle");
|
||||
const DODOZoo = artifacts.require("DODOZoo");
|
||||
// const SmartApprove = artifacts.require("DODOApprove");
|
||||
// const SmartSwap = artifacts.require("SmartSwap");
|
||||
// const DODOSellHelper = artifacts.require("DODOSellHelper");
|
||||
// const TestERC20 = artifacts.require("TestERC20");
|
||||
// const NaiveOracle = artifacts.require("NaiveOracle");
|
||||
// const DODOZoo = artifacts.require("DODOZoo");
|
||||
|
||||
const DEPLOY_ROUTE = true;
|
||||
const DEPLOY_ROUTE = false;
|
||||
const DEPLOY_KOVAN_TOKEN = false;
|
||||
|
||||
module.exports = async (deployer, network, accounts) => {
|
||||
let DODOSellHelperAddress = "";
|
||||
let DODOZooAddress = "";
|
||||
let WETHAddress = "";
|
||||
let SmartApproveAddress = "";
|
||||
if (network == "kovan") {
|
||||
DODOSellHelperAddress = "0xbdEae617F2616b45DCB69B287D52940a76035Fe3";
|
||||
DODOZooAddress = "0x92230e929a2226b29ed3441ae5524886347c60c8";
|
||||
WETHAddress = "0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b";
|
||||
SmartApproveAddress = "0x5627b7DEb3055e1e899003FDca0716b32C382084";
|
||||
} else if (network == "live") {
|
||||
DODOSellHelperAddress = "0x533da777aedce766ceae696bf90f8541a4ba80eb";
|
||||
DODOZooAddress = "0x3a97247df274a17c59a3bd12735ea3fcdfb49950";
|
||||
WETHAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
|
||||
SmartApproveAddress = "0xe380Ad3181A69BF92133D2feb609867c4adC61eA";
|
||||
} else return;
|
||||
// let DODOSellHelperAddress = "";
|
||||
// let DODOZooAddress = "";
|
||||
// let WETHAddress = "";
|
||||
// let SmartApproveAddress = "";
|
||||
// if (network == "kovan") {
|
||||
// DODOSellHelperAddress = "0xbdEae617F2616b45DCB69B287D52940a76035Fe3";
|
||||
// DODOZooAddress = "0x92230e929a2226b29ed3441ae5524886347c60c8";
|
||||
// WETHAddress = "0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b";
|
||||
// SmartApproveAddress = "0x5627b7DEb3055e1e899003FDca0716b32C382084";
|
||||
// } else if (network == "live") {
|
||||
// DODOSellHelperAddress = "0x533da777aedce766ceae696bf90f8541a4ba80eb";
|
||||
// DODOZooAddress = "0x3a97247df274a17c59a3bd12735ea3fcdfb49950";
|
||||
// WETHAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
|
||||
// SmartApproveAddress = "0xe380Ad3181A69BF92133D2feb609867c4adC61eA";
|
||||
// } else return;
|
||||
|
||||
logger.log("====================================================");
|
||||
logger.log("network type: " + network);
|
||||
logger.log("Deploy time: " + new Date().toLocaleString());
|
||||
// logger.log("====================================================");
|
||||
// logger.log("network type: " + network);
|
||||
// logger.log("Deploy time: " + new Date().toLocaleString());
|
||||
|
||||
if (DEPLOY_ROUTE) {
|
||||
logger.log("Deploy type: Smart Route");
|
||||
if (SmartApproveAddress == "") {
|
||||
await deployer.deploy(SmartApprove);
|
||||
SmartApproveAddress = SmartApprove.address;
|
||||
}
|
||||
if (DODOSellHelperAddress == "") {
|
||||
await deployer.deploy(DODOSellHelper);
|
||||
DODOSellHelperAddress = DODOSellHelper.address;
|
||||
}
|
||||
logger.log("SmartApprove Address: ", SmartApproveAddress);
|
||||
logger.log("DODOSellHelper Address: ", DODOSellHelperAddress);
|
||||
await deployer.deploy(
|
||||
SmartSwap,
|
||||
SmartApproveAddress,
|
||||
DODOSellHelperAddress,
|
||||
WETHAddress
|
||||
);
|
||||
logger.log("SmartSwap Address: ", SmartSwap.address);
|
||||
// if (DEPLOY_ROUTE) {
|
||||
// logger.log("Deploy type: Smart Route");
|
||||
// if (SmartApproveAddress == "") {
|
||||
// await deployer.deploy(SmartApprove);
|
||||
// SmartApproveAddress = SmartApprove.address;
|
||||
// }
|
||||
// if (DODOSellHelperAddress == "") {
|
||||
// await deployer.deploy(DODOSellHelper);
|
||||
// DODOSellHelperAddress = DODOSellHelper.address;
|
||||
// }
|
||||
// logger.log("SmartApprove Address: ", SmartApproveAddress);
|
||||
// logger.log("DODOSellHelper Address: ", DODOSellHelperAddress);
|
||||
// await deployer.deploy(
|
||||
// SmartSwap,
|
||||
// SmartApproveAddress,
|
||||
// DODOSellHelperAddress,
|
||||
// WETHAddress
|
||||
// );
|
||||
// logger.log("SmartSwap Address: ", SmartSwap.address);
|
||||
|
||||
// const SmartApproveInstance = await SmartApprove.at(SmartApproveAddress);
|
||||
// var tx = await SmartApproveInstance.setSmartSwap(SmartSwap.address);
|
||||
// logger.log("SmartApprovce setSmartSwap tx: ", tx.tx);
|
||||
}
|
||||
// // const SmartApproveInstance = await SmartApprove.at(SmartApproveAddress);
|
||||
// // var tx = await SmartApproveInstance.setSmartSwap(SmartSwap.address);
|
||||
// // logger.log("SmartApprovce setSmartSwap tx: ", tx.tx);
|
||||
// }
|
||||
|
||||
if (DEPLOY_KOVAN_TOKEN) {
|
||||
logger.log("Deploy type: Create Tokens and Trading Pairs");
|
||||
await deployer.deploy(TestERC20, "USDC", 6, "USDC");
|
||||
const USDCAddr = TestERC20.address;
|
||||
logger.log("USDC Addr: ", USDCAddr);
|
||||
await deployer.deploy(TestERC20, "USDT", 6, "USDT");
|
||||
const USDTAddr = TestERC20.address;
|
||||
logger.log("USDT Addr: ", USDTAddr);
|
||||
await deployer.deploy(TestERC20, "DODO", 18, "DODO");
|
||||
const DODOAddr = TestERC20.address;
|
||||
logger.log("DODO Addr: ", DODOAddr);
|
||||
await deployer.deploy(TestERC20, "WOO", 18, "WOO");
|
||||
const WooAddr = TestERC20.address;
|
||||
logger.log("WOO Addr: ", WooAddr);
|
||||
const WETHAddr = WETHAddress;
|
||||
logger.log("WETH Addr: ", WETHAddr);
|
||||
// if (DEPLOY_KOVAN_TOKEN) {
|
||||
// logger.log("Deploy type: Create Tokens and Trading Pairs");
|
||||
// await deployer.deploy(TestERC20, "USDC", 6, "USDC");
|
||||
// const USDCAddr = TestERC20.address;
|
||||
// logger.log("USDC Addr: ", USDCAddr);
|
||||
// await deployer.deploy(TestERC20, "USDT", 6, "USDT");
|
||||
// const USDTAddr = TestERC20.address;
|
||||
// logger.log("USDT Addr: ", USDTAddr);
|
||||
// await deployer.deploy(TestERC20, "DODO", 18, "DODO");
|
||||
// const DODOAddr = TestERC20.address;
|
||||
// logger.log("DODO Addr: ", DODOAddr);
|
||||
// await deployer.deploy(TestERC20, "WOO", 18, "WOO");
|
||||
// const WooAddr = TestERC20.address;
|
||||
// logger.log("WOO Addr: ", WooAddr);
|
||||
// const WETHAddr = WETHAddress;
|
||||
// logger.log("WETH Addr: ", WETHAddr);
|
||||
|
||||
let config = {
|
||||
lpFeeRate: "2000000000000000",
|
||||
mtFeeRate: "1000000000000000",
|
||||
k: "100000000000000000",
|
||||
gasPriceLimit: "100000000000",
|
||||
};
|
||||
// let config = {
|
||||
// lpFeeRate: "2000000000000000",
|
||||
// mtFeeRate: "1000000000000000",
|
||||
// k: "100000000000000000",
|
||||
// gasPriceLimit: "100000000000",
|
||||
// };
|
||||
|
||||
const DODOZooInstance = await DODOZoo.at(DODOZooAddress);
|
||||
// const DODOZooInstance = await DODOZoo.at(DODOZooAddress);
|
||||
|
||||
//USDT-USDC
|
||||
await deployer.deploy(NaiveOracle);
|
||||
var USDT_USDC_Oracle = NaiveOracle.address;
|
||||
await DODOZooInstance.breedDODO(
|
||||
accounts[0],
|
||||
USDTAddr,
|
||||
USDCAddr,
|
||||
USDT_USDC_Oracle,
|
||||
config.lpFeeRate,
|
||||
config.mtFeeRate,
|
||||
config.k,
|
||||
config.gasPriceLimit
|
||||
);
|
||||
const USDT_USDC_Addr = await DODOZooInstance.getDODO(USDTAddr, USDCAddr);
|
||||
logger.log("USDT_USDC_Addr:", USDT_USDC_Addr);
|
||||
// //USDT-USDC
|
||||
// await deployer.deploy(NaiveOracle);
|
||||
// var USDT_USDC_Oracle = NaiveOracle.address;
|
||||
// await DODOZooInstance.breedDODO(
|
||||
// accounts[0],
|
||||
// USDTAddr,
|
||||
// USDCAddr,
|
||||
// USDT_USDC_Oracle,
|
||||
// config.lpFeeRate,
|
||||
// config.mtFeeRate,
|
||||
// config.k,
|
||||
// config.gasPriceLimit
|
||||
// );
|
||||
// const USDT_USDC_Addr = await DODOZooInstance.getDODO(USDTAddr, USDCAddr);
|
||||
// logger.log("USDT_USDC_Addr:", USDT_USDC_Addr);
|
||||
|
||||
// DODO-USDT
|
||||
await deployer.deploy(NaiveOracle);
|
||||
var DODO_USDT_Oracle = NaiveOracle.address;
|
||||
await DODOZooInstance.breedDODO(
|
||||
accounts[0],
|
||||
DODOAddr,
|
||||
USDTAddr,
|
||||
DODO_USDT_Oracle,
|
||||
config.lpFeeRate,
|
||||
config.mtFeeRate,
|
||||
config.k,
|
||||
config.gasPriceLimit
|
||||
);
|
||||
const DODO_USDT_Addr = await DODOZooInstance.getDODO(DODOAddr, USDTAddr);
|
||||
logger.log("DODO_USDT_Addr:", DODO_USDT_Addr);
|
||||
// // DODO-USDT
|
||||
// await deployer.deploy(NaiveOracle);
|
||||
// var DODO_USDT_Oracle = NaiveOracle.address;
|
||||
// await DODOZooInstance.breedDODO(
|
||||
// accounts[0],
|
||||
// DODOAddr,
|
||||
// USDTAddr,
|
||||
// DODO_USDT_Oracle,
|
||||
// config.lpFeeRate,
|
||||
// config.mtFeeRate,
|
||||
// config.k,
|
||||
// config.gasPriceLimit
|
||||
// );
|
||||
// const DODO_USDT_Addr = await DODOZooInstance.getDODO(DODOAddr, USDTAddr);
|
||||
// logger.log("DODO_USDT_Addr:", DODO_USDT_Addr);
|
||||
|
||||
// //WETH-USDC
|
||||
await deployer.deploy(NaiveOracle);
|
||||
var WETH_USDC_Oracle = NaiveOracle.address;
|
||||
await DODOZooInstance.breedDODO(
|
||||
accounts[0],
|
||||
WETHAddr,
|
||||
USDCAddr,
|
||||
WETH_USDC_Oracle,
|
||||
config.lpFeeRate,
|
||||
config.mtFeeRate,
|
||||
config.k,
|
||||
config.gasPriceLimit
|
||||
);
|
||||
const WETH_USDC_Addr = await DODOZooInstance.getDODO(WETHAddr, USDCAddr);
|
||||
logger.log("WETH_USDC_Addr:", WETH_USDC_Addr);
|
||||
// // //WETH-USDC
|
||||
// await deployer.deploy(NaiveOracle);
|
||||
// var WETH_USDC_Oracle = NaiveOracle.address;
|
||||
// await DODOZooInstance.breedDODO(
|
||||
// accounts[0],
|
||||
// WETHAddr,
|
||||
// USDCAddr,
|
||||
// WETH_USDC_Oracle,
|
||||
// config.lpFeeRate,
|
||||
// config.mtFeeRate,
|
||||
// config.k,
|
||||
// config.gasPriceLimit
|
||||
// );
|
||||
// const WETH_USDC_Addr = await DODOZooInstance.getDODO(WETHAddr, USDCAddr);
|
||||
// logger.log("WETH_USDC_Addr:", WETH_USDC_Addr);
|
||||
|
||||
//WOO-USDT
|
||||
await deployer.deploy(NaiveOracle);
|
||||
var WOO_USDT_Oracle = NaiveOracle.address;
|
||||
await DODOZooInstance.breedDODO(
|
||||
accounts[0],
|
||||
WooAddr,
|
||||
USDTAddr,
|
||||
WOO_USDT_Oracle,
|
||||
config.lpFeeRate,
|
||||
config.mtFeeRate,
|
||||
config.k,
|
||||
config.gasPriceLimit
|
||||
);
|
||||
const WOO_USDT_Addr = await DODOZooInstance.getDODO(WooAddr, USDTAddr);
|
||||
logger.log("WOO_USDT_Addr:", WOO_USDT_Addr);
|
||||
// //WOO-USDT
|
||||
// await deployer.deploy(NaiveOracle);
|
||||
// var WOO_USDT_Oracle = NaiveOracle.address;
|
||||
// await DODOZooInstance.breedDODO(
|
||||
// accounts[0],
|
||||
// WooAddr,
|
||||
// USDTAddr,
|
||||
// WOO_USDT_Oracle,
|
||||
// config.lpFeeRate,
|
||||
// config.mtFeeRate,
|
||||
// config.k,
|
||||
// config.gasPriceLimit
|
||||
// );
|
||||
// const WOO_USDT_Addr = await DODOZooInstance.getDODO(WooAddr, USDTAddr);
|
||||
// logger.log("WOO_USDT_Addr:", WOO_USDT_Addr);
|
||||
|
||||
//TODO:ing enableBaseDeposit enableQuoteDeposit enableTrading
|
||||
//TODO:ing apporve pair to token
|
||||
//TODO:ing mint to lp
|
||||
//TODO:ing deposit to Base && quote pool
|
||||
}
|
||||
// //TODO:ing enableBaseDeposit enableQuoteDeposit enableTrading
|
||||
// //TODO:ing apporve pair to token
|
||||
// //TODO:ing mint to lp
|
||||
// //TODO:ing deposit to Base && quote pool
|
||||
// }
|
||||
};
|
||||
|
||||
@@ -48,16 +48,19 @@ describe("Trader", () => {
|
||||
});
|
||||
|
||||
describe("trade", () => {
|
||||
// it.only("basic check", async () => {
|
||||
// console.log(await ctx.DVM.methods.getVaultReserve().call())
|
||||
// console.log(await ctx.DVM.methods.getPMMState().call())
|
||||
// console.log(await ctx.DVM.methods.getMidPrice().call())
|
||||
// console.log(await ctx.DVM.methods.querySellQuote(ctx.Deployer, decimalStr("200")).call())
|
||||
// console.log(ctx.BASE.options.address)
|
||||
// console.log(await ctx.DVM.methods._BASE_TOKEN_().call())
|
||||
// console.log(ctx.QUOTE.options.address)
|
||||
// console.log(await ctx.DVM.methods._QUOTE_TOKEN_().call())
|
||||
// })
|
||||
it.only("basic check", async () => {
|
||||
console.log(await ctx.DVM.methods.symbol().call())
|
||||
console.log(await ctx.DVM.methods.decimals().call())
|
||||
console.log(await ctx.DVM.methods.name().call())
|
||||
// console.log(await ctx.DVM.methods.getVaultReserve().call())
|
||||
// console.log(await ctx.DVM.methods.getPMMState().call())
|
||||
// console.log(await ctx.DVM.methods.getMidPrice().call())
|
||||
// console.log(await ctx.DVM.methods.querySellQuote(ctx.Deployer, decimalStr("200")).call())
|
||||
// console.log(ctx.BASE.options.address)
|
||||
// console.log(await ctx.DVM.methods._BASE_TOKEN_().call())
|
||||
// console.log(ctx.QUOTE.options.address)
|
||||
// console.log(await ctx.DVM.methods._QUOTE_TOKEN_().call())
|
||||
})
|
||||
|
||||
// it.only("mannually buy", async () => {
|
||||
// await ctx.QUOTE.methods.transfer(ctx.DVM.options.address, decimalStr("100")).send(ctx.sendParam(lp))
|
||||
|
||||
Reference in New Issue
Block a user