编译器
0.6.11+commit.5ef660b1
文件 1 的 21:AccessControl.sol
pragma solidity ^0.6.0;
import "./EnumerableSet.sol";
import "./Address.sol";
import "./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 的 21:Address.sol
pragma solidity 0.6.11;
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");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
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 的 21: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 的 21: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 的 21:ChainlinkETHUSDPriceConsumer.sol
pragma solidity 0.6.11;
pragma experimental ABIEncoderV2;
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 的 21:Context.sol
pragma solidity 0.6.11;
contract Context {
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 7 的 21:ERC20.sol
pragma solidity 0.6.11;
import "./Context.sol";
import "./IERC20.sol";
import "./SafeMath.sol";
import "./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 的 21:ERC20Custom.sol
pragma solidity 0.6.11;
import "./Context.sol";
import "./IERC20.sol";
import "./SafeMath.sol";
import "./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 的 21:EnumerableSet.sol
pragma solidity ^0.6.0;
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(uint256(value)));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(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(uint256(_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 的 21:FXS.sol
pragma solidity 0.6.11;
pragma experimental ABIEncoderV2;
import "./Context.sol";
import "./ERC20Custom.sol";
import "./IERC20.sol";
import "./Frax.sol";
import "./SafeMath.sol";
import "./AccessControl.sol";
contract FRAXShares is ERC20Custom, AccessControl {
using SafeMath for uint256;
string public symbol;
string public name;
uint8 public constant decimals = 18;
address public FRAXStablecoinAdd;
uint256 public constant genesis_supply = 100000000e18;
uint256 public FXS_DAO_min;
address public owner_address;
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_address || msg.sender == timelock_address, "You are not an owner or the governance timelock");
_;
}
constructor(
string memory _name,
string memory _symbol,
address _oracle_address,
address _owner_address,
address _timelock_address
) public {
name = _name;
symbol = _symbol;
owner_address = _owner_address;
oracle_address = _oracle_address;
timelock_address = _timelock_address;
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_mint(owner_address, genesis_supply);
_writeCheckpoint(owner_address, 0, 0, uint96(genesis_supply));
}
function setOracle(address new_oracle) external onlyByOwnerOrGovernance {
oracle_address = new_oracle;
}
function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {
timelock_address = new_timelock;
}
function setFRAXAddress(address frax_contract_address) external onlyByOwnerOrGovernance {
FRAX = FRAXStablecoin(frax_contract_address);
}
function setFXSMinDAO(uint256 min_FXS) external onlyByOwnerOrGovernance {
FXS_DAO_min = min_FXS;
}
function setOwner(address _owner_address) external onlyByOwnerOrGovernance {
owner_address = _owner_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;
}
function getChainId() internal pure returns (uint) {
uint256 chainId;
assembly { chainId := chainid() }
return chainId;
}
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);
}
文件 11 的 21: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 的 21:Frax.sol
pragma solidity 0.6.11;
pragma experimental ABIEncoderV2;
import "./Context.sol";
import "./IERC20.sol";
import "./ERC20Custom.sol";
import "./ERC20.sol";
import "./SafeMath.sol";
import "./FXS.sol";
import "./FraxPool.sol";
import "./UniswapPairOracle.sol";
import "./ChainlinkETHUSDPriceConsumer.sol";
import "./AccessControl.sol";
contract FRAXStablecoin is ERC20Custom, AccessControl {
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 owner_address;
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_address || msg.sender == timelock_address || msg.sender == controller_address, "You are not the owner, controller, or the governance timelock");
_;
}
modifier onlyByOwnerGovernanceOrPool() {
require(
msg.sender == owner_address
|| 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 {
name = _name;
symbol = _symbol;
creator_address = _creator_address;
timelock_address = _timelock_address;
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
DEFAULT_ADMIN_ADDRESS = _msgSender();
owner_address = _creator_address;
_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;
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;
}
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(frax_pools[pool_address] == false, "address already exists");
frax_pools[pool_address] = true;
frax_pools_array.push(pool_address);
}
function removePool(address pool_address) public onlyByOwnerOrGovernance {
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;
}
}
}
function setOwner(address _owner_address) external onlyByOwnerOrGovernance {
owner_address = _owner_address;
}
function setRedemptionFee(uint256 red_fee) public onlyByOwnerOrGovernance {
redemption_fee = red_fee;
}
function setMintingFee(uint256 min_fee) public onlyByOwnerOrGovernance {
minting_fee = min_fee;
}
function setFraxStep(uint256 _new_step) public onlyByOwnerOrGovernance {
frax_step = _new_step;
}
function setPriceTarget (uint256 _new_price_target) public onlyByOwnerOrGovernance {
price_target = _new_price_target;
}
function setRefreshCooldown(uint256 _new_cooldown) public onlyByOwnerOrGovernance {
refresh_cooldown = _new_cooldown;
}
function setFXSAddress(address _fxs_address) public onlyByOwnerOrGovernance {
fxs_address = _fxs_address;
}
function setETHUSDOracle(address _eth_usd_consumer_address) public onlyByOwnerOrGovernance {
eth_usd_consumer_address = _eth_usd_consumer_address;
eth_usd_pricer = ChainlinkETHUSDPriceConsumer(eth_usd_consumer_address);
eth_usd_pricer_decimals = eth_usd_pricer.getDecimals();
}
function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {
timelock_address = new_timelock;
}
function setController(address _controller_address) external onlyByOwnerOrGovernance {
controller_address = _controller_address;
}
function setPriceBand(uint256 _price_band) external onlyByOwnerOrGovernance {
price_band = _price_band;
}
function setFRAXEthOracle(address _frax_oracle_addr, address _weth_address) public onlyByOwnerOrGovernance {
frax_eth_oracle_address = _frax_oracle_addr;
fraxEthOracle = UniswapPairOracle(_frax_oracle_addr);
weth_address = _weth_address;
}
function setFXSEthOracle(address _fxs_oracle_addr, address _weth_address) public onlyByOwnerOrGovernance {
fxs_eth_oracle_address = _fxs_oracle_addr;
fxsEthOracle = UniswapPairOracle(_fxs_oracle_addr);
weth_address = _weth_address;
}
function toggleCollateralRatio() public onlyCollateralRatioPauser {
collateral_ratio_paused = !collateral_ratio_paused;
}
event FRAXBurned(address indexed from, address indexed to, uint256 amount);
event FRAXMinted(address indexed from, address indexed to, uint256 amount);
}
文件 13 的 21:FraxPool.sol
pragma solidity 0.6.11;
pragma experimental ABIEncoderV2;
import "./SafeMath.sol";
import "./FXS.sol";
import "./Frax.sol";
import "./ERC20.sol";
import "./UniswapPairOracle.sol";
import "./AccessControl.sol";
import "./FraxPoolLibrary.sol";
contract FraxPool is AccessControl {
using SafeMath for uint256;
ERC20 private collateral_token;
address private collateral_address;
address private owner_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 private collat_eth_oracle_address;
address private weth_address;
uint256 private minting_fee;
uint256 private redemption_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 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 private mintPaused = false;
bool private redeemPaused = false;
bool private recollateralizePaused = false;
bool private buyBackPaused = false;
bool private collateralPricePaused = false;
modifier onlyByOwnerOrGovernance() {
require(msg.sender == timelock_address || msg.sender == owner_address, "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 {
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;
owner_address = _creator_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) {
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);
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
require(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(),
minting_fee,
collateral_amount_d18
);
require(FRAX_out_min <= frax_amount_d18, "Slippage limit reached");
collateral_token.transferFrom(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();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
require(global_collateral_ratio == 0, "Collateral ratio must be 0");
(uint256 frax_amount_d18) = FraxPoolLibrary.calcMintAlgorithmicFRAX(
minting_fee,
fxs_price,
fxs_amount_d18
);
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 frax_price = FRAX.frax_price();
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(
minting_fee,
fxs_price,
frax_price,
getCollateralPrice(),
fxs_amount,
collateral_amount_d18,
(collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral)),
pool_ceiling,
global_collateral_ratio
);
(uint256 mint_amount, uint256 fxs_needed) = FraxPoolLibrary.calcMintFractionalFRAX(input_params);
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);
collateral_token.transferFrom(msg.sender, address(this), collateral_amount);
FRAX.pool_mint(msg.sender, mint_amount);
}
function redeem1t1FRAX(uint256 FRAX_amount, uint256 COLLATERAL_out_min) external notRedeemPaused {
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
require(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,
redemption_fee
);
require(collateral_needed <= collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral), "Not enough collateral in pool");
redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender].add(collateral_needed);
unclaimedPoolCollateral = unclaimedPoolCollateral.add(collateral_needed);
lastRedeemed[msg.sender] = block.number;
require(COLLATERAL_out_min <= collateral_needed, "Slippage limit reached");
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.sub((FRAX_amount.mul(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);
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;
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]");
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.sub((fxs_dollar_value_d18.mul(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;
uint CollateralAmount;
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 == true){
FXS.transfer(msg.sender, FXSAmount);
}
if(sendCollateral == true){
collateral_token.transfer(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)).div(fxs_price);
require(FXS_out_min <= fxs_paid_back, "Slippage limit reached");
collateral_token.transferFrom(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);
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);
collateral_token.transfer(msg.sender, collateral_precision);
}
function toggleMinting() external {
require(hasRole(MINT_PAUSER, msg.sender));
mintPaused = !mintPaused;
}
function toggleRedeeming() external {
require(hasRole(REDEEM_PAUSER, msg.sender));
redeemPaused = !redeemPaused;
}
function toggleRecollateralize() external {
require(hasRole(RECOLLATERALIZE_PAUSER, msg.sender));
recollateralizePaused = !recollateralizePaused;
}
function toggleBuyBack() external {
require(hasRole(BUYBACK_PAUSER, msg.sender));
buyBackPaused = !buyBackPaused;
}
function toggleCollateralPrice() external {
require(hasRole(COLLATERAL_PRICE_PAUSER, msg.sender));
if(collateralPricePaused == false){
pausedPrice = getCollateralPrice();
} else {
pausedPrice = 0;
}
collateralPricePaused = !collateralPricePaused;
}
function setPoolParameters(uint256 new_ceiling, uint256 new_bonus_rate, uint256 new_redemption_delay) external onlyByOwnerOrGovernance {
pool_ceiling = new_ceiling;
bonus_rate = new_bonus_rate;
redemption_delay = new_redemption_delay;
minting_fee = FRAX.minting_fee();
redemption_fee = FRAX.redemption_fee();
}
function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {
timelock_address = new_timelock;
}
function setOwner(address _owner_address) external onlyByOwnerOrGovernance {
owner_address = _owner_address;
}
}
文件 14 的 21:FraxPoolLibrary.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "./SafeMath.sol";
library FraxPoolLibrary {
using SafeMath for uint256;
uint256 private constant PRICE_PRECISION = 1e6;
struct MintFF_Params {
uint256 mint_fee;
uint256 fxs_price_usd;
uint256 frax_price_usd;
uint256 col_price_usd;
uint256 fxs_amount;
uint256 collateral_amount;
uint256 collateral_token_balance;
uint256 pool_ceiling;
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 mint_fee, uint256 collateral_amount_d18) public pure returns (uint256) {
uint256 col_price_usd = col_price;
uint256 c_dollar_value_d18 = (collateral_amount_d18.mul(col_price_usd)).div(1e6);
return c_dollar_value_d18.sub((c_dollar_value_d18.mul(mint_fee)).div(1e6));
}
function calcMintAlgorithmicFRAX(uint256 mint_fee, uint256 fxs_price_usd, uint256 fxs_amount_d18) public pure returns (uint256) {
uint256 fxs_dollar_value_d18 = fxs_amount_d18.mul(fxs_price_usd).div(1e6);
return fxs_dollar_value_d18.sub((fxs_dollar_value_d18.mul(mint_fee)).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)).sub(((c_dollar_value_d18.add(calculated_fxs_dollar_value_d18)).mul(params.mint_fee)).div(1e6)),
calculated_fxs_needed
);
}
function calcRedeem1t1FRAX(uint256 col_price_usd, uint256 FRAX_amount, uint256 redemption_fee) public pure returns (uint256) {
uint256 collateral_needed_d18 = FRAX_amount.mul(1e6).div(col_price_usd);
return collateral_needed_d18.sub((collateral_needed_d18.mul(redemption_fee)).div(1e6));
}
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);
uint256 recollateralization_left = target_collat_value.sub(global_collat_value);
return(recollateralization_left);
}
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 的 21:IERC20.sol
pragma solidity 0.6.11;
import "./Context.sol";
import "./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 的 21: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 的 21: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 的 21: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;
}
}
文件 19 的 21:UniswapPairOracle.sol
pragma solidity 0.6.11;
import './IUniswapV2Factory.sol';
import './IUniswapV2Pair.sol';
import './FixedPoint.sol';
import './UniswapV2OracleLibrary.sol';
import './UniswapV2Library.sol';
contract UniswapPairOracle {
using FixedPoint for *;
address owner_address;
address timelock_address;
uint public PERIOD = 3600;
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_address || 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 {
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');
owner_address = _owner_address;
timelock_address = _timelock_address;
}
function setOwner(address _owner_address) external onlyByOwnerOrGovernance {
owner_address = _owner_address;
}
function setTimelock(address _timelock_address) external onlyByOwnerOrGovernance {
timelock_address = _timelock_address;
}
function setPeriod(uint _period) external onlyByOwnerOrGovernance {
PERIOD = _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) {
if (token == token0) {
amountOut = price0Average.mul(amountIn).decode144();
} else {
require(token == token1, 'UniswapPairOracle: INVALID_TOKEN');
amountOut = price1Average.mul(amountIn).decode144();
}
}
}
文件 20 的 21:UniswapV2Library.sol
pragma solidity 0.6.11;
import './IUniswapV2Pair.sol';
import './IUniswapV2Factory.sol';
import "./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(uint(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; 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);
}
}
}
文件 21 的 21:UniswapV2OracleLibrary.sol
pragma solidity 0.6.11;
import './IUniswapV2Pair.sol';
import './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": {
"FXS.sol": "FRAXShares"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100000
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_oracle_address","type":"address"},{"internalType":"address","name":"_owner_address","type":"address"},{"internalType":"address","name":"_timelock_address","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FXSBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FXSMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"VoterVotesChanged","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FRAXStablecoinAdd","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FXS_DAO_min","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint96","name":"votes","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesis_supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCurrentVotes","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPriorVotes","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"b_address","type":"address"},{"internalType":"uint256","name":"b_amount","type":"uint256"}],"name":"pool_burn_from","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"m_address","type":"address"},{"internalType":"uint256","name":"m_amount","type":"uint256"}],"name":"pool_mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"frax_contract_address","type":"address"}],"name":"setFRAXAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"min_FXS","type":"uint256"}],"name":"setFXSMinDAO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"new_oracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner_address","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"new_timelock","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelock_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleVotes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trackingVotes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]