文件 1 的 29:AccessControl.sol
pragma solidity >=0.6.11;
import "../Utils/EnumerableSet.sol";
import "../Utils/Address.sol";
import "../Common/Context.sol";
abstract contract AccessControl is Context {
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 29:Address.sol
pragma solidity >=0.6.11 <0.9.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
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");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 3 的 29:AggregatorV3Interface.sol
pragma solidity >=0.6.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
文件 4 的 29:Babylonian.sol
pragma solidity >=0.6.11;
library Babylonian {
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
文件 5 的 29:ChainlinkETHUSDPriceConsumer.sol
pragma solidity >=0.6.11;
import "./AggregatorV3Interface.sol";
contract ChainlinkETHUSDPriceConsumer {
AggregatorV3Interface internal priceFeed;
constructor() public {
priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
}
function getLatestPrice() public view returns (int) {
(
,
int price,
,
,
) = priceFeed.latestRoundData();
return price;
}
function getDecimals() public view returns (uint8) {
return priceFeed.decimals();
}
}
文件 6 的 29:Context.sol
pragma solidity >=0.6.11;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return payable(msg.sender);
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 7 的 29:ERC20.sol
pragma solidity >=0.6.11;
import "../Common/Context.sol";
import "./IERC20.sol";
import "../Math/SafeMath.sol";
import "../Utils/Address.sol";
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
constructor (string memory __name, string memory __symbol) public {
_name = __name;
_symbol = __symbol;
_decimals = 18;
}
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;
}
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public virtual override 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 virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual 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 virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_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 virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_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 virtual {
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 virtual {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
文件 8 的 29:ERC20Custom.sol
pragma solidity >=0.6.11;
import "../Common/Context.sol";
import "./IERC20.sol";
import "../Math/SafeMath.sol";
import "../Utils/Address.sol";
contract ERC20Custom is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) internal _balances;
mapping (address => mapping (address => uint256)) internal _allowances;
uint256 private _totalSupply;
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public virtual override 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 virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual 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 virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_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 virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_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 virtual {
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 virtual {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
文件 9 的 29:EnumerableSet.sol
pragma solidity >=0.6.11;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping (bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
bytes32 lastvalue = set._values[lastIndex];
set._values[toDeleteIndex] = lastvalue;
set._indexes[lastvalue] = toDeleteIndex + 1;
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(bytes20(value)));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(bytes20(value)));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(bytes20(value)));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(bytes20(_at(set._inner, index)));
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
文件 10 的 29:FXS.sol
pragma solidity >=0.6.11;
import "../Common/Context.sol";
import "../ERC20/ERC20Custom.sol";
import "../ERC20/IERC20.sol";
import "../Frax/Frax.sol";
import "../Staking/Owned.sol";
import "../Math/SafeMath.sol";
import "../Governance/AccessControl.sol";
contract FRAXShares is ERC20Custom, AccessControl, Owned {
using SafeMath for uint256;
string public symbol;
string public name;
uint8 public constant decimals = 18;
address public FRAXStablecoinAdd;
uint256 public constant genesis_supply = 100000000e18;
address public oracle_address;
address public timelock_address;
FRAXStablecoin private FRAX;
bool public trackingVotes = true;
struct Checkpoint {
uint32 fromBlock;
uint96 votes;
}
mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
mapping (address => uint32) public numCheckpoints;
modifier onlyPools() {
require(FRAX.frax_pools(msg.sender) == true, "Only frax pools can mint new FRAX");
_;
}
modifier onlyByOwnerOrGovernance() {
require(msg.sender == owner || msg.sender == timelock_address, "You are not an owner or the governance timelock");
_;
}
constructor(
string memory _name,
string memory _symbol,
address _oracle_address,
address _creator_address,
address _timelock_address
) public Owned(_creator_address){
require((_oracle_address != address(0)) && (_timelock_address != address(0)), "Zero address detected");
name = _name;
symbol = _symbol;
oracle_address = _oracle_address;
timelock_address = _timelock_address;
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_mint(_creator_address, genesis_supply);
_writeCheckpoint(_creator_address, 0, 0, uint96(genesis_supply));
}
function setOracle(address new_oracle) external onlyByOwnerOrGovernance {
require(new_oracle != address(0), "Zero address detected");
oracle_address = new_oracle;
}
function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {
require(new_timelock != address(0), "Timelock address cannot be 0");
timelock_address = new_timelock;
}
function setFRAXAddress(address frax_contract_address) external onlyByOwnerOrGovernance {
require(frax_contract_address != address(0), "Zero address detected");
FRAX = FRAXStablecoin(frax_contract_address);
emit FRAXAddressSet(frax_contract_address);
}
function mint(address to, uint256 amount) public onlyPools {
_mint(to, amount);
}
function pool_mint(address m_address, uint256 m_amount) external onlyPools {
if(trackingVotes){
uint32 srcRepNum = numCheckpoints[address(this)];
uint96 srcRepOld = srcRepNum > 0 ? checkpoints[address(this)][srcRepNum - 1].votes : 0;
uint96 srcRepNew = add96(srcRepOld, uint96(m_amount), "pool_mint new votes overflows");
_writeCheckpoint(address(this), srcRepNum, srcRepOld, srcRepNew);
trackVotes(address(this), m_address, uint96(m_amount));
}
super._mint(m_address, m_amount);
emit FXSMinted(address(this), m_address, m_amount);
}
function pool_burn_from(address b_address, uint256 b_amount) external onlyPools {
if(trackingVotes){
trackVotes(b_address, address(this), uint96(b_amount));
uint32 srcRepNum = numCheckpoints[address(this)];
uint96 srcRepOld = srcRepNum > 0 ? checkpoints[address(this)][srcRepNum - 1].votes : 0;
uint96 srcRepNew = sub96(srcRepOld, uint96(b_amount), "pool_burn_from new votes underflows");
_writeCheckpoint(address(this), srcRepNum, srcRepOld, srcRepNew);
}
super._burnFrom(b_address, b_amount);
emit FXSBurned(b_address, address(this), b_amount);
}
function toggleVotes() external onlyByOwnerOrGovernance {
trackingVotes = !trackingVotes;
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
if(trackingVotes){
trackVotes(_msgSender(), recipient, uint96(amount));
}
_transfer(_msgSender(), recipient, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
if(trackingVotes){
trackVotes(sender, recipient, uint96(amount));
}
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
function getCurrentVotes(address account) external view returns (uint96) {
uint32 nCheckpoints = numCheckpoints[account];
return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
}
function getPriorVotes(address account, uint blockNumber) public view returns (uint96) {
require(blockNumber < block.number, "FXS::getPriorVotes: not yet determined");
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2;
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
function trackVotes(address srcRep, address dstRep, uint96 amount) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint32 srcRepNum = numCheckpoints[srcRep];
uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint96 srcRepNew = sub96(srcRepOld, amount, "FXS::_moveVotes: vote amount underflows");
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint32 dstRepNum = numCheckpoints[dstRep];
uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint96 dstRepNew = add96(dstRepOld, amount, "FXS::_moveVotes: vote amount overflows");
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function _writeCheckpoint(address voter, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal {
uint32 blockNumber = safe32(block.number, "FXS::_writeCheckpoint: block number exceeds 32 bits");
if (nCheckpoints > 0 && checkpoints[voter][nCheckpoints - 1].fromBlock == blockNumber) {
checkpoints[voter][nCheckpoints - 1].votes = newVotes;
} else {
checkpoints[voter][nCheckpoints] = Checkpoint(blockNumber, newVotes);
numCheckpoints[voter] = nCheckpoints + 1;
}
emit VoterVotesChanged(voter, oldVotes, newVotes);
}
function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
function safe96(uint n, string memory errorMessage) internal pure returns (uint96) {
require(n < 2**96, errorMessage);
return uint96(n);
}
function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
uint96 c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
require(b <= a, errorMessage);
return a - b;
}
event VoterVotesChanged(address indexed voter, uint previousBalance, uint newBalance);
event FXSBurned(address indexed from, address indexed to, uint256 amount);
event FXSMinted(address indexed from, address indexed to, uint256 amount);
event FRAXAddressSet(address addr);
}
文件 11 的 29:FixedPoint.sol
pragma solidity >=0.6.11;
import './Babylonian.sol';
library FixedPoint {
struct uq112x112 {
uint224 _x;
}
struct uq144x112 {
uint _x;
}
uint8 private constant RESOLUTION = 112;
uint private constant Q112 = uint(1) << RESOLUTION;
uint private constant Q224 = Q112 << RESOLUTION;
function encode(uint112 x) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(x) << RESOLUTION);
}
function encode144(uint144 x) internal pure returns (uq144x112 memory) {
return uq144x112(uint256(x) << RESOLUTION);
}
function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) {
require(x != 0, 'FixedPoint: DIV_BY_ZERO');
return uq112x112(self._x / uint224(x));
}
function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) {
uint z;
require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW");
return uq144x112(z);
}
function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) {
require(denominator > 0, "FixedPoint: DIV_BY_ZERO");
return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
}
function decode(uq112x112 memory self) internal pure returns (uint112) {
return uint112(self._x >> RESOLUTION);
}
function decode144(uq144x112 memory self) internal pure returns (uint144) {
return uint144(self._x >> RESOLUTION);
}
function reciprocal(uq112x112 memory self) internal pure returns (uq112x112 memory) {
require(self._x != 0, 'FixedPoint: ZERO_RECIPROCAL');
return uq112x112(uint224(Q224 / self._x));
}
function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(Babylonian.sqrt(uint256(self._x)) << 56));
}
}
文件 12 的 29:Frax.sol
pragma solidity >=0.6.11;
import "../Common/Context.sol";
import "../ERC20/IERC20.sol";
import "../ERC20/ERC20Custom.sol";
import "../ERC20/ERC20.sol";
import "../Math/SafeMath.sol";
import "../Staking/Owned.sol";
import "../FXS/FXS.sol";
import "./Pools/FraxPool.sol";
import "../Oracle/UniswapPairOracle.sol";
import "../Oracle/ChainlinkETHUSDPriceConsumer.sol";
import "../Governance/AccessControl.sol";
contract FRAXStablecoin is ERC20Custom, AccessControl, Owned {
using SafeMath for uint256;
enum PriceChoice { FRAX, FXS }
ChainlinkETHUSDPriceConsumer private eth_usd_pricer;
uint8 private eth_usd_pricer_decimals;
UniswapPairOracle private fraxEthOracle;
UniswapPairOracle private fxsEthOracle;
string public symbol;
string public name;
uint8 public constant decimals = 18;
address public creator_address;
address public timelock_address;
address public controller_address;
address public fxs_address;
address public frax_eth_oracle_address;
address public fxs_eth_oracle_address;
address public weth_address;
address public eth_usd_consumer_address;
uint256 public constant genesis_supply = 2000000e18;
address[] public frax_pools_array;
mapping(address => bool) public frax_pools;
uint256 private constant PRICE_PRECISION = 1e6;
uint256 public global_collateral_ratio;
uint256 public redemption_fee;
uint256 public minting_fee;
uint256 public frax_step;
uint256 public refresh_cooldown;
uint256 public price_target;
uint256 public price_band;
address public DEFAULT_ADMIN_ADDRESS;
bytes32 public constant COLLATERAL_RATIO_PAUSER = keccak256("COLLATERAL_RATIO_PAUSER");
bool public collateral_ratio_paused = false;
modifier onlyCollateralRatioPauser() {
require(hasRole(COLLATERAL_RATIO_PAUSER, msg.sender));
_;
}
modifier onlyPools() {
require(frax_pools[msg.sender] == true, "Only frax pools can call this function");
_;
}
modifier onlyByOwnerOrGovernance() {
require(msg.sender == owner || msg.sender == timelock_address || msg.sender == controller_address, "You are not the owner, controller, or the governance timelock");
_;
}
modifier onlyByOwnerGovernanceOrPool() {
require(
msg.sender == owner
|| msg.sender == timelock_address
|| frax_pools[msg.sender] == true,
"You are not the owner, the governance timelock, or a pool");
_;
}
constructor(
string memory _name,
string memory _symbol,
address _creator_address,
address _timelock_address
) public Owned(_creator_address){
require(_timelock_address != address(0), "Zero address detected");
name = _name;
symbol = _symbol;
creator_address = _creator_address;
timelock_address = _timelock_address;
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
DEFAULT_ADMIN_ADDRESS = _msgSender();
_mint(creator_address, genesis_supply);
grantRole(COLLATERAL_RATIO_PAUSER, creator_address);
grantRole(COLLATERAL_RATIO_PAUSER, timelock_address);
frax_step = 2500;
global_collateral_ratio = 1000000;
refresh_cooldown = 3600;
price_target = 1000000;
price_band = 5000;
}
function oracle_price(PriceChoice choice) internal view returns (uint256) {
uint256 __eth_usd_price = uint256(eth_usd_pricer.getLatestPrice()).mul(PRICE_PRECISION).div(uint256(10) ** eth_usd_pricer_decimals);
uint256 price_vs_eth = 0;
if (choice == PriceChoice.FRAX) {
price_vs_eth = uint256(fraxEthOracle.consult(weth_address, PRICE_PRECISION));
}
else if (choice == PriceChoice.FXS) {
price_vs_eth = uint256(fxsEthOracle.consult(weth_address, PRICE_PRECISION));
}
else revert("INVALID PRICE CHOICE. Needs to be either 0 (FRAX) or 1 (FXS)");
return __eth_usd_price.mul(PRICE_PRECISION).div(price_vs_eth);
}
function frax_price() public view returns (uint256) {
return oracle_price(PriceChoice.FRAX);
}
function fxs_price() public view returns (uint256) {
return oracle_price(PriceChoice.FXS);
}
function eth_usd_price() public view returns (uint256) {
return uint256(eth_usd_pricer.getLatestPrice()).mul(PRICE_PRECISION).div(uint256(10) ** eth_usd_pricer_decimals);
}
function frax_info() public view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) {
return (
oracle_price(PriceChoice.FRAX),
oracle_price(PriceChoice.FXS),
totalSupply(),
global_collateral_ratio,
globalCollateralValue(),
minting_fee,
redemption_fee,
uint256(eth_usd_pricer.getLatestPrice()).mul(PRICE_PRECISION).div(uint256(10) ** eth_usd_pricer_decimals)
);
}
function globalCollateralValue() public view returns (uint256) {
uint256 total_collateral_value_d18 = 0;
for (uint i = 0; i < frax_pools_array.length; i++){
if (frax_pools_array[i] != address(0)){
total_collateral_value_d18 = total_collateral_value_d18.add(FraxPool(frax_pools_array[i]).collatDollarBalance());
}
}
return total_collateral_value_d18;
}
uint256 public last_call_time;
function refreshCollateralRatio() public {
require(collateral_ratio_paused == false, "Collateral Ratio has been paused");
uint256 frax_price_cur = frax_price();
require(block.timestamp - last_call_time >= refresh_cooldown, "Must wait for the refresh cooldown since last refresh");
if (frax_price_cur > price_target.add(price_band)) {
if(global_collateral_ratio <= frax_step){
global_collateral_ratio = 0;
} else {
global_collateral_ratio = global_collateral_ratio.sub(frax_step);
}
} else if (frax_price_cur < price_target.sub(price_band)) {
if(global_collateral_ratio.add(frax_step) >= 1000000){
global_collateral_ratio = 1000000;
} else {
global_collateral_ratio = global_collateral_ratio.add(frax_step);
}
}
last_call_time = block.timestamp;
emit CollateralRatioRefreshed(global_collateral_ratio);
}
function pool_burn_from(address b_address, uint256 b_amount) public onlyPools {
super._burnFrom(b_address, b_amount);
emit FRAXBurned(b_address, msg.sender, b_amount);
}
function pool_mint(address m_address, uint256 m_amount) public onlyPools {
super._mint(m_address, m_amount);
emit FRAXMinted(msg.sender, m_address, m_amount);
}
function addPool(address pool_address) public onlyByOwnerOrGovernance {
require(pool_address != address(0), "Zero address detected");
require(frax_pools[pool_address] == false, "address already exists");
frax_pools[pool_address] = true;
frax_pools_array.push(pool_address);
emit PoolAdded(pool_address);
}
function removePool(address pool_address) public onlyByOwnerOrGovernance {
require(pool_address != address(0), "Zero address detected");
require(frax_pools[pool_address] == true, "address doesn't exist already");
delete frax_pools[pool_address];
for (uint i = 0; i < frax_pools_array.length; i++){
if (frax_pools_array[i] == pool_address) {
frax_pools_array[i] = address(0);
break;
}
}
emit PoolRemoved(pool_address);
}
function setRedemptionFee(uint256 red_fee) public onlyByOwnerOrGovernance {
redemption_fee = red_fee;
emit RedemptionFeeSet(red_fee);
}
function setMintingFee(uint256 min_fee) public onlyByOwnerOrGovernance {
minting_fee = min_fee;
emit MintingFeeSet(min_fee);
}
function setFraxStep(uint256 _new_step) public onlyByOwnerOrGovernance {
frax_step = _new_step;
emit FraxStepSet(_new_step);
}
function setPriceTarget (uint256 _new_price_target) public onlyByOwnerOrGovernance {
price_target = _new_price_target;
emit PriceTargetSet(_new_price_target);
}
function setRefreshCooldown(uint256 _new_cooldown) public onlyByOwnerOrGovernance {
refresh_cooldown = _new_cooldown;
emit RefreshCooldownSet(_new_cooldown);
}
function setFXSAddress(address _fxs_address) public onlyByOwnerOrGovernance {
require(_fxs_address != address(0), "Zero address detected");
fxs_address = _fxs_address;
emit FXSAddressSet(_fxs_address);
}
function setETHUSDOracle(address _eth_usd_consumer_address) public onlyByOwnerOrGovernance {
require(_eth_usd_consumer_address != address(0), "Zero address detected");
eth_usd_consumer_address = _eth_usd_consumer_address;
eth_usd_pricer = ChainlinkETHUSDPriceConsumer(eth_usd_consumer_address);
eth_usd_pricer_decimals = eth_usd_pricer.getDecimals();
emit ETHUSDOracleSet(_eth_usd_consumer_address);
}
function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {
require(new_timelock != address(0), "Zero address detected");
timelock_address = new_timelock;
emit TimelockSet(new_timelock);
}
function setController(address _controller_address) external onlyByOwnerOrGovernance {
require(_controller_address != address(0), "Zero address detected");
controller_address = _controller_address;
emit ControllerSet(_controller_address);
}
function setPriceBand(uint256 _price_band) external onlyByOwnerOrGovernance {
price_band = _price_band;
emit PriceBandSet(_price_band);
}
function setFRAXEthOracle(address _frax_oracle_addr, address _weth_address) public onlyByOwnerOrGovernance {
require((_frax_oracle_addr != address(0)) && (_weth_address != address(0)), "Zero address detected");
frax_eth_oracle_address = _frax_oracle_addr;
fraxEthOracle = UniswapPairOracle(_frax_oracle_addr);
weth_address = _weth_address;
emit FRAXETHOracleSet(_frax_oracle_addr, _weth_address);
}
function setFXSEthOracle(address _fxs_oracle_addr, address _weth_address) public onlyByOwnerOrGovernance {
require((_fxs_oracle_addr != address(0)) && (_weth_address != address(0)), "Zero address detected");
fxs_eth_oracle_address = _fxs_oracle_addr;
fxsEthOracle = UniswapPairOracle(_fxs_oracle_addr);
weth_address = _weth_address;
emit FXSEthOracleSet(_fxs_oracle_addr, _weth_address);
}
function toggleCollateralRatio() public onlyCollateralRatioPauser {
collateral_ratio_paused = !collateral_ratio_paused;
emit CollateralRatioToggled(collateral_ratio_paused);
}
event FRAXBurned(address indexed from, address indexed to, uint256 amount);
event FRAXMinted(address indexed from, address indexed to, uint256 amount);
event CollateralRatioRefreshed(uint256 global_collateral_ratio);
event PoolAdded(address pool_address);
event PoolRemoved(address pool_address);
event RedemptionFeeSet(uint256 red_fee);
event MintingFeeSet(uint256 min_fee);
event FraxStepSet(uint256 new_step);
event PriceTargetSet(uint256 new_price_target);
event RefreshCooldownSet(uint256 new_cooldown);
event FXSAddressSet(address _fxs_address);
event ETHUSDOracleSet(address eth_usd_consumer_address);
event TimelockSet(address new_timelock);
event ControllerSet(address controller_address);
event PriceBandSet(uint256 price_band);
event FRAXETHOracleSet(address frax_oracle_addr, address weth_address);
event FXSEthOracleSet(address fxs_oracle_addr, address weth_address);
event CollateralRatioToggled(bool collateral_ratio_paused);
}
文件 13 的 29:FraxPool.sol
pragma solidity >=0.6.11;
import "../../Math/SafeMath.sol";
import '../../Uniswap/TransferHelper.sol';
import "../../Staking/Owned.sol";
import "../../FXS/FXS.sol";
import "../../Frax/Frax.sol";
import "../../ERC20/ERC20.sol";
import "../../Oracle/UniswapPairOracle.sol";
import "../../Governance/AccessControl.sol";
import "./FraxPoolLibrary.sol";
contract FraxPool is AccessControl, Owned {
using SafeMath for uint256;
ERC20 private collateral_token;
address private collateral_address;
address private frax_contract_address;
address private fxs_contract_address;
address private timelock_address;
FRAXShares private FXS;
FRAXStablecoin private FRAX;
UniswapPairOracle private collatEthOracle;
address public collat_eth_oracle_address;
address private weth_address;
uint256 public minting_fee;
uint256 public redemption_fee;
uint256 public buyback_fee;
uint256 public recollat_fee;
mapping (address => uint256) public redeemFXSBalances;
mapping (address => uint256) public redeemCollateralBalances;
uint256 public unclaimedPoolCollateral;
uint256 public unclaimedPoolFXS;
mapping (address => uint256) public lastRedeemed;
uint256 private constant PRICE_PRECISION = 1e6;
uint256 private constant COLLATERAL_RATIO_PRECISION = 1e6;
uint256 private constant COLLATERAL_RATIO_MAX = 1e6;
uint256 private immutable missing_decimals;
uint256 public pool_ceiling = 0;
uint256 public pausedPrice = 0;
uint256 public bonus_rate = 7500;
uint256 public redemption_delay = 1;
bytes32 private constant MINT_PAUSER = keccak256("MINT_PAUSER");
bytes32 private constant REDEEM_PAUSER = keccak256("REDEEM_PAUSER");
bytes32 private constant BUYBACK_PAUSER = keccak256("BUYBACK_PAUSER");
bytes32 private constant RECOLLATERALIZE_PAUSER = keccak256("RECOLLATERALIZE_PAUSER");
bytes32 private constant COLLATERAL_PRICE_PAUSER = keccak256("COLLATERAL_PRICE_PAUSER");
bool public mintPaused = false;
bool public redeemPaused = false;
bool public recollateralizePaused = false;
bool public buyBackPaused = false;
bool public collateralPricePaused = false;
modifier onlyByOwnerOrGovernance() {
require(msg.sender == timelock_address || msg.sender == owner, "You are not the owner or the governance timelock");
_;
}
modifier notRedeemPaused() {
require(redeemPaused == false, "Redeeming is paused");
_;
}
modifier notMintPaused() {
require(mintPaused == false, "Minting is paused");
_;
}
constructor(
address _frax_contract_address,
address _fxs_contract_address,
address _collateral_address,
address _creator_address,
address _timelock_address,
uint256 _pool_ceiling
) public Owned(_creator_address){
require(
(_frax_contract_address != address(0))
&& (_fxs_contract_address != address(0))
&& (_collateral_address != address(0))
&& (_creator_address != address(0))
&& (_timelock_address != address(0))
, "Zero address detected");
FRAX = FRAXStablecoin(_frax_contract_address);
FXS = FRAXShares(_fxs_contract_address);
frax_contract_address = _frax_contract_address;
fxs_contract_address = _fxs_contract_address;
collateral_address = _collateral_address;
timelock_address = _timelock_address;
collateral_token = ERC20(_collateral_address);
pool_ceiling = _pool_ceiling;
missing_decimals = uint(18).sub(collateral_token.decimals());
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
grantRole(MINT_PAUSER, timelock_address);
grantRole(REDEEM_PAUSER, timelock_address);
grantRole(RECOLLATERALIZE_PAUSER, timelock_address);
grantRole(BUYBACK_PAUSER, timelock_address);
grantRole(COLLATERAL_PRICE_PAUSER, timelock_address);
}
function collatDollarBalance() public view returns (uint256) {
if(collateralPricePaused == true){
return (collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral)).mul(10 ** missing_decimals).mul(pausedPrice).div(PRICE_PRECISION);
} else {
uint256 eth_usd_price = FRAX.eth_usd_price();
uint256 eth_collat_price = collatEthOracle.consult(weth_address, (PRICE_PRECISION * (10 ** missing_decimals)));
uint256 collat_usd_price = eth_usd_price.mul(PRICE_PRECISION).div(eth_collat_price);
return (collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral)).mul(10 ** missing_decimals).mul(collat_usd_price).div(PRICE_PRECISION);
}
}
function availableExcessCollatDV() public view returns (uint256) {
uint256 total_supply = FRAX.totalSupply();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
uint256 global_collat_value = FRAX.globalCollateralValue();
if (global_collateral_ratio > COLLATERAL_RATIO_PRECISION) global_collateral_ratio = COLLATERAL_RATIO_PRECISION;
uint256 required_collat_dollar_value_d18 = (total_supply.mul(global_collateral_ratio)).div(COLLATERAL_RATIO_PRECISION);
if (global_collat_value > required_collat_dollar_value_d18) return global_collat_value.sub(required_collat_dollar_value_d18);
else return 0;
}
function getCollateralPrice() public view returns (uint256) {
if(collateralPricePaused == true){
return pausedPrice;
} else {
uint256 eth_usd_price = FRAX.eth_usd_price();
return eth_usd_price.mul(PRICE_PRECISION).div(collatEthOracle.consult(weth_address, PRICE_PRECISION * (10 ** missing_decimals)));
}
}
function setCollatETHOracle(address _collateral_weth_oracle_address, address _weth_address) external onlyByOwnerOrGovernance {
collat_eth_oracle_address = _collateral_weth_oracle_address;
collatEthOracle = UniswapPairOracle(_collateral_weth_oracle_address);
weth_address = _weth_address;
}
function mint1t1FRAX(uint256 collateral_amount, uint256 FRAX_out_min) external notMintPaused {
uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals);
require(FRAX.global_collateral_ratio() >= COLLATERAL_RATIO_MAX, "Collateral ratio must be >= 1");
require((collateral_token.balanceOf(address(this))).sub(unclaimedPoolCollateral).add(collateral_amount) <= pool_ceiling, "[Pool's Closed]: Ceiling reached");
(uint256 frax_amount_d18) = FraxPoolLibrary.calcMint1t1FRAX(
getCollateralPrice(),
collateral_amount_d18
);
frax_amount_d18 = (frax_amount_d18.mul(uint(1e6).sub(minting_fee))).div(1e6);
require(FRAX_out_min <= frax_amount_d18, "Slippage limit reached");
TransferHelper.safeTransferFrom(address(collateral_token), msg.sender, address(this), collateral_amount);
FRAX.pool_mint(msg.sender, frax_amount_d18);
}
function mintAlgorithmicFRAX(uint256 fxs_amount_d18, uint256 FRAX_out_min) external notMintPaused {
uint256 fxs_price = FRAX.fxs_price();
require(FRAX.global_collateral_ratio() == 0, "Collateral ratio must be 0");
(uint256 frax_amount_d18) = FraxPoolLibrary.calcMintAlgorithmicFRAX(
fxs_price,
fxs_amount_d18
);
frax_amount_d18 = (frax_amount_d18.mul(uint(1e6).sub(minting_fee))).div(1e6);
require(FRAX_out_min <= frax_amount_d18, "Slippage limit reached");
FXS.pool_burn_from(msg.sender, fxs_amount_d18);
FRAX.pool_mint(msg.sender, frax_amount_d18);
}
function mintFractionalFRAX(uint256 collateral_amount, uint256 fxs_amount, uint256 FRAX_out_min) external notMintPaused {
uint256 fxs_price = FRAX.fxs_price();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
require(global_collateral_ratio < COLLATERAL_RATIO_MAX && global_collateral_ratio > 0, "Collateral ratio needs to be between .000001 and .999999");
require(collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral).add(collateral_amount) <= pool_ceiling, "Pool ceiling reached, no more FRAX can be minted with this collateral");
uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals);
FraxPoolLibrary.MintFF_Params memory input_params = FraxPoolLibrary.MintFF_Params(
fxs_price,
getCollateralPrice(),
fxs_amount,
collateral_amount_d18,
global_collateral_ratio
);
(uint256 mint_amount, uint256 fxs_needed) = FraxPoolLibrary.calcMintFractionalFRAX(input_params);
mint_amount = (mint_amount.mul(uint(1e6).sub(minting_fee))).div(1e6);
require(FRAX_out_min <= mint_amount, "Slippage limit reached");
require(fxs_needed <= fxs_amount, "Not enough FXS inputted");
FXS.pool_burn_from(msg.sender, fxs_needed);
TransferHelper.safeTransferFrom(address(collateral_token), msg.sender, address(this), collateral_amount);
FRAX.pool_mint(msg.sender, mint_amount);
}
function redeem1t1FRAX(uint256 FRAX_amount, uint256 COLLATERAL_out_min) external notRedeemPaused {
require(FRAX.global_collateral_ratio() == COLLATERAL_RATIO_MAX, "Collateral ratio must be == 1");
uint256 FRAX_amount_precision = FRAX_amount.div(10 ** missing_decimals);
(uint256 collateral_needed) = FraxPoolLibrary.calcRedeem1t1FRAX(
getCollateralPrice(),
FRAX_amount_precision
);
collateral_needed = (collateral_needed.mul(uint(1e6).sub(redemption_fee))).div(1e6);
require(collateral_needed <= collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral), "Not enough collateral in pool");
require(COLLATERAL_out_min <= collateral_needed, "Slippage limit reached");
redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender].add(collateral_needed);
unclaimedPoolCollateral = unclaimedPoolCollateral.add(collateral_needed);
lastRedeemed[msg.sender] = block.number;
FRAX.pool_burn_from(msg.sender, FRAX_amount);
}
function redeemFractionalFRAX(uint256 FRAX_amount, uint256 FXS_out_min, uint256 COLLATERAL_out_min) external notRedeemPaused {
uint256 fxs_price = FRAX.fxs_price();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
require(global_collateral_ratio < COLLATERAL_RATIO_MAX && global_collateral_ratio > 0, "Collateral ratio needs to be between .000001 and .999999");
uint256 col_price_usd = getCollateralPrice();
uint256 FRAX_amount_post_fee = (FRAX_amount.mul(uint(1e6).sub(redemption_fee))).div(PRICE_PRECISION);
uint256 fxs_dollar_value_d18 = FRAX_amount_post_fee.sub(FRAX_amount_post_fee.mul(global_collateral_ratio).div(PRICE_PRECISION));
uint256 fxs_amount = fxs_dollar_value_d18.mul(PRICE_PRECISION).div(fxs_price);
uint256 FRAX_amount_precision = FRAX_amount_post_fee.div(10 ** missing_decimals);
uint256 collateral_dollar_value = FRAX_amount_precision.mul(global_collateral_ratio).div(PRICE_PRECISION);
uint256 collateral_amount = collateral_dollar_value.mul(PRICE_PRECISION).div(col_price_usd);
require(collateral_amount <= collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral), "Not enough collateral in pool");
require(COLLATERAL_out_min <= collateral_amount, "Slippage limit reached [collateral]");
require(FXS_out_min <= fxs_amount, "Slippage limit reached [FXS]");
redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender].add(collateral_amount);
unclaimedPoolCollateral = unclaimedPoolCollateral.add(collateral_amount);
redeemFXSBalances[msg.sender] = redeemFXSBalances[msg.sender].add(fxs_amount);
unclaimedPoolFXS = unclaimedPoolFXS.add(fxs_amount);
lastRedeemed[msg.sender] = block.number;
FRAX.pool_burn_from(msg.sender, FRAX_amount);
FXS.pool_mint(address(this), fxs_amount);
}
function redeemAlgorithmicFRAX(uint256 FRAX_amount, uint256 FXS_out_min) external notRedeemPaused {
uint256 fxs_price = FRAX.fxs_price();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
require(global_collateral_ratio == 0, "Collateral ratio must be 0");
uint256 fxs_dollar_value_d18 = FRAX_amount;
fxs_dollar_value_d18 = (fxs_dollar_value_d18.mul(uint(1e6).sub(redemption_fee))).div(PRICE_PRECISION);
uint256 fxs_amount = fxs_dollar_value_d18.mul(PRICE_PRECISION).div(fxs_price);
redeemFXSBalances[msg.sender] = redeemFXSBalances[msg.sender].add(fxs_amount);
unclaimedPoolFXS = unclaimedPoolFXS.add(fxs_amount);
lastRedeemed[msg.sender] = block.number;
require(FXS_out_min <= fxs_amount, "Slippage limit reached");
FRAX.pool_burn_from(msg.sender, FRAX_amount);
FXS.pool_mint(address(this), fxs_amount);
}
function collectRedemption() external {
require((lastRedeemed[msg.sender].add(redemption_delay)) <= block.number, "Must wait for redemption_delay blocks before collecting redemption");
bool sendFXS = false;
bool sendCollateral = false;
uint FXSAmount = 0;
uint CollateralAmount = 0;
if(redeemFXSBalances[msg.sender] > 0){
FXSAmount = redeemFXSBalances[msg.sender];
redeemFXSBalances[msg.sender] = 0;
unclaimedPoolFXS = unclaimedPoolFXS.sub(FXSAmount);
sendFXS = true;
}
if(redeemCollateralBalances[msg.sender] > 0){
CollateralAmount = redeemCollateralBalances[msg.sender];
redeemCollateralBalances[msg.sender] = 0;
unclaimedPoolCollateral = unclaimedPoolCollateral.sub(CollateralAmount);
sendCollateral = true;
}
if(sendFXS){
TransferHelper.safeTransfer(address(FXS), msg.sender, FXSAmount);
}
if(sendCollateral){
TransferHelper.safeTransfer(address(collateral_token), msg.sender, CollateralAmount);
}
}
function recollateralizeFRAX(uint256 collateral_amount, uint256 FXS_out_min) external {
require(recollateralizePaused == false, "Recollateralize is paused");
uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals);
uint256 fxs_price = FRAX.fxs_price();
uint256 frax_total_supply = FRAX.totalSupply();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
uint256 global_collat_value = FRAX.globalCollateralValue();
(uint256 collateral_units, uint256 amount_to_recollat) = FraxPoolLibrary.calcRecollateralizeFRAXInner(
collateral_amount_d18,
getCollateralPrice(),
global_collat_value,
frax_total_supply,
global_collateral_ratio
);
uint256 collateral_units_precision = collateral_units.div(10 ** missing_decimals);
uint256 fxs_paid_back = amount_to_recollat.mul(uint(1e6).add(bonus_rate).sub(recollat_fee)).div(fxs_price);
require(FXS_out_min <= fxs_paid_back, "Slippage limit reached");
TransferHelper.safeTransferFrom(address(collateral_token), msg.sender, address(this), collateral_units_precision);
FXS.pool_mint(msg.sender, fxs_paid_back);
}
function buyBackFXS(uint256 FXS_amount, uint256 COLLATERAL_out_min) external {
require(buyBackPaused == false, "Buyback is paused");
uint256 fxs_price = FRAX.fxs_price();
FraxPoolLibrary.BuybackFXS_Params memory input_params = FraxPoolLibrary.BuybackFXS_Params(
availableExcessCollatDV(),
fxs_price,
getCollateralPrice(),
FXS_amount
);
(uint256 collateral_equivalent_d18) = (FraxPoolLibrary.calcBuyBackFXS(input_params)).mul(uint(1e6).sub(buyback_fee)).div(1e6);
uint256 collateral_precision = collateral_equivalent_d18.div(10 ** missing_decimals);
require(COLLATERAL_out_min <= collateral_precision, "Slippage limit reached");
FXS.pool_burn_from(msg.sender, FXS_amount);
TransferHelper.safeTransfer(address(collateral_token), msg.sender, collateral_precision);
}
function toggleMinting() external {
require(hasRole(MINT_PAUSER, msg.sender));
mintPaused = !mintPaused;
emit MintingToggled(mintPaused);
}
function toggleRedeeming() external {
require(hasRole(REDEEM_PAUSER, msg.sender));
redeemPaused = !redeemPaused;
emit RedeemingToggled(redeemPaused);
}
function toggleRecollateralize() external {
require(hasRole(RECOLLATERALIZE_PAUSER, msg.sender));
recollateralizePaused = !recollateralizePaused;
emit RecollateralizeToggled(recollateralizePaused);
}
function toggleBuyBack() external {
require(hasRole(BUYBACK_PAUSER, msg.sender));
buyBackPaused = !buyBackPaused;
emit BuybackToggled(buyBackPaused);
}
function toggleCollateralPrice(uint256 _new_price) external {
require(hasRole(COLLATERAL_PRICE_PAUSER, msg.sender));
if(collateralPricePaused == false){
pausedPrice = _new_price;
} else {
pausedPrice = 0;
}
collateralPricePaused = !collateralPricePaused;
emit CollateralPriceToggled(collateralPricePaused);
}
function setPoolParameters(uint256 new_ceiling, uint256 new_bonus_rate, uint256 new_redemption_delay, uint256 new_mint_fee, uint256 new_redeem_fee, uint256 new_buyback_fee, uint256 new_recollat_fee) external onlyByOwnerOrGovernance {
pool_ceiling = new_ceiling;
bonus_rate = new_bonus_rate;
redemption_delay = new_redemption_delay;
minting_fee = new_mint_fee;
redemption_fee = new_redeem_fee;
buyback_fee = new_buyback_fee;
recollat_fee = new_recollat_fee;
emit PoolParametersSet(new_ceiling, new_bonus_rate, new_redemption_delay, new_mint_fee, new_redeem_fee, new_buyback_fee, new_recollat_fee);
}
function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {
timelock_address = new_timelock;
emit TimelockSet(new_timelock);
}
event PoolParametersSet(uint256 new_ceiling, uint256 new_bonus_rate, uint256 new_redemption_delay, uint256 new_mint_fee, uint256 new_redeem_fee, uint256 new_buyback_fee, uint256 new_recollat_fee);
event TimelockSet(address new_timelock);
event MintingToggled(bool toggled);
event RedeemingToggled(bool toggled);
event RecollateralizeToggled(bool toggled);
event BuybackToggled(bool toggled);
event CollateralPriceToggled(bool toggled);
}
文件 14 的 29:FraxPoolLibrary.sol
pragma solidity >=0.6.11;
import "../../Math/SafeMath.sol";
library FraxPoolLibrary {
using SafeMath for uint256;
uint256 private constant PRICE_PRECISION = 1e6;
struct MintFF_Params {
uint256 fxs_price_usd;
uint256 col_price_usd;
uint256 fxs_amount;
uint256 collateral_amount;
uint256 col_ratio;
}
struct BuybackFXS_Params {
uint256 excess_collateral_dollar_value_d18;
uint256 fxs_price_usd;
uint256 col_price_usd;
uint256 FXS_amount;
}
function calcMint1t1FRAX(uint256 col_price, uint256 collateral_amount_d18) public pure returns (uint256) {
return (collateral_amount_d18.mul(col_price)).div(1e6);
}
function calcMintAlgorithmicFRAX(uint256 fxs_price_usd, uint256 fxs_amount_d18) public pure returns (uint256) {
return fxs_amount_d18.mul(fxs_price_usd).div(1e6);
}
function calcMintFractionalFRAX(MintFF_Params memory params) internal pure returns (uint256, uint256) {
uint256 fxs_dollar_value_d18;
uint256 c_dollar_value_d18;
{
fxs_dollar_value_d18 = params.fxs_amount.mul(params.fxs_price_usd).div(1e6);
c_dollar_value_d18 = params.collateral_amount.mul(params.col_price_usd).div(1e6);
}
uint calculated_fxs_dollar_value_d18 =
(c_dollar_value_d18.mul(1e6).div(params.col_ratio))
.sub(c_dollar_value_d18);
uint calculated_fxs_needed = calculated_fxs_dollar_value_d18.mul(1e6).div(params.fxs_price_usd);
return (
c_dollar_value_d18.add(calculated_fxs_dollar_value_d18),
calculated_fxs_needed
);
}
function calcRedeem1t1FRAX(uint256 col_price_usd, uint256 FRAX_amount) public pure returns (uint256) {
return FRAX_amount.mul(1e6).div(col_price_usd);
}
function calcBuyBackFXS(BuybackFXS_Params memory params) internal pure returns (uint256) {
require(params.excess_collateral_dollar_value_d18 > 0, "No excess collateral to buy back!");
uint256 fxs_dollar_value_d18 = params.FXS_amount.mul(params.fxs_price_usd).div(1e6);
require(fxs_dollar_value_d18 <= params.excess_collateral_dollar_value_d18, "You are trying to buy back more than the excess!");
uint256 collateral_equivalent_d18 = fxs_dollar_value_d18.mul(1e6).div(params.col_price_usd);
return (
collateral_equivalent_d18
);
}
function recollateralizeAmount(uint256 total_supply, uint256 global_collateral_ratio, uint256 global_collat_value) public pure returns (uint256) {
uint256 target_collat_value = total_supply.mul(global_collateral_ratio).div(1e6);
return target_collat_value.sub(global_collat_value);
}
function calcRecollateralizeFRAXInner(
uint256 collateral_amount,
uint256 col_price,
uint256 global_collat_value,
uint256 frax_total_supply,
uint256 global_collateral_ratio
) public pure returns (uint256, uint256) {
uint256 collat_value_attempted = collateral_amount.mul(col_price).div(1e6);
uint256 effective_collateral_ratio = global_collat_value.mul(1e6).div(frax_total_supply);
uint256 recollat_possible = (global_collateral_ratio.mul(frax_total_supply).sub(frax_total_supply.mul(effective_collateral_ratio))).div(1e6);
uint256 amount_to_recollat;
if(collat_value_attempted <= recollat_possible){
amount_to_recollat = collat_value_attempted;
} else {
amount_to_recollat = recollat_possible;
}
return (amount_to_recollat.mul(1e6).div(col_price), amount_to_recollat);
}
}
文件 15 的 29:IERC20.sol
pragma solidity >=0.6.11;
import "../Common/Context.sol";
import "../Math/SafeMath.sol";
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);
}
文件 16 的 29:IUniswapV2Factory.sol
pragma solidity >=0.6.11;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
文件 17 的 29:IUniswapV2Pair.sol
pragma solidity >=0.6.11;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
文件 18 的 29:IveFXS.sol
pragma solidity >=0.6.11;
interface IveFXS {
struct LockedBalance {
int128 amount;
uint256 end;
}
function balanceOf(address addr) external view returns (uint256);
function balanceOf(address addr, uint256 _t) external view returns (uint256);
function balanceOfAt(address addr, uint256 _block) external returns (uint256);
function totalSupply() external view returns (uint256);
function totalSupply(uint256 t) external view returns (uint256);
function totalSupplyAt(uint256 _block) external returns (uint256);
function totalFXSSupply() external view returns (uint256);
function totalFXSSupplyAt(uint256 _block) external view returns (uint256);
function locked(address addr) external view returns (LockedBalance memory);
function checkpoint() external;
}
文件 19 的 29:Math.sol
pragma solidity >=0.6.11;
library Math {
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
文件 20 的 29:Owned.sol
pragma solidity >=0.6.11;
contract Owned {
address public owner;
address public nominatedOwner;
constructor(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner {
require(msg.sender == owner, "Only the contract owner may perform this action");
_;
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}
文件 21 的 29:ReentrancyGuard.sol
pragma solidity >=0.6.11;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 22 的 29:SafeERC20.sol
pragma solidity >=0.6.11;
import "./IERC20.sol";
import "../Math/SafeMath.sol";
import "../Utils/Address.sol";
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 {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 23 的 29:SafeMath.sol
pragma solidity >=0.6.11;
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;
}
}
文件 24 的 29:StakingRewardsDualV3.sol
pragma solidity >=0.6.11;
pragma experimental ABIEncoderV2;
import "../Math/Math.sol";
import "../Math/SafeMath.sol";
import "../Curve/IveFXS.sol";
import "../ERC20/ERC20.sol";
import '../Uniswap/TransferHelper.sol';
import "../ERC20/SafeERC20.sol";
import "../Frax/Frax.sol";
import "../Uniswap/Interfaces/IUniswapV2Pair.sol";
import "../Utils/ReentrancyGuard.sol";
import "./Owned.sol";
contract StakingRewardsDualV3 is Owned, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for ERC20;
IveFXS private veFXS;
ERC20 private rewardsToken0;
ERC20 private rewardsToken1;
IUniswapV2Pair private stakingToken;
uint256 private constant MULTIPLIER_PRECISION = 1e18;
address public timelock_address;
uint256 public periodFinish;
uint256 public lastUpdateTime;
uint256 public lock_max_multiplier = uint256(3e18);
uint256 public lock_time_for_max_multiplier = 3 * 365 * 86400;
uint256 public lock_time_min = 86400;
uint256 public vefxs_per_frax_for_max_boost = uint256(4e18);
uint256 public vefxs_max_multiplier = uint256(25e17);
mapping(address => uint256) private _vefxsMultiplierStored;
uint256 public rewardRate0;
uint256 public rewardRate1;
uint256 public rewardsDuration = 604800;
uint256 public rewardPerTokenStored0 = 0;
uint256 public rewardPerTokenStored1 = 0;
mapping(address => uint256) public userRewardPerTokenPaid0;
mapping(address => uint256) public userRewardPerTokenPaid1;
mapping(address => uint256) public rewards0;
mapping(address => uint256) public rewards1;
uint256 private _total_liquidity_locked = 0;
uint256 private _total_combined_weight = 0;
mapping(address => uint256) private _locked_liquidity;
mapping(address => uint256) private _combined_weights;
bool frax_is_token0;
mapping(address => LockedStake[]) private lockedStakes;
mapping(address => bool) public valid_migrators;
address[] public valid_migrators_array;
mapping(address => mapping(address => bool)) public staker_allowed_migrators;
mapping(address => bool) public greylist;
bool public token1_rewards_on = true;
bool public migrationsOn = false;
bool public stakesUnlocked = false;
bool public withdrawalsPaused = false;
bool public rewardsCollectionPaused = false;
bool public stakingPaused = false;
struct LockedStake {
bytes32 kek_id;
uint256 start_timestamp;
uint256 liquidity;
uint256 ending_timestamp;
uint256 lock_multiplier;
}
modifier onlyByOwnerOrGovernance() {
require(msg.sender == owner || msg.sender == timelock_address, "You are not the owner or the governance timelock");
_;
}
modifier onlyByOwnerOrGovernanceOrMigrator() {
require(msg.sender == owner || msg.sender == timelock_address || valid_migrators[msg.sender] == true, "You are not the owner, governance timelock, or a migrator");
_;
}
modifier isMigrating() {
require(migrationsOn == true, "Contract is not in migration");
_;
}
modifier notStakingPaused() {
require(stakingPaused == false, "Staking is paused");
_;
}
modifier notWithdrawalsPaused() {
require(withdrawalsPaused == false, "Withdrawals are paused");
_;
}
modifier notRewardsCollectionPaused() {
require(rewardsCollectionPaused == false, "Rewards collection is paused");
_;
}
modifier updateRewardAndBalance(address account, bool sync_too) {
_updateRewardAndBalance(account, sync_too);
_;
}
constructor(
address _owner,
address _rewardsToken0,
address _rewardsToken1,
address _stakingToken,
address _frax_address,
address _timelock_address,
address _veFXS_address
) Owned(_owner){
rewardsToken0 = ERC20(_rewardsToken0);
rewardsToken1 = ERC20(_rewardsToken1);
stakingToken = IUniswapV2Pair(_stakingToken);
veFXS = IveFXS(_veFXS_address);
lastUpdateTime = block.timestamp;
timelock_address = _timelock_address;
rewardRate0 = (uint256(3650e18)).div(365 * 86400);
rewardRate1 = (uint256(365e18)).div(365 * 86400);
address token0 = stakingToken.token0();
if (token0 == _frax_address) frax_is_token0 = true;
else frax_is_token0 = false;
migrationsOn = false;
stakesUnlocked = false;
}
function totalLiquidityLocked() external view returns (uint256) {
return _total_liquidity_locked;
}
function totalCombinedWeight() external view returns (uint256) {
return _total_combined_weight;
}
function lockMultiplier(uint256 secs) public view returns (uint256) {
uint256 lock_multiplier =
uint256(MULTIPLIER_PRECISION).add(
secs
.mul(lock_max_multiplier.sub(MULTIPLIER_PRECISION))
.div(lock_time_for_max_multiplier)
);
if (lock_multiplier > lock_max_multiplier) lock_multiplier = lock_max_multiplier;
return lock_multiplier;
}
function lockedLiquidityOf(address account) public view returns (uint256) {
return _locked_liquidity[account];
}
function combinedWeightOf(address account) external view returns (uint256) {
return _combined_weights[account];
}
function lockedStakesOf(address account) external view returns (LockedStake[] memory) {
return lockedStakes[account];
}
function lastTimeRewardApplicable() public view returns (uint256) {
return Math.min(block.timestamp, periodFinish);
}
function fraxPerLPToken() public view returns (uint256) {
uint256 frax_per_lp_token;
{
uint256 total_frax_reserves;
(uint256 reserve0, uint256 reserve1, ) = (stakingToken.getReserves());
if (frax_is_token0) total_frax_reserves = reserve0;
else total_frax_reserves = reserve1;
frax_per_lp_token = total_frax_reserves.mul(1e18).div(stakingToken.totalSupply());
}
return frax_per_lp_token;
}
function userStakedFrax(address account) public view returns (uint256) {
return (fraxPerLPToken()).mul(_locked_liquidity[account]).div(1e18);
}
function minVeFXSForMaxBoost(address account) public view returns (uint256) {
return (userStakedFrax(account)).mul(vefxs_per_frax_for_max_boost).div(MULTIPLIER_PRECISION);
}
function veFXSMultiplier(address account) public view returns (uint256) {
uint256 veFXS_needed_for_max_boost = minVeFXSForMaxBoost(account);
if (veFXS_needed_for_max_boost > 0){
uint256 user_vefxs_fraction = (veFXS.balanceOf(account)).mul(MULTIPLIER_PRECISION).div(veFXS_needed_for_max_boost);
uint256 vefxs_multiplier = uint256(MULTIPLIER_PRECISION).add(
(user_vefxs_fraction)
.mul(vefxs_max_multiplier.sub(MULTIPLIER_PRECISION))
.div(MULTIPLIER_PRECISION)
);
if (vefxs_multiplier > vefxs_max_multiplier) vefxs_multiplier = vefxs_max_multiplier;
return vefxs_multiplier;
}
else return MULTIPLIER_PRECISION;
}
function calcCurCombinedWeight(address account) public view
returns (
uint256 old_combined_weight,
uint256 new_vefxs_multiplier,
uint256 new_combined_weight
)
{
old_combined_weight = _combined_weights[account];
new_vefxs_multiplier = veFXSMultiplier(account);
uint256 midpoint_vefxs_multiplier = ((new_vefxs_multiplier).add(_vefxsMultiplierStored[account])).div(2);
new_combined_weight = 0;
for (uint256 i = 0; i < lockedStakes[account].length; i++) {
LockedStake memory thisStake = lockedStakes[account][i];
uint256 lock_multiplier = thisStake.lock_multiplier;
if (thisStake.ending_timestamp <= block.timestamp){
lock_multiplier = MULTIPLIER_PRECISION;
}
uint256 liquidity = thisStake.liquidity;
uint256 extra_vefxs_boost = midpoint_vefxs_multiplier.sub(MULTIPLIER_PRECISION);
uint256 combined_boosted_amount = liquidity.mul(lock_multiplier.add(extra_vefxs_boost)).div(MULTIPLIER_PRECISION);
new_combined_weight = new_combined_weight.add(combined_boosted_amount);
}
}
function rewardPerToken() public view returns (uint256, uint256) {
if (_total_liquidity_locked == 0 || _total_combined_weight == 0) {
return (rewardPerTokenStored0, rewardPerTokenStored1);
}
else {
return (
rewardPerTokenStored0.add(
lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate0).mul(1e18).div(_total_combined_weight)
),
rewardPerTokenStored1.add(
lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate1).mul(1e18).div(_total_combined_weight)
)
);
}
}
function earned(address account) public view returns (uint256, uint256) {
(uint256 reward0, uint256 reward1) = rewardPerToken();
if (_combined_weights[account] == 0){
return (0, 0);
}
return (
(_combined_weights[account].mul(reward0.sub(userRewardPerTokenPaid0[account]))).div(1e18).add(rewards0[account]),
(_combined_weights[account].mul(reward1.sub(userRewardPerTokenPaid1[account]))).div(1e18).add(rewards1[account])
);
}
function getRewardForDuration() external view returns (uint256, uint256) {
return (
rewardRate0.mul(rewardsDuration),
rewardRate1.mul(rewardsDuration)
);
}
function migratorApprovedForStaker(address staker_address, address migrator_address) public view returns (bool) {
if (valid_migrators[migrator_address] == false) return false;
if (staker_allowed_migrators[staker_address][migrator_address] == true) return true;
return false;
}
function _updateRewardAndBalance(address account, bool sync_too) internal {
if (sync_too){
sync();
}
if (account != address(0)) {
(
uint256 old_combined_weight,
uint256 new_vefxs_multiplier,
uint256 new_combined_weight
) = calcCurCombinedWeight(account);
_syncEarned(account);
_vefxsMultiplierStored[account] = new_vefxs_multiplier;
if (new_combined_weight >= old_combined_weight) {
uint256 weight_diff = new_combined_weight.sub(old_combined_weight);
_total_combined_weight = _total_combined_weight.add(weight_diff);
_combined_weights[account] = old_combined_weight.add(weight_diff);
} else {
uint256 weight_diff = old_combined_weight.sub(new_combined_weight);
_total_combined_weight = _total_combined_weight.sub(weight_diff);
_combined_weights[account] = old_combined_weight.sub(weight_diff);
}
}
}
function _syncEarned(address account) internal {
if (account != address(0)) {
(uint256 earned0, uint256 earned1) = earned(account);
rewards0[account] = earned0;
rewards1[account] = earned1;
userRewardPerTokenPaid0[account] = rewardPerTokenStored0;
userRewardPerTokenPaid1[account] = rewardPerTokenStored1;
}
}
function stakerAllowMigrator(address migrator_address) public {
require(staker_allowed_migrators[msg.sender][migrator_address] == false, "Address already exists");
require(valid_migrators[migrator_address], "Invalid migrator address");
staker_allowed_migrators[msg.sender][migrator_address] = true;
}
function stakerDisallowMigrator(address migrator_address) public {
require(staker_allowed_migrators[msg.sender][migrator_address] == true, "Address doesn't exist already");
delete staker_allowed_migrators[msg.sender][migrator_address];
}
function stakeLocked(uint256 liquidity, uint256 secs) public {
_stakeLocked(msg.sender, msg.sender, liquidity, secs);
}
function _stakeLocked(address staker_address, address source_address, uint256 liquidity, uint256 secs) internal nonReentrant updateRewardAndBalance(staker_address, true) {
require((!stakingPaused && migrationsOn == false) || valid_migrators[msg.sender] == true, "Staking is paused, or migration is happening");
require(liquidity > 0, "Must stake more than zero");
require(greylist[staker_address] == false, "Address has been greylisted");
require(secs >= lock_time_min, "Minimum stake time not met");
require(secs <= lock_time_for_max_multiplier,"You are trying to lock for too long");
uint256 lock_multiplier = lockMultiplier(secs);
bytes32 kek_id = keccak256(abi.encodePacked(staker_address, block.timestamp, liquidity));
lockedStakes[staker_address].push(LockedStake(
kek_id,
block.timestamp,
liquidity,
block.timestamp.add(secs),
lock_multiplier
));
TransferHelper.safeTransferFrom(address(stakingToken), source_address, address(this), liquidity);
_total_liquidity_locked = _total_liquidity_locked.add(liquidity);
_locked_liquidity[staker_address] = _locked_liquidity[staker_address].add(liquidity);
_updateRewardAndBalance(staker_address, false);
emit StakeLocked(staker_address, liquidity, secs, kek_id, source_address);
}
function withdrawLocked(bytes32 kek_id) public {
_withdrawLocked(msg.sender, msg.sender, kek_id);
}
function _withdrawLocked(address staker_address, address destination_address, bytes32 kek_id) internal nonReentrant notWithdrawalsPaused updateRewardAndBalance(staker_address, true) {
LockedStake memory thisStake;
thisStake.liquidity = 0;
uint theArrayIndex;
for (uint i = 0; i < lockedStakes[staker_address].length; i++){
if (kek_id == lockedStakes[staker_address][i].kek_id){
thisStake = lockedStakes[staker_address][i];
theArrayIndex = i;
break;
}
}
require(thisStake.kek_id == kek_id, "Stake not found");
require(block.timestamp >= thisStake.ending_timestamp || stakesUnlocked == true || valid_migrators[msg.sender] == true, "Stake is still locked!");
uint256 liquidity = thisStake.liquidity;
if (liquidity > 0) {
_total_liquidity_locked = _total_liquidity_locked.sub(liquidity);
_locked_liquidity[staker_address] = _locked_liquidity[staker_address].sub(liquidity);
delete lockedStakes[staker_address][theArrayIndex];
_updateRewardAndBalance(staker_address, false);
stakingToken.transfer(destination_address, liquidity);
emit WithdrawLocked(staker_address, liquidity, kek_id, destination_address);
}
}
function getReward() external returns (uint256, uint256) {
return _getReward(msg.sender, msg.sender);
}
function _getReward(address rewardee, address destination_address) internal nonReentrant notRewardsCollectionPaused updateRewardAndBalance(rewardee, true) returns (uint256 reward0, uint256 reward1) {
reward0 = rewards0[rewardee];
reward1 = rewards1[rewardee];
if (reward0 > 0) {
rewards0[rewardee] = 0;
rewardsToken0.transfer(destination_address, reward0);
emit RewardPaid(rewardee, reward0, address(rewardsToken0), destination_address);
}
if (reward1 > 0) {
rewards1[rewardee] = 0;
rewardsToken1.transfer(destination_address, reward1);
emit RewardPaid(rewardee, reward1, address(rewardsToken1), destination_address);
}
}
function renewIfApplicable() external {
if (block.timestamp > periodFinish) {
retroCatchUp();
}
}
function retroCatchUp() internal {
require(block.timestamp > periodFinish, "Period has not expired yet!");
uint256 num_periods_elapsed = uint256(block.timestamp.sub(periodFinish)) / rewardsDuration;
uint balance0 = rewardsToken0.balanceOf(address(this));
uint balance1 = rewardsToken1.balanceOf(address(this));
require(rewardRate0.mul(rewardsDuration).mul(num_periods_elapsed + 1) <= balance0, "Not enough FXS available for rewards!");
if (token1_rewards_on){
require(rewardRate1.mul(rewardsDuration).mul(num_periods_elapsed + 1) <= balance1, "Not enough token1 available for rewards!");
}
periodFinish = periodFinish.add((num_periods_elapsed.add(1)).mul(rewardsDuration));
(uint256 reward0, uint256 reward1) = rewardPerToken();
rewardPerTokenStored0 = reward0;
rewardPerTokenStored1 = reward1;
lastUpdateTime = lastTimeRewardApplicable();
emit RewardsPeriodRenewed(address(stakingToken));
}
function sync() public {
if (block.timestamp > periodFinish) {
retroCatchUp();
}
else {
(uint256 reward0, uint256 reward1) = rewardPerToken();
rewardPerTokenStored0 = reward0;
rewardPerTokenStored1 = reward1;
lastUpdateTime = lastTimeRewardApplicable();
}
}
function migrator_stakeLocked_for(address staker_address, uint256 amount, uint256 secs) external isMigrating {
require(migratorApprovedForStaker(staker_address, msg.sender), "msg.sender is either an invalid migrator or the staker has not approved them");
_stakeLocked(staker_address, msg.sender, amount, secs);
}
function migrator_withdraw_locked(address staker_address, bytes32 kek_id) external isMigrating {
require(migratorApprovedForStaker(staker_address, msg.sender), "msg.sender is either an invalid migrator or the staker has not approved them");
_withdrawLocked(staker_address, msg.sender, kek_id);
}
function addMigrator(address migrator_address) public onlyByOwnerOrGovernance {
require(valid_migrators[migrator_address] == false, "address already exists");
valid_migrators[migrator_address] = true;
valid_migrators_array.push(migrator_address);
}
function removeMigrator(address migrator_address) public onlyByOwnerOrGovernance {
require(valid_migrators[migrator_address] == true, "address doesn't exist already");
delete valid_migrators[migrator_address];
for (uint i = 0; i < valid_migrators_array.length; i++){
if (valid_migrators_array[i] == migrator_address) {
valid_migrators_array[i] = address(0);
break;
}
}
}
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnerOrGovernance {
if(!migrationsOn){
require(tokenAddress != address(stakingToken), "Cannot withdraw staking tokens unless migration is on");
}
ERC20(tokenAddress).transfer(owner, tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
function setRewardsDuration(uint256 _rewardsDuration) external onlyByOwnerOrGovernance {
require(
periodFinish == 0 || block.timestamp > periodFinish,
"Previous rewards period must be complete before changing the duration for the new period"
);
rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(rewardsDuration);
}
function setMultipliers(uint256 _lock_max_multiplier, uint256 _vefxs_max_multiplier, uint256 _vefxs_per_frax_for_max_boost) external onlyByOwnerOrGovernance {
require(_lock_max_multiplier >= uint256(1e6), "Multiplier must be greater than or equal to 1e6");
require(_vefxs_max_multiplier >= uint256(1e18), "Max veFXS multiplier must be greater than or equal to 1e18");
require(_vefxs_per_frax_for_max_boost > 0, "veFXS per FRAX must be greater than 0");
lock_max_multiplier = _lock_max_multiplier;
vefxs_max_multiplier = _vefxs_max_multiplier;
vefxs_per_frax_for_max_boost = _vefxs_per_frax_for_max_boost;
emit MaxVeFXSMultiplier(vefxs_max_multiplier);
emit LockedStakeMaxMultiplierUpdated(lock_max_multiplier);
emit veFXSPerFraxForMaxBoostUpdated(vefxs_per_frax_for_max_boost);
}
function setLockedStakeTimeForMinAndMaxMultiplier(uint256 _lock_time_for_max_multiplier, uint256 _lock_time_min) external onlyByOwnerOrGovernance {
require(_lock_time_for_max_multiplier >= 1, "Multiplier Max Time must be greater than or equal to 1");
require(_lock_time_min >= 1, "Multiplier Min Time must be greater than or equal to 1");
lock_time_for_max_multiplier = _lock_time_for_max_multiplier;
lock_time_min = _lock_time_min;
emit LockedStakeTimeForMaxMultiplier(lock_time_for_max_multiplier);
emit LockedStakeMinTime(_lock_time_min);
}
function initializeDefault() external onlyByOwnerOrGovernance {
lastUpdateTime = block.timestamp;
periodFinish = block.timestamp.add(rewardsDuration);
emit DefaultInitialization();
}
function greylistAddress(address _address) external onlyByOwnerOrGovernance {
greylist[_address] = !(greylist[_address]);
}
function unlockStakes() external onlyByOwnerOrGovernance {
stakesUnlocked = !stakesUnlocked;
}
function toggleMigrations() external onlyByOwnerOrGovernance {
migrationsOn = !migrationsOn;
}
function toggleStaking() external onlyByOwnerOrGovernance {
stakingPaused = !stakingPaused;
}
function toggleWithdrawals() external onlyByOwnerOrGovernance {
withdrawalsPaused = !withdrawalsPaused;
}
function toggleRewardsCollection() external onlyByOwnerOrGovernance {
rewardsCollectionPaused = !rewardsCollectionPaused;
}
function setRewardRates(uint256 _new_rate0, uint256 _new_rate1, bool sync_too) external onlyByOwnerOrGovernance {
rewardRate0 = _new_rate0;
rewardRate1 = _new_rate1;
if (sync_too){
sync();
}
}
function toggleToken1Rewards() external onlyByOwnerOrGovernance {
if (token1_rewards_on) {
rewardRate1 = 0;
}
token1_rewards_on = !token1_rewards_on;
}
function setTimelock(address _new_timelock) external onlyByOwnerOrGovernance {
timelock_address = _new_timelock;
}
event RewardAdded(uint256 reward);
event StakeLocked(address indexed user, uint256 amount, uint256 secs, bytes32 kek_id, address source_address);
event WithdrawLocked(address indexed user, uint256 amount, bytes32 kek_id, address destination_address);
event RewardPaid(address indexed user, uint256 reward, address token_address, address destination_address);
event RewardsDurationUpdated(uint256 newDuration);
event Recovered(address token, uint256 amount);
event RewardsPeriodRenewed(address token);
event DefaultInitialization();
event LockedStakeMaxMultiplierUpdated(uint256 multiplier);
event LockedStakeTimeForMaxMultiplier(uint256 secs);
event LockedStakeMinTime(uint256 secs);
event MaxVeFXSMultiplier(uint256 multiplier);
event veFXSPerFraxForMaxBoostUpdated(uint256 scale_factor);
}
文件 25 的 29:StakingRewardsDualV3_FRAX_IQ.sol
pragma solidity >=0.6.11;
pragma experimental ABIEncoderV2;
import "../StakingRewardsDualV3.sol";
contract StakingRewardsDualV3_FRAX_IQ is StakingRewardsDualV3 {
constructor(
address _owner,
address _rewardsToken0,
address _rewardsToken1,
address _stakingToken,
address _frax_address,
address _timelock_address,
address _veFXS_address
)
StakingRewardsDualV3(_owner, _rewardsToken0, _rewardsToken1, _stakingToken, _frax_address, _timelock_address, _veFXS_address )
{}
}
文件 26 的 29:TransferHelper.sol
pragma solidity >=0.6.11;
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
}
function safeTransfer(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
}
function safeTransferETH(address to, uint value) internal {
(bool success,) = to.call{value:value}(new bytes(0));
require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
}
}
文件 27 的 29:UniswapPairOracle.sol
pragma solidity >=0.6.11;
import '../Uniswap/Interfaces/IUniswapV2Factory.sol';
import '../Uniswap/Interfaces/IUniswapV2Pair.sol';
import '../Math/FixedPoint.sol';
import '../Uniswap/UniswapV2OracleLibrary.sol';
import '../Uniswap/UniswapV2Library.sol';
import "../Staking/Owned.sol";
contract UniswapPairOracle is Owned {
using FixedPoint for *;
address timelock_address;
uint public PERIOD = 3600;
uint public CONSULT_LENIENCY = 120;
bool public ALLOW_STALE_CONSULTS = false;
IUniswapV2Pair public immutable pair;
address public immutable token0;
address public immutable token1;
uint public price0CumulativeLast;
uint public price1CumulativeLast;
uint32 public blockTimestampLast;
FixedPoint.uq112x112 public price0Average;
FixedPoint.uq112x112 public price1Average;
modifier onlyByOwnerOrGovernance() {
require(msg.sender == owner || msg.sender == timelock_address, "You are not an owner or the governance timelock");
_;
}
constructor(address factory, address tokenA, address tokenB, address _owner_address, address _timelock_address) public Owned(_owner_address) {
IUniswapV2Pair _pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
pair = _pair;
token0 = _pair.token0();
token1 = _pair.token1();
price0CumulativeLast = _pair.price0CumulativeLast();
price1CumulativeLast = _pair.price1CumulativeLast();
uint112 reserve0;
uint112 reserve1;
(reserve0, reserve1, blockTimestampLast) = _pair.getReserves();
require(reserve0 != 0 && reserve1 != 0, 'UniswapPairOracle: NO_RESERVES');
timelock_address = _timelock_address;
}
function setTimelock(address _timelock_address) external onlyByOwnerOrGovernance {
timelock_address = _timelock_address;
}
function setPeriod(uint _period) external onlyByOwnerOrGovernance {
PERIOD = _period;
}
function setConsultLeniency(uint _consult_leniency) external onlyByOwnerOrGovernance {
CONSULT_LENIENCY = _consult_leniency;
}
function setAllowStaleConsults(bool _allow_stale_consults) external onlyByOwnerOrGovernance {
ALLOW_STALE_CONSULTS = _allow_stale_consults;
}
function canUpdate() public view returns (bool) {
uint32 blockTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
return (timeElapsed >= PERIOD);
}
function update() external {
(uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =
UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
require(timeElapsed >= PERIOD, 'UniswapPairOracle: PERIOD_NOT_ELAPSED');
price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));
price0CumulativeLast = price0Cumulative;
price1CumulativeLast = price1Cumulative;
blockTimestampLast = blockTimestamp;
}
function consult(address token, uint amountIn) external view returns (uint amountOut) {
uint32 blockTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
require((timeElapsed < (PERIOD + CONSULT_LENIENCY)) || ALLOW_STALE_CONSULTS, 'UniswapPairOracle: PRICE_IS_STALE_NEED_TO_CALL_UPDATE');
if (token == token0) {
amountOut = price0Average.mul(amountIn).decode144();
} else {
require(token == token1, 'UniswapPairOracle: INVALID_TOKEN');
amountOut = price1Average.mul(amountIn).decode144();
}
}
}
文件 28 的 29:UniswapV2Library.sol
pragma solidity >=0.6.11;
import './Interfaces/IUniswapV2Pair.sol';
import './Interfaces/IUniswapV2Factory.sol';
import "../Math/SafeMath.sol";
library UniswapV2Library {
using SafeMath for uint;
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
}
function pairFor(address factory, address tokenA, address tokenB) internal view returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = IUniswapV2Factory(factory).getPair(token0, token1);
}
function pairForCreate2(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint160(bytes20(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
)))));
}
function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
(address token0,) = sortTokens(tokenA, tokenB);
(uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
amountB = amountA.mul(reserveB) / reserveA;
}
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint numerator = reserveIn.mul(amountOut).mul(1000);
uint denominator = reserveOut.sub(amountOut).mul(997);
amountIn = (numerator / denominator).add(1);
}
function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
amounts = new uint[](path.length);
amounts[0] = amountIn;
for (uint i = 0; i < path.length - 1; i++) {
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
amounts = new uint[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint i = path.length - 1; i > 0; i--) {
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
}
}
}
文件 29 的 29:UniswapV2OracleLibrary.sol
pragma solidity >=0.6.11;
import '../Uniswap/Interfaces/IUniswapV2Pair.sol';
import '../Math/FixedPoint.sol';
library UniswapV2OracleLibrary {
using FixedPoint for *;
function currentBlockTimestamp() internal view returns (uint32) {
return uint32(block.timestamp % 2 ** 32);
}
function currentCumulativePrices(
address pair
) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) {
blockTimestamp = currentBlockTimestamp();
price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves();
if (blockTimestampLast != blockTimestamp) {
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed;
price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed;
}
}
}
{
"compilationTarget": {
"contracts/Staking/Variants/StakingRewardsDualV3_FRAX_IQ.sol": "StakingRewardsDualV3_FRAX_IQ"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 100000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_rewardsToken0","type":"address"},{"internalType":"address","name":"_rewardsToken1","type":"address"},{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"_frax_address","type":"address"},{"internalType":"address","name":"_timelock_address","type":"address"},{"internalType":"address","name":"_veFXS_address","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[],"name":"DefaultInitialization","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"LockedStakeMaxMultiplierUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"secs","type":"uint256"}],"name":"LockedStakeMinTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"secs","type":"uint256"}],"name":"LockedStakeTimeForMaxMultiplier","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"MaxVeFXSMultiplier","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"address","name":"token_address","type":"address"},{"indexed":false,"internalType":"address","name":"destination_address","type":"address"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RewardsPeriodRenewed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"secs","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"kek_id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"source_address","type":"address"}],"name":"StakeLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"kek_id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"destination_address","type":"address"}],"name":"WithdrawLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"scale_factor","type":"uint256"}],"name":"veFXSPerFraxForMaxBoostUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_address","type":"address"}],"name":"addMigrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calcCurCombinedWeight","outputs":[{"internalType":"uint256","name":"old_combined_weight","type":"uint256"},{"internalType":"uint256","name":"new_vefxs_multiplier","type":"uint256"},{"internalType":"uint256","name":"new_combined_weight","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"combinedWeightOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fraxPerLPToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"greylist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"greylistAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initializeDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"secs","type":"uint256"}],"name":"lockMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock_max_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock_time_for_max_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock_time_min","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockedLiquidityOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockedStakesOf","outputs":[{"components":[{"internalType":"bytes32","name":"kek_id","type":"bytes32"},{"internalType":"uint256","name":"start_timestamp","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"ending_timestamp","type":"uint256"},{"internalType":"uint256","name":"lock_multiplier","type":"uint256"}],"internalType":"struct StakingRewardsDualV3.LockedStake[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrationsOn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker_address","type":"address"},{"internalType":"address","name":"migrator_address","type":"address"}],"name":"migratorApprovedForStaker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker_address","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"secs","type":"uint256"}],"name":"migrator_stakeLocked_for","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker_address","type":"address"},{"internalType":"bytes32","name":"kek_id","type":"bytes32"}],"name":"migrator_withdraw_locked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"minVeFXSForMaxBoost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_address","type":"address"}],"name":"removeMigrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renewIfApplicable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsCollectionPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lock_time_for_max_multiplier","type":"uint256"},{"internalType":"uint256","name":"_lock_time_min","type":"uint256"}],"name":"setLockedStakeTimeForMinAndMaxMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lock_max_multiplier","type":"uint256"},{"internalType":"uint256","name":"_vefxs_max_multiplier","type":"uint256"},{"internalType":"uint256","name":"_vefxs_per_frax_for_max_boost","type":"uint256"}],"name":"setMultipliers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_new_rate0","type":"uint256"},{"internalType":"uint256","name":"_new_rate1","type":"uint256"},{"internalType":"bool","name":"sync_too","type":"bool"}],"name":"setRewardRates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_new_timelock","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"secs","type":"uint256"}],"name":"stakeLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_address","type":"address"}],"name":"stakerAllowMigrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"migrator_address","type":"address"}],"name":"stakerDisallowMigrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"staker_allowed_migrators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakesUnlocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timelock_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleMigrations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleRewardsCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleToken1Rewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token1_rewards_on","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCombinedWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidityLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlockStakes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"userStakedFrax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"valid_migrators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"valid_migrators_array","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"veFXSMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vefxs_max_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vefxs_per_frax_for_max_boost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"kek_id","type":"bytes32"}],"name":"withdrawLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]