init test env

This commit is contained in:
mingda
2020-10-26 19:19:10 +08:00
parent f36410f8f1
commit 081c0ecde4
26 changed files with 147 additions and 55 deletions

View File

@@ -0,0 +1,19 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {DVMStorage} from "./DVMStorage.sol";
contract DVMAdmin is DVMStorage{
function setI(uint256 newI) external onlyOwner{}
function setK(uint256 newK) external onlyOwner{}
}

View File

@@ -0,0 +1,39 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {DVMTrader} from "./DVMTrader.sol";
import {DVMFunding} from "./DVMFunding.sol";
import {DVMAdmin} from "./DVMAdmin.sol";
import {DVMVault} from "./DVMVault.sol";
import {IFeeRateModel} from "../../intf/IFeeRateModel.sol";
contract DVMController is DVMTrader, DVMFunding, DVMAdmin {
function init(
address owner,
address maintainer,
address vault,
address lpFeeRateModel,
address mtFeeRateModel,
uint256 i,
uint256 k,
uint256 gasPriceLimit
) external {
initOwner(owner);
_MAINTAINER_ = maintainer;
_VAULT_ = DVMVault(vault);
_BASE_TOKEN_ = _VAULT_._BASE_TOKEN_();
_QUOTE_TOKEN_ = _VAULT_._QUOTE_TOKEN_();
_LP_FEE_RATE_MODEL_ = IFeeRateModel(lpFeeRateModel);
_MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel);
_I_ = i;
_K_ = k;
_GAS_PRICE_LIMIT_ = gasPriceLimit;
}
}

View File

@@ -0,0 +1,63 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {DVMStorage} from "./DVMStorage.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
contract DVMFunding is DVMStorage {
function buyShares(address account) external returns (uint256) {
uint256 baseInput = _VAULT_.getBaseInput();
uint256 quoteInput = _VAULT_.getQuoteInput();
require(baseInput > 0, "NO_BASE_INPUT");
uint256 baseReserve = _VAULT_._BASE_RESERVE_();
uint256 quoteReserve = _VAULT_._QUOTE_RESERVE_();
uint256 mintAmount;
// case 1. initial supply
if (baseReserve == 0 && quoteReserve == 0) {
mintAmount = baseInput;
}
// // case 2. supply when quote reserve is 0
// if (baseReserve > 0 && quoteReserve == 0) {
// uint256 mintRatio = DecimalMath.divFloor(baseInput, baseReserve);
// mintAmount = DecimalMath.mulFloor(_VAULT_.totalSupply(), mintRatio);
// }
// // case 3. normal case
// if (baseReserve > 0 && quoteReserve > 0) {
// uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
// uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
// uint256 mintRatio = baseInputRatio > quoteInputRatio ? quoteInputRatio : baseInputRatio;
// // 在提币的时候向下取整。因此永远不会出现balance为0但totalsupply不为0的情况
// // 但有可能出现reserve>0但totalSupply=0的场景
// uint256 totalShare = _VAULT_.totalSupply();
// if (totalShare > 0) {
// mintAmount = DecimalMath.mulFloor(totalShare, mintRatio);
// } else {
// mintAmount = baseInput;
// }
// }
// _VAULT_.mint(account, mintAmount);
// _VAULT_.sync();
}
function sellShares(
address account,
address to,
uint256 amount
) external returns (uint256) {
require(msg.sender == account, "PERMISSION_DENY");
require(_VAULT_.balanceOf(account) >= amount, "SHARES_NOT_ENOUGH");
(uint256 baseBalance, uint256 quoteBalance) = _VAULT_.getVaultBalance();
uint256 totalShares = _VAULT_.totalSupply();
_VAULT_.burn(account, amount);
_VAULT_.transferBaseOut(to, baseBalance.mul(amount).div(totalShares));
_VAULT_.transferQuoteOut(to, quoteBalance.mul(amount).div(totalShares));
_VAULT_.sync();
}
}

View File

