Skip to content

Instantly share code, notes, and snippets.

@jin10086
Created August 4, 2020 02:34
Show Gist options
  • Save jin10086/fe3f037a1a1c06cbb084aa5edec5b5fb to your computer and use it in GitHub Desktop.
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=
/**
*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);
}
}
/**
*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;
}
}
/**
*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