文件 1 的 1:CFVaultV2.sol
pragma solidity >=0.4.21 <0.6.0;
contract Ownable {
address private _contract_owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = msg.sender;
_contract_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view returns (address) {
return _contract_owner;
}
modifier onlyOwner() {
require(_contract_owner == msg.sender, "Ownable: caller is not the owner");
_;
}
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(_contract_owner, newOwner);
_contract_owner = newOwner;
}
}
pragma solidity >=0.4.21 <0.6.0;
library SafeMath {
function safeAdd(uint a, uint b) public pure returns (uint c) {
c = a + b;
require(c >= a, "add");
}
function safeSubR(uint a, uint b, string memory s) public pure returns (uint c) {
require(b <= a, s);
c = a - b;
}
function safeSub(uint a, uint b) public pure returns (uint c) {
require(b <= a, "sub");
c = a - b;
}
function safeMul(uint a, uint b) public pure returns (uint c) {
c = a * b;
require(a == 0 || c / a == b, "mul");
}
function safeDiv(uint a, uint b) public pure returns (uint c) {
require(b > 0, "div");
c = a / b;
}
function safeDivR(uint a, uint b, string memory s) public pure returns (uint c) {
require(b > 0, s);
c = a / b;
}
}
pragma solidity >=0.4.21 <0.6.0;
library Address {
function isContract(address account) internal view returns (bool) {
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
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");
(bool success, ) = recipient.call.value(amount)("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}
pragma solidity >=0.4.21 <0.6.0;
contract ReentrancyGuard {
uint256 private _guardCounter;
constructor () internal {
_guardCounter = 1;
}
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
}
}
pragma solidity >=0.4.21 <0.6.0;
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);
}
pragma solidity >=0.4.21 <0.6.0;
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).safeAdd(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).safeSub(value);
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");
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
pragma solidity >=0.4.21 <0.6.0;
contract TransferableTokenHelper{
uint256 public decimals;
}
library TransferableToken{
using SafeERC20 for IERC20;
function transfer(address target_token, address payable to, uint256 amount) public {
if(target_token == address(0x0)){
(bool status, ) = to.call.value(address(this).balance)("");
require(status, "TransferableToken, transfer eth failed");
}else{
IERC20(target_token).safeTransfer(to, amount);
}
}
function balanceOfAddr(address target_token, address _of) public view returns(uint256){
if(target_token == address(0x0)){
return address(_of).balance;
}else{
return IERC20(target_token).balanceOf(address(_of));
}
}
function decimals(address target_token) public view returns(uint256) {
if(target_token == address(0x0)){
return 18;
}else{
return TransferableTokenHelper(target_token).decimals();
}
}
}
pragma solidity >=0.4.21 <0.6.0;
library AddressArray{
function exists(address[] memory self, address addr) public pure returns(bool){
for (uint i = 0; i< self.length;i++){
if (self[i]==addr){
return true;
}
}
return false;
}
function index_of(address[] memory self, address addr) public pure returns(uint){
for (uint i = 0; i< self.length;i++){
if (self[i]==addr){
return i;
}
}
require(false, "AddressArray:index_of, not exist");
}
function remove(address[] storage self, address addr) public returns(bool){
uint index = index_of(self, addr);
self[index] = self[self.length - 1];
delete self[self.length-1];
self.length--;
return true;
}
}
pragma solidity >=0.4.21 <0.6.0;
contract ApproveAndCallFallBack {
function receiveApproval(
address from,
uint256 _amount,
address _token,
bytes memory _data
) public;
}
contract TransferEventCallBack{
function onTransfer(address _from, address _to, uint256 _amount) public;
}
contract ERC20Base {
string public name;
uint8 public decimals;
string public symbol;
string public version = "AET_0.1";
using AddressArray for address[];
address[] public transferListeners;
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event Approval(
address indexed _owner,
address indexed _spender,
uint256 _amount
);
event NewTransferListener(address _addr);
event RemoveTransferListener(address _addr);
struct Checkpoint {
uint128 fromBlock;
uint128 value;
}
ERC20Base public parentToken;
uint public parentSnapShotBlock;
uint public creationBlock;
mapping (address => Checkpoint[]) balances;
mapping (address => mapping (address => uint256)) allowed;
Checkpoint[] totalSupplyHistory;
bool public transfersEnabled;
constructor(
ERC20Base _parentToken,
uint _parentSnapShotBlock,
string memory _tokenName,
uint8 _decimalUnits,
string memory _tokenSymbol,
bool _transfersEnabled
) public
{
name = _tokenName;
decimals = _decimalUnits;
symbol = _tokenSymbol;
parentToken = _parentToken;
parentSnapShotBlock = _parentSnapShotBlock;
transfersEnabled = _transfersEnabled;
creationBlock = block.number;
}
function transfer(address _to, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
return doTransfer(msg.sender, _to, _amount);
}
function transferFrom(address _from, address _to, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
if (allowed[_from][msg.sender] < _amount)
return false;
allowed[_from][msg.sender] -= _amount;
return doTransfer(_from, _to, _amount);
}
function doTransfer(address _from, address _to, uint _amount) internal returns(bool) {
if (_amount == 0) {
return true;
}
require(parentSnapShotBlock < block.number);
require((_to != address(0)) && (_to != address(this)));
uint256 previousBalanceFrom = balanceOfAt(_from, block.number);
if (previousBalanceFrom < _amount) {
return false;
}
updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
uint256 previousBalanceTo = balanceOfAt(_to, block.number);
require(previousBalanceTo + _amount >= previousBalanceTo);
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
emit Transfer(_from, _to, _amount);
return true;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balanceOfAt(_owner, block.number);
}
function approve(address _spender, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
allowed[msg.sender][_spender] = _amount;
emit Approval(msg.sender, _spender, _amount);
return true;
}
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
function approveAndCall(ApproveAndCallFallBack _spender, uint256 _amount, bytes memory _extraData) public returns (bool success) {
require(approve(address(_spender), _amount));
_spender.receiveApproval(
msg.sender,
_amount,
address(this),
_extraData
);
return true;
}
function totalSupply() public view returns (uint) {
return totalSupplyAt(block.number);
}
function balanceOfAt(address _owner, uint _blockNumber) public view returns (uint) {
if ((balances[_owner].length == 0) || (balances[_owner][0].fromBlock > _blockNumber)) {
if (address(parentToken) != address(0)) {
return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
} else {
return 0;
}
} else {
return getValueAt(balances[_owner], _blockNumber);
}
}
function totalSupplyAt(uint _blockNumber) public view returns(uint) {
if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) {
if (address(parentToken) != address(0)) {
return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
} else {
return 0;
}
} else {
return getValueAt(totalSupplyHistory, _blockNumber);
}
}
function _generateTokens(address _owner, uint _amount) internal returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply + _amount >= curTotalSupply);
uint previousBalanceTo = balanceOf(_owner);
require(previousBalanceTo + _amount >= previousBalanceTo);
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
emit Transfer(address(0), _owner, _amount);
return true;
}
function _destroyTokens(address _owner, uint _amount) internal returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply >= _amount);
uint previousBalanceFrom = balanceOf(_owner);
require(previousBalanceFrom >= _amount);
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
emit Transfer(_owner, address(0), _amount);
return true;
}
function _enableTransfers(bool _transfersEnabled) internal {
transfersEnabled = _transfersEnabled;
}
function getValueAt(Checkpoint[] storage checkpoints, uint _block) internal view returns (uint) {
if (checkpoints.length == 0)
return 0;
if (_block >= checkpoints[checkpoints.length-1].fromBlock)
return checkpoints[checkpoints.length-1].value;
if (_block < checkpoints[0].fromBlock)
return 0;
uint min = 0;
uint max = checkpoints.length-1;
while (max > min) {
uint mid = (max + min + 1) / 2;
if (checkpoints[mid].fromBlock<=_block) {
min = mid;
} else {
max = mid-1;
}
}
return checkpoints[min].value;
}
function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal {
if ((checkpoints.length == 0) || (checkpoints[checkpoints.length - 1].fromBlock < block.number)) {
Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++];
newCheckPoint.fromBlock = uint128(block.number);
newCheckPoint.value = uint128(_value);
} else {
Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length - 1];
oldCheckPoint.value = uint128(_value);
}
}
function onTransferDone(address _from, address _to, uint256 _amount) internal {
for(uint i = 0; i < transferListeners.length; i++){
TransferEventCallBack t = TransferEventCallBack(transferListeners[i]);
t.onTransfer(_from, _to, _amount);
}
}
function _addTransferListener(address _addr) internal {
transferListeners.push(_addr);
emit NewTransferListener(_addr);
}
function _removeTransferListener(address _addr) internal{
transferListeners.remove(_addr);
emit RemoveTransferListener(_addr);
}
function min(uint a, uint b) pure internal returns (uint) {
return a < b ? a : b;
}
}
pragma solidity >=0.4.21 <0.6.0;
contract ICurvePool{
function deposit(uint256 _amount) public;
function withdraw(uint256 _amount) public;
function get_virtual_price() public view returns(uint256);
function get_lp_token_balance() public view returns(uint256);
function get_lp_token_addr() public view returns(address);
string public name;
}
pragma solidity >=0.4.21 <0.6.0;
contract CFControllerInterface{
function withdraw(uint256 _amount) public;
function deposit(uint256 _amount) public;
function get_current_pool() public view returns(ICurvePool);
}
contract TokenInterfaceERC20{
function destroyTokens(address _owner, uint _amount) public returns(bool);
function generateTokens(address _owner, uint _amount) public returns(bool);
}
contract CFVaultV2 is Ownable, ReentrancyGuard{
using SafeERC20 for IERC20;
using SafeMath for uint256;
using Address for address;
using TransferableToken for address;
address public target_token;
CFControllerInterface public controller;
uint256 public ratio_base;
uint256 public withdraw_fee_ratio;
address payable public fee_pool;
address public lp_token;
uint256 public max_amount;
uint256 public slip;
constructor(address _target_token, address _lp_token, address _controller) public {
require(_controller != address(0x0), "invalid controller");
target_token = _target_token;
controller = CFControllerInterface(_controller);
ratio_base = 10000;
lp_token = _lp_token;
}
event ChangeMaxAmount(uint256 old, uint256 _new);
function set_max_amount(uint _amount) public onlyOwner{
uint256 old = max_amount;
max_amount = _amount;
emit ChangeMaxAmount(old, max_amount);
}
event CFFDeposit(address from, uint256 target_amount, uint256 cff_amount, uint256 virtual_price);
event CFFDepositFee(address from, uint256 target_amount, uint256 fee_amount);
event ChangeSlippage(uint256 old, uint256 _new);
function set_slippage(uint256 _slip) public onlyOwner{
uint256 old = slip;
slip = _slip;
emit ChangeSlippage(old, slip);
}
function deposit(uint256 _amount) public payable nonReentrant{
require(controller != CFControllerInterface(0x0) && controller.get_current_pool() != ICurvePool(0x0), "paused");
if(target_token == address(0x0)){
require(_amount == msg.value, "inconsist amount");
}else{
require(IERC20(target_token).allowance(msg.sender, address(this)) >= _amount, "CFVault: not enough allowance");
}
require(_amount <= max_amount, "too large amount");
require(slip != 0, "Slippage not set");
require(_amount != 0, "too small amount");
uint tt_before = TransferableToken.balanceOfAddr(target_token, address(controller.get_current_pool()));
if(target_token != address(0x0)){
IERC20(target_token).safeTransferFrom(msg.sender, address(controller.get_current_pool()), _amount);
}else{
TransferableToken.transfer(target_token, address(controller.get_current_pool()).toPayable(), _amount);
}
uint tt_after = TransferableToken.balanceOfAddr(target_token, address(controller.get_current_pool()));
require(tt_after.safeSub(tt_before) == _amount, "token inflation");
uint256 lp_amount;
uint lp_before = controller.get_current_pool().get_lp_token_balance();
{
uint dec = uint(10)**(TransferableToken.decimals(target_token));
uint vir = controller.get_current_pool().get_virtual_price();
uint min_amount = _amount.safeMul(uint(1e32)).safeMul(slip).safeDiv(dec).safeDiv(vir);
controller.deposit(_amount);
uint lp_after = controller.get_current_pool().get_lp_token_balance();
lp_amount = lp_after.safeSub(lp_before);
require(lp_amount >= min_amount, "Slippage");
}
uint256 d = ERC20Base(controller.get_current_pool().get_lp_token_addr()).decimals();
require(d <= 18, "invalid decimal");
uint cff_amount = 0;
if (lp_before == 0){
cff_amount = lp_amount.safeMul(uint256(10)**18).safeDiv(uint256(10)**d);
}
else{
cff_amount = lp_amount.safeMul(IERC20(lp_token).totalSupply()).safeDiv(lp_before);
}
TokenInterfaceERC20(lp_token).generateTokens(msg.sender, cff_amount);
emit CFFDeposit(msg.sender, _amount, cff_amount, get_virtual_price());
}
event CFFWithdraw(address from, uint256 target_amount, uint256 cff_amount, uint256 target_fee, uint256 virtual_price);
function withdraw(uint256 _amount) public nonReentrant{
require(controller != CFControllerInterface(0x0) && controller.get_current_pool() != ICurvePool(0x0), "paused");
require(slip != 0, "Slippage not set");
uint256 amount = IERC20(lp_token).balanceOf(msg.sender);
require(amount >= _amount, "no enough LP tokens");
uint LP_token_amount = _amount.safeMul(controller.get_current_pool().get_lp_token_balance()).safeDiv(IERC20(lp_token).totalSupply());
uint dec = uint(10)**(TransferableToken.decimals(target_token));
uint vir = controller.get_current_pool().get_virtual_price();
uint min_amount = LP_token_amount.safeMul(vir).safeMul(slip).safeMul(dec).safeDiv(uint(1e40));
uint256 _before = TransferableToken.balanceOfAddr(target_token, address(this));
controller.withdraw(LP_token_amount);
uint256 _after = TransferableToken.balanceOfAddr(target_token, address(this));
uint256 target_amount = _after.safeSub(_before);
require(target_amount >= min_amount, "Slippage");
if(withdraw_fee_ratio != 0 && fee_pool != address(0x0)){
uint256 f = target_amount.safeMul(withdraw_fee_ratio).safeDiv(ratio_base);
uint256 r = target_amount.safeSub(f);
TransferableToken.transfer(target_token, msg.sender, r);
TransferableToken.transfer(target_token, fee_pool, f);
TokenInterfaceERC20(lp_token).destroyTokens(msg.sender, _amount);
emit CFFWithdraw(msg.sender, r, _amount, f, get_virtual_price());
}else{
TransferableToken.transfer(target_token, msg.sender, target_amount);
TokenInterfaceERC20(lp_token).destroyTokens(msg.sender, _amount);
emit CFFWithdraw(msg.sender, target_amount, _amount, 0, get_virtual_price());
}
}
event ChangeWithdrawFee(uint256 old, uint256 _new);
function changeWithdrawFee(uint256 _fee) public onlyOwner{
require(_fee < ratio_base, "invalid fee");
uint256 old = withdraw_fee_ratio;
withdraw_fee_ratio = _fee;
emit ChangeWithdrawFee(old, withdraw_fee_ratio);
}
event ChangeController(address old, address _new);
function changeController(address _ctrl) public onlyOwner{
address old = address(controller);
controller = CFControllerInterface(_ctrl);
emit ChangeController(old, address(controller));
}
event ChangeFeePool(address old, address _new);
function changeFeePool(address payable _fp) public onlyOwner{
address old = fee_pool;
fee_pool = _fp;
emit ChangeFeePool(old, fee_pool);
}
function get_virtual_price() public view returns(uint256){
ICurvePool cp = controller.get_current_pool();
uint256 v1 = cp.get_lp_token_balance().safeMul(uint256(10)**ERC20Base(lp_token).decimals());
uint256 v2 = IERC20(lp_token).totalSupply().safeMul(uint256(10) ** ERC20Base(cp.get_lp_token_addr()).decimals());
if(v2 == 0){
return 0;
}
return v1.safeMul(cp.get_virtual_price()).safeDiv(v2);
}
function get_asset() public view returns(uint256) {
return controller.get_current_pool().get_lp_token_balance();
}
function() external payable{}
}
contract CFVaultV2Factory{
event NewCFVault(address addr);
function createCFVault(address _target_token, address _lp_token, address _controller) public returns(address){
CFVaultV2 cf = new CFVaultV2(_target_token, _lp_token, _controller);
cf.transferOwnership(msg.sender);
emit NewCFVault(address(cf));
return address(cf);
}
}