@@ -0,0 +1,75 @@
/*
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 {SafeMath} from "../../lib/SafeMath.sol";
import {DODOMath} from "../../lib/DODOMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {PermissionManager} from "../../lib/PermissionManager.sol";
import {IFeeRateModel} from "../../intf/IFeeRateModel.sol";
import {DVMVault} from "./DVMVault.sol";
contract DVMStorage is InitializableOwnable, ReentrancyGuard {
using SafeMath for uint256;
// ============ Variables for Control ============
bool public _CLOSED_;
uint256 public _GAS_PRICE_LIMIT_;
// ============ Advanced Controls ============
bool public _BUYING_ALLOWED_;
bool public _SELLING_ALLOWED_;
PermissionManager public _TRADE_PERMISSION_;
PermissionManager public _FUNDING_PERMISSION_;
// ============ Core Address ============
address public _MAINTAINER_; // collect maintainer fee
address public _BASE_TOKEN_;
address public _QUOTE_TOKEN_;
// ============ Variables for Pricing ============
IFeeRateModel public _LP_FEE_RATE_MODEL_;
IFeeRateModel public _MT_FEE_RATE_MODEL_;
uint256 public _K_;
uint256 public _I_;
uint256 public _BASE0_;
DVMVault public _VAULT_;
DVMVault public _PROTECTION_VAULT_;
// ============ Modifiers ============
modifier notClosed() {
require(!_CLOSED_, "DODO_CLOSED");
_;
}
// ============ Helper Functions ============
function _updateBase0() internal {
uint256 fairAmount = DecimalMath.divFloor(_VAULT_._QUOTE_RESERVE_(), _I_);
_BASE0_ = DODOMath._SolveQuadraticFunctionForTarget(
_VAULT_._BASE_RESERVE_(),
_K_,
fairAmount
);
}
// ============ Version Control ============
function version() external pure returns (uint256) {
return 101; // 1.0.1
}
}

View File

@@ -0,0 +1,82 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {DVMStorage} from "./DVMStorage.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {DODOMath} from "../../lib/DODOMath.sol";
contract DVMTrader is DVMStorage {
using SafeMath for uint256;
function sellBase(address to) external returns (uint256 receiveQuoteAmount) {
uint256 baseInput = _VAULT_.getBaseInput();
uint256 mtFee;
(receiveQuoteAmount, mtFee) = querySellBase(baseInput);
_VAULT_.transferQuoteOut(to, receiveQuoteAmount);
if (mtFee > 0) {
_VAULT_.transferQuoteOut(_MAINTAINER_, mtFee);
}
_VAULT_.sync();
_updateBase0(); // 这里需要想想原则上不需要update B0. 但精度问题或者用户往合约里充值可能导致需要updateBase0
return receiveQuoteAmount;
}
function sellQuote(address to) external returns (uint256 receiveBaseAmount) {
uint256 quoteInput = _VAULT_.getQuoteInput();
uint256 mtFee;
(receiveBaseAmount, mtFee) = querySellQuote(quoteInput);
_VAULT_.transferBaseOut(to, receiveBaseAmount);
if (mtFee > 0) {
_VAULT_.transferBaseOut(_MAINTAINER_, mtFee);
}
_VAULT_.sync();
_updateBase0();
return receiveBaseAmount;
}
function querySellBase(uint256 payBaseAmount)
public
view
returns (uint256 receiveQuoteAmount, uint256 mtFee)
{
uint256 B2 = _VAULT_._BASE_RESERVE_();
uint256 B1 = B2.add(payBaseAmount);
require(_BASE0_ >= B1, "DODO_BASE_BALANCE_NOT_ENOUGH");
uint256 Q = DODOMath._GeneralIntegrate(_BASE0_, B1, B2, _I_, _K_);
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(Q);
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(Q);
mtFee = DecimalMath.mulCeil(Q, mtFeeRate);
receiveQuoteAmount = Q.sub(mtFee).sub(DecimalMath.mulCeil(Q, lpFeeRate));
return (receiveQuoteAmount, mtFee);
}
function querySellQuote(uint256 payQuoteAmount)
public
view
returns (uint256 receiveBaseAmount, uint256 mtFee)
{
uint256 B1 = _VAULT_._BASE_RESERVE_();
uint256 fairAmount = DecimalMath.divFloor(payQuoteAmount, _I_);
uint256 newBaseReserve = DODOMath._SolveQuadraticFunctionForTrade(
_BASE0_,
B1,
fairAmount,
false,
_K_
);
uint256 deltaBase = B1.sub(newBaseReserve);
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(payQuoteAmount);
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(payQuoteAmount);
mtFee = DecimalMath.mulCeil(deltaBase, mtFeeRate);
receiveBaseAmount = deltaBase.sub(mtFee).sub(DecimalMath.mulCeil(deltaBase, lpFeeRate));
return (receiveBaseAmount, mtFee);
}
}

View File

@@ -0,0 +1,211 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {IERC20} from "../../intf/IERC20.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {SafeERC20} from "../../lib/SafeERC20.sol";
import {Ownable} from "../../lib/Ownable.sol";
import {InitializableOwnable} from "../../lib/InitializableOwnable.sol";
contract DVMVault is InitializableOwnable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
address public _BASE_TOKEN_;
address public _QUOTE_TOKEN_;
uint256 public _BASE_RESERVE_;
uint256 public _QUOTE_RESERVE_;
string public symbol;
uint256 public decimals;
string public name;
uint256 public totalSupply;
mapping(address => uint256) internal _SHARES_;
mapping(address => mapping(address => uint256)) internal _ALLOWED_;
// ============ Events ============
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
event Mint(address indexed user, uint256 value);
event Burn(address indexed user, uint256 value);
// init functions
function init(
address owner,
address _baseToken,
address _quoteToken
) public notInitialized {
initOwner(owner);
string memory connect = "_";
string memory suffix = "DLP";
string memory uid = string(abi.encodePacked(address(this)));
name = string(
abi.encodePacked(
suffix,
connect,
IERC20(_baseToken).symbol(),
connect,
IERC20(_quoteToken).symbol(),
connect,
uid
)
);
symbol = "DLP";
decimals = IERC20(_baseToken).decimals();
_BASE_TOKEN_ = _baseToken;
_QUOTE_TOKEN_ = _quoteToken;
}
// Vault related
function getVaultBalance() public view returns (uint256 baseBalance, uint256 quoteBalance) {
return (
IERC20(_BASE_TOKEN_).balanceOf(address(this)),
IERC20(_QUOTE_TOKEN_).balanceOf(address(this))
);
}
function getVaultReserve() public view returns (uint256 baseReserve, uint256 quoteReserve) {
return (_BASE_RESERVE_, _QUOTE_RESERVE_);
}
function getBaseBalance() public view returns (uint256 baseBalance) {
return IERC20(_BASE_TOKEN_).balanceOf(address(this));
}
function getQuoteBalance() public view returns (uint256 quoteBalance) {
return IERC20(_QUOTE_TOKEN_).balanceOf(address(this));
}
function getBaseInput() public view returns (uint256 input) {
return IERC20(_BASE_TOKEN_).balanceOf(address(this)).sub(_BASE_RESERVE_);
}
function getQuoteInput() public view returns (uint256 input) {
return IERC20(_QUOTE_TOKEN_).balanceOf(address(this)).sub(_QUOTE_RESERVE_);
}
function sync() public onlyOwner {
(uint256 baseBalance, uint256 quoteBalance) = getVaultBalance();
if (baseBalance != _BASE_RESERVE_) {
_BASE_RESERVE_ = baseBalance;
}
if (quoteBalance != _QUOTE_RESERVE_) {
_QUOTE_RESERVE_ = quoteBalance;
}
}
function transferOut(
address token,
address to,
uint256 amount
) public onlyOwner {
IERC20(token).safeTransfer(to, amount);
}
function transferBaseOut(address to, uint256 amount) public onlyOwner {
IERC20(_BASE_TOKEN_).safeTransfer(to, amount);
}
function transferQuoteOut(address to, uint256 amount) public onlyOwner {
IERC20(_QUOTE_TOKEN_).safeTransfer(to, amount);
}
// Shares related
/**
* @dev transfer token for a specified address
* @param to The address to transfer to.
* @param amount The amount to be transferred.
*/
function transfer(address to, uint256 amount) public returns (bool) {
require(amount <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH");
_SHARES_[msg.sender] = _SHARES_[msg.sender].sub(amount);
_SHARES_[to] = _SHARES_[to].add(amount);
emit Transfer(msg.sender, to, amount);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the the balance of.
* @return balance An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) external view returns (uint256 balance) {
return _SHARES_[owner];
}
function shareRatioOf(address owner) external view returns (uint256 shareRatio) {
return DecimalMath.divFloor(_SHARES_[owner], totalSupply);
}
/**
* @dev Transfer tokens from one address to another
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param amount uint256 the amount of tokens to be transferred
*/
function transferFrom(
address from,
address to,
uint256 amount
) public returns (bool) {
require(amount <= _SHARES_[from], "BALANCE_NOT_ENOUGH");
require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
_SHARES_[from] = _SHARES_[from].sub(amount);
_SHARES_[to] = _SHARES_[to].add(amount);
_ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount);
emit Transfer(from, to, amount);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param spender The address which will spend the funds.
* @param amount The amount of tokens to be spent.
*/
function approve(address spender, uint256 amount) public returns (bool) {
_ALLOWED_[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner _ALLOWED_ to a spender.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _ALLOWED_[owner][spender];
}
function mint(address user, uint256 value) external onlyOwner {
_SHARES_[user] = _SHARES_[user].add(value);
totalSupply = totalSupply.add(value);
emit Mint(user, value);
emit Transfer(address(0), user, value);
}
function burn(address user, uint256 value) external onlyOwner {
_SHARES_[user] = _SHARES_[user].sub(value);
totalSupply = totalSupply.sub(value);
emit Burn(user, value);
emit Transfer(user, address(0), value);
}
}