init test env
This commit is contained in:
19
contracts/DODOVendorMachine/impl/DVMAdmin.sol
Normal file
19
contracts/DODOVendorMachine/impl/DVMAdmin.sol
Normal 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{}
|
||||
|
||||
}
|
||||
39
contracts/DODOVendorMachine/impl/DVMController.sol
Normal file
39
contracts/DODOVendorMachine/impl/DVMController.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
63
contracts/DODOVendorMachine/impl/DVMFunding.sol
Normal file
63
contracts/DODOVendorMachine/impl/DVMFunding.sol
Normal 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();
|
||||
}
|
||||
}
|
||||
75
contracts/DODOVendorMachine/impl/DVMStorage.sol
Normal file
75
contracts/DODOVendorMachine/impl/DVMStorage.sol
Normal 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
|
||||
}
|
||||
}
|
||||
82
contracts/DODOVendorMachine/impl/DVMTrader.sol
Normal file
82
contracts/DODOVendorMachine/impl/DVMTrader.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
211
contracts/DODOVendorMachine/impl/DVMVault.sol
Normal file
211
contracts/DODOVendorMachine/impl/DVMVault.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user