diff --git a/README.md b/README.md index 99ceea7..e14aed9 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,4 @@ Anyone who reports a unique, previously-unreported vulnerability that results in ## Contact Us Send E-mail to contact@dodoex.io + diff --git a/contracts/DODOToken/DODOBscToken.sol b/contracts/DODOToken/DODOBscToken.sol new file mode 100644 index 0000000..66fc2b6 --- /dev/null +++ b/contracts/DODOToken/DODOBscToken.sol @@ -0,0 +1,95 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {SafeMath} from "../lib/SafeMath.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +/** + * @title DODO Token Mapped on BSC + * @author DODO Breeder + */ +contract DODOBscToken is InitializableOwnable { + using SafeMath for uint256; + + string public name = "DODO bird"; + uint256 public decimals = 18; + string public symbol = "DODO"; + uint256 public totalSupply; + + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) internal allowed; + + + 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); + event Redeem(address indexed sender, address indexed redeemToEthAccount, uint256 value); + + function transfer(address to, uint256 amount) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH"); + + balances[msg.sender] = balances[msg.sender].sub(amount); + balances[to] = balances[to].add(amount); + emit Transfer(msg.sender, to, amount); + return true; + } + + function balanceOf(address owner) public view returns (uint256 balance) { + return balances[owner]; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[from], "BALANCE_NOT_ENOUGH"); + require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + + balances[from] = balances[from].sub(amount); + balances[to] = balances[to].add(amount); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + function approve(address spender, uint256 amount) public returns (bool) { + allowed[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return allowed[owner][spender]; + } + + function redeem(uint256 amount, uint256 value, address redeemToEthAccount) external { + require(balances[msg.sender] >= value, "DODOBscToken: NOT_ENOUGH"); + balances[msg.sender] = balances[msg.sender].sub(value); + totalSupply = totalSupply.sub(value); + emit Redeem(msg.sender, redeemToEthAccount, value); + } + + function mint(address user, uint256 value) external onlyOwner { + balances[user] = balances[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 { + balances[user] = balances[user].sub(value); + totalSupply = totalSupply.sub(value); + emit Burn(user, value); + emit Transfer(user, address(0), value); + } +} diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol new file mode 100644 index 0000000..5b57bbd --- /dev/null +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -0,0 +1,88 @@ +/* + + 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 {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + + +contract DODOCirculationHelper is InitializableOwnable { + using SafeMath for uint256; + + // ============ Storage ============ + + address immutable _VDODO_TOKEN_; + address immutable _DODO_TOKEN_; + address[] _LOCKED_CONTRACT_ADDRESS_; + + uint256 public _MIN_PENALTY_RATIO_ = 5 * 10**16; // 5% + uint256 public _MAX_PENALTY_RATIO_ = 15 * 10**16; // 15% + + constructor(address vDodoToken,address dodoToken) public { + _VDODO_TOKEN_ = vDodoToken; + _DODO_TOKEN_ = dodoToken; + } + + function addLockedContractAddress(address lockedContract) external onlyOwner { + require(lockedContract != address(0)); + _LOCKED_CONTRACT_ADDRESS_.push(lockedContract); + } + + function removeLockedContractAddress(address lockedContract) external onlyOwner { + require(lockedContract != address(0)); + address[] memory lockedContractAddress = _LOCKED_CONTRACT_ADDRESS_; + for (uint256 i = 0; i < lockedContractAddress.length; i++) { + if (lockedContractAddress[i] == lockedContract) { + lockedContractAddress[i] = lockedContractAddress[lockedContractAddress.length - 1]; + break; + } + } + _LOCKED_CONTRACT_ADDRESS_ = lockedContractAddress; + _LOCKED_CONTRACT_ADDRESS_.pop(); + } + + function getCirculation() public view returns (uint256 circulation) { + circulation = 10**10 * 10**18; + for (uint256 i = 0; i < _LOCKED_CONTRACT_ADDRESS_.length; i++) { + circulation -= IERC20(_DODO_TOKEN_).balanceOf(_LOCKED_CONTRACT_ADDRESS_[i]); + } + } + + function getVDODOWithdrawFeeRatio() external view returns (uint256 ratio) { + uint256 dodoCirculationAmout = getCirculation(); + uint256 x = + DecimalMath.divCeil( + dodoCirculationAmout, + IERC20(_VDODO_TOKEN_).totalSupply() + ); + + ratio = geRatioValue(x); + } + + function geRatioValue(uint256 input) public view returns (uint256 ratio) { + + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 + // y = 5% (x ≤ 1) + // y = 15% (x ≥ 10) + // y = 15% - 10% * sqrt(1-[(x-1)/9]^2) + + if (input <= 10**18) { + return _MIN_PENALTY_RATIO_; + } else if (input >= 10**19) { + return _MAX_PENALTY_RATIO_; + } else { + uint256 xTemp = input.sub(DecimalMath.ONE).div(9); + uint256 premium = DecimalMath.ONE2.sub(xTemp.mul(xTemp)).sqrt(); + ratio = + _MAX_PENALTY_RATIO_ - + DecimalMath.mulFloor(_MAX_PENALTY_RATIO_ - _MIN_PENALTY_RATIO_, premium); + } + } +} diff --git a/contracts/DODOToken/DODOMigrationBSC.sol b/contracts/DODOToken/DODOMigrationBSC.sol new file mode 100644 index 0000000..90b26f2 --- /dev/null +++ b/contracts/DODOToken/DODOMigrationBSC.sol @@ -0,0 +1,58 @@ +/* + + 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 {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; + +/** + * @title DODOMigration between Ethereum and BSC + * @author DODO Breeder + */ +contract DODOMigrationBSC is InitializableOwnable { + using SafeMath for uint256; + + // ============ Storage ============ + + address immutable _ETH_DODO_TOKEN_; + address immutable _DODO_APPROVE_PROXY_; + mapping(address => uint256) internal balances; + + constructor(address ethDodoToken, address dodoApproveProxy) public { + _ETH_DODO_TOKEN_ = ethDodoToken; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + } + + // ============ Events ============ + + event Lock(address indexed sender, address indexed mintToBscAccount, uint256 amount); + event Unlock(address indexed to, uint256 amount); + + // ============ Functions ============ + + function lock(uint256 amount, address mintToBscAccount) external { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _ETH_DODO_TOKEN_, + msg.sender, + address(this), + amount + ); + balances[msg.sender] = balances[msg.sender].add(amount); + emit Lock(msg.sender, mintToBscAccount, amount); + } + + function unlock(address unlockTo, uint256 amount) external onlyOwner { + require(balances[unlockTo] >= amount); + balances[unlockTo] = balances[unlockTo].sub(amount); + IERC20(_ETH_DODO_TOKEN_).transfer(unlockTo, amount); + emit Unlock(unlockTo, amount); + } + +} diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol new file mode 100644 index 0000000..37a1657 --- /dev/null +++ b/contracts/DODOToken/Governance.sol @@ -0,0 +1,26 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +//todo +contract Governance is InitializableOwnable { + + // ============ Storage ============ + + address _VDODO_TOKEN_; + + function setVDODOAddress(address vodoToken) public onlyOwner{ + _VDODO_TOKEN_ = vodoToken; + } + + function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { + lockedvDODO = 0;//todo for test + } +} diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol new file mode 100644 index 0000000..805a27c --- /dev/null +++ b/contracts/DODOToken/vDODOToken.sol @@ -0,0 +1,361 @@ +/* + + 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 {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; + +interface IGovernance { + function getLockedvDODO(address account) external view returns (uint256); +} + +interface IDODOCirculationHelper { + // Locked vDOOD not counted in circulation + function getCirculation() external view returns (uint256); + + function getVDODOWithdrawFeeRatio() external view returns (uint256); +} + +contract vDODOToken is InitializableOwnable { + using SafeMath for uint256; + + // ============ Storage(ERC20) ============ + + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + mapping(address => mapping(address => uint256)) internal _ALLOWED_; + + // ============ Storage ============ + + address immutable _DODO_TOKEN_; + address immutable _DODO_APPROVE_PROXY_; + address public _DOOD_GOV_; + address public _DODO_CIRCULATION_HELPER_; + + bool public _CAN_TRANSFER_; + + // staking reward parameters + uint256 public dodoPerBlock; + uint256 public constant _SUPERIOR_RATIO_ = 10**17; // 0.1 + uint256 public dodoFeeBurnRation; + + // accounting + uint128 public alpha = 100 * 10**18; // 100 + uint128 public lastRewardBlock; + mapping(address => UserInfo) public userInfo; + + struct UserInfo { + uint128 VDODOAmount; + uint128 superiorVDODO; + address superior; + uint256 credit; + } + + // ============ Events ============ + + event MintVDODO(address user, address superior, uint256 amount); + event RedeemVDODO(address user, uint256 amount); + event SetCantransfer(bool allowed); + + event ChangePerReward(uint256 dodoPerBlock); + event UpdatedodoFeeBurnRation(uint256 dodoFeeBurnRation); + + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); + + // ============ Modifiers ============ + + modifier canTransfer() { + require(_CAN_TRANSFER_, "vDODOToken: not allowed transfer"); + _; + } + + modifier balanceEnough(address account, uint256 amount) { + require(availableBalanceOf(account) >= amount, "vDODOToken: available amount not enough"); + _; + } + + // ============ Constructor ============ + + constructor( + address dodoGov, + address dodoToken, + address dodoCirculationHelper, + address dodoApproveProxy, + string memory _name, + string memory _symbol + ) public { + name = _name; + symbol = _symbol; + decimals = 18; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DOOD_GOV_ = dodoGov; + _DODO_CIRCULATION_HELPER_ = dodoCirculationHelper; + _DODO_TOKEN_ = dodoToken; + lastRewardBlock = uint128(block.number); + } + + // ============ Ownable Functions ============` + + function setCantransfer(bool allowed) public onlyOwner { + _CAN_TRANSFER_ = allowed; + emit SetCantransfer(allowed); + } + + function changePerReward(uint256 _dodoPerBlock) public onlyOwner { + _updateAlpha(); + dodoPerBlock = _dodoPerBlock; + emit ChangePerReward(_dodoPerBlock); + } + + function updatedodoFeeBurnRation(uint256 _dodoFeeBurnRation) public onlyOwner { + dodoFeeBurnRation = _dodoFeeBurnRation; + emit UpdatedodoFeeBurnRation(_dodoFeeBurnRation); + } + + function updateDODOCirculationHelper(address helper) public onlyOwner { + _DODO_CIRCULATION_HELPER_ = helper; + } + + function updateGovernance(address governance) public onlyOwner { + _DOOD_GOV_ = governance; + } + + function emergencyWithdraw() public onlyOwner { + uint256 dodoBalance = IERC20(_DODO_TOKEN_).balanceOf(address(this)); + IERC20(_DODO_TOKEN_).transfer(_OWNER_, dodoBalance); + } + + // ============ Functions ============ + + function mint(uint256 dodoAmount, address superiorAddress) public { + require(superiorAddress != address(0) && superiorAddress != msg.sender, "vDODOToken: Superior INVALID"); + require(dodoAmount > 0, "vDODOToken: must mint greater than 0"); + + _updateAlpha(); + + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _DODO_TOKEN_, + msg.sender, + address(this), + dodoAmount + ); + + uint256 newVdodoAmount = DecimalMath.divFloor(dodoAmount, alpha); + + UserInfo storage user = userInfo[msg.sender]; + _mint(user, newVdodoAmount); + + uint256 increSuperiorVDODO = DecimalMath.mulFloor(newVdodoAmount, _SUPERIOR_RATIO_); + + if (user.superior == address(0)) { + user.superior = superiorAddress; + } + + _mintToSuperior(user, increSuperiorVDODO); + + emit MintVDODO(msg.sender, superiorAddress, dodoAmount); + } + + function redeem(uint256 vDodoAmount) + public + balanceEnough(msg.sender, vDodoAmount) + { + _updateAlpha(); + + UserInfo storage user = userInfo[msg.sender]; + _redeem(user, vDodoAmount); + + if (user.superior != address(0)) { + uint256 superiorRedeemVDODO = DecimalMath.mulFloor(vDodoAmount, _SUPERIOR_RATIO_); + _redeemFromSuperior(user, superiorRedeemVDODO); + } + + (uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(vDodoAmount); + + IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); + + if(burnDodoAmount > 0){ + _transfer(address(this), address(0), burnDodoAmount); + } + + if(withdrawFeeDodoAmount > 0) { + alpha = uint128(uint256(alpha).add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply))); + } + + emit RedeemVDODO(msg.sender, vDodoAmount); + } + + + function donate(uint256 dodoAmount) public { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _DODO_TOKEN_, + msg.sender, + address(this), + dodoAmount + ); + alpha = uint128(uint256(alpha).add(DecimalMath.divFloor(dodoAmount, totalSupply))); + } + + // ============ Functions(ERC20) ============ + + function balanceOf(address account) public view returns (uint256 balance) { + UserInfo memory user = userInfo[account]; + balance = uint256(user.VDODOAmount).sub(DecimalMath.divFloor(user.credit, getLatestAlpha())); + } + + function availableBalanceOf(address account) public view returns (uint256 balance) { + uint256 lockedBalance = IGovernance(_DOOD_GOV_).getLockedvDODO(account); + balance = balanceOf(account).sub(lockedBalance); + } + + function transfer(address to, uint256 amount) public returns (bool) { + _updateAlpha(); + _transfer(msg.sender, to, amount); + return true; + } + + function approve(address spender, uint256 amount) public returns (bool) { + _ALLOWED_[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + _updateAlpha(); + _transfer(from, to, amount); + _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return _ALLOWED_[owner][spender]; + } + + // ============ View Functions ============ + + function dodoBalanceOf(address account) public view returns (uint256 dodoAmount) { + UserInfo memory user = userInfo[account]; + dodoAmount = DecimalMath.mulFloor(uint256(user.VDODOAmount),getLatestAlpha()).sub(user.credit); + } + + function getLatestAlpha() public view returns(uint256) { + uint256 accuDODO = dodoPerBlock * (block.number - lastRewardBlock); + if (totalSupply > 0) { + return uint256(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() internal { + uint256 newAlpha = getLatestAlpha(); + require(newAlpha <= uint128(-1), "OVERFLOW"); + alpha = uint128(newAlpha); + lastRewardBlock = uint128(block.number); + } + + function _mint(UserInfo storage to, uint256 vdodoAmount) internal { + require(vdodoAmount <= uint128(-1), "OVERFLOW"); + to.VDODOAmount = uint128(uint256(to.VDODOAmount).add(vdodoAmount)); + totalSupply = totalSupply.add(vdodoAmount); + } + + function _mintToSuperior(UserInfo storage user, uint256 vdodoAmount) internal { + if (vdodoAmount > 0) { + user.superiorVDODO = uint128(uint256(user.superiorVDODO).add(vdodoAmount)); + UserInfo storage superiorUser = userInfo[user.superior]; + _mint(superiorUser, vdodoAmount); + uint256 dodoAmount = DecimalMath.mulCeil(vdodoAmount, alpha); + superiorUser.credit = superiorUser.credit.add(dodoAmount); + } + } + + function _redeem(UserInfo storage from, uint256 vdodoAmount) internal { + from.VDODOAmount = uint128(uint256(from.VDODOAmount).sub(vdodoAmount)); + totalSupply = totalSupply.sub(vdodoAmount); + } + + function _redeemFromSuperior(UserInfo storage user, uint256 vdodoAmount) internal { + if (vdodoAmount > 0) { + vdodoAmount = user.superiorVDODO <= vdodoAmount ? user.superiorVDODO : vdodoAmount; + user.superiorVDODO = uint128(uint256(user.superiorVDODO).sub(vdodoAmount)); + + UserInfo storage superiorUser = userInfo[user.superior]; + uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, alpha); + + if (vdodoAmount >= creditVDODO) { + superiorUser.credit = 0; + _redeem(superiorUser, creditVDODO); + } else { + superiorUser.credit = superiorUser.credit.sub( + DecimalMath.mulFloor(vdodoAmount, alpha) + ); + _redeem(superiorUser, vdodoAmount); + } + } + } + + function _transfer( + address from, + address to, + uint256 _amount + ) internal balanceEnough(from, _amount) canTransfer { + require(from != address(0), "transfer from the zero address"); + require(to != address(0), "transfer to the zero address"); + + UserInfo storage fromUser = userInfo[from]; + fromUser.VDODOAmount = uint128(uint256(fromUser.VDODOAmount).sub(_amount)); + + UserInfo storage toUser = userInfo[to]; + toUser.VDODOAmount = uint128(uint256(toUser.VDODOAmount).add(_amount)); + + uint256 superiorRedeemVDODO = DecimalMath.mulFloor(_amount, _SUPERIOR_RATIO_); + + if (fromUser.superior != address(0)) { + _redeemFromSuperior(fromUser, superiorRedeemVDODO); + } + + if (toUser.superior != address(0)) { + _mintToSuperior(toUser, superiorRedeemVDODO); + } + + emit Transfer(from, to, _amount); + } +} diff --git a/contracts/SmartRoute/helper/DODOCalleeHelper.sol b/contracts/SmartRoute/helper/DODOCalleeHelper.sol index a77f606..5ea225f 100644 --- a/contracts/SmartRoute/helper/DODOCalleeHelper.sol +++ b/contracts/SmartRoute/helper/DODOCalleeHelper.sol @@ -76,7 +76,9 @@ contract DODOCalleeHelper is ReentrancyGuard { to.transfer(amount); } } else { - SafeERC20.safeTransfer(IERC20(token), to, amount); + if (amount > 0) { + SafeERC20.safeTransfer(IERC20(token), to, amount); + } } } } diff --git a/deploy-detail-v1.5.txt b/deploy-detail-v1.5.txt index 10ef18c..488512c 100644 --- a/deploy-detail-v1.5.txt +++ b/deploy-detail-v1.5.txt @@ -112,3 +112,11 @@ Deploy time: 2021/1/26 下午5:41:41 Deploy type: Proxy DODOV1Proxy04 Address: 0xa2CB66EBB947D217f61510882096F6e95c1DE97D Set DODOProxy Owner tx: 0xe49234c67bc710a7f4797aacbb2ef26fac458832f101e856d8113cc21721cd81 +==================================================== +network type: kovan +Deploy time: 2021/2/1 下午11:07:47 +Deploy type: Proxy +==================================================== +network type: kovan +Deploy time: 2021/2/1 下午11:08:57 +Deploy type: Proxy diff --git a/deploy-detail-v2.0.txt b/deploy-detail-v2.0.txt index 83b513d..894105c 100644 --- a/deploy-detail-v2.0.txt +++ b/deploy-detail-v2.0.txt @@ -542,3 +542,33 @@ Deploy time: 2021/1/26 下午5:29:51 ==================================================== network type: live Deploy time: 2021/1/26 下午5:42:23 +==================================================== +network type: kovan +Deploy time: 2021/2/1 下午11:09:38 +Deploy type: V2 +DODOApprove Address: 0x929ce7b28aA759a1C0E6e855ff2545f9e5ca846A +DODOApproveProxy Address: 0x8a61c59e2DE32af51A252d754c7f852aA44191C8 +DODOV2Proxy02 Address: 0xf47BEA4D26026810787b56670502E058CEe5c386 +Init DODOProxyV2 Tx: 0x48c4de1d6008c96d82a92da63747385b218b56fe3451d4256b9b8429623d34f4 +DODOApproveProxy Init tx: 0xb12443d847d426fbeed3c0c3d0e796d52b214ebff65ca604a72dd7400b94e42c +DODOApprove Init tx: 0x26d3f4be069eccf94b0b6d3ee1db07a534cadc98c4dd2b96fd5921d00f57403c +DODOIncentive ChangeProxy tx: 0x9564307e19b26aed9980d9ac007dc88b789450893540426618de4f1039f5584f +==================================================== +network type: kovan +Deploy time: 2021/2/2 下午2:28:36 +==================================================== +network type: kovan +Deploy time: 2021/2/2 下午2:38:26 +Deploy type: V2 +DppTemplateAddress: 0x6b9Db3908ddFD853AD2A42Ab75b5de3f22f137a5 +DppAdminTemplateAddress: 0x2d69731283ac620760309d8b561De11C6166a5F5 +DODOApprove Address: 0x7BD4dBc1F3cd0333E910aa9eeA14A2d96e3684C2 +DODOApproveProxy Address: 0x0cB68c60E4C2F023C4fdbC6Cbe283aAb34866e8a +DppFactoryAddress: 0x80c03749C22Acbe5EaFEb1d550a32C707a67fc34 +Init DppFactory Tx: 0x643c1fdcefe736f782f0c1be2478ff09515030eb9e6c6030f0ec9513bd327bf7 +DODOV2RouteHelper Address: 0xD5171044E369Ef316125da5A0Af8E75ea6Cd3A90 +DODOV2Proxy02 Address: 0xFC6840905E7E550A209B3856f313C523D2f9a3e3 +Init DODOProxyV2 Tx: 0xb41219ef20f0496c8c2088bf8d30a64dd3fa1ef5cdaeaa9161a6da52349dd76d +DODOApproveProxy Init tx: 0x38ced5f643938891ad553b40bafe55ffc6065a15e4bbc81f4cdfbe4f3a0b9494 +DODOApprove Init tx: 0x7eac3f1fa6dbe499351ea52066623980615bdc7901a84f2afa8fc67e34e4be59 +DODOIncentive ChangeProxy tx: 0x6d64bae7ee09f12db0bfc18823ac8e2ca1d682184c1ed3cc157f2b39641409e3 diff --git a/migrations/3_deploy_v2.js b/migrations/3_deploy_v2.js index 0013aef..946a977 100644 --- a/migrations/3_deploy_v2.js +++ b/migrations/3_deploy_v2.js @@ -85,7 +85,7 @@ module.exports = async (deployer, network, accounts) => { DefaultPermissionAddress = "0xACc7E23368261e1E02103c4e5ae672E7D01f5797"; DvmTemplateAddress = "0xA6384D1501842e9907D43148E2ca0d50E4ad56E2"; - DppTemplateAddress = "0x044b48D64E77Ab8854C46c8456dC05C540c9dd53"; + DppTemplateAddress = ""; DppAdminTemplateAddress = ""; CpTemplateAddress = "0x81c802080c3CE0dE98fcb625670A14Eb8440184a"; //Factory @@ -379,8 +379,8 @@ module.exports = async (deployer, network, accounts) => { logger.log("DODOIncentive ChangeProxy tx: ", tx.tx); //3. Open trade incentive - var tx = await DODOIncentiveInstance.changePerReward("10000000000000000000"); - logger.log("DODOIncentive OpenSwitch tx: ", tx.tx); + // var tx = await DODOIncentiveInstance.changePerReward("10000000000000000000"); + // logger.log("DODOIncentive OpenSwitch tx: ", tx.tx); //4. Transfer DODO to Trade Incentive } diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 5842d33..7bc7c90 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -47,6 +47,9 @@ export const DODO_CALLEE_HELPER_NAME = "DODOCalleeHelper" export const CROWD_POOLING_NAME = "CP" export const CROWD_POOLING_FACTORY = "CrowdPoolingFactory" export const DODO_INCENTIVE = "DODOIncentive" +export const VDODO_NAME = "vDODOToken" +export const DODO_CULATION_HELPER = "DODOCirculationHelper" +export const DODO_GOVERNANCE = "Governance" interface ContractJson { abi: any; diff --git a/test/utils/Converter.ts b/test/utils/Converter.ts index e01ac5d..d1d67b9 100644 --- a/test/utils/Converter.ts +++ b/test/utils/Converter.ts @@ -1,4 +1,6 @@ import BigNumber from "bignumber.js"; +import { getDefaultWeb3 } from './EVM'; + export const MAX_UINT256 = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" @@ -12,4 +14,9 @@ export function gweiStr(gwei: string): string { export function mweiStr(gwei: string): string { return new BigNumber(gwei).multipliedBy(10 ** 6).toFixed(0, BigNumber.ROUND_DOWN) +} + +export function fromWei(value: string, unit: any): string { + var web3 = getDefaultWeb3(); + return web3.utils.fromWei(value, unit); } \ No newline at end of file diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts new file mode 100644 index 0000000..c04775c --- /dev/null +++ b/test/utils/VDODOContext.ts @@ -0,0 +1,148 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import BigNumber from 'bignumber.js'; +import Web3 from 'web3'; +import { Contract } from 'web3-eth-contract'; + +import * as contracts from './Contracts'; +import { decimalStr, MAX_UINT256 } from './Converter'; +import { EVM, getDefaultWeb3 } from './EVM'; +import * as log from './Log'; + +BigNumber.config({ + EXPONENTIAL_AT: 1000, + DECIMAL_PLACES: 80, +}); + +export class VDODOContext { + EVM: EVM; + Web3: Web3; + Deployer: string; + Maintainer: string; + SpareAccounts: string[]; + + //token + DODO: Contract; + VDODO: Contract; + + DODOApprove: Contract; + DODOApproveProxy: Contract; + + DODOCirculationHelper: Contract; + Governance: Contract; + + lastRewardBlock: number; + alpha: number; + + + + constructor() { } + + async init() { + this.EVM = new EVM(); + this.Web3 = getDefaultWeb3(); + + const allAccounts = await this.Web3.eth.getAccounts(); + this.Deployer = allAccounts[0]; + this.Maintainer = allAccounts[1]; + this.SpareAccounts = allAccounts.slice(2, 10); + + this.DODO = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["DODO Token", "DODO", 18] + ); + + this.DODOApprove = await contracts.newContract( + contracts.SMART_APPROVE + ); + + this.DODOApproveProxy = await contracts.newContract( + contracts.SMART_APPROVE_PROXY, + [this.DODOApprove.options.address] + ) + + this.Governance = await contracts.newContract( + contracts.DODO_GOVERNANCE, + [ + this.DODO.options.address + ] + ) + + this.VDODO = await contracts.newContract( + contracts.VDODO_NAME, + [ + this.Governance.options.address, + this.DODO.options.address, + "0x0000000000000000000000000000000000000000", + this.DODOApproveProxy.options.address, + "VDODO Token", "VDODO" + ] + ) + + this.DODOCirculationHelper = await contracts.newContract( + contracts.DODO_CULATION_HELPER, + [ + this.VDODO.options.address, + this.DODO.options.address + ] + ); + + await this.Governance.methods.initOwner( + this.Deployer + ).send(this.sendParam(this.Deployer)) + + await this.Governance.methods.setVDODOAddress( + this.VDODO.options.address + ).send(this.sendParam(this.Deployer)) + + await this.DODOApprove.methods.init(this.Deployer, this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); + await this.DODOApproveProxy.methods.init(this.Deployer, [this.VDODO.options.address]).send(this.sendParam(this.Deployer)); + + + await this.VDODO.methods.initOwner( + this.Deployer + ).send(this.sendParam(this.Deployer)) + + await this.VDODO.methods.changePerReward(decimalStr("1")).send(this.sendParam(this.Deployer)); + await this.VDODO.methods.updateDODOCirculationHelper(this.DODOCirculationHelper.options.address).send(this.sendParam(this.Deployer)); + await this.mintTestToken(this.VDODO.options.address, decimalStr("100000")); + + this.alpha = await this.VDODO.methods.alpha().call(); + this.lastRewardBlock = await this.VDODO.methods.lastRewardBlock().call(); + + console.log(log.blueText("[Init VDODO context]")); + + console.log("init alpha = " + this.alpha); + console.log("init lastRewardBlock = " + this.lastRewardBlock); + } + + sendParam(sender, value = "0") { + return { + from: sender, + gas: process.env["COVERAGE"] ? 10000000000 : 7000000, + gasPrice: process.env.GAS_PRICE, + value: decimalStr(value), + }; + } + + async mintTestToken(to: string, amount: string) { + await this.DODO.methods.mint(to, amount).send(this.sendParam(this.Deployer)); + } + + async approveProxy(account: string) { + await this.DODO.methods + .approve(this.DODOApprove.options.address, MAX_UINT256) + .send(this.sendParam(account)); + } +} + +export async function getVDODOContext(): Promise { + var context = new VDODOContext(); + await context.init(); + return context; +} diff --git a/test/vDODO/erc20.test.ts b/test/vDODO/erc20.test.ts new file mode 100644 index 0000000..8936e6a --- /dev/null +++ b/test/vDODO/erc20.test.ts @@ -0,0 +1,346 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, fromWei } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); + +let account0: string; +let account1: string; +let account2: string; +let account3: string; +let defaultSuperAddress: string; +let owner: string; + +async function init(ctx: VDODOContext): Promise { + account0 = ctx.SpareAccounts[0]; + account1 = ctx.SpareAccounts[1]; + account2 = ctx.SpareAccounts[2]; + account3 = ctx.SpareAccounts[3]; + defaultSuperAddress = ctx.Maintainer + owner = ctx.Deployer + + await ctx.mintTestToken(account0, decimalStr("1000")); + await ctx.mintTestToken(account2, decimalStr("1000")); + + await ctx.approveProxy(account0); + await ctx.approveProxy(account1); + await ctx.approveProxy(account2); + await ctx.approveProxy(account3); + + await ctx.VDODO.methods.setCantransfer(true).send(ctx.sendParam(owner)) +} + +async function getGlobalState(ctx: VDODOContext, logInfo?: string) { + var alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + // console.log(logInfo + " alpha:" + fromWei(alpha, 'ether') + " lastRewardBlock:" + lastRewardBlock + " totalSuppy:" + fromWei(totalSuppy, 'ether')); + return [alpha, lastRewardBlock,totalSuppy] + } + + async function dodoBalance(ctx: VDODOContext, user: string, logInfo?: string) { + var dodo_contract = await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(); + var dodo_account = await ctx.DODO.methods.balanceOf(user).call(); + + // console.log(logInfo + " DODO:" + fromWei(dodo_contract, 'ether') + " account:" + fromWei(dodo_account, 'ether')); + return [dodo_contract, dodo_account] + } + + async function getUserInfo(ctx: VDODOContext, user: string, logInfo?: string) { + var info = await ctx.VDODO.methods.userInfo(user).call(); + var res = { + "VDODOAmount": info.VDODOAmount, + "superiorVDODO": info.superiorVDODO, + "superior": info.superior, + "credit": info.credit + } + // console.log(logInfo + " VDODOAmount:" + fromWei(info.VDODOAmount, 'ether') + " superiorVDODO:" + fromWei(info.superiorVDODO, 'ether') + " superior:" + info.superior + " credit:" + fromWei(info.credit, 'ether')); + return res + } + + async function mint(ctx: VDODOContext, user: string, mintAmount: string, superior: string) { + await ctx.VDODO.methods.mint( + mintAmount, + superior + ).send(ctx.sendParam(user)); + } + +describe("vDODO-erc20", () => { + let snapshotId: string; + let ctx: VDODOContext; + + before(async () => { + ctx = await getVDODOContext(); + //打开transfer开关 + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("vdodo-erc20", () => { + + it("transfer-vdodo", async () => { + //检查四个人 【包括from, to 以及各自的上级】,info变化 + //alpha lastRewardBlock + //各自dodo余额变化 + + let [,lastRewardBlockStart,] = await getGlobalState(ctx, "before"); + await ctx.VDODO.methods.mint(decimalStr("10"),account1).send(ctx.sendParam(account0)) + await ctx.VDODO.methods.mint(decimalStr("10"),account3).send(ctx.sendParam(account2)) + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + let [alpha,lastRewardBlock,] = await getGlobalState(ctx, "after"); + + assert.equal(lastRewardBlock,Number(lastRewardBlockStart)+11); + + assert.equal(alpha, "113833992094861660108"); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + assert.equal( + totalSuppy + , decimalStr("0.210833333333333332")); + + + + let userInfo0 = await getUserInfo(ctx, account0, "User0 "); + assert.equal(userInfo0.VDODOAmount, decimalStr("0.1")); + assert.equal(userInfo0.superiorVDODO, decimalStr("0.01")); + assert.equal(userInfo0.credit, "0"); + let userInfo1 = await getUserInfo(ctx, account1, "User0 Superior ") + assert.equal(userInfo1.VDODOAmount, decimalStr("0.01")); + assert.equal(userInfo1.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1.credit, decimalStr("1")); + + let userInfo2 = await getUserInfo(ctx, account2, "User2 "); + assert.equal(userInfo2.VDODOAmount, decimalStr("0.091666666666666666")); + assert.equal(userInfo2.superiorVDODO, decimalStr("0.009166666666666666")); + assert.equal(userInfo2.credit, decimalStr("0")); + let userInfo3 = await getUserInfo(ctx, account3, "User2 Superior"); + assert.equal(userInfo3.VDODOAmount, decimalStr("0.009166666666666666")); + assert.equal(userInfo3.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3.credit, decimalStr("0.999999999999999928")); + + + let [, dodo_u0] = await dodoBalance(ctx, account0, "start") + assert.equal(dodo_u0, "990000000000000000000"); + let [, dodo_u1] = await dodoBalance(ctx, account1, "start") + assert.equal(dodo_u1, "0"); + let [, dodo_u2] = await dodoBalance(ctx, account2, "start") + assert.equal(dodo_u2, "990000000000000000000"); + let [, dodo_u3] = await dodoBalance(ctx, account3, "start") + assert.equal(dodo_u3, "0"); + + await logGas(await ctx.VDODO.methods.transfer( + account2, + decimalStr("0.1") + ), ctx.sendParam(account0), "transfer"); + + + // await ctx.VDODO.methods.transfer(account2,decimalStr("0.1")).send(ctx.sendParam(account0)) + + let userInfo0_after = await getUserInfo(ctx, account0, "userInfo0_after"); + let userInfo1_after = await getUserInfo(ctx, account1, "userInfo1_after"); + let userInfo2_after = await getUserInfo(ctx, account2, "userInfo2_after"); + let userInfo3_after = await getUserInfo(ctx, account3, "userInfo3_after"); + + + assert.equal(userInfo0_after.VDODOAmount, "0"); + assert.equal(userInfo0_after.superiorVDODO, "0"); + assert.equal(userInfo0_after.credit, "0"); + + assert.equal(userInfo1_after.VDODOAmount, decimalStr("0.001566666666666667")); + assert.equal(userInfo1_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1_after.credit, "0"); + + assert.equal(userInfo2_after.VDODOAmount, decimalStr("0.191666666666666666")); + assert.equal(userInfo2_after.superiorVDODO, decimalStr("0.019166666666666666")); + assert.equal(userInfo2_after.credit, "0"); + + assert.equal(userInfo3_after.VDODOAmount, decimalStr("0.019166666666666666")); + assert.equal(userInfo3_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3_after.credit, decimalStr("2.185770750988142222")); + + + + let [alphaEnd,lastRewardBlockEnd,totalSuppyEnd] = await getGlobalState(ctx, "end"); + assert.equal(alphaEnd, decimalStr("118.577075098814229308")); + assert.equal(totalSuppyEnd, decimalStr("0.212399999999999999")); + assert.equal(lastRewardBlockEnd,Number(lastRewardBlock)+2); + + + let [, dodo_u0_end] = await dodoBalance(ctx, account0, "end") + assert.equal(dodo_u0_end, "990000000000000000000"); + let [, dodo_u1_end] = await dodoBalance(ctx, account1, "end") + assert.equal(dodo_u1_end, "0"); + let [, dodo_u2_end] = await dodoBalance(ctx, account2, "end") + assert.equal(dodo_u2_end, "990000000000000000000"); + let [, dodo_u3_end] = await dodoBalance(ctx, account3, "end") + assert.equal(dodo_u3_end, "0"); + + }); + + it("transferFrom-vdodo", async () => { + //检查四个人 【包括from, to 以及各自的上级】,info变化 + //alpha lastRewardBlock + //各自dodo余额变化 + //approve 状态变化 + + let [,lastRewardBlockStart,] = await getGlobalState(ctx, "before"); + await ctx.VDODO.methods.mint(decimalStr("10"),account1).send(ctx.sendParam(account0)) + await ctx.VDODO.methods.mint(decimalStr("10"),account3).send(ctx.sendParam(account2)) + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + let [alpha,lastRewardBlock,] = await getGlobalState(ctx, "after"); + + assert.equal(lastRewardBlock,Number(lastRewardBlockStart)+11); + + assert.equal(alpha, "113833992094861660108"); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + assert.equal( + totalSuppy + , decimalStr("0.210833333333333332")); + + + + let userInfo0 = await getUserInfo(ctx, account0, "User0 "); + assert.equal(userInfo0.VDODOAmount, decimalStr("0.1")); + assert.equal(userInfo0.superiorVDODO, decimalStr("0.01")); + assert.equal(userInfo0.credit, "0"); + let userInfo1 = await getUserInfo(ctx, account1, "User0 Superior ") + assert.equal(userInfo1.VDODOAmount, decimalStr("0.01")); + assert.equal(userInfo1.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1.credit, decimalStr("1")); + + let userInfo2 = await getUserInfo(ctx, account2, "User2 "); + assert.equal(userInfo2.VDODOAmount, decimalStr("0.091666666666666666")); + assert.equal(userInfo2.superiorVDODO, decimalStr("0.009166666666666666")); + assert.equal(userInfo2.credit, decimalStr("0")); + let userInfo3 = await getUserInfo(ctx, account3, "User2 Superior"); + assert.equal(userInfo3.VDODOAmount, decimalStr("0.009166666666666666")); + assert.equal(userInfo3.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3.credit, decimalStr("0.999999999999999928")); + + + let [, dodo_u0] = await dodoBalance(ctx, account0, "start") + assert.equal(dodo_u0, "990000000000000000000"); + let [, dodo_u1] = await dodoBalance(ctx, account1, "start") + assert.equal(dodo_u1, "0"); + let [, dodo_u2] = await dodoBalance(ctx, account2, "start") + assert.equal(dodo_u2, "990000000000000000000"); + let [, dodo_u3] = await dodoBalance(ctx, account3, "start") + assert.equal(dodo_u3, "0"); + + + await logGas(await ctx.VDODO.methods.approve( + account3, + decimalStr("0.1") + ), ctx.sendParam(account0), "approve"); + + await logGas(await ctx.VDODO.methods.transferFrom( + account0, + account2, + decimalStr("0.1") + ), ctx.sendParam(account3), "transferFrom"); + + let userInfo0_after = await getUserInfo(ctx, account0, "userInfo0_after"); + let userInfo1_after = await getUserInfo(ctx, account1, "userInfo1_after"); + let userInfo2_after = await getUserInfo(ctx, account2, "userInfo2_after"); + let userInfo3_after = await getUserInfo(ctx, account3, "userInfo3_after"); + + + assert.equal(userInfo0_after.VDODOAmount, "0"); + assert.equal(userInfo0_after.superiorVDODO, "0"); + assert.equal(userInfo0_after.credit, "0"); + + assert.equal(userInfo1_after.VDODOAmount, decimalStr("0.001891025641025642")); + assert.equal(userInfo1_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1_after.credit, "0"); + + assert.equal(userInfo2_after.VDODOAmount, decimalStr("0.191666666666666666")); + assert.equal(userInfo2_after.superiorVDODO, decimalStr("0.019166666666666666")); + assert.equal(userInfo2_after.credit, "0"); + + assert.equal(userInfo3_after.VDODOAmount, decimalStr("0.019166666666666666")); + assert.equal(userInfo3_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3_after.credit, decimalStr("2.233201581027667914")); + + + + let [alphaEnd,lastRewardBlockEnd,totalSuppyEnd] = await getGlobalState(ctx, "end"); + assert.equal(alphaEnd, decimalStr("123.320158102766798508")); + assert.equal(totalSuppyEnd, decimalStr("0.212724358974358974")); + assert.equal(lastRewardBlockEnd,Number(lastRewardBlock)+3); + + + let [, dodo_u0_end] = await dodoBalance(ctx, account0, "end") + assert.equal(dodo_u0_end, "990000000000000000000"); + let [, dodo_u1_end] = await dodoBalance(ctx, account1, "end") + assert.equal(dodo_u1_end, "0"); + let [, dodo_u2_end] = await dodoBalance(ctx, account2, "end") + assert.equal(dodo_u2_end, "990000000000000000000"); + let [, dodo_u3_end] = await dodoBalance(ctx, account3, "end") + assert.equal(dodo_u3_end, "0"); + + + //再次transferFrom 预期revert + //预期revert + await truffleAssert.reverts( + ctx.VDODO.methods.transferFrom(account0,account2,decimalStr("0.1")).send(ctx.sendParam(account3)), + "ALLOWANCE_NOT_ENOUGH" + ) + }); + + it("transfer - close", async () => { + + await ctx.VDODO.methods.setCantransfer(false).send(ctx.sendParam(owner)) + + await ctx.VDODO.methods.mint(decimalStr("10"),defaultSuperAddress).send(ctx.sendParam(account0)) + assert.equal( + await ctx.DODO.methods.balanceOf(account0).call(), + decimalStr("990") + ); + assert.equal( + await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), + decimalStr("100010") + ); + assert.equal( + await ctx.VDODO.methods.balanceOf(account0).call(), + decimalStr("0.1") + ); + + assert.equal( + await ctx.VDODO.methods.balanceOf(account1).call(), + decimalStr("0") + ); + //预期revert + await truffleAssert.reverts( + ctx.VDODO.methods.transfer(account1,decimalStr("0.1")).send(ctx.sendParam(account0)), + "vDODOToken: not allowed transfer" + ) + assert.equal( + await ctx.VDODO.methods.balanceOf(account0).call(), + decimalStr("0.1") + ); + assert.equal( + await ctx.VDODO.methods.balanceOf(account1).call(), + decimalStr("0") + ); + + }); + }) +}); diff --git a/test/vDODO/global.test.ts b/test/vDODO/global.test.ts new file mode 100644 index 0000000..1fcd32a --- /dev/null +++ b/test/vDODO/global.test.ts @@ -0,0 +1,132 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, fromWei } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; + +let account0: string; +let account1: string; + +async function init(ctx: VDODOContext): Promise { + account0 = ctx.SpareAccounts[0]; + account1 = ctx.SpareAccounts[1]; + + await ctx.mintTestToken(account0, decimalStr("1000")); + await ctx.mintTestToken(account1, decimalStr("1000")); + + await ctx.approveProxy(account0); + await ctx.approveProxy(account1); +} + +async function getGlobalState(ctx: VDODOContext, logInfo?: string) { + var alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + var dodoPerBlock = await ctx.VDODO.methods.dodoPerBlock().call(); + // console.log(logInfo + "==> alpha:" + fromWei(alpha, 'ether') + " lastRewardBlock:" + lastRewardBlock + " totalSuppy:" + fromWei(totalSuppy, 'ether')+ " dodoPerBlock:" + fromWei(dodoPerBlock, 'ether')); + return [alpha, lastRewardBlock,dodoPerBlock] + } +describe("vDODO-owner", () => { + let snapshotId: string; + let ctx: VDODOContext; + + before(async () => { + ctx = await getVDODOContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("vdodo-erc20", () => { + + it("change-reward", async () => { + //改变前alpha lastRewardBlock 状态 + let [alpha,lastRewardBlock,dodoPerBlock] = await getGlobalState(ctx, "before"); + + //change-reward + await ctx.VDODO.methods.changePerReward(decimalStr("2")).send(ctx.sendParam(ctx.Deployer)) + //改变后状态 + let [alphaAfter,lastRewardBlockAfter,dodoPerBlockAfter] = await getGlobalState(ctx, "after"); + + assert.equal( + await lastRewardBlock, + Number(lastRewardBlockAfter)-7 + ); + assert.equal(//totalSupply==0 + await alpha, + alphaAfter + ); + assert.notEqual( + await dodoPerBlock, + dodoPerBlockAfter + ); + }); + + + it("donate", async () => { + //改变前alpha lastRewardBlock 状态 + let [before,lastRewardBlock,] = await getGlobalState(ctx, "before"); + + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account1 + ), ctx.sendParam(account0), "mint-fisrt"); + + await logGas(await ctx.VDODO.methods.donate( + decimalStr("100") + ), ctx.sendParam(account0), "donate"); + + + let [alphaAfter,lastRewardBlockAfter,] = await getGlobalState(ctx, "after"); + assert.notEqual( + before, + alphaAfter + ); + assert.equal( + alphaAfter, + "191818181818181818180"//newAlpha +amount/totalSupply + ); + assert.equal( + lastRewardBlock, + Number(lastRewardBlockAfter)-7 + ); + }); + it("read-helper", async () => { + //不同amount对应的feeRatio (5 5-15 15) + let ratio0 = await ctx.DODOCirculationHelper.methods.geRatioValue(decimalStr("0.2")).call()//<=1 ->5 + assert.equal( + ratio0, + decimalStr("0.05") + ); + + let ratio1 = await ctx.DODOCirculationHelper.methods.geRatioValue(decimalStr("11")).call()//>=10 ->15 + assert.equal( + ratio1, + decimalStr("0.15") + ); + + let ratio2 = await ctx.DODOCirculationHelper.methods.geRatioValue(decimalStr("6")).call()//-->5-15 + assert.equal( + ratio2, + decimalStr("0.066852058071690192") + ); + // console.log("ratio2 = "+ fromWei(ratio2, 'ether')); + assert.isAbove(Number(ratio2),Number(ratio0)) + assert.isBelow(Number(ratio2),Number(ratio1)) + + }); + }) +}); diff --git a/test/vDODO/mintRedeem.test.ts b/test/vDODO/mintRedeem.test.ts new file mode 100644 index 0000000..1da9efc --- /dev/null +++ b/test/vDODO/mintRedeem.test.ts @@ -0,0 +1,354 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, fromWei } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; +import { assert } from 'chai'; + +let account0: string; +let account1: string; +let account2: string; +let account3: string; +let account4: string; + +async function init(ctx: VDODOContext): Promise { + account0 = ctx.SpareAccounts[0]; + account1 = ctx.SpareAccounts[1]; + account2 = ctx.SpareAccounts[2]; + account3 = ctx.SpareAccounts[3]; + account4 = ctx.SpareAccounts[4]; + + await ctx.mintTestToken(account0, decimalStr("100000")); + await ctx.mintTestToken(account1, decimalStr("1000")); + await ctx.mintTestToken(account2, decimalStr("1000")); + await ctx.mintTestToken(account3, decimalStr("1000")); + await ctx.mintTestToken(account4, decimalStr("1000")); + + await ctx.approveProxy(account0); + await ctx.approveProxy(account1); + await ctx.approveProxy(account2); + await ctx.approveProxy(account3); + await ctx.approveProxy(account4); +} + +async function getGlobalState(ctx: VDODOContext, logInfo?: string) { + var alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + console.log(logInfo + " alpha:" + fromWei(alpha, 'ether') + " lastRewardBlock:" + lastRewardBlock + " totalSuppy:" + fromWei(totalSuppy, 'ether')); + return [alpha, lastRewardBlock] +} + +async function dodoBalance(ctx: VDODOContext, user: string, logInfo?: string) { + var dodo_contract = await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(); + var dodo_account = await ctx.DODO.methods.balanceOf(user).call(); + + console.log(logInfo + " DODO:" + fromWei(dodo_contract, 'ether') + " account:" + fromWei(dodo_account, 'ether')); + return [dodo_contract, dodo_account] +} + +async function getUserInfo(ctx: VDODOContext, user: string, logInfo?: string) { + var info = await ctx.VDODO.methods.userInfo(user).call(); + var res = { + "VDODOAmount": info.VDODOAmount, + "superiorVDODO": info.superiorVDODO, + "superior": info.superior, + "credit": info.credit + } + console.log(logInfo + " VDODOAmount:" + fromWei(info.VDODOAmount, 'ether') + " superiorVDODO:" + fromWei(info.superiorVDODO, 'ether') + " superior:" + info.superior + " credit:" + fromWei(info.credit, 'ether')); + return res +} + +async function mint(ctx: VDODOContext, user: string, mintAmount: string, superior: string) { + await ctx.VDODO.methods.mint( + mintAmount, + superior + ).send(ctx.sendParam(user)); +} + +describe("VDODO", () => { + let snapshotId: string; + let ctx: VDODOContext; + + before(async () => { + ctx = await getVDODOContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("vdodo", () => { + + it("vdodo-mint-first", async () => { + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account1 + ), ctx.sendParam(account0), "mint-fisrt"); + + //增加两个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + await ctx.mintTestToken(account0, decimalStr("0")); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101818181818181818181"); + assert.equal(userInfo.VDODOAmount, "1000000000000000000"); + assert.equal(userInfo.superiorVDODO, "100000000000000000"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "100000000000000000"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "10000000000000000000"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "99900000000000000000000") + }); + + it("vdodo-mint-second", async () => { + await mint(ctx, account0, decimalStr("100"), account1) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account1 + ), ctx.sendParam(account0), "mint-second"); + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101365693130399012751"); + assert.equal(userInfo.VDODOAmount, "1990990990990990990"); + assert.equal(userInfo.superiorVDODO, "199099099099099099"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "199099099099099099"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "19999999999999999990"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "99800000000000000000000") + }); + + + it("vdodo-mint-second-otherSuperior", async () => { + await mint(ctx, account0, decimalStr("100"), account1) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account2 + ), ctx.sendParam(account0), "mint-second"); + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101365693130399012751"); + assert.equal(userInfo.VDODOAmount, "1990990990990990990"); + assert.equal(userInfo.superiorVDODO, "199099099099099099"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "199099099099099099"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "19999999999999999990"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + let otherInfo = await getUserInfo(ctx, account2, "Superior after") + + assert.equal(otherInfo.VDODOAmount, "0"); + assert.equal(otherInfo.superiorVDODO, "0"); + assert.equal(otherInfo.credit, "0"); + assert.equal(otherInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "99800000000000000000000") + }); + + + it("redeem-amount-read", async () => { + await mint(ctx, account0, decimalStr("100"), account1) + + let [dodoReceive, burnDodoAmount, withdrawFeeDodoAmount] = await ctx.VDODO.methods.getWithdrawAmount(decimalStr("1")).call(); + + assert.equal(dodoReceive, decimalStr("85")); + assert.equal(burnDodoAmount, decimalStr("0")); + assert.equal(withdrawFeeDodoAmount, decimalStr("15")); + }); + + + it("redeem-partial-haveMint", async () => { + await mint(ctx, account0, decimalStr("10000"), account1) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.redeem(decimalStr("10")), ctx.sendParam(account0), "redeem-partial-haveMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101524380165289256197"); + assert.equal(userInfo.VDODOAmount, "90000000000000000000"); + assert.equal(userInfo.superiorVDODO, "9000000000000000000"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "9000000000000000000"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "899990909090909090910"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "90850077272727272727265") + + }); + + + it("redeem-partial-NotMint", async () => { + //多个下级引用 + await mint(ctx, account1, decimalStr("100"), account0) + await mint(ctx, account2, decimalStr("100"), account0) + await mint(ctx, account3, decimalStr("100"), account0) + await mint(ctx, account4, decimalStr("100"), account0) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account3, "One of referer before"); + await dodoBalance(ctx, account0, "before") + + let account0VdodoAmount = await ctx.VDODO.methods.balanceOf(account0).call() + + await logGas(await ctx.VDODO.methods.redeem((account0VdodoAmount - 3000) + ""), ctx.sendParam(account0), "redeem-partial-NotMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account3, "One of referer after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101909933011338172201"); + assert.equal(userInfo.VDODOAmount, "393425809544634067"); + assert.equal(userInfo.superiorVDODO, "0"); + assert.equal(userInfo.credit, "39999999999999999876"); + assert.equal(userInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(superiorInfo.VDODOAmount, "986527067608148689"); + assert.equal(superiorInfo.superiorVDODO, "98652706760814868"); + assert.equal(superiorInfo.credit, "0"); + assert.equal(superiorInfo.superior, account0); + + assert.equal(dodo_u, "100000232341473424735076") + }); + + + it("redeem-all-haveMint", async () => { + //第一笔mint不动,防止totalSupply过小 + await mint(ctx, account0, decimalStr("10000"), account1) + await mint(ctx, account1, decimalStr("100"), account2) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account1, "User before"); + await getUserInfo(ctx, account2, "Superior before") + await dodoBalance(ctx, account1, "before") + + let account1VdodoAmount = await ctx.VDODO.methods.balanceOf(account1).call() + + await logGas(await ctx.VDODO.methods.redeem(account1VdodoAmount), ctx.sendParam(account1), "redeem-all-haveMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account1, "User after"); + let superiorInfo = await getUserInfo(ctx, account2, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account1, "after") + + assert.equal(alpha, "100154592821433302856"); + assert.equal(userInfo.VDODOAmount, "9999090991728024725"); + assert.equal(userInfo.superiorVDODO, "0"); + assert.equal(userInfo.credit, "1000000000000000000000"); + assert.equal(userInfo.superior, account2); + + assert.equal(superiorInfo.VDODOAmount, "8998462015594"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "0"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "985084929758388492933") + + }); + + + it("redeem-all-NoMint", async () => { + //多个下级引用 + await mint(ctx, account1, decimalStr("100"), account0) + await mint(ctx, account2, decimalStr("100"), account0) + await mint(ctx, account3, decimalStr("100"), account0) + await mint(ctx, account4, decimalStr("100"), account0) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account3, "One of referer before"); + await dodoBalance(ctx, account0, "before") + + let account0VdodoAmount = await ctx.VDODO.methods.balanceOf(account0).call() + + await logGas(await ctx.VDODO.methods.redeem(account0VdodoAmount), ctx.sendParam(account0), "redeem-all-NotMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account3, "One of referer after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101909933011338182738"); + assert.equal(userInfo.VDODOAmount, "393425809544631067"); + assert.equal(userInfo.superiorVDODO, "0"); + assert.equal(userInfo.credit, "39999999999999999876"); + assert.equal(userInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(superiorInfo.VDODOAmount, "986527067608148689"); + assert.equal(superiorInfo.superiorVDODO, "98652706760814868"); + assert.equal(superiorInfo.credit, "0"); + assert.equal(superiorInfo.superior, account0); + + assert.equal(dodo_u, "100000232341473424994923") + }); + }) +}); diff --git a/truffle-config.js b/truffle-config.js index 5559589..e2ad8f4 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -38,7 +38,7 @@ module.exports = { * $ truffle test --network */ deploySwitch: { - DEPLOY_V1: true, + DEPLOY_V1: false, DEPLOY_V2: false, ADAPTER: false, MOCK_TOKEN: false, diff --git a/truffle-test.sh b/truffle-test.sh index b3c15f4..3f5940a 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -36,6 +36,12 @@ then truffle test ./test/V2Proxy/proxy.twap.test.ts fi +if [ "$1"x = "vdodo-mintRedeem"x ] +then + truffle test ./test/vDODO/mintRedeem.test.ts +fi + + # if [ "$1"x = "route-incentive"x ] # then # truffle test ./test/Route/Incentive.test.ts