This commit is contained in:
owen05
2021-02-01 18:35:00 +08:00
parent 95717561de
commit 19106c7eae

View File

@@ -16,14 +16,14 @@ import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol";
import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol";
interface IGovernance { interface IGovernance {
function getLockedvDODO(address account) external returns (uint256); function getLockedvDODO(address account) external view returns (uint256);
} }
interface IDODOCirculationHelper { interface IDODOCirculationHelper {
// vDODO 锁仓不算流通 // vDODO 锁仓不算流通
function getCirculation() external returns (uint256); function getCirculation() external view returns (uint256);
function getVDODOWithdrawFeeRatio() external returns (uint256); function getVDODOWithdrawFeeRatio() external view returns (uint256);
} }
contract vDODOToken is InitializableOwnable, ReentrancyGuard { contract vDODOToken is InitializableOwnable, ReentrancyGuard {
@@ -49,7 +49,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard {
// staking reward parameters // staking reward parameters
uint256 public dodoPerBlock; uint256 public dodoPerBlock;
uint256 public constant _SUPERIOR_RATIO_ = 10**17; // 0.1 uint256 public constant _SUPERIOR_RATIO_ = 10**17; // 0.1
uint256 public dodoFeeDestroyRatio; uint256 public dodoFeeBurnRation;
// accounting // accounting
uint256 public alpha = 100 * 10**18; // 100 uint256 public alpha = 100 * 10**18; // 100
@@ -70,7 +70,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard {
event SetCantransfer(bool allowed); event SetCantransfer(bool allowed);
event ChangePerReward(uint256 dodoPerBlock); event ChangePerReward(uint256 dodoPerBlock);
event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); event UpdatedodoFeeBurnRation(uint256 dodoFeeBurnRation);
event Transfer(address indexed from, address indexed to, uint256 amount); event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount);
@@ -90,44 +90,44 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard {
// ============ Constructor ============ // ============ Constructor ============
constructor( constructor(
// address _dodoGov, address dodoGov,
address _dodoToken, address dodoToken,
address _dodoCirculationHelper, address dodoCirculationHelper,
address _dodoApproveProxy, address dodoApproveProxy,
string memory _name, string memory name,
string memory _symbol string memory symbol
) public { ) public {
initOwner(msg.sender); initOwner(msg.sender);
name = _name; name = name;
symbol = _symbol; symbol = symbol;
decimals = 18; decimals = 18;
_DODO_APPROVE_PROXY_ = _dodoApproveProxy; _DODO_APPROVE_PROXY_ = dodoApproveProxy;
// _DOOD_GOV_ = _dodoGov; _DOOD_GOV_ = dodoGov;
_DODO_CIRCULATION_HELPER_ = _dodoCirculationHelper; _DODO_CIRCULATION_HELPER_ = dodoCirculationHelper;
_DODO_TOKEN_ = _dodoToken; _DODO_TOKEN_ = dodoToken;
lastRewardBlock = block.number; lastRewardBlock = block.number;
} }
// ============ Ownable Functions ============` // ============ Ownable Functions ============`
function setCantransfer(bool _allowed) public onlyOwner { function setCantransfer(bool allowed) public onlyOwner {
_CAN_TRANSFER_ = _allowed; _CAN_TRANSFER_ = allowed;
emit SetCantransfer(_allowed); emit SetCantransfer(allowed);
} }
function changePerReward(uint256 _dodoPerBlock) public onlyOwner { function changePerReward(uint256 dodoPerBlock) public onlyOwner {
_updateAlpha(); _updateAlpha(getLatestAlpha());
dodoPerBlock = _dodoPerBlock; dodoPerBlock = dodoPerBlock;
emit ChangePerReward(dodoPerBlock); emit ChangePerReward(dodoPerBlock);
} }
function updateDodoFeeDestroyRatio(uint256 _dodoFeeDestroyRatio) public onlyOwner { function updatedodoFeeBurnRation(uint256 dodoFeeBurnRation) public onlyOwner {
dodoFeeDestroyRatio = _dodoFeeDestroyRatio; dodoFeeBurnRation = dodoFeeBurnRation;
emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); emit UpdatedodoFeeBurnRation(dodoFeeBurnRation);
} }
function updateDODOCirculationHelper(address _helper) public onlyOwner { function updateDODOCirculationHelper(address helper) public onlyOwner {
_DODO_CIRCULATION_HELPER_ = _helper; _DODO_CIRCULATION_HELPER_ = helper;
} }
function updateGovernance(address _governance) public onlyOwner { function updateGovernance(address _governance) public onlyOwner {
_DOOD_GOV_ = _governance; _DOOD_GOV_ = _governance;
@@ -135,66 +135,78 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard {
// ============ Functions ============ // ============ Functions ============
function mint(uint256 _dodoAmount, address _superiorAddress) public preventReentrant { function mint(uint256 dodoAmount, address superiorAddress) public preventReentrant {
require(_dodoAmount > 0, "vDODOToken: must deposit greater than 0"); require(dodoAmount > 0, "vDODOToken: must mint greater than 0");
IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(
_DODO_TOKEN_, _DODO_TOKEN_,
msg.sender, msg.sender,
address(this), address(this),
_dodoAmount dodoAmount
); );
uint256 newAlpha = getLatestAlpha();
_updateAlpha(); uint256 newVdodoAmount = DecimalMath.divFloor(dodoAmount, newAlpha);
uint256 newVdodoAmount = DecimalMath.divFloor(_dodoAmount, alpha);
UserInfo storage user = userInfo[msg.sender]; UserInfo storage user = userInfo[msg.sender];
_mint(user, newVdodoAmount); _mint(user, newVdodoAmount);
uint256 superiorVDODO; uint256 superiorVDODO;
if (user.superior == address(0) && _superiorAddress != address(0) && _superiorAddress != msg.sender ) { if (user.superior == address(0) && superiorAddress != address(0)) {
require(_superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR"); require(superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR");
superiorVDODO = DecimalMath.divFloor(user.VDODOAmount, _SUPERIOR_RATIO_); superiorVDODO = DecimalMath.mulFloor(user.VDODOAmount, _SUPERIOR_RATIO_);
user.superior = _superiorAddress; user.superior = superiorAddress;
} else if (user.superior != address(0)) { } else if (user.superior != address(0)) {
superiorVDODO = DecimalMath.divFloor(newVdodoAmount, _SUPERIOR_RATIO_); superiorVDODO = DecimalMath.mulFloor(newVdodoAmount, _SUPERIOR_RATIO_);
} }
_mintToSuperior(user, superiorVDODO); _mintToSuperior(user, superiorVDODO);
_updateAlpha(newAlpha);
emit Deposit(msg.sender, _superiorAddress, _dodoAmount); emit Deposit(msg.sender, superiorAddress, dodoAmount);
} }
function redeem(uint256 _vDodoAmount) function redeem(uint256 vDodoAmount)
public public
preventReentrant preventReentrant
balanceEnough(msg.sender, _vDodoAmount) balanceEnough(msg.sender, vDodoAmount)
{ {
_updateAlpha(); uint256 newAlpha = getLatestAlpha();
UserInfo storage user = userInfo[msg.sender]; UserInfo storage user = userInfo[msg.sender];
_redeem(user, _vDodoAmount); _redeem(user, vDodoAmount);
if (user.superior != address(0)) { if (user.superior != address(0)) {
uint256 superiorRedeemVDODO = DecimalMath.divFloor(_vDodoAmount, _SUPERIOR_RATIO_); uint256 superiorRedeemVDODO = DecimalMath.mulFloor(vDodoAmount, _SUPERIOR_RATIO_);
_redeemFromSuperior(user, superiorRedeemVDODO); _redeemFromSuperior(user, superiorRedeemVDODO);
} }
(uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(_vDodoAmount); (uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(vDodoAmount);
IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive);
_transfer(address(this), address(0), destroyDodoAmount); _transfer(address(this), address(0), burnDodoAmount);
tmpAlpha = tmpAlpha.add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply)); newAlpha = newAlpha.add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply));
_updateAlpha(tmpAlpha); _updateAlpha(newAlpha);
emit Withdraw(msg.sender, _vDodoAmount); emit Withdraw(msg.sender, vDodoAmount);
}
function donate(uint256 dodoAmount) public {
IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(
_DODO_TOKEN_,
msg.sender,
address(this),
dodoAmount
);
alpha = alpha.add(DecimalMath.divFloor(dodoAmount, totalSupply));
} }
// ============ Functions(ERC20) ============ // ============ Functions(ERC20) ============
function balanceOf(address account) public view returns (uint256 balance) { function balanceOf(address account) public view returns (uint256 balance) {
UserInfo memory user = userInfo[account]; UserInfo memory user = userInfo[account];
balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, alpha)); balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, getLatestAlpha()));
} }
function availableBalanceOf(address account) public returns (uint256 balance) { function availableBalanceOf(address account) public returns (uint256 balance) {
@@ -231,56 +243,82 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard {
// ============ View Functions ============ // ============ View Functions ============
function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { function canWithdraw(address account) public view returns (uint256 dodoAmount) {
UserInfo memory user = userInfo[_address]; UserInfo memory user = userInfo[account];
withDrawAmount = user.VDODOAmount.mul(alpha).sub(user.credit); dodoAmount = DecimalMath.mulFloor(user.VDODOAmount,getLatestAlpha()).sub(user.credit);
} }
// ============ internal function ============ function getLatestAlpha() public view returns(uint256) {
function _updateAlpha() internal {
uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock));
if (totalSupply > 0) { if (totalSupply > 0) {
alpha = alpha.add(DecimalMath.divFloor(accuDODO, totalSupply)); return alpha.add(DecimalMath.divFloor(accuDODO, totalSupply));
} else {
return alpha;
} }
}
function getWithdrawAmount(uint256 vDodoAmount) public view returns(uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) {
uint256 feeRatio = IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio();
uint256 newAlpha = getLatestAlpha();
uint256 withdrawDodoAmount = DecimalMath.mulFloor(vDodoAmount, newAlpha);
withdrawFeeDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio);
dodoReceive = withdrawDodoAmount.sub(withdrawFeeDodoAmount);
if(dodoFeeBurnRation > 0){
burnDodoAmount = DecimalMath.mulFloor(withdrawFeeDodoAmount,dodoFeeBurnRation);
withdrawFeeDodoAmount = withdrawFeeDodoAmount.sub(burnDodoAmount);
}else {
burnDodoAmount = 0;
}
}
// ============ Internal Functions ============
function _updateAlpha(uint256 newAlpha) internal {
alpha = newAlpha;
lastRewardBlock = block.number; lastRewardBlock = block.number;
} }
function _mint(UserInfo storage to, uint256 amount) internal { function _mint(UserInfo storage to, uint256 vdodoAmount) internal {
to.VDODOAmount = to.VDODOAmount.add(amount); to.VDODOAmount = to.VDODOAmount.add(vdodoAmount);
totalSupply = totalSupply.add(amount); totalSupply = totalSupply.add(vdodoAmount);
} }
function _mintToSuperior(UserInfo storage user, uint256 vdodoAmount) internal { function _mintToSuperior(UserInfo storage user, uint256 vdodoAmount) internal {
if (vdodoAmount > 0) { if (vdodoAmount > 0) {
uint256 newAlpha = getLatestAlpha();
user.superiorVDODO = user.superiorVDODO.add(vdodoAmount); user.superiorVDODO = user.superiorVDODO.add(vdodoAmount);
UserInfo storage superiorUser = userInfo[user.superior]; UserInfo storage superiorUser = userInfo[user.superior];
_mint(superiorUser, vdodoAmount); _mint(superiorUser, vdodoAmount);
superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(vdodoAmount, alpha)); uint256 dodoAmount = DecimalMath.mulFloor(vdodoAmount, newAlpha);
superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(dodoAmount, _SUPERIOR_RATIO_));
} }
} }
function _redeem(UserInfo storage from, uint256 amount) internal { function _redeem(UserInfo storage from, uint256 vdodoAmount) internal {
from.VDODOAmount = from.VDODOAmount.sub(amount); from.VDODOAmount = from.VDODOAmount.sub(vdodoAmount);
totalSupply = totalSupply.sub(amount); totalSupply = totalSupply.sub(vdodoAmount);
} }
function _redeemFromSuperior(UserInfo storage user, uint256 vdodoAmount) internal { function _redeemFromSuperior(UserInfo storage user, uint256 vdodoAmount) internal {
if (vdodoAmount > 0) { if (vdodoAmount > 0) {
// 最多撤销当时给superior的 uint256 newAlpha = getLatestAlpha();
vdodoAmount = user.superiorVDODO <= vdodoAmount ? user.superiorVDODO : vdodoAmount; vdodoAmount = user.superiorVDODO <= vdodoAmount ? user.superiorVDODO : vdodoAmount;
user.superiorVDODO = user.superiorVDODO.sub(vdodoAmount); user.superiorVDODO = user.superiorVDODO.sub(vdodoAmount);
// 最多撤销superior的全部credit
UserInfo storage superiorUser = userInfo[user.superior]; UserInfo storage superiorUser = userInfo[user.superior];
uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, alpha); uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, newAlpha);
if (vdodoAmount >= creditVDODO) { if (vdodoAmount >= creditVDODO) {
superiorUser.credit = 0; superiorUser.credit = 0;
_redeem(superiorUser, creditVDODO); _redeem(superiorUser, creditVDODO);
} else { } else {
superiorUser.credit = superiorUser.credit.sub( superiorUser.credit = superiorUser.credit.sub(
DecimalMath.mulFloor(vdodoAmount, alpha) DecimalMath.mulFloor(vdodoAmount, newAlpha)
); );
_redeem(superiorUser, vdodoAmount); _redeem(superiorUser, vdodoAmount);
} }
@@ -301,47 +339,16 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard {
UserInfo storage toUser = userInfo[to]; UserInfo storage toUser = userInfo[to];
toUser.VDODOAmount = toUser.VDODOAmount.add(_amount); toUser.VDODOAmount = toUser.VDODOAmount.add(_amount);
uint256 superiorRedeemVDODO = DecimalMath.divFloor(_amount, _SUPERIOR_RATIO_); uint256 superiorRedeemVDODO = DecimalMath.mulFloor(_amount, _SUPERIOR_RATIO_);
address fromSuperiorAddr = fromUser.superior; if (fromUser.superior != address(0)) {
if (fromSuperiorAddr != address(0)) {
_redeemFromSuperior(fromUser, superiorRedeemVDODO); _redeemFromSuperior(fromUser, superiorRedeemVDODO);
}
address toSuperiorAddr = toUser.superior; if (toUser.superior != address(0)) {
if (toSuperiorAddr != address(0)) {
_mintToSuperior(toUser, superiorRedeemVDODO); _mintToSuperior(toUser, superiorRedeemVDODO);
} }
emit Transfer(from, to, _amount); emit Transfer(from, to, _amount);
}
function getWithdrawAmount(uint256 vDodoAmount) public view returns(uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeDodoAmount) {
uint256 feeRatio =
IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio();
uint256 tmpAlpha = getAlpha();
uint256 withdrawDodoAmount = DecimalMath.mulFloor(_vDodoAmount, tmpAlpha);
withdrawFeeDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio);
dodoReceive = withdrawDodoAmount.sub(withdrawFeeDodoAmount);
if(dodoFeeDestroyRatio > 0){
destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100);
withdrawFeeDodoAmount = withdrawFeeDodoAmount.sub(destroyDodoAmount);
}else {
destroyDodoAmount = 0;
}
// ============ internal function ============
function _updateAlpha(uint256 newAlpha) internal {
alpha = newAlpha;
lastRewardBlock = block.number;
}
function donate(uint256 amount) public {
IERC20(_DODO_TOKEN_).transferFrom(msg.sender, address(this), amount);
alpha = alpha.add(DecimalMath.divFloor(amount, totalSupply));
} }
} }