Created
August 4, 2020 02:34
-
-
Save jin10086/fe3f037a1a1c06cbb084aa5edec5b5fb to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.17+commit.d19bba13.js&optimize=true&gist=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
*Submitted for verification at Etherscan.io on 2020-07-26 | |
*/ | |
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.5.16; | |
interface IERC20 { | |
function totalSupply() external view returns (uint256); | |
function balanceOf(address account) external view returns (uint256); | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint256); | |
function approve(address spender, uint256 amount) external returns (bool); | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
library SafeMath { | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
return c; | |
} | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
bytes32 codehash; | |
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { codehash := extcodehash(account) } | |
return (codehash != 0x0 && codehash != accountHash); | |
} | |
function toPayable(address account) internal pure returns (address payable) { | |
return address(uint160(account)); | |
} | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
// solhint-disable-next-line avoid-call-value | |
(bool success, ) = recipient.call.value(amount)(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
} | |
library SafeERC20 { | |
using SafeMath for uint256; | |
using Address for address; | |
function safeTransfer(IERC20 token, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | |
} | |
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); | |
} | |
function safeApprove(IERC20 token, address spender, uint256 value) internal { | |
require((value == 0) || (token.allowance(address(this), spender) == 0), | |
"SafeERC20: approve from non-zero to non-zero allowance" | |
); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | |
} | |
function callOptionalReturn(IERC20 token, bytes memory data) private { | |
require(address(token).isContract(), "SafeERC20: call to non-contract"); | |
// solhint-disable-next-line avoid-low-level-calls | |
(bool success, bytes memory returndata) = address(token).call(data); | |
require(success, "SafeERC20: low-level call failed"); | |
if (returndata.length > 0) { // Return data is optional | |
// solhint-disable-next-line max-line-length | |
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | |
} | |
} | |
} | |
interface Strategy { | |
function want() external view returns (address); | |
function deposit() external; | |
function withdraw(address) external; | |
function withdraw(uint) external; | |
function withdrawAll() external returns (uint); | |
function balanceOf() external view returns (uint); | |
} | |
interface Converter { | |
function convert(address) external returns (uint); | |
} | |
interface OneSplitAudit { | |
function swap( | |
address fromToken, | |
address destToken, | |
uint256 amount, | |
uint256 minReturn, | |
uint256[] calldata distribution, | |
uint256 flags | |
) | |
external | |
payable | |
returns(uint256 returnAmount); | |
function getExpectedReturn( | |
address fromToken, | |
address destToken, | |
uint256 amount, | |
uint256 parts, | |
uint256 flags // See constants in IOneSplit.sol | |
) | |
external | |
view | |
returns( | |
uint256 returnAmount, | |
uint256[] memory distribution | |
); | |
} | |
contract Controller { | |
using SafeERC20 for IERC20; | |
using Address for address; | |
using SafeMath for uint256; | |
address public governance; | |
address public onesplit; | |
address public rewards; | |
address public factory; | |
mapping(address => address) public vaults; | |
mapping(address => address) public strategies; | |
mapping(address => mapping(address => address)) public converters; | |
uint public split = 5000; | |
uint public constant max = 10000; | |
constructor() public { | |
governance = tx.origin; | |
onesplit = address(0x50FDA034C0Ce7a8f7EFDAebDA7Aa7cA21CC1267e); | |
rewards = 0xc487E91aac75D048EeACA7360E479Ae7cCEa0b86; //yfii gov multi-sign contract | |
} | |
function setFactory(address _factory) public { | |
require(msg.sender == governance, "!governance"); | |
factory = _factory; | |
} | |
function setSplit(uint _split) public { | |
require(msg.sender == governance, "!governance"); | |
split = _split; | |
} | |
function setOneSplit(address _onesplit) public { | |
require(msg.sender == governance, "!governance"); | |
onesplit = _onesplit; | |
} | |
function setGovernance(address _governance) public { | |
require(msg.sender == governance, "!governance"); | |
governance = _governance; | |
} | |
function setVault(address _token, address _vault) public { | |
require(msg.sender == governance, "!governance"); | |
vaults[_token] = _vault; | |
} | |
function setConverter(address _input, address _output, address _converter) public { | |
require(msg.sender == governance, "!governance"); | |
converters[_input][_output] = _converter; | |
} | |
function setStrategy(address _token, address _strategy) public { | |
//某个币对应一个策略,比如现在的ycrv就是挖 yfii | |
require(msg.sender == governance, "!governance"); | |
address _current = strategies[_token]; | |
if (_current != address(0)) {//之前的策略存在的话,那么就先提取所有资金 | |
Strategy(_current).withdrawAll(); | |
} | |
strategies[_token] = _strategy; | |
} | |
// | |
function earn(address _token, uint _amount) public { | |
address _strategy = strategies[_token]; //获取策略的合约地址 | |
address _want = Strategy(_strategy).want();//策略需要的token地址 | |
if (_want != _token) {//如果策略需要的和输入的不一样,需要先转换 | |
address converter = converters[_token][_want];//转换器合约地址. | |
IERC20(_token).safeTransfer(converter, _amount);//给转换器打钱 | |
_amount = Converter(converter).convert(_strategy);//执行转换... | |
IERC20(_want).safeTransfer(_strategy, _amount); | |
} else { | |
IERC20(_token).safeTransfer(_strategy, _amount); | |
} | |
Strategy(_strategy).deposit();//存钱 | |
} | |
function balanceOf(address _token) external view returns (uint) { | |
return Strategy(strategies[_token]).balanceOf(); | |
} | |
function withdrawAll(address _token) public { | |
require(msg.sender == governance, "!governance"); | |
Strategy(strategies[_token]).withdrawAll(); | |
} | |
function inCaseTokensGetStuck(address _token, uint _amount) public {//转任意erc20 | |
require(msg.sender == governance, "!governance"); | |
IERC20(_token).safeTransfer(governance, _amount); | |
} | |
function getExpectedReturn(address _strategy, address _token, uint parts) public view returns (uint expected) { | |
uint _balance = IERC20(_token).balanceOf(_strategy);//获取策略器 某个代币的余额 | |
address _want = Strategy(_strategy).want();//策略器需要的代币. | |
(expected,) = OneSplitAudit(onesplit).getExpectedReturn(_token, _want, _balance, parts, 0); | |
} | |
// Only allows to withdraw non-core strategy tokens ~ this is over and above normal yield | |
function yearn(address _strategy, address _token, uint parts) public { | |
// This contract should never have value in it, but just incase since this is a public call | |
uint _before = IERC20(_token).balanceOf(address(this)); | |
Strategy(_strategy).withdraw(_token); | |
uint _after = IERC20(_token).balanceOf(address(this)); | |
if (_after > _before) { | |
uint _amount = _after.sub(_before); | |
address _want = Strategy(_strategy).want(); | |
uint[] memory _distribution; | |
uint _expected; | |
_before = IERC20(_want).balanceOf(address(this)); | |
IERC20(_token).safeApprove(onesplit, 0); | |
IERC20(_token).safeApprove(onesplit, _amount); | |
(_expected, _distribution) = OneSplitAudit(onesplit).getExpectedReturn(_token, _want, _amount, parts, 0); | |
OneSplitAudit(onesplit).swap(_token, _want, _amount, _expected, _distribution, 0); | |
_after = IERC20(_want).balanceOf(address(this)); | |
if (_after > _before) { | |
_amount = _after.sub(_before); | |
uint _reward = _amount.mul(split).div(max); | |
earn(_want, _amount.sub(_reward)); | |
IERC20(_want).safeTransfer(rewards, _reward); | |
} | |
} | |
} | |
function withdraw(address _token, uint _amount) public { | |
require(msg.sender == vaults[_token], "!vault"); | |
Strategy(strategies[_token]).withdraw(_amount); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
*Submitted for verification at Etherscan.io on 2020-07-31 | |
*/ | |
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.5.17; | |
interface IERC20 { | |
function totalSupply() external view returns (uint256); | |
function balanceOf(address account) external view returns (uint256); | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint256); | |
function decimals() external view returns (uint); | |
function approve(address spender, uint256 amount) external returns (bool); | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
library SafeMath { | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
return c; | |
} | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
bytes32 codehash; | |
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { codehash := extcodehash(account) } | |
return (codehash != 0x0 && codehash != accountHash); | |
} | |
function toPayable(address account) internal pure returns (address payable) { | |
return address(uint160(account)); | |
} | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
// solhint-disable-next-line avoid-call-value | |
(bool success, ) = recipient.call.value(amount)(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
} | |
library SafeERC20 { | |
using SafeMath for uint256; | |
using Address for address; | |
function safeTransfer(IERC20 token, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | |
} | |
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); | |
} | |
function safeApprove(IERC20 token, address spender, uint256 value) internal { | |
require((value == 0) || (token.allowance(address(this), spender) == 0), | |
"SafeERC20: approve from non-zero to non-zero allowance" | |
); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | |
} | |
function callOptionalReturn(IERC20 token, bytes memory data) private { | |
require(address(token).isContract(), "SafeERC20: call to non-contract"); | |
// solhint-disable-next-line avoid-low-level-calls | |
(bool success, bytes memory returndata) = address(token).call(data); | |
require(success, "SafeERC20: low-level call failed"); | |
if (returndata.length > 0) { // Return data is optional | |
// solhint-disable-next-line max-line-length | |
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | |
} | |
} | |
} | |
interface Controller { | |
function vaults(address) external view returns (address); | |
function rewards() external view returns (address); | |
} | |
/* | |
A strategy must implement the following calls; | |
- deposit() | |
- withdraw(address) must exclude any tokens used in the yield - Controller role - withdraw should return to Controller | |
- withdraw(uint) - Controller | Vault role - withdraw should always return to vault | |
- withdrawAll() - Controller | Vault role - withdraw should always return to vault | |
- balanceOf() | |
Where possible, strategies must remain as immutable as possible, instead of updating variables, we update the contract by linking it in the controller | |
*/ | |
interface Yfii { | |
function withdraw(uint) external; | |
function getReward() external; | |
function stake(uint) external; | |
function balanceOf(address) external view returns (uint); | |
function exit() external; | |
} | |
contract Balancer { | |
function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external; | |
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external; | |
function swapExactAmountIn( | |
address tokenIn, | |
uint tokenAmountIn, | |
address tokenOut, | |
uint minAmountOut, | |
uint maxPrice | |
) external returns (uint tokenAmountOut, uint spotPriceAfter); | |
function swapExactAmountOut( | |
address tokenIn, | |
uint maxAmountIn, | |
address tokenOut, | |
uint tokenAmountOut, | |
uint maxPrice | |
) external returns (uint tokenAmountIn, uint spotPriceAfter); | |
function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) external returns (uint poolAmountOut); | |
function exitswapPoolAmountIn(address tokenOut, uint poolAmountIn, uint minAmountOut) external returns (uint tokenAmountOut); | |
} | |
interface yERC20 { | |
function deposit(uint256 _amount) external; | |
function withdraw(uint256 _amount) external; | |
} | |
interface ICurveFi { | |
function get_virtual_price() external view returns (uint); | |
function add_liquidity( | |
uint256[4] calldata amounts, | |
uint256 min_mint_amount | |
) external; | |
function remove_liquidity_imbalance( | |
uint256[4] calldata amounts, | |
uint256 max_burn_amount | |
) external; | |
function remove_liquidity( | |
uint256 _amount, | |
uint256[4] calldata amounts | |
) external; | |
function exchange( | |
int128 from, int128 to, uint256 _from_amount, uint256 _min_to_amount | |
) external; | |
} | |
interface Yvault{ | |
function make_profit(uint256 amount) external; | |
} | |
contract StrategyYfii { | |
using SafeERC20 for IERC20; | |
using Address for address; | |
using SafeMath for uint256; | |
address constant public want = address(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); | |
address constant public pool = address(0xb81D3cB2708530ea990a287142b82D058725C092); | |
address constant public yfii = address(0xa1d0E215a23d7030842FC67cE582a6aFa3CCaB83); | |
address constant public balancer = address(0x16cAC1403377978644e78769Daa49d8f6B6CF565); | |
address constant public curve = address(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); | |
address constant public dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); | |
address constant public ydai = address(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01); | |
uint constant public fee = 50; | |
uint constant public max = 10000; | |
address public governance; | |
address public controller; | |
constructor(address _controller) public { | |
governance = msg.sender; | |
controller = _controller; | |
} | |
function deposit() external { //把ycrv stake到 pool1 | |
IERC20(want).safeApprove(pool, 0); | |
IERC20(want).safeApprove(pool, IERC20(want).balanceOf(address(this))); | |
Yfii(pool).stake(IERC20(want).balanceOf(address(this))); | |
} | |
// Controller only function for creating additional rewards from dust | |
function withdraw(IERC20 _asset) external returns (uint balance) { | |
require(msg.sender == controller, "!controller"); | |
require(want != address(_asset), "want"); | |
balance = _asset.balanceOf(address(this)); | |
_asset.safeTransfer(controller, balance); | |
} | |
// Withdraw partial funds, normally used with a vault withdrawal | |
function withdraw(uint _amount) external { | |
require(msg.sender == controller, "!controller"); | |
uint _balance = IERC20(want).balanceOf(address(this)); | |
if (_balance < _amount) { | |
_amount = _withdrawSome(_amount.sub(_balance));//要提取的钱不够,从pool提缺少的出来 | |
_amount = _amount.add(_balance); | |
} | |
address _vault = Controller(controller).vaults(address(want));//金库的地址. | |
require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds | |
IERC20(want).safeTransfer(_vault, _amount); | |
} | |
// Withdraw all funds, normally used when migrating strategies | |
function withdrawAll() external returns (uint balance) { //把ycrv 还到金库 走人 | |
require(msg.sender == controller, "!controller"); | |
_withdrawAll(); | |
balance = IERC20(want).balanceOf(address(this)); | |
address _vault = Controller(controller).vaults(address(want)); | |
require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds | |
IERC20(want).safeTransfer(_vault, balance); | |
} | |
function _withdrawAll() internal { //退池子 已经领取收益. 收益换成ycrv | |
Yfii(pool).exit(); | |
harvest(); | |
} | |
function harvest() public {//砸盘yfii | |
Yfii(pool).getReward(); //领取yfii | |
address _vault = Controller(controller).vaults(address(want)); | |
require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds | |
//把yfii 存进去分红. | |
IERC20(yfii).safeApprove(_vault, 0); | |
IERC20(yfii).safeApprove(_vault, IERC20(yfii).balanceOf(address(this))); | |
Yvault(_vault).make_profit(IERC20(yfii).balanceOf(address(this))); | |
} | |
function _withdrawSome(uint256 _amount) internal returns (uint) { | |
Yfii(pool).withdraw(_amount); | |
return _amount; | |
} | |
function balanceOfCurve() public view returns (uint) { | |
return IERC20(want).balanceOf(address(this)); | |
} | |
function balanceOfYfii() public view returns (uint) { | |
return Yfii(pool).balanceOf(address(this)); | |
} | |
function balanceOf() public view returns (uint) { | |
return balanceOfCurve() | |
.add(balanceOfYfii()); | |
} | |
function setGovernance(address _governance) external { | |
require(msg.sender == governance, "!governance"); | |
governance = _governance; | |
} | |
function setController(address _controller) external { | |
require(msg.sender == governance, "!governance"); | |
controller = _controller; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
*Submitted for verification at Etherscan.io on 2020-07-26 | |
*/ | |
pragma solidity ^0.5.16; | |
interface IERC20 { | |
function totalSupply() external view returns (uint256); | |
function balanceOf(address account) external view returns (uint256); | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint256); | |
function approve(address spender, uint256 amount) external returns (bool); | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
contract Context { | |
constructor () internal { } | |
// solhint-disable-previous-line no-empty-blocks | |
function _msgSender() internal view returns (address payable) { | |
return msg.sender; | |
} | |
function _msgData() internal view returns (bytes memory) { | |
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 | |
return msg.data; | |
} | |
} | |
contract Ownable is Context { | |
address private _owner; | |
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | |
constructor () internal { | |
_owner = _msgSender(); | |
emit OwnershipTransferred(address(0), _owner); | |
} | |
function owner() public view returns (address) { | |
return _owner; | |
} | |
modifier onlyOwner() { | |
require(isOwner(), "Ownable: caller is not the owner"); | |
_; | |
} | |
function isOwner() public view returns (bool) { | |
return _msgSender() == _owner; | |
} | |
function renounceOwnership() public onlyOwner { | |
emit OwnershipTransferred(_owner, address(0)); | |
_owner = address(0); | |
} | |
function transferOwnership(address newOwner) public onlyOwner { | |
_transferOwnership(newOwner); | |
} | |
function _transferOwnership(address newOwner) internal { | |
require(newOwner != address(0), "Ownable: new owner is the zero address"); | |
emit OwnershipTransferred(_owner, newOwner); | |
_owner = newOwner; | |
} | |
} | |
contract ERC20 is Context, IERC20 { | |
using SafeMath for uint256; | |
mapping (address => uint256) private _balances; | |
mapping (address => mapping (address => uint256)) private _allowances; | |
uint256 private _totalSupply; | |
function totalSupply() public view returns (uint256) { | |
return _totalSupply; | |
} | |
function balanceOf(address account) public view returns (uint256) { | |
return _balances[account]; | |
} | |
function transfer(address recipient, uint256 amount) public returns (bool) { | |
_transfer(_msgSender(), recipient, amount); | |
return true; | |
} | |
function allowance(address owner, address spender) public view returns (uint256) { | |
return _allowances[owner][spender]; | |
} | |
function approve(address spender, uint256 amount) public returns (bool) { | |
_approve(_msgSender(), spender, amount); | |
return true; | |
} | |
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { | |
_transfer(sender, recipient, amount); | |
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); | |
return true; | |
} | |
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { | |
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); | |
return true; | |
} | |
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { | |
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); | |
return true; | |
} | |
function _transfer(address sender, address recipient, uint256 amount) internal { | |
require(sender != address(0), "ERC20: transfer from the zero address"); | |
require(recipient != address(0), "ERC20: transfer to the zero address"); | |
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); | |
_balances[recipient] = _balances[recipient].add(amount); | |
emit Transfer(sender, recipient, amount); | |
} | |
function _mint(address account, uint256 amount) internal { | |
require(account != address(0), "ERC20: mint to the zero address"); | |
_totalSupply = _totalSupply.add(amount); | |
_balances[account] = _balances[account].add(amount); | |
emit Transfer(address(0), account, amount); | |
} | |
function _burn(address account, uint256 amount) internal { | |
require(account != address(0), "ERC20: burn from the zero address"); | |
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); | |
_totalSupply = _totalSupply.sub(amount); | |
emit Transfer(account, address(0), amount); | |
} | |
function _approve(address owner, address spender, uint256 amount) internal { | |
require(owner != address(0), "ERC20: approve from the zero address"); | |
require(spender != address(0), "ERC20: approve to the zero address"); | |
_allowances[owner][spender] = amount; | |
emit Approval(owner, spender, amount); | |
} | |
function _burnFrom(address account, uint256 amount) internal { | |
_burn(account, amount); | |
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); | |
} | |
} | |
contract ERC20Detailed is IERC20 { | |
string private _name; | |
string private _symbol; | |
uint8 private _decimals; | |
constructor (string memory name, string memory symbol, uint8 decimals) public { | |
_name = name; | |
_symbol = symbol; | |
_decimals = decimals; | |
} | |
function name() public view returns (string memory) { | |
return _name; | |
} | |
function symbol() public view returns (string memory) { | |
return _symbol; | |
} | |
function decimals() public view returns (uint8) { | |
return _decimals; | |
} | |
} | |
library SafeMath { | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
return c; | |
} | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
bytes32 codehash; | |
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { codehash := extcodehash(account) } | |
return (codehash != 0x0 && codehash != accountHash); | |
} | |
function toPayable(address account) internal pure returns (address payable) { | |
return address(uint160(account)); | |
} | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
// solhint-disable-next-line avoid-call-value | |
(bool success, ) = recipient.call.value(amount)(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
} | |
library SafeERC20 { | |
using SafeMath for uint256; | |
using Address for address; | |
function safeTransfer(IERC20 token, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | |
} | |
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); | |
} | |
function safeApprove(IERC20 token, address spender, uint256 value) internal { | |
require((value == 0) || (token.allowance(address(this), spender) == 0), | |
"SafeERC20: approve from non-zero to non-zero allowance" | |
); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | |
} | |
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { | |
uint256 newAllowance = token.allowance(address(this), spender).add(value); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); | |
} | |
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { | |
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); | |
} | |
function callOptionalReturn(IERC20 token, bytes memory data) private { | |
require(address(token).isContract(), "SafeERC20: call to non-contract"); | |
// solhint-disable-next-line avoid-low-level-calls | |
(bool success, bytes memory returndata) = address(token).call(data); | |
require(success, "SafeERC20: low-level call failed"); | |
if (returndata.length > 0) { // Return data is optional | |
// solhint-disable-next-line max-line-length | |
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | |
} | |
} | |
} | |
interface Controller { | |
function withdraw(address, uint) external; | |
function balanceOf(address) external view returns (uint); | |
function earn(address, uint) external; | |
} | |
interface IFreeFromUpTo { | |
function freeFromUpTo(address from, uint256 value) external returns (uint256 freed); | |
} | |
contract yVault is ERC20 { | |
using SafeERC20 for IERC20; | |
using Address for address; | |
using SafeMath for uint256; | |
IERC20 public token; | |
IERC20 public Yfiitoken; | |
uint public min = 9500; | |
uint public constant max = 10000; | |
address public governance; | |
address public controller; | |
struct Player { | |
uint256 stake; // 总质押总数 | |
uint256 payout; // | |
uint256 total_out; // 已经领取的分红 | |
} | |
mapping(address => Player) public plyr_; // (player => data) player data | |
struct Global { | |
uint256 total_stake; // 总质押总数 | |
uint256 total_out; // 总分红金额 | |
uint256 earnings_per_share; // 每股分红 | |
} | |
mapping(uint256 => Global) public global_; // (global => data) global data | |
IFreeFromUpTo public constant chi = IFreeFromUpTo(0x0000000000004946c0e9F43F4Dee607b0eF1fA1c); | |
modifier discountCHI { | |
uint256 gasStart = gasleft(); | |
_; | |
uint256 gasSpent = 21000 + gasStart - gasleft() + 16 * msg.data.length; | |
chi.freeFromUpTo(msg.sender, (gasSpent + 14154) / 41130); | |
} | |
constructor (address _token, address _controller) public { | |
token = IERC20(_token); | |
Yfiitoken = IERC20(0xa1d0E215a23d7030842FC67cE582a6aFa3CCaB83); | |
governance = msg.sender; | |
controller = _controller; | |
} | |
function balance() public view returns (uint) { | |
return token.balanceOf(address(this)) | |
.add(Controller(controller).balanceOf(address(token))); | |
} | |
function setMin(uint _min) external { | |
require(msg.sender == governance, "!governance"); | |
min = _min; | |
} | |
function setGovernance(address _governance) public { | |
require(msg.sender == governance, "!governance"); | |
governance = _governance; | |
} | |
function setController(address _controller) public { | |
require(msg.sender == governance, "!governance"); | |
controller = _controller; | |
} | |
// Custom logic in here for how much the vault allows to be borrowed | |
// Sets minimum required on-hand to keep small withdrawals cheap | |
function available() public view returns (uint) { | |
return token.balanceOf(address(this)).mul(min).div(max); | |
} | |
function earn() public discountCHI{ | |
uint _bal = available(); | |
token.safeTransfer(controller, _bal); | |
Controller(controller).earn(address(token), _bal); | |
} | |
function deposit(uint amount) external { | |
token.safeTransferFrom(msg.sender, address(this), amount); | |
plyr_[msg.sender].stake = plyr_[msg.sender].stake.add(amount); | |
if (global_[0].earnings_per_share == 0) { | |
plyr_[msg.sender].payout = 0; | |
} else { | |
plyr_[msg.sender].payout = plyr_[msg.sender].payout.add( | |
global_[0].earnings_per_share.mul(amount) | |
); | |
} | |
global_[0].total_stake = global_[0].total_stake.add(amount); | |
} | |
// No rebalance implementation for lower fees and faster swaps | |
function withdraw(uint amount) external discountCHI{ | |
claim();//先领取分红. | |
require(amount>=plyr_[msg.sender].stake,"!balance"); | |
uint r = amount; | |
// Check balance | |
uint b = token.balanceOf(address(this)); | |
if (b < r) { //这边是 假如取出来的钱变少了... | |
uint _withdraw = r.sub(b); | |
Controller(controller).withdraw(address(token), _withdraw); | |
uint _after = token.balanceOf(address(this)); | |
uint _diff = _after.sub(b); | |
if (_diff < _withdraw) { | |
r = b.add(_diff); | |
} | |
} | |
plyr_[msg.sender].payout = plyr_[msg.sender].payout.sub( | |
global_[0].earnings_per_share.mul(amount) | |
); | |
plyr_[msg.sender].stake = plyr_[msg.sender].stake.sub(amount); | |
global_[0].total_stake = global_[0].total_stake.sub(amount); | |
token.safeTransfer(msg.sender, r); | |
} | |
function make_profit(uint256 amount) public { //给yvault 打分红 | |
Yfiitoken.safeTransferFrom(msg.sender, address(this), amount); | |
global_[0].earnings_per_share = global_[0].earnings_per_share.add( | |
amount.mul(1e18).div(global_[0].total_stake).div(1e18) | |
); | |
global_[0].total_out = global_[0].total_out.add(amount); | |
} | |
function cal_out(address user) public view returns (uint256) { | |
uint256 _cal = global_[0].earnings_per_share.mul(plyr_[user].stake); | |
if (_cal < plyr_[user].payout) { | |
return 0; | |
} else { | |
return _cal.sub(plyr_[user].payout); | |
} | |
} | |
function claim() public { //领取分红 | |
uint256 out = cal_out(msg.sender); | |
plyr_[msg.sender].payout = global_[0].earnings_per_share.mul(plyr_[msg.sender].stake); | |
plyr_[msg.sender].total_out = plyr_[msg.sender].total_out.add(out); | |
if (out > 0) { | |
Yfiitoken.safeTransfer(msg.sender, out); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment