文件 1 的 7:3XBTCUSDVault.sol
pragma solidity >= 0.6.6;
import './ownable.sol';
import './SafeMath.sol';
import './IERC20.sol';
import './priceCalculatorInterface.sol';
import './vaultHelperInterface.sol';
import './priceAggregatorInterface.sol';
contract vault is Owned {
using SafeMath for uint256;
constructor() public {
priceAggregatorInterface(0x7196545d854D03D9c87B7588F6D9e1e42D876E95).registerVaultAggregator(0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c);
priceAggregator = priceAggregatorInterface(0xb658E8680c1E1f148fb09cDbB3Bd0d58F9c14c00);
priceCalculator = priceCalculatorInterface(0x80D129A01879422EB102c47Ed32DC6E8B123D05f);
vaultHelper = vaultHelperInterface(0x70873daAa742bEA6D0EDf03f4f85c615983C01D7);
synStakingProxy = 0x0070F3e1147c03a1Bb0caF80035B7c362D312119;
buyFee = 10**7;
sellFee = 10**7;
}
event PriceUpdate(
uint256 bullPrice,
uint256 bearPrice,
uint256 bullLiqEquity,
uint256 bearLiqEquity,
uint256 bullEquity,
uint256 bearEquity,
uint256 roundId,
bool updated
);
event TokenBuy(
address account,
address token,
uint256 tokensMinted,
uint256 ethin,
uint256 fees,
uint256 bonus
);
event TokenSell(
address account,
address token,
uint256 tokensBurned,
uint256 ethout,
uint256 fees,
uint256 penalty
);
event LiquidityAdd(
address account,
uint256 eth,
uint256 shares,
uint256 shareprice
);
event LiquidityRemove(
address account,
uint256 eth,
uint256 shares,
uint256 shareprice
);
modifier isActive() {
require(active == true);
if(active == true && !priceAggregator.roundIdCheck(address(this))) {
updatePrice();
}
_;
}
modifier updateIfActive() {
if(active == true && !priceAggregator.roundIdCheck(address(this))) {
updatePrice();
}
_;
}
bool private active;
uint256 constant private multiplier = 3;
address private bull;
address private bear;
uint256 private latestRoundId;
mapping(address => uint256) private price;
mapping(address => uint256) private equity;
uint256 private buyFee;
uint256 private sellFee;
uint256 private totalLiqShares;
uint256 private liqFees;
uint256 private balanceEquity;
mapping(address => uint256) private liqTokens;
mapping(address => uint256) private liqEquity;
mapping(address => uint256) private userShares;
priceAggregatorInterface public priceAggregator;
priceCalculatorInterface public priceCalculator;
vaultHelperInterface public vaultHelper;
address payable public synStakingProxy;
receive() external payable {}
function tokenBuy(address token, address account)
public
virtual
isActive()
{
uint256 ethin = getDepositEquity();
require(ethin > 0);
require(token == bull || token == bear);
IERC20 itkn = IERC20(token);
uint256 fees = ethin.mul(buyFee).div(10**9);
uint256 buyeth = ethin.sub(fees);
uint256 bonus = vaultHelper.getBonus(address(this), token, buyeth);
uint256 tokensToMint = buyeth.add(bonus).mul(10**18).div(price[token]);
equity[token] = equity[token].add(buyeth).add(bonus);
if(bonus != 0) balanceEquity = balanceEquity.sub(bonus);
payFees(fees);
itkn.mint(account, tokensToMint);
emit TokenBuy(account, token, tokensToMint, ethin, fees, bonus);
}
function tokenSell(address token, address payable account)
public
virtual
isActive()
{
IERC20 itkn = IERC20(token);
uint256 tokensToBurn = itkn.balanceOf(address(this));
require(tokensToBurn > 0);
require(token == bull || token == bear);
uint256 selleth = tokensToBurn.mul(price[token]).div(10**18);
uint256 penalty = vaultHelper.getPenalty(address(this), token, selleth);
uint256 fees = sellFee.mul(selleth.sub(penalty)).div(10**9);
uint256 ethout = selleth.sub(penalty).sub(fees);
equity[token] = equity[token].sub(selleth);
if(penalty != 0) balanceEquity = balanceEquity.add(penalty);
payFees(fees);
itkn.burn(tokensToBurn);
account.transfer(ethout);
emit TokenSell(account, token, tokensToBurn, ethout, fees, penalty);
}
function addLiquidity(address account)
public
payable
virtual
updateIfActive()
{
uint256 ethin = getDepositEquity();
(
uint256 bullEquity,
uint256 bearEquity,
uint256 bullTokens,
uint256 bearTokens
) = vaultHelper.getLiqAddTokens(address(this), ethin);
uint256 sharePrice = vaultHelper.getSharePrice(address(this));
uint256 resultingShares = ethin.mul(10**18).div(sharePrice);
liqEquity[bull] = liqEquity[bull].add(bullEquity);
liqEquity[bear] = liqEquity[bear].add(bearEquity);
liqTokens[bull] = liqTokens[bull].add(bullTokens);
liqTokens[bear] = liqTokens[bear].add(bearTokens);
userShares[account] = userShares[account].add(resultingShares);
totalLiqShares = totalLiqShares.add(resultingShares);
emit LiquidityAdd(account, ethin, resultingShares, sharePrice);
}
function removeLiquidity(uint256 shares)
public
virtual
updateIfActive()
{
require(shares <= userShares[msg.sender]);
(
uint256 bullEquity,
uint256 bearEquity,
uint256 bullTokens,
uint256 bearTokens,
uint256 feesPaid
) = vaultHelper.getLiqRemoveTokens(address(this), shares);
uint256 sharePrice = vaultHelper.getSharePrice(address(this));
uint256 resultingEth = bullEquity.add(bearEquity).add(feesPaid);
liqEquity[bull] = liqEquity[bull].sub(bullEquity);
liqEquity[bear] = liqEquity[bear].sub(bearEquity);
liqTokens[bull] = liqTokens[bull].sub(bullTokens);
liqTokens[bear] = liqTokens[bear].sub(bearTokens);
userShares[msg.sender] = userShares[msg.sender].sub(shares);
totalLiqShares = totalLiqShares.sub(shares);
liqFees = liqFees.sub(feesPaid);
msg.sender.transfer(resultingEth);
emit LiquidityRemove(msg.sender, resultingEth, shares, sharePrice);
}
function updatePrice()
public
{
require(active == true);
(
uint256[6] memory priceArray,
uint256 roundId,
bool updated
) = priceCalculator.getUpdatedPrice(address(this), latestRoundId);
if(updated == true) {
(
price[bull],
price[bear],
liqEquity[bull],
liqEquity[bear],
equity[bull],
equity[bear],
latestRoundId
) =
(
priceArray[0],
priceArray[1],
priceArray[2],
priceArray[3],
priceArray[4],
priceArray[5],
roundId
);
}
emit PriceUpdate(
price[bull],
price[bear],
liqEquity[bull],
liqEquity[bear],
equity[bull],
equity[bear],
latestRoundId,
updated
);
}
function payFees(uint256 amount) internal {
synStakingProxy.transfer(amount.div(2));
liqFees += amount.sub(amount.div(2));
}
function getActive() public view returns(bool) {return(active);}
function getMultiplier() public pure returns(uint256) {return(multiplier);}
function getBullToken() public view returns(address) {return(bull);}
function getBearToken() public view returns(address) {return(bear);}
function getLatestRoundId() public view returns(uint256) {return(latestRoundId);}
function getPrice(address token) public view returns(uint256) {return(price[token]);}
function getEquity(address token) public view returns(uint256) {return(equity[token]);}
function getBuyFee() public view returns(uint256) {return(buyFee);}
function getSellFee() public view returns(uint256) {return(sellFee);}
function getTotalLiqShares() public view returns(uint256) {return(totalLiqShares);}
function getLiqFees() public view returns(uint256) {return(liqFees);}
function getBalanceEquity() public view returns(uint256) {return(balanceEquity);}
function getLiqTokens(address token) public view returns(uint256) {return(liqTokens[token]);}
function getLiqEquity(address token) public view returns(uint256) {return(liqEquity[token]);}
function getUserShares(address account) public view returns(uint256) {return(userShares[account]);}
function getTotalEquity() public view returns(uint256) {
return(getTokenEquity(bear).add(getTokenEquity(bull)));
}
function getTokenEquity(address token) public view returns(uint256) {
return(equity[token].add(liqEquity[token]));
}
function getTokenLiqEquity(address token) public view returns(uint256) {
return(liqTokens[token].mul(price[token]).div(10**18));
}
function getDepositEquity() public view returns(uint256) {
return(address(this).balance.sub(liqFees.add(balanceEquity).add(getTotalEquity())));
}
function setTokens(address bearAddress, address bullAddress) public onlyOwner() {
require(bear == address(0) || bull == address(0));
(bull, bear) = (bullAddress, bearAddress);
(price[bull], price[bear]) = (10**16, 10**16);
}
function setActive(bool state, uint256 roundId) public onlyOwner() {
if(roundId != 0) {
advanceRoundId(roundId);
}
active = state;
}
function advanceRoundId(uint256 roundId) public onlyOwner() {
require(active == false);
require(roundId > latestRoundId);
( , uint256 lastRoundId) = priceAggregator.priceRequest(address(this), latestRoundId);
latestRoundId = lastRoundId >= roundId ? roundId : lastRoundId;
}
function setBuyFee(uint256 amount) public onlyOwner() {
require(amount <= 10**9);
buyFee = amount;
}
function setSellFee(uint256 amount) public onlyOwner() {
require(amount <= 10**7);
sellFee = amount;
}
}
文件 2 的 7:IERC20.sol
pragma solidity >= 0.6.4;
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);
function mint(address account, uint256 amount) external;
function burn(uint256 amount) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 3 的 7:SafeMath.sol
pragma solidity ^0.6.0;
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) {
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;
}
}
文件 4 的 7:ownable.sol
pragma solidity ^0.6.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
contract Owned is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 5 的 7:priceAggregatorInterface.sol
pragma solidity >= 0.6.6;
interface priceAggregatorInterface {
function registerVaultAggregator(address oracle) external;
function priceRequest(
address vault,
uint256 lastUpdated
)
external
view
returns(int256[] memory, uint256);
function roundIdCheck(address vault) external view returns(bool);
}
文件 6 的 7:priceCalculatorInterface.sol
pragma solidity >= 0.6.6;
interface priceCalculatorInterface {
function getUpdatedPrice(
address vault,
uint256 latestRoundId
)
external
view
returns(
uint256[6] memory latestPrice,
uint256 rRoundId,
bool updated
);
function getKFactor(
uint256 targetEquity,
uint256 bullEquity,
uint256 bearEquity,
uint256 totalEquity
)
external
view
returns(uint256 kFactor);
}
文件 7 的 7:vaultHelperInterface.sol
pragma solidity >= 0.6.6;
interface vaultHelperInterface {
function getBonus(address vault, address token, uint256 eth)
external
view
returns(uint256 bonus);
function getPenalty(address vault, address token, uint256 eth)
external
view
returns(uint256 penalty);
function getSharePrice(address vault)
external
view
returns(uint256 sharePrice);
function getLiqAddTokens(address vault, uint256 eth)
external
view
returns(
uint256 bullEquity,
uint256 bearEquity,
uint256 bullTokens,
uint256 bearTokens
);
function getLiqRemoveTokens(address vault, uint256 eth)
external
view
returns(
uint256 bullEquity,
uint256 bearEquity,
uint256 bullTokens,
uint256 bearTokens,
uint256 feesPaid
);
}
{
"compilationTarget": {
"3XBTCUSDVault.sol": "vault"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"eth","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareprice","type":"uint256"}],"name":"LiquidityAdd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"eth","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareprice","type":"uint256"}],"name":"LiquidityRemove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bullPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bearPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bullLiqEquity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bearLiqEquity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bullEquity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bearEquity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"updated","type":"bool"}],"name":"PriceUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensMinted","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bonus","type":"uint256"}],"name":"TokenBuy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensBurned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethout","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"TokenSell","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"advanceRoundId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalanceEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBearToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBullToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBuyFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDepositEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestRoundId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getLiqEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiqFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getLiqTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSellFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenLiqEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLiqShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getUserShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceAggregator","outputs":[{"internalType":"contract priceAggregatorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceCalculator","outputs":[{"internalType":"contract priceCalculatorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"setActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setBuyFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setSellFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bearAddress","type":"address"},{"internalType":"address","name":"bullAddress","type":"address"}],"name":"setTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"synStakingProxy","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"tokenBuy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address payable","name":"account","type":"address"}],"name":"tokenSell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updatePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultHelper","outputs":[{"internalType":"contract vaultHelperInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]