// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;
// Sources flattened with hardhat v2.9.3 https://hardhat.org
// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.5.0
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// File @openzeppelin/contracts/utils/Context.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.5.0
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, _allowances[owner][spender] + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = _allowances[owner][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
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);
}
/**
* @dev Spend `amount` form the allowance of `owner` toward `spender`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// File @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol@v4.5.0
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20Burnable is Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
}
// File @openzeppelin/contracts/security/Pausable.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
// File @openzeppelin/contracts/access/IAccessControl.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
// File @openzeppelin/contracts/utils/Strings.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
// File @openzeppelin/contracts/utils/introspection/IERC165.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File @openzeppelin/contracts/utils/introspection/ERC165.sol@v4.5.0
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// File @openzeppelin/contracts/access/AccessControl.sol@v4.5.0
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
// File contracts/Staking/Owned.sol
// https://docs.synthetix.io/contracts/Owned
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);
}
// File contracts/ERC20/ERC20PermissionedMint.sol
contract ERC20PermissionedMint is ERC20, ERC20Burnable, Owned {
// Core
address public timelock_address;
// Minters
address[] public minters_array; // Allowed to mint
mapping(address => bool) public minters; // Mapping is also used for faster verification
/* ========== CONSTRUCTOR ========== */
constructor(
address _creator_address,
address _timelock_address,
string memory _name,
string memory _symbol
)
ERC20(_name, _symbol)
Owned(_creator_address)
{
timelock_address = _timelock_address;
}
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
_;
}
modifier onlyMinters() {
require(minters[msg.sender] == true, "Only minters");
_;
}
/* ========== RESTRICTED FUNCTIONS ========== */
// Used by minters when user redeems
function minter_burn_from(address b_address, uint256 b_amount) public onlyMinters {
super.burnFrom(b_address, b_amount);
emit TokenMinterBurned(b_address, msg.sender, b_amount);
}
// This function is what other minters will call to mint new tokens
function minter_mint(address m_address, uint256 m_amount) public onlyMinters {
super._mint(m_address, m_amount);
emit TokenMinterMinted(msg.sender, m_address, m_amount);
}
// Adds whitelisted minters
function addMinter(address minter_address) public onlyByOwnGov {
require(minter_address != address(0), "Zero address detected");
require(minters[minter_address] == false, "Address already exists");
minters[minter_address] = true;
minters_array.push(minter_address);
emit MinterAdded(minter_address);
}
// Remove a minter
function removeMinter(address minter_address) public onlyByOwnGov {
require(minter_address != address(0), "Zero address detected");
require(minters[minter_address] == true, "Address nonexistant");
// Delete from the mapping
delete minters[minter_address];
// 'Delete' from the array by setting the address to 0x0
for (uint i = 0; i < minters_array.length; i++){
if (minters_array[i] == minter_address) {
minters_array[i] = address(0); // This will leave a null in the array and keep the indices the same
break;
}
}
emit MinterRemoved(minter_address);
}
/* ========== EVENTS ========== */
event TokenMinterBurned(address indexed from, address indexed to, uint256 amount);
event TokenMinterMinted(address indexed from, address indexed to, uint256 amount);
event MinterAdded(address minter_address);
event MinterRemoved(address minter_address);
}
// File contracts/FPI/FPI.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ================================ FPI ===============================
// ====================================================================
// Frax Price Index
// Initial peg target is the US CPI-U (Consumer Price Index, All Urban Consumers)
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Jack Corddry: https://github.com/corddry
// Reviewer(s) / Contributor(s)
// Sam Kazemian: https://github.com/samkazemian
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett
contract FPI is ERC20PermissionedMint {
/* ========== CONSTRUCTOR ========== */
constructor(
address _creator_address,
address _timelock_address
)
ERC20PermissionedMint(_creator_address, _timelock_address, "Frax Price Index", "FPI")
{
_mint(_creator_address, 100000000e18); // Genesis mint
}
}
// File contracts/Frax/IFrax.sol
interface IFrax {
function COLLATERAL_RATIO_PAUSER() external view returns (bytes32);
function DEFAULT_ADMIN_ADDRESS() external view returns (address);
function DEFAULT_ADMIN_ROLE() external view returns (bytes32);
function addPool(address pool_address ) external;
function allowance(address owner, address spender ) external view returns (uint256);
function approve(address spender, uint256 amount ) external returns (bool);
function balanceOf(address account ) external view returns (uint256);
function burn(uint256 amount ) external;
function burnFrom(address account, uint256 amount ) external;
function collateral_ratio_paused() external view returns (bool);
function controller_address() external view returns (address);
function creator_address() external view returns (address);
function decimals() external view returns (uint8);
function decreaseAllowance(address spender, uint256 subtractedValue ) external returns (bool);
function eth_usd_consumer_address() external view returns (address);
function eth_usd_price() external view returns (uint256);
function frax_eth_oracle_address() external view returns (address);
function frax_info() external view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256);
function frax_pools(address ) external view returns (bool);
function frax_pools_array(uint256 ) external view returns (address);
function frax_price() external view returns (uint256);
function frax_step() external view returns (uint256);
function fxs_address() external view returns (address);
function fxs_eth_oracle_address() external view returns (address);
function fxs_price() external view returns (uint256);
function genesis_supply() external view returns (uint256);
function getRoleAdmin(bytes32 role ) external view returns (bytes32);
function getRoleMember(bytes32 role, uint256 index ) external view returns (address);
function getRoleMemberCount(bytes32 role ) external view returns (uint256);
function globalCollateralValue() external view returns (uint256);
function global_collateral_ratio() external view returns (uint256);
function grantRole(bytes32 role, address account ) external;
function hasRole(bytes32 role, address account ) external view returns (bool);
function increaseAllowance(address spender, uint256 addedValue ) external returns (bool);
function last_call_time() external view returns (uint256);
function minting_fee() external view returns (uint256);
function name() external view returns (string memory);
function owner_address() external view returns (address);
function pool_burn_from(address b_address, uint256 b_amount ) external;
function pool_mint(address m_address, uint256 m_amount ) external;
function price_band() external view returns (uint256);
function price_target() external view returns (uint256);
function redemption_fee() external view returns (uint256);
function refreshCollateralRatio() external;
function refresh_cooldown() external view returns (uint256);
function removePool(address pool_address ) external;
function renounceRole(bytes32 role, address account ) external;
function revokeRole(bytes32 role, address account ) external;
function setController(address _controller_address ) external;
function setETHUSDOracle(address _eth_usd_consumer_address ) external;
function setFRAXEthOracle(address _frax_oracle_addr, address _weth_address ) external;
function setFXSAddress(address _fxs_address ) external;
function setFXSEthOracle(address _fxs_oracle_addr, address _weth_address ) external;
function setFraxStep(uint256 _new_step ) external;
function setMintingFee(uint256 min_fee ) external;
function setOwner(address _owner_address ) external;
function setPriceBand(uint256 _price_band ) external;
function setPriceTarget(uint256 _new_price_target ) external;
function setRedemptionFee(uint256 red_fee ) external;
function setRefreshCooldown(uint256 _new_cooldown ) external;
function setTimelock(address new_timelock ) external;
function symbol() external view returns (string memory);
function timelock_address() external view returns (address);
function toggleCollateralRatio() external;
function totalSupply() external view returns (uint256);
function transfer(address recipient, uint256 amount ) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount ) external returns (bool);
function weth_address() external view returns (address);
}
// File contracts/Frax/IFraxAMOMinter.sol
// MAY need to be updated
interface IFraxAMOMinter {
function FRAX() external view returns(address);
function FXS() external view returns(address);
function acceptOwnership() external;
function addAMO(address amo_address, bool sync_too) external;
function allAMOAddresses() external view returns(address[] memory);
function allAMOsLength() external view returns(uint256);
function amos(address) external view returns(bool);
function amos_array(uint256) external view returns(address);
function burnFraxFromAMO(uint256 frax_amount) external;
function burnFxsFromAMO(uint256 fxs_amount) external;
function col_idx() external view returns(uint256);
function collatDollarBalance() external view returns(uint256);
function collatDollarBalanceStored() external view returns(uint256);
function collat_borrow_cap() external view returns(int256);
function collat_borrowed_balances(address) external view returns(int256);
function collat_borrowed_sum() external view returns(int256);
function collateral_address() external view returns(address);
function collateral_token() external view returns(address);
function correction_offsets_amos(address, uint256) external view returns(int256);
function custodian_address() external view returns(address);
function dollarBalances() external view returns(uint256 frax_val_e18, uint256 collat_val_e18);
// function execute(address _to, uint256 _value, bytes _data) external returns(bool, bytes);
function fraxDollarBalanceStored() external view returns(uint256);
function fraxTrackedAMO(address amo_address) external view returns(int256);
function fraxTrackedGlobal() external view returns(int256);
function frax_mint_balances(address) external view returns(int256);
function frax_mint_cap() external view returns(int256);
function frax_mint_sum() external view returns(int256);
function fxs_mint_balances(address) external view returns(int256);
function fxs_mint_cap() external view returns(int256);
function fxs_mint_sum() external view returns(int256);
function giveCollatToAMO(address destination_amo, uint256 collat_amount) external;
function min_cr() external view returns(uint256);
function mintFraxForAMO(address destination_amo, uint256 frax_amount) external;
function mintFxsForAMO(address destination_amo, uint256 fxs_amount) external;
function missing_decimals() external view returns(uint256);
function nominateNewOwner(address _owner) external;
function nominatedOwner() external view returns(address);
function oldPoolCollectAndGive(address destination_amo) external;
function oldPoolRedeem(uint256 frax_amount) external;
function old_pool() external view returns(address);
function owner() external view returns(address);
function pool() external view returns(address);
function receiveCollatFromAMO(uint256 usdc_amount) external;
function recoverERC20(address tokenAddress, uint256 tokenAmount) external;
function removeAMO(address amo_address, bool sync_too) external;
function setAMOCorrectionOffsets(address amo_address, int256 frax_e18_correction, int256 collat_e18_correction) external;
function setCollatBorrowCap(uint256 _collat_borrow_cap) external;
function setCustodian(address _custodian_address) external;
function setFraxMintCap(uint256 _frax_mint_cap) external;
function setFraxPool(address _pool_address) external;
function setFxsMintCap(uint256 _fxs_mint_cap) external;
function setMinimumCollateralRatio(uint256 _min_cr) external;
function setTimelock(address new_timelock) external;
function syncDollarBalances() external;
function timelock_address() external view returns(address);
}
// File contracts/Oracle/AggregatorV3Interface.sol
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
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
);
}
// File @chainlink/contracts/src/v0.8/vendor/BufferChainlink.sol@v0.4.0
/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library BufferChainlink {
/**
* @dev Represents a mutable buffer. Buffers have a current value (buf) and
* a capacity. The capacity may be longer than the current value, in
* which case it can be extended without the need to allocate more memory.
*/
struct buffer {
bytes buf;
uint256 capacity;
}
/**
* @dev Initializes a buffer with an initial capacity.
* @param buf The buffer to initialize.
* @param capacity The number of bytes of space to allocate the buffer.
* @return The buffer, for chaining.
*/
function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) {
if (capacity % 32 != 0) {
capacity += 32 - (capacity % 32);
}
// Allocate space for the buffer data
buf.capacity = capacity;
assembly {
let ptr := mload(0x40)
mstore(buf, ptr)
mstore(ptr, 0)
mstore(0x40, add(32, add(ptr, capacity)))
}
return buf;
}
/**
* @dev Initializes a new buffer from an existing bytes object.
* Changes to the buffer may mutate the original value.
* @param b The bytes object to initialize the buffer with.
* @return A new buffer.
*/
function fromBytes(bytes memory b) internal pure returns (buffer memory) {
buffer memory buf;
buf.buf = b;
buf.capacity = b.length;
return buf;
}
function resize(buffer memory buf, uint256 capacity) private pure {
bytes memory oldbuf = buf.buf;
init(buf, capacity);
append(buf, oldbuf);
}
function max(uint256 a, uint256 b) private pure returns (uint256) {
if (a > b) {
return a;
}
return b;
}
/**
* @dev Sets buffer length to 0.
* @param buf The buffer to truncate.
* @return The original buffer, for chaining..
*/
function truncate(buffer memory buf) internal pure returns (buffer memory) {
assembly {
let bufptr := mload(buf)
mstore(bufptr, 0)
}
return buf;
}
/**
* @dev Writes a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The start offset to write to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function write(
buffer memory buf,
uint256 off,
bytes memory data,
uint256 len
) internal pure returns (buffer memory) {
require(len <= data.length);
if (off + len > buf.capacity) {
resize(buf, max(buf.capacity, len + off) * 2);
}
uint256 dest;
uint256 src;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Start address = buffer address + offset + sizeof(buffer length)
dest := add(add(bufptr, 32), off)
// Update buffer length if we're extending it
if gt(add(len, off), buflen) {
mstore(bufptr, add(len, off))
}
src := add(data, 32)
}
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
unchecked {
uint256 mask = (256**(32 - len)) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
return buf;
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function append(
buffer memory buf,
bytes memory data,
uint256 len
) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, len);
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, data.length);
}
/**
* @dev Writes a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write the byte at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeUint8(
buffer memory buf,
uint256 off,
uint8 data
) internal pure returns (buffer memory) {
if (off >= buf.capacity) {
resize(buf, buf.capacity * 2);
}
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Address = buffer address + sizeof(buffer length) + off
let dest := add(add(bufptr, off), 32)
mstore8(dest, data)
// Update buffer length if we extended it
if eq(off, buflen) {
mstore(bufptr, add(buflen, 1))
}
}
return buf;
}
/**
* @dev Appends a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) {
return writeUint8(buf, buf.buf.length, data);
}
/**
* @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (left-aligned).
* @return The original buffer, for chaining.
*/
function write(
buffer memory buf,
uint256 off,
bytes32 data,
uint256 len
) private pure returns (buffer memory) {
if (len + off > buf.capacity) {
resize(buf, (len + off) * 2);
}
unchecked {
uint256 mask = (256**len) - 1;
// Right-align data
data = data >> (8 * (32 - len));
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + sizeof(buffer length) + off + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
}
return buf;
}
/**
* @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeBytes20(
buffer memory buf,
uint256 off,
bytes20 data
) internal pure returns (buffer memory) {
return write(buf, off, bytes32(data), 20);
}
/**
* @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chhaining.
*/
function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, bytes32(data), 20);
}
/**
* @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, 32);
}
/**
* @dev Writes an integer to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (right-aligned).
* @return The original buffer, for chaining.
*/
function writeInt(
buffer memory buf,
uint256 off,
uint256 data,
uint256 len
) private pure returns (buffer memory) {
if (len + off > buf.capacity) {
resize(buf, (len + off) * 2);
}
uint256 mask = (256**len) - 1;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + off + sizeof(buffer length) + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
return buf;
}
/**
* @dev Appends a byte to the end of the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer.
*/
function appendInt(
buffer memory buf,
uint256 data,
uint256 len
) internal pure returns (buffer memory) {
return writeInt(buf, buf.buf.length, data, len);
}
}
// File @chainlink/contracts/src/v0.8/vendor/CBORChainlink.sol@v0.4.0
library CBORChainlink {
using BufferChainlink for BufferChainlink.buffer;
uint8 private constant MAJOR_TYPE_INT = 0;
uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1;
uint8 private constant MAJOR_TYPE_BYTES = 2;
uint8 private constant MAJOR_TYPE_STRING = 3;
uint8 private constant MAJOR_TYPE_ARRAY = 4;
uint8 private constant MAJOR_TYPE_MAP = 5;
uint8 private constant MAJOR_TYPE_TAG = 6;
uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7;
uint8 private constant TAG_TYPE_BIGNUM = 2;
uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3;
function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure {
if(value <= 23) {
buf.appendUint8(uint8((major << 5) | value));
} else if (value <= 0xFF) {
buf.appendUint8(uint8((major << 5) | 24));
buf.appendInt(value, 1);
} else if (value <= 0xFFFF) {
buf.appendUint8(uint8((major << 5) | 25));
buf.appendInt(value, 2);
} else if (value <= 0xFFFFFFFF) {
buf.appendUint8(uint8((major << 5) | 26));
buf.appendInt(value, 4);
} else {
buf.appendUint8(uint8((major << 5) | 27));
buf.appendInt(value, 8);
}
}
function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure {
buf.appendUint8(uint8((major << 5) | 31));
}
function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure {
if(value > 0xFFFFFFFFFFFFFFFF) {
encodeBigNum(buf, value);
} else {
encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value));
}
}
function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure {
if(value < -0x10000000000000000) {
encodeSignedBigNum(buf, value);
} else if(value > 0xFFFFFFFFFFFFFFFF) {
encodeBigNum(buf, uint(value));
} else if(value >= 0) {
encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value)));
} else {
encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value)));
}
}
function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure {
encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length));
buf.append(value);
}
function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure {
buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM));
encodeBytes(buf, abi.encode(value));
}
function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure {
buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM));
encodeBytes(buf, abi.encode(uint256(-1 - input)));
}
function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure {
encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length));
buf.append(bytes(value));
}
function startArray(BufferChainlink.buffer memory buf) internal pure {
encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY);
}
function startMap(BufferChainlink.buffer memory buf) internal pure {
encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP);
}
function endSequence(BufferChainlink.buffer memory buf) internal pure {
encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE);
}
}
// File @chainlink/contracts/src/v0.8/Chainlink.sol@v0.4.0
/**
* @title Library for common Chainlink functions
* @dev Uses imported CBOR library for encoding to buffer
*/
library Chainlink {
uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase
using CBORChainlink for BufferChainlink.buffer;
struct Request {
bytes32 id;
address callbackAddress;
bytes4 callbackFunctionId;
uint256 nonce;
BufferChainlink.buffer buf;
}
/**
* @notice Initializes a Chainlink request
* @dev Sets the ID, callback address, and callback function signature on the request
* @param self The uninitialized request
* @param jobId The Job Specification ID
* @param callbackAddr The callback address
* @param callbackFunc The callback function signature
* @return The initialized request
*/
function initialize(
Request memory self,
bytes32 jobId,
address callbackAddr,
bytes4 callbackFunc
) internal pure returns (Chainlink.Request memory) {
BufferChainlink.init(self.buf, defaultBufferSize);
self.id = jobId;
self.callbackAddress = callbackAddr;
self.callbackFunctionId = callbackFunc;
return self;
}
/**
* @notice Sets the data for the buffer without encoding CBOR on-chain
* @dev CBOR can be closed with curly-brackets {} or they can be left off
* @param self The initialized request
* @param data The CBOR data
*/
function setBuffer(Request memory self, bytes memory data) internal pure {
BufferChainlink.init(self.buf, data.length);
BufferChainlink.append(self.buf, data);
}
/**
* @notice Adds a string value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The string value to add
*/
function add(
Request memory self,
string memory key,
string memory value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeString(value);
}
/**
* @notice Adds a bytes value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The bytes value to add
*/
function addBytes(
Request memory self,
string memory key,
bytes memory value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeBytes(value);
}
/**
* @notice Adds a int256 value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The int256 value to add
*/
function addInt(
Request memory self,
string memory key,
int256 value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeInt(value);
}
/**
* @notice Adds a uint256 value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The uint256 value to add
*/
function addUint(
Request memory self,
string memory key,
uint256 value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeUInt(value);
}
/**
* @notice Adds an array of strings to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param values The array of string values to add
*/
function addStringArray(
Request memory self,
string memory key,
string[] memory values
) internal pure {
self.buf.encodeString(key);
self.buf.startArray();
for (uint256 i = 0; i < values.length; i++) {
self.buf.encodeString(values[i]);
}
self.buf.endSequence();
}
}
// File @chainlink/contracts/src/v0.8/interfaces/ENSInterface.sol@v0.4.0
interface ENSInterface {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(
bytes32 node,
bytes32 label,
address owner
) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
// File @chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol@v0.4.0
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(address owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool success);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool success);
}
// File @chainlink/contracts/src/v0.8/interfaces/ChainlinkRequestInterface.sol@v0.4.0
interface ChainlinkRequestInterface {
function oracleRequest(
address sender,
uint256 requestPrice,
bytes32 serviceAgreementID,
address callbackAddress,
bytes4 callbackFunctionId,
uint256 nonce,
uint256 dataVersion,
bytes calldata data
) external;
function cancelOracleRequest(
bytes32 requestId,
uint256 payment,
bytes4 callbackFunctionId,
uint256 expiration
) external;
}
// File @chainlink/contracts/src/v0.8/interfaces/OracleInterface.sol@v0.4.0
interface OracleInterface {
function fulfillOracleRequest(
bytes32 requestId,
uint256 payment,
address callbackAddress,
bytes4 callbackFunctionId,
uint256 expiration,
bytes32 data
) external returns (bool);
function isAuthorizedSender(address node) external view returns (bool);
function withdraw(address recipient, uint256 amount) external;
function withdrawable() external view returns (uint256);
}
// File @chainlink/contracts/src/v0.8/interfaces/OperatorInterface.sol@v0.4.0
interface OperatorInterface is OracleInterface, ChainlinkRequestInterface {
function operatorRequest(
address sender,
uint256 payment,
bytes32 specId,
bytes4 callbackFunctionId,
uint256 nonce,
uint256 dataVersion,
bytes calldata data
) external;
function fulfillOracleRequest2(
bytes32 requestId,
uint256 payment,
address callbackAddress,
bytes4 callbackFunctionId,
uint256 expiration,
bytes calldata data
) external returns (bool);
function ownerTransferAndCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool success);
function distributeFunds(address payable[] calldata receivers, uint256[] calldata amounts) external payable;
function getAuthorizedSenders() external returns (address[] memory);
function setAuthorizedSenders(address[] calldata senders) external;
function getForwarder() external returns (address);
}
// File @chainlink/contracts/src/v0.8/interfaces/PointerInterface.sol@v0.4.0
interface PointerInterface {
function getAddress() external view returns (address);
}
// File @chainlink/contracts/src/v0.8/vendor/ENSResolver.sol@v0.4.0
abstract contract ENSResolver_Chainlink {
function addr(bytes32 node) public view virtual returns (address);
}
// File @chainlink/contracts/src/v0.8/ChainlinkClient.sol@v0.4.0
/**
* @title The ChainlinkClient contract
* @notice Contract writers can inherit this contract in order to create requests for the
* Chainlink network
*/
abstract contract ChainlinkClient {
using Chainlink for Chainlink.Request;
uint256 internal constant LINK_DIVISIBILITY = 10**18;
uint256 private constant AMOUNT_OVERRIDE = 0;
address private constant SENDER_OVERRIDE = address(0);
uint256 private constant ORACLE_ARGS_VERSION = 1;
uint256 private constant OPERATOR_ARGS_VERSION = 2;
bytes32 private constant ENS_TOKEN_SUBNAME = keccak256("link");
bytes32 private constant ENS_ORACLE_SUBNAME = keccak256("oracle");
address private constant LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571;
ENSInterface private s_ens;
bytes32 private s_ensNode;
LinkTokenInterface private s_link;
OperatorInterface private s_oracle;
uint256 private s_requestCount = 1;
mapping(bytes32 => address) private s_pendingRequests;
event ChainlinkRequested(bytes32 indexed id);
event ChainlinkFulfilled(bytes32 indexed id);
event ChainlinkCancelled(bytes32 indexed id);
/**
* @notice Creates a request that can hold additional parameters
* @param specId The Job Specification ID that the request will be created for
* @param callbackAddr address to operate the callback on
* @param callbackFunctionSignature function signature to use for the callback
* @return A Chainlink Request struct in memory
*/
function buildChainlinkRequest(
bytes32 specId,
address callbackAddr,
bytes4 callbackFunctionSignature
) internal pure returns (Chainlink.Request memory) {
Chainlink.Request memory req;
return req.initialize(specId, callbackAddr, callbackFunctionSignature);
}
/**
* @notice Creates a request that can hold additional parameters
* @param specId The Job Specification ID that the request will be created for
* @param callbackFunctionSignature function signature to use for the callback
* @return A Chainlink Request struct in memory
*/
function buildOperatorRequest(bytes32 specId, bytes4 callbackFunctionSignature)
internal
view
returns (Chainlink.Request memory)
{
Chainlink.Request memory req;
return req.initialize(specId, address(this), callbackFunctionSignature);
}
/**
* @notice Creates a Chainlink request to the stored oracle address
* @dev Calls `chainlinkRequestTo` with the stored oracle address
* @param req The initialized Chainlink Request
* @param payment The amount of LINK to send for the request
* @return requestId The request ID
*/
function sendChainlinkRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) {
return sendChainlinkRequestTo(address(s_oracle), req, payment);
}
/**
* @notice Creates a Chainlink request to the specified oracle address
* @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
* send LINK which creates a request on the target oracle contract.
* Emits ChainlinkRequested event.
* @param oracleAddress The address of the oracle for the request
* @param req The initialized Chainlink Request
* @param payment The amount of LINK to send for the request
* @return requestId The request ID
*/
function sendChainlinkRequestTo(
address oracleAddress,
Chainlink.Request memory req,
uint256 payment
) internal returns (bytes32 requestId) {
uint256 nonce = s_requestCount;
s_requestCount = nonce + 1;
bytes memory encodedRequest = abi.encodeWithSelector(
ChainlinkRequestInterface.oracleRequest.selector,
SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
req.id,
address(this),
req.callbackFunctionId,
nonce,
ORACLE_ARGS_VERSION,
req.buf.buf
);
return _rawRequest(oracleAddress, nonce, payment, encodedRequest);
}
/**
* @notice Creates a Chainlink request to the stored oracle address
* @dev This function supports multi-word response
* @dev Calls `sendOperatorRequestTo` with the stored oracle address
* @param req The initialized Chainlink Request
* @param payment The amount of LINK to send for the request
* @return requestId The request ID
*/
function sendOperatorRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) {
return sendOperatorRequestTo(address(s_oracle), req, payment);
}
/**
* @notice Creates a Chainlink request to the specified oracle address
* @dev This function supports multi-word response
* @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
* send LINK which creates a request on the target oracle contract.
* Emits ChainlinkRequested event.
* @param oracleAddress The address of the oracle for the request
* @param req The initialized Chainlink Request
* @param payment The amount of LINK to send for the request
* @return requestId The request ID
*/
function sendOperatorRequestTo(
address oracleAddress,
Chainlink.Request memory req,
uint256 payment
) internal returns (bytes32 requestId) {
uint256 nonce = s_requestCount;
s_requestCount = nonce + 1;
bytes memory encodedRequest = abi.encodeWithSelector(
OperatorInterface.operatorRequest.selector,
SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
req.id,
req.callbackFunctionId,
nonce,
OPERATOR_ARGS_VERSION,
req.buf.buf
);
return _rawRequest(oracleAddress, nonce, payment, encodedRequest);
}
/**
* @notice Make a request to an oracle
* @param oracleAddress The address of the oracle for the request
* @param nonce used to generate the request ID
* @param payment The amount of LINK to send for the request
* @param encodedRequest data encoded for request type specific format
* @return requestId The request ID
*/
function _rawRequest(
address oracleAddress,
uint256 nonce,
uint256 payment,
bytes memory encodedRequest
) private returns (bytes32 requestId) {
requestId = keccak256(abi.encodePacked(this, nonce));
s_pendingRequests[requestId] = oracleAddress;
emit ChainlinkRequested(requestId);
require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle");
}
/**
* @notice Allows a request to be cancelled if it has not been fulfilled
* @dev Requires keeping track of the expiration value emitted from the oracle contract.
* Deletes the request from the `pendingRequests` mapping.
* Emits ChainlinkCancelled event.
* @param requestId The request ID
* @param payment The amount of LINK sent for the request
* @param callbackFunc The callback function specified for the request
* @param expiration The time of the expiration for the request
*/
function cancelChainlinkRequest(
bytes32 requestId,
uint256 payment,
bytes4 callbackFunc,
uint256 expiration
) internal {
OperatorInterface requested = OperatorInterface(s_pendingRequests[requestId]);
delete s_pendingRequests[requestId];
emit ChainlinkCancelled(requestId);
requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration);
}
/**
* @notice the next request count to be used in generating a nonce
* @dev starts at 1 in order to ensure consistent gas cost
* @return returns the next request count to be used in a nonce
*/
function getNextRequestCount() internal view returns (uint256) {
return s_requestCount;
}
/**
* @notice Sets the stored oracle address
* @param oracleAddress The address of the oracle contract
*/
function setChainlinkOracle(address oracleAddress) internal {
s_oracle = OperatorInterface(oracleAddress);
}
/**
* @notice Sets the LINK token address
* @param linkAddress The address of the LINK token contract
*/
function setChainlinkToken(address linkAddress) internal {
s_link = LinkTokenInterface(linkAddress);
}
/**
* @notice Sets the Chainlink token address for the public
* network as given by the Pointer contract
*/
function setPublicChainlinkToken() internal {
setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress());
}
/**
* @notice Retrieves the stored address of the LINK token
* @return The address of the LINK token
*/
function chainlinkTokenAddress() internal view returns (address) {
return address(s_link);
}
/**
* @notice Retrieves the stored address of the oracle contract
* @return The address of the oracle contract
*/
function chainlinkOracleAddress() internal view returns (address) {
return address(s_oracle);
}
/**
* @notice Allows for a request which was created on another contract to be fulfilled
* on this contract
* @param oracleAddress The address of the oracle contract that will fulfill the request
* @param requestId The request ID used for the response
*/
function addChainlinkExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) {
s_pendingRequests[requestId] = oracleAddress;
}
/**
* @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS
* @dev Accounts for subnodes having different resolvers
* @param ensAddress The address of the ENS contract
* @param node The ENS node hash
*/
function useChainlinkWithENS(address ensAddress, bytes32 node) internal {
s_ens = ENSInterface(ensAddress);
s_ensNode = node;
bytes32 linkSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_TOKEN_SUBNAME));
ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(linkSubnode));
setChainlinkToken(resolver.addr(linkSubnode));
updateChainlinkOracleWithENS();
}
/**
* @notice Sets the stored oracle contract with the address resolved by ENS
* @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously
*/
function updateChainlinkOracleWithENS() internal {
bytes32 oracleSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_ORACLE_SUBNAME));
ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(oracleSubnode));
setChainlinkOracle(resolver.addr(oracleSubnode));
}
/**
* @notice Ensures that the fulfillment is valid for this contract
* @dev Use if the contract developer prefers methods instead of modifiers for validation
* @param requestId The request ID for fulfillment
*/
function validateChainlinkCallback(bytes32 requestId)
internal
recordChainlinkFulfillment(requestId)
// solhint-disable-next-line no-empty-blocks
{
}
/**
* @dev Reverts if the sender is not the oracle of the request.
* Emits ChainlinkFulfilled event.
* @param requestId The request ID for fulfillment
*/
modifier recordChainlinkFulfillment(bytes32 requestId) {
require(msg.sender == s_pendingRequests[requestId], "Source must be the oracle of the request");
delete s_pendingRequests[requestId];
emit ChainlinkFulfilled(requestId);
_;
}
/**
* @dev Reverts if the request is already pending
* @param requestId The request ID for fulfillment
*/
modifier notPendingRequest(bytes32 requestId) {
require(s_pendingRequests[requestId] == address(0), "Request is already pending");
_;
}
}
// File contracts/Math/BokkyPooBahsDateTimeLibrary.sol
// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.01
//
// A gas-efficient Solidity date and time library
//
// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
//
// Tested date range 1970/01/01 to 2345/12/31
//
// Conventions:
// Unit | Range | Notes
// :-------- |:-------------:|:-----
// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
// year | 1970 ... 2345 |
// month | 1 ... 12 |
// day | 1 ... 31 |
// hour | 0 ... 23 |
// minute | 0 ... 59 |
// second | 0 ... 59 |
// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
//
//
// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
// ----------------------------------------------------------------------------
library BokkyPooBahsDateTimeLibrary {
uint constant SECONDS_PER_DAY = 24 * 60 * 60;
uint constant SECONDS_PER_HOUR = 60 * 60;
uint constant SECONDS_PER_MINUTE = 60;
int constant OFFSET19700101 = 2440588;
uint constant DOW_MON = 1;
uint constant DOW_TUE = 2;
uint constant DOW_WED = 3;
uint constant DOW_THU = 4;
uint constant DOW_FRI = 5;
uint constant DOW_SAT = 6;
uint constant DOW_SUN = 7;
// ------------------------------------------------------------------------
// Calculate the number of days from 1970/01/01 to year/month/day using
// the date conversion algorithm from
// http://aa.usno.navy.mil/faq/docs/JD_Formula.php
// and subtracting the offset 2440588 so that 1970/01/01 is day 0
//
// days = day
// - 32075
// + 1461 * (year + 4800 + (month - 14) / 12) / 4
// + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
// - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
// - offset
// ------------------------------------------------------------------------
function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) {
require(year >= 1970);
int _year = int(year);
int _month = int(month);
int _day = int(day);
int __days = _day
- 32075
+ 1461 * (_year + 4800 + (_month - 14) / 12) / 4
+ 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12
- 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4
- OFFSET19700101;
_days = uint(__days);
}
// ------------------------------------------------------------------------
// Calculate year/month/day from the number of days since 1970/01/01 using
// the date conversion algorithm from
// http://aa.usno.navy.mil/faq/docs/JD_Formula.php
// and adding the offset 2440588 so that 1970/01/01 is day 0
//
// int L = days + 68569 + offset
// int N = 4 * L / 146097
// L = L - (146097 * N + 3) / 4
// year = 4000 * (L + 1) / 1461001
// L = L - 1461 * year / 4 + 31
// month = 80 * L / 2447
// dd = L - 2447 * month / 80
// L = month / 11
// month = month + 2 - 12 * L
// year = 100 * (N - 49) + year + L
// ------------------------------------------------------------------------
function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) {
int __days = int(_days);
int L = __days + 68569 + OFFSET19700101;
int N = 4 * L / 146097;
L = L - (146097 * N + 3) / 4;
int _year = 4000 * (L + 1) / 1461001;
L = L - 1461 * _year / 4 + 31;
int _month = 80 * L / 2447;
int _day = L - 2447 * _month / 80;
L = _month / 11;
_month = _month + 2 - 12 * L;
_year = 100 * (N - 49) + _year + L;
year = uint(_year);
month = uint(_month);
day = uint(_day);
}
function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) {
timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
}
function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) {
timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second;
}
function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) {
(year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
(year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
uint secs = timestamp % SECONDS_PER_DAY;
hour = secs / SECONDS_PER_HOUR;
secs = secs % SECONDS_PER_HOUR;
minute = secs / SECONDS_PER_MINUTE;
second = secs % SECONDS_PER_MINUTE;
}
function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) {
if (year >= 1970 && month > 0 && month <= 12) {
uint daysInMonth = _getDaysInMonth(year, month);
if (day > 0 && day <= daysInMonth) {
valid = true;
}
}
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) {
if (isValidDate(year, month, day)) {
if (hour < 24 && minute < 60 && second < 60) {
valid = true;
}
}
}
function isLeapYear(uint timestamp) internal pure returns (bool leapYear) {
(uint year,,) = _daysToDate(timestamp / SECONDS_PER_DAY);
leapYear = _isLeapYear(year);
}
function _isLeapYear(uint year) internal pure returns (bool leapYear) {
leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
function isWeekDay(uint timestamp) internal pure returns (bool weekDay) {
weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
}
function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) {
weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
}
function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) {
(uint year, uint month,) = _daysToDate(timestamp / SECONDS_PER_DAY);
daysInMonth = _getDaysInMonth(year, month);
}
function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) {
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
daysInMonth = 31;
} else if (month != 2) {
daysInMonth = 30;
} else {
daysInMonth = _isLeapYear(year) ? 29 : 28;
}
}
// 1 = Monday, 7 = Sunday
function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) {
uint _days = timestamp / SECONDS_PER_DAY;
dayOfWeek = (_days + 3) % 7 + 1;
}
function getYear(uint timestamp) internal pure returns (uint year) {
(year,,) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function getMonth(uint timestamp) internal pure returns (uint month) {
(,month,) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function getDay(uint timestamp) internal pure returns (uint day) {
(,,day) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
function getHour(uint timestamp) internal pure returns (uint hour) {
uint secs = timestamp % SECONDS_PER_DAY;
hour = secs / SECONDS_PER_HOUR;
}
function getMinute(uint timestamp) internal pure returns (uint minute) {
uint secs = timestamp % SECONDS_PER_HOUR;
minute = secs / SECONDS_PER_MINUTE;
}
function getSecond(uint timestamp) internal pure returns (uint second) {
second = timestamp % SECONDS_PER_MINUTE;
}
function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
year += _years;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp >= timestamp);
}
function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
month += _months;
year += (month - 1) / 12;
month = (month - 1) % 12 + 1;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp >= timestamp);
}
function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _days * SECONDS_PER_DAY;
require(newTimestamp >= timestamp);
}
function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
require(newTimestamp >= timestamp);
}
function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
require(newTimestamp >= timestamp);
}
function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp + _seconds;
require(newTimestamp >= timestamp);
}
function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
year -= _years;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp <= timestamp);
}
function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) {
(uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY);
uint yearMonth = year * 12 + (month - 1) - _months;
year = yearMonth / 12;
month = yearMonth % 12 + 1;
uint daysInMonth = _getDaysInMonth(year, month);
if (day > daysInMonth) {
day = daysInMonth;
}
newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY;
require(newTimestamp <= timestamp);
}
function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _days * SECONDS_PER_DAY;
require(newTimestamp <= timestamp);
}
function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
require(newTimestamp <= timestamp);
}
function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
require(newTimestamp <= timestamp);
}
function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) {
newTimestamp = timestamp - _seconds;
require(newTimestamp <= timestamp);
}
function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) {
require(fromTimestamp <= toTimestamp);
(uint fromYear,,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
(uint toYear,,) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
_years = toYear - fromYear;
}
function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) {
require(fromTimestamp <= toTimestamp);
(uint fromYear, uint fromMonth,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
(uint toYear, uint toMonth,) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
_months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
}
function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) {
require(fromTimestamp <= toTimestamp);
_days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
}
function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) {
require(fromTimestamp <= toTimestamp);
_hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
}
function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) {
require(fromTimestamp <= toTimestamp);
_minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
}
function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) {
require(fromTimestamp <= toTimestamp);
_seconds = toTimestamp - fromTimestamp;
}
}
// File contracts/Math/BokkyPooBahsDateTimeContract.sol
// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.00 - Contract Instance
//
// A gas-efficient Solidity date and time library
//
// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
//
// Tested date range 1970/01/01 to 2345/12/31
//
// Conventions:
// Unit | Range | Notes
// :-------- |:-------------:|:-----
// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
// year | 1970 ... 2345 |
// month | 1 ... 12 |
// day | 1 ... 31 |
// hour | 0 ... 23 |
// minute | 0 ... 59 |
// second | 0 ... 59 |
// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
//
//
// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018.
//
// GNU Lesser General Public License 3.0
// https://www.gnu.org/licenses/lgpl-3.0.en.html
// ----------------------------------------------------------------------------
contract BokkyPooBahsDateTimeContract {
uint public constant SECONDS_PER_DAY = 24 * 60 * 60;
uint public constant SECONDS_PER_HOUR = 60 * 60;
uint public constant SECONDS_PER_MINUTE = 60;
int public constant OFFSET19700101 = 2440588;
uint public constant DOW_MON = 1;
uint public constant DOW_TUE = 2;
uint public constant DOW_WED = 3;
uint public constant DOW_THU = 4;
uint public constant DOW_FRI = 5;
uint public constant DOW_SAT = 6;
uint public constant DOW_SUN = 7;
function _now() public view returns (uint timestamp) {
timestamp = block.timestamp;
}
function _nowDateTime() public view returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
(year, month, day, hour, minute, second) = BokkyPooBahsDateTimeLibrary.timestampToDateTime(block.timestamp);
}
function _daysFromDate(uint year, uint month, uint day) public pure returns (uint _days) {
return BokkyPooBahsDateTimeLibrary._daysFromDate(year, month, day);
}
function _daysToDate(uint _days) public pure returns (uint year, uint month, uint day) {
return BokkyPooBahsDateTimeLibrary._daysToDate(_days);
}
function timestampFromDate(uint year, uint month, uint day) public pure returns (uint timestamp) {
return BokkyPooBahsDateTimeLibrary.timestampFromDate(year, month, day);
}
function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (uint timestamp) {
return BokkyPooBahsDateTimeLibrary.timestampFromDateTime(year, month, day, hour, minute, second);
}
function timestampToDate(uint timestamp) public pure returns (uint year, uint month, uint day) {
(year, month, day) = BokkyPooBahsDateTimeLibrary.timestampToDate(timestamp);
}
function timestampToDateTime(uint timestamp) public pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) {
(year, month, day, hour, minute, second) = BokkyPooBahsDateTimeLibrary.timestampToDateTime(timestamp);
}
function isValidDate(uint year, uint month, uint day) public pure returns (bool valid) {
valid = BokkyPooBahsDateTimeLibrary.isValidDate(year, month, day);
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (bool valid) {
valid = BokkyPooBahsDateTimeLibrary.isValidDateTime(year, month, day, hour, minute, second);
}
function isLeapYear(uint timestamp) public pure returns (bool leapYear) {
leapYear = BokkyPooBahsDateTimeLibrary.isLeapYear(timestamp);
}
function _isLeapYear(uint year) public pure returns (bool leapYear) {
leapYear = BokkyPooBahsDateTimeLibrary._isLeapYear(year);
}
function isWeekDay(uint timestamp) public pure returns (bool weekDay) {
weekDay = BokkyPooBahsDateTimeLibrary.isWeekDay(timestamp);
}
function isWeekEnd(uint timestamp) public pure returns (bool weekEnd) {
weekEnd = BokkyPooBahsDateTimeLibrary.isWeekEnd(timestamp);
}
function getDaysInMonth(uint timestamp) public pure returns (uint daysInMonth) {
daysInMonth = BokkyPooBahsDateTimeLibrary.getDaysInMonth(timestamp);
}
function _getDaysInMonth(uint year, uint month) public pure returns (uint daysInMonth) {
daysInMonth = BokkyPooBahsDateTimeLibrary._getDaysInMonth(year, month);
}
function getDayOfWeek(uint timestamp) public pure returns (uint dayOfWeek) {
dayOfWeek = BokkyPooBahsDateTimeLibrary.getDayOfWeek(timestamp);
}
function getYear(uint timestamp) public pure returns (uint year) {
year = BokkyPooBahsDateTimeLibrary.getYear(timestamp);
}
function getMonth(uint timestamp) public pure returns (uint month) {
month = BokkyPooBahsDateTimeLibrary.getMonth(timestamp);
}
function getDay(uint timestamp) public pure returns (uint day) {
day = BokkyPooBahsDateTimeLibrary.getDay(timestamp);
}
function getHour(uint timestamp) public pure returns (uint hour) {
hour = BokkyPooBahsDateTimeLibrary.getHour(timestamp);
}
function getMinute(uint timestamp) public pure returns (uint minute) {
minute = BokkyPooBahsDateTimeLibrary.getMinute(timestamp);
}
function getSecond(uint timestamp) public pure returns (uint second) {
second = BokkyPooBahsDateTimeLibrary.getSecond(timestamp);
}
function addYears(uint timestamp, uint _years) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.addYears(timestamp, _years);
}
function addMonths(uint timestamp, uint _months) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.addMonths(timestamp, _months);
}
function addDays(uint timestamp, uint _days) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.addDays(timestamp, _days);
}
function addHours(uint timestamp, uint _hours) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.addHours(timestamp, _hours);
}
function addMinutes(uint timestamp, uint _minutes) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.addMinutes(timestamp, _minutes);
}
function addSeconds(uint timestamp, uint _seconds) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.addSeconds(timestamp, _seconds);
}
function subYears(uint timestamp, uint _years) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.subYears(timestamp, _years);
}
function subMonths(uint timestamp, uint _months) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.subMonths(timestamp, _months);
}
function subDays(uint timestamp, uint _days) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.subDays(timestamp, _days);
}
function subHours(uint timestamp, uint _hours) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.subHours(timestamp, _hours);
}
function subMinutes(uint timestamp, uint _minutes) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.subMinutes(timestamp, _minutes);
}
function subSeconds(uint timestamp, uint _seconds) public pure returns (uint newTimestamp) {
newTimestamp = BokkyPooBahsDateTimeLibrary.subSeconds(timestamp, _seconds);
}
function diffYears(uint fromTimestamp, uint toTimestamp) public pure returns (uint _years) {
_years = BokkyPooBahsDateTimeLibrary.diffYears(fromTimestamp, toTimestamp);
}
function diffMonths(uint fromTimestamp, uint toTimestamp) public pure returns (uint _months) {
_months = BokkyPooBahsDateTimeLibrary.diffMonths(fromTimestamp, toTimestamp);
}
function diffDays(uint fromTimestamp, uint toTimestamp) public pure returns (uint _days) {
_days = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, toTimestamp);
}
function diffHours(uint fromTimestamp, uint toTimestamp) public pure returns (uint _hours) {
_hours = BokkyPooBahsDateTimeLibrary.diffHours(fromTimestamp, toTimestamp);
}
function diffMinutes(uint fromTimestamp, uint toTimestamp) public pure returns (uint _minutes) {
_minutes = BokkyPooBahsDateTimeLibrary.diffMinutes(fromTimestamp, toTimestamp);
}
function diffSeconds(uint fromTimestamp, uint toTimestamp) public pure returns (uint _seconds) {
_seconds = BokkyPooBahsDateTimeLibrary.diffSeconds(fromTimestamp, toTimestamp);
}
}
// File contracts/Uniswap/TransferHelper.sol
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(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 {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(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 {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(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');
}
}
// File contracts/Oracle/CPITrackerOracle.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ========================= CPITrackerOracle =========================
// ====================================================================
// Pull in CPI data and track it in Dec 2021 dollars
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Reviewer(s) / Contributor(s)
// Sam Kazemian: https://github.com/samkazemian
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett
// References
// https://docs.chain.link/docs/make-a-http-get-request/#api-consumer-example
contract CPITrackerOracle is Owned, ChainlinkClient {
using Chainlink for Chainlink.Request;
// Core
BokkyPooBahsDateTimeContract public time_contract;
address public timelock_address;
address public bot_address;
// Data
uint256 public cpi_last = 28012600000; // Dec 2021 CPI-U, 280.126 * 100000000
uint256 public cpi_target = 28193300000; // Jan 2022 CPI-U, 281.933 * 100000000
uint256 public peg_price_last = 1e18; // Use currPegPrice(). Will always be in Dec 2021 dollars
uint256 public peg_price_target = 1006450668627688968; // Will always be in Dec 2021 dollars
// Chainlink
address public oracle; // Chainlink CPI oracle address
bytes32 public jobId; // Job ID for the CPI-U date
uint256 public fee; // LINK token fee
// Tracking
uint256 public stored_year = 2022; // Last time (year) the stored CPI data was updated
uint256 public stored_month = 1; // Last time (month) the stored CPI data was updated
uint256 public lastUpdateTime = 1644886800; // Last time the stored CPI data was updated.
uint256 public ramp_period = 28 * 86400; // Apply the CPI delta to the peg price over a set period
uint256 public future_ramp_period = 28 * 86400;
CPIObservation[] public cpi_observations; // Historical tracking of CPI data
// Safety
uint256 public max_delta_frac = 25000; // 2.5%. Max month-to-month CPI delta.
// Misc
string[13] public month_names; // English names of the 12 months
uint256 public fulfill_ready_day = 15; // Date of the month that CPI data is expected to by ready by
/* ========== STRUCTS ========== */
struct CPIObservation {
uint256 result_year;
uint256 result_month;
uint256 cpi_target;
uint256 peg_price_target;
uint256 timestamp;
}
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == owner || msg.sender == timelock_address, "Not owner or timelock");
_;
}
modifier onlyByOwnGovBot() {
require(msg.sender == owner || msg.sender == timelock_address || msg.sender == bot_address, "Not owner, tlck, or bot");
_;
}
/* ========== CONSTRUCTOR ========== */
constructor (
address _creator_address,
address _timelock_address
) Owned(_creator_address) {
timelock_address = _timelock_address;
// Initialize the array. Cannot be done in the declaration
month_names = [
'',
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
// CPI [Ethereum]
// =================================
// setPublicChainlinkToken();
// time_contract = BokkyPooBahsDateTimeContract(0x90503D86E120B3B309CEBf00C2CA013aB3624736);
// oracle = 0x049Bd8C3adC3fE7d3Fc2a44541d955A537c2A484;
// jobId = "1c309d42c7084b34b1acf1a89e7b51fc";
// fee = 50e18; // 50 LINK
// CPI [Polygon Mainnet]
// =================================
// setChainlinkToken(0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39);
// time_contract = BokkyPooBahsDateTimeContract(0x998da4fCB229Db1AA84395ef6f0c6be6Ef3dbE58);
// oracle = 0x9B44870bcc35734c08e40F847cC068c0bA618194;
// jobId = "8107f18343a24980b2fe7d3c8f32630f";
// fee = 1e17; // 0.1 LINK
// CPI [Polygon Mumbai]
// =================================
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
time_contract = BokkyPooBahsDateTimeContract(0x2Dd1B4D4548aCCeA497050619965f91f78b3b532);
oracle = 0x3c30c5c415B2410326297F0f65f5Cbb32f3aefCc;
jobId = "32c3e7b12fe44665a4e2bb87aa9779af";
fee = 1e17; // 0.1 LINK
// Add the first observation
cpi_observations.push(CPIObservation(
2021,
12,
cpi_last,
peg_price_last,
1642208400 // Dec data observed on Jan 15 2021
));
// Add the second observation
cpi_observations.push(CPIObservation(
2022,
1,
cpi_target,
peg_price_target,
1644886800 // Jan data observed on Feb 15 2022
));
}
/* ========== VIEWS ========== */
function upcomingCPIParams() public view returns (
uint256 upcoming_year,
uint256 upcoming_month,
uint256 upcoming_timestamp
) {
if (stored_month == 12) {
upcoming_year = stored_year + 1;
upcoming_month = 1;
}
else {
upcoming_year = stored_year;
upcoming_month = stored_month + 1;
}
// Data is usually released by the 15th day of the next month (fulfill_ready_day)
// https://www.usinflationcalculator.com/inflation/consumer-price-index-release-schedule/
upcoming_timestamp = time_contract.timestampFromDate(upcoming_year, upcoming_month, fulfill_ready_day);
}
// Display the upcoming CPI month
function upcomingSerie() external view returns (string memory serie_name) {
// Get the upcoming CPI params
(uint256 upcoming_year, uint256 upcoming_month, ) = upcomingCPIParams();
// Convert to a string
return string(abi.encodePacked("CUSR0000SA0", " ", month_names[upcoming_month], " ", Strings.toString(upcoming_year)));
}
// Delta between the current and previous peg prices
function currDeltaFracE6() public view returns (int256) {
return int256(((peg_price_target - peg_price_last) * 1e6) / peg_price_last);
}
// Absolute value of the delta between the current and previous peg prices
function currDeltaFracAbsE6() public view returns (uint256) {
int256 curr_delta_frac = currDeltaFracE6();
if (curr_delta_frac > 0) return uint256(curr_delta_frac);
else return uint256(-curr_delta_frac);
}
// Current peg price in E18, accounting for the ramping
function currPegPrice() external view returns (uint256) {
uint256 elapsed_time = block.timestamp - lastUpdateTime;
if (elapsed_time >= ramp_period) {
return peg_price_target;
}
else {
// Calculate the fraction of the delta to use, based on the elapsed time
// Can be negative in case of deflation (that never happens right :])
int256 fractional_price_delta = (int256(peg_price_target - peg_price_last) * int256(elapsed_time)) / int256(ramp_period);
return uint256(int256(peg_price_last) + int256(fractional_price_delta));
}
}
/* ========== MUTATIVE ========== */
// Fetch the CPI data from the Chainlink oracle
function requestCPIData() external onlyByOwnGovBot returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Get the upcoming CPI params
(uint256 upcoming_year, uint256 upcoming_month, uint256 upcoming_timestamp) = upcomingCPIParams();
// Don't update too fast
require(block.timestamp >= upcoming_timestamp, "Too early");
request.add("serie", "CUSR0000SA0"); // CPI-U: https://data.bls.gov/timeseries/CUSR0000SA0
request.add("month", month_names[upcoming_month]);
request.add("year", Strings.toString(upcoming_year));
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Callback function
*/
// Called by the Chainlink oracle
function fulfill(bytes32 _requestId, uint256 result) public recordChainlinkFulfillment(_requestId)
{
// Set the stored CPI and price to the old targets
cpi_last = cpi_target;
peg_price_last = peg_price_target;
// Set the target CPI and price based on the results
cpi_target = result;
peg_price_target = (peg_price_last * cpi_target) / cpi_last;
// Make sure the delta isn't too large
require(currDeltaFracAbsE6() <= max_delta_frac, "Delta too high");
// Update the timestamp
lastUpdateTime = block.timestamp;
// Update the year and month
(uint256 result_year, uint256 result_month, ) = upcomingCPIParams();
stored_year = result_year;
stored_month = result_month;
// Update the future ramp period, if applicable
// A ramp cannot be updated mid-month as it will mess up the last_price math;
ramp_period = future_ramp_period;
// Add the observation
cpi_observations.push(CPIObservation(
result_year,
result_month,
cpi_target,
peg_price_target,
block.timestamp
));
emit CPIUpdated(result_year, result_month, result, peg_price_target, ramp_period);
}
function cancelRequest(
bytes32 _requestId,
uint256 _payment,
bytes4 _callbackFunc,
uint256 _expiration
) external onlyByOwnGovBot {
cancelChainlinkRequest(_requestId, _payment, _callbackFunc, _expiration);
}
/* ========== RESTRICTED FUNCTIONS ========== */
function setTimelock(address _new_timelock_address) external onlyByOwnGov {
timelock_address = _new_timelock_address;
}
function setBot(address _new_bot_address) external onlyByOwnGov {
bot_address = _new_bot_address;
}
function setOracleInfo(address _oracle, bytes32 _jobId, uint256 _fee) external onlyByOwnGov {
oracle = _oracle;
jobId = _jobId;
fee = _fee;
}
function setMaxDeltaFrac(uint256 _max_delta_frac) external onlyByOwnGov {
max_delta_frac = _max_delta_frac;
}
function setFulfillReadyDay(uint256 _fulfill_ready_day) external onlyByOwnGov {
fulfill_ready_day = _fulfill_ready_day;
}
function setFutureRampPeriod(uint256 _future_ramp_period) external onlyByOwnGov {
future_ramp_period = _future_ramp_period; // In sec
}
// Mainly for recovering LINK
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
// Only the owner address can ever receive the recovery withdrawal
TransferHelper.safeTransfer(tokenAddress, owner, tokenAmount);
}
/* ========== EVENTS ========== */
event CPIUpdated(uint256 year, uint256 month, uint256 result, uint256 peg_price_target, uint256 ramp_period);
}
// File contracts/Fraxswap/core/interfaces/IUniswapV2PairV5.sol
interface IUniswapV2PairV5 {
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;
}
// File contracts/Fraxswap/core/interfaces/IFraxswapPair.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ========================= IFraxswapPair ==========================
// ====================================================================
// Fraxswap LP Pair Interface
// Inspired by https://www.paradigm.xyz/2021/07/twamm
// https://github.com/para-dave/twamm
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett
// Reviewer(s) / Contributor(s)
// Travis Moore: https://github.com/FortisFortuna
// Sam Kazemian: https://github.com/samkazemian
interface IFraxswapPair is IUniswapV2PairV5 {
// TWAMM
event LongTermSwap0To1(address indexed addr, uint256 orderId, uint256 amount0In, uint256 numberOfTimeIntervals);
event LongTermSwap1To0(address indexed addr, uint256 orderId, uint256 amount1In, uint256 numberOfTimeIntervals);
event CancelLongTermOrder(address indexed addr, uint256 orderId, address sellToken, uint256 unsoldAmount, address buyToken, uint256 purchasedAmount);
event WithdrawProceedsFromLongTermOrder(address indexed addr, uint256 orderId, address indexed proceedToken, uint256 proceeds, bool orderExpired);
function longTermSwapFrom0To1(uint256 amount0In, uint256 numberOfTimeIntervals) external returns (uint256 orderId);
function longTermSwapFrom1To0(uint256 amount1In, uint256 numberOfTimeIntervals) external returns (uint256 orderId);
function cancelLongTermSwap(uint256 orderId) external;
function withdrawProceedsFromLongTermSwap(uint256 orderId) external returns (bool is_expired, address rewardTkn, uint256 totalReward);
function executeVirtualOrders(uint256 blockTimestamp) external;
function orderTimeInterval() external returns (uint256);
function getTWAPHistoryLength() external view returns (uint);
function getTwammReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast, uint112 _twammReserve0, uint112 _twammReserve1);
function getReserveAfterTwamm(uint256 blockTimestamp) external view returns (uint112 _reserve0, uint112 _reserve1, uint256 lastVirtualOrderTimestamp, uint112 _twammReserve0, uint112 _twammReserve1);
function getNextOrderID() external view returns (uint256);
function getOrderIDsForUser(address user) external view returns (uint256[] memory);
function getOrderIDsForUserLength(address user) external view returns (uint256);
// function getDetailedOrdersForUser(address user, uint256 offset, uint256 limit) external view returns (LongTermOrdersLib.Order[] memory detailed_orders);
function twammUpToDate() external view returns (bool);
function getTwammState() external view returns (uint256 token0Rate, uint256 token1Rate, uint256 lastVirtualOrderTimestamp, uint256 orderTimeInterval_rtn, uint256 rewardFactorPool0, uint256 rewardFactorPool1);
function getTwammSalesRateEnding(uint256 _blockTimestamp) external view returns (uint256 orderPool0SalesRateEnding, uint256 orderPool1SalesRateEnding);
function getTwammRewardFactor(uint256 _blockTimestamp) external view returns (uint256 rewardFactorPool0AtTimestamp, uint256 rewardFactorPool1AtTimestamp);
function getTwammOrder(uint256 orderId) external view returns (uint256 id, uint256 expirationTimestamp, uint256 saleRate, address owner, address sellTokenAddr, address buyTokenAddr);
function getTwammOrderProceedsView(uint256 orderId, uint256 blockTimestamp) external view returns (bool orderExpired, uint256 totalReward);
function getTwammOrderProceeds(uint256 orderId) external returns (bool orderExpired, uint256 totalReward);
function togglePauseNewSwaps() external;
}
// File contracts/Fraxswap/core/interfaces/IUniswapV2FactoryV5.sol
interface IUniswapV2FactoryV5 {
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;
}
// File contracts/Fraxswap/libraries/Babylonian.sol
// computes square roots using the babylonian method
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
library Babylonian {
// credit for this implementation goes to
// https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
function sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
// this block is equivalent to r = uint256(1) << (BitMath.mostSignificantBit(x) / 2);
// however that code costs significantly more gas
uint256 xx = x;
uint256 r = 1;
if (xx >= 0x100000000000000000000000000000000) {
xx >>= 128;
r <<= 64;
}
if (xx >= 0x10000000000000000) {
xx >>= 64;
r <<= 32;
}
if (xx >= 0x100000000) {
xx >>= 32;
r <<= 16;
}
if (xx >= 0x10000) {
xx >>= 16;
r <<= 8;
}
if (xx >= 0x100) {
xx >>= 8;
r <<= 4;
}
if (xx >= 0x10) {
xx >>= 4;
r <<= 2;
}
if (xx >= 0x8) {
r <<= 1;
}
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1; // Seven iterations should be enough
uint256 r1 = x / r;
return (r < r1 ? r : r1);
}
}
// File contracts/Fraxswap/libraries/FullMath.sol
/// @notice Math library that facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision.
/// @author Adapted from https://github.com/Uniswap/uniswap-v3-core/blob/main/contracts/libraries/FullMath.sol.
/// @dev Handles "phantom overflow", i.e., allows multiplication and division where an intermediate value overflows 256 bits.
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision - throws if result overflows an uint256 or denominator == 0.
/// @param a The multiplicand.
/// @param b The multiplier.
/// @param denominator The divisor.
/// @return result The 256-bit result.
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = a * b.
// Compute the product mod 2**256 and mod 2**256 - 1,
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0.
uint256 prod0; // Least significant 256 bits of the product.
uint256 prod1; // Most significant 256 bits of the product.
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256 -
// also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0] -
// compute remainder using mulmod.
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number.
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator -
// compute largest power of two divisor of denominator
// (always >= 1).
uint256 twos = uint256(-int256(denominator)) & denominator;
// Divide denominator by power of two.
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two.
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos -
// if twos is zero, then it becomes one.
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256 -
// now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// for four bits. That is, denominator * inv = 1 mod 2**4.
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // Inverse mod 2**8.
inv *= 2 - denominator * inv; // Inverse mod 2**16.
inv *= 2 - denominator * inv; // Inverse mod 2**32.
inv *= 2 - denominator * inv; // Inverse mod 2**64.
inv *= 2 - denominator * inv; // Inverse mod 2**128.
inv *= 2 - denominator * inv; // Inverse mod 2**256.
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
/// @notice Calculates ceil(a×b÷denominator) with full precision - throws if result overflows an uint256 or denominator == 0.
/// @param a The multiplicand.
/// @param b The multiplier.
/// @param denominator The divisor.
/// @return result The 256-bit result.
function mulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
unchecked {
if (mulmod(a, b, denominator) != 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
// File contracts/Fraxswap/periphery/libraries/UniswapV2LiquidityMathLibraryMini.sol
// library containing some math for dealing with the liquidity shares of a pair, e.g. computing their exact value
// in terms of the underlying tokens
library UniswapV2LiquidityMathLibraryMini {
// computes the direction and magnitude of the profit-maximizing trade
// function computeProfitMaximizingTrade(
// uint256 truePriceTokenA,
// uint256 truePriceTokenB,
// uint256 reserveA,
// uint256 reserveB
// ) pure internal returns (uint256 amountIn) {
// bool aToB = ((reserveA * truePriceTokenB) / reserveB) < truePriceTokenA;
// uint256 invariant = reserveA * reserveB;
// // true price is expressed as a ratio, so both values must be non-zero
// require(truePriceTokenA != 0 && truePriceTokenB != 0, "CPMT: ZERO_PRICE");
// uint256 leftSide = Babylonian.sqrt(
// FullMath.mulDiv(
// (invariant * 1000),
// aToB ? truePriceTokenA : truePriceTokenB,
// (aToB ? truePriceTokenB : truePriceTokenA) * 997
// )
// );
// uint256 rightSide = (aToB ? reserveA * 1000 : reserveB * 1000) / 997;
// if (leftSide < rightSide) return (0);
// // compute the amount that must be sent to move the price to the profit-maximizing price
// amountIn = leftSide - rightSide;
// }
function computeProfitMaximizingTrade(
uint256 inTokenTruePrice,
uint256 outTokenTruePrice,
uint256 reserveIn,
uint256 reserveOut
) pure internal returns (uint256 amountIn) {
uint256 invariant = reserveIn * reserveOut;
// true price is expressed as a ratio, so both values must be non-zero
require(inTokenTruePrice != 0 && outTokenTruePrice != 0, "CPMT: ZERO_PRICE");
uint256 leftSide = Babylonian.sqrt(
FullMath.mulDiv(
(invariant * 1000),
inTokenTruePrice,
outTokenTruePrice * 997
)
);
uint256 rightSide = (reserveIn * 1000) / 997;
if (leftSide < rightSide) return (0);
// compute the amount that must be sent to move the price to the profit-maximizing price
amountIn = leftSide - rightSide;
}
}
// File contracts/FPI/FPIControllerPool.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ========================= FPIControllerPool =========================
// ====================================================================
// Makes sure FPI is targeting the CPI peg
// First method is minting / redeeming with FRAX
// Second is bulk TWAMM trades
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Reviewer(s) / Contributor(s)
// Sam Kazemian: https://github.com/samkazemian
// Rich Gee: https://github.com/zer0blockchain
// Dennis: https://github.com/denett
// Jack Corddry: https://github.com/corddry
contract FPIControllerPool is Owned {
// Core
address public timelock_address;
FPI public FPI_TKN;
IFrax public FRAX;
IFraxswapPair public TWAMM;
// Oracles
AggregatorV3Interface public priceFeedFRAXUSD;
AggregatorV3Interface public priceFeedFPIUSD;
uint256 public chainlink_frax_usd_decimals;
uint256 public chainlink_fpi_usd_decimals;
CPITrackerOracle public cpiTracker;
// Tracking
uint256 public last_order_id_twamm; // Last TWAMM order ID that was used
// AMO addresses (lend out FRAX)
address[] public amos_array;
mapping(address => bool) public amos; // Mapping is also used for faster verification
// FRAX borrowed balances
mapping(address => int256) public frax_borrowed_balances; // Amount of FRAX the contract borrowed, by AMO
int256 public frax_borrowed_sum = 0; // Across all AMOs
int256 public frax_borrow_cap = int256(10000000e18); // Max amount of FRAX the contract can borrow from this contract
// Mint Fee Related
bool public use_manual_mint_fee = true;
uint256 public mint_fee_manual = 3000; // E6
uint256 public mint_fee_multiplier = 1000000; // E6
// Redeem Fee Related
bool public use_manual_redeem_fee = true;
uint256 public redeem_fee_manual = 3000; // E6
uint256 public redeem_fee_multiplier = 1000000; // E6
// Safety
uint256 public fpi_mint_cap = 110000000e18; // 110M
uint256 public peg_band_mint_redeem = 50000; // 5%
uint256 public peg_band_twamm = 100000; // 10%
uint256 public max_swap_frax_amt_in = 10000000e18; // 10M, mainly fat-finger precautions
uint256 public max_swap_fpi_amt_in = 10000000e18; // 10M, mainly fat-finger precautions
bool public mints_paused = false;
bool public redeems_paused = false;
// Constants for various precisions
uint256 public constant PRICE_PRECISION = 1e18;
uint256 public constant FEE_PRECISION = 1e6;
uint256 public constant PEG_BAND_PRECISION = 1e6;
// Misc
bool public frax_is_token0;
bool public pending_twamm_order = false;
uint256 public num_twamm_intervals = 168; // Each interval is default 3600 sec (1 hr)
uint256 public swap_period = 7 * 86400; // 7 days
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == owner || msg.sender == timelock_address, "Not owner or timelock");
_;
}
modifier validAMO(address amo_address) {
require(amos[amo_address], "Invalid AMO");
_;
}
/* ========== CONSTRUCTOR ========== */
constructor (
address _creator_address,
address _timelock_address,
address[6] memory _address_pack
) Owned(_creator_address) {
timelock_address = _timelock_address;
// Set instances
FRAX = IFrax(_address_pack[0]);
FPI_TKN = FPI(_address_pack[1]);
TWAMM = IFraxswapPair(_address_pack[2]);
priceFeedFRAXUSD = AggregatorV3Interface(_address_pack[3]);
priceFeedFPIUSD = AggregatorV3Interface(_address_pack[4]);
cpiTracker = CPITrackerOracle(_address_pack[5]);
// Set the oracle decimals
chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
chainlink_fpi_usd_decimals = priceFeedFPIUSD.decimals();
// Need to know which token FRAX is (0 or 1)
address token0 = TWAMM.token0();
if (token0 == address(FRAX)) frax_is_token0 = true;
else frax_is_token0 = false;
// Get the number of TWAMM intervals. Truncation desired
num_twamm_intervals = swap_period / TWAMM.orderTimeInterval();
}
/* ========== VIEWS ========== */
// Needed as a FRAX AMO
function dollarBalances() public view returns (uint256 frax_val_e18, uint256 collat_val_e18) {
// Dummy values here. FPI is not FRAX and should not be treated as FRAX collateral
frax_val_e18 = 1e18;
collat_val_e18 = 1e18;
}
// In Chainlink decimals
function getFRAXPriceE18() public view returns (uint256) {
(uint80 roundID, int price, , uint256 updatedAt, uint80 answeredInRound) = priceFeedFRAXUSD.latestRoundData();
require(price >= 0 && updatedAt!= 0 && answeredInRound >= roundID, "Invalid chainlink price");
return ((uint256(price) * 1e18) / (10 ** chainlink_frax_usd_decimals));
}
// In Chainlink decimals
function getFPIPriceE18() public view returns (uint256) {
(uint80 roundID, int price, , uint256 updatedAt, uint80 answeredInRound) = priceFeedFPIUSD.latestRoundData();
require(price >= 0 && updatedAt!= 0 && answeredInRound >= roundID, "Invalid chainlink price");
return ((uint256(price) * 1e18) / (10 ** chainlink_fpi_usd_decimals));
}
// Reserve spot price (fpi_price is dangerous / flash loan susceptible, so use carefully)
function getReservesAndFPISpot() public returns (uint256 reserveFRAX, uint256 reserveFPI, uint256 fpi_price) {
// Update and get the reserves
TWAMM.executeVirtualOrders(block.timestamp);
{
(uint256 reserveA, uint256 reserveB, ) = TWAMM.getReserves();
if (frax_is_token0){
reserveFRAX = reserveA;
reserveFPI = reserveB;
}
else {
reserveFRAX = reserveB;
reserveFPI = reserveA;
}
}
// Get the TWAMM reserve spot price
fpi_price = (reserveFRAX * 1e18) / reserveFPI;
}
// function getTwammToPegAmt() public returns (uint256 frax_in, uint256 fpi_in) {
// // Update and get the reserves
// (uint256 reserveFRAX, uint256 reserveFPI, uint256 reservePriceFPI) = getReservesAndFPISpot();
// // Get the CPI price
// uint256 cpi_peg_price = cpiTracker.currPegPrice();
// // Sort the pricing. NOTE: IN RATIOS, NOT PRICE
// uint256 truePriceFRAX = 1e18;
// uint256 truePriceFPI = cpi_peg_price;
// // Determine the direction
// if (fpi_to_frax) {
// return UniswapV2LiquidityMathLibraryMini.computeProfitMaximizingTrade(
// truePriceFPI, truePriceFRAX,
// reserveFPI, reserveFRAX
// );
// }
// else {
// return UniswapV2LiquidityMathLibraryMini.computeProfitMaximizingTrade(
// truePriceFRAX, truePriceFPI,
// reserveFRAX, reserveFPI
// );
// }
// }
// In E6
function mint_fee() public view returns (uint256 fee) {
if (use_manual_mint_fee) fee = mint_fee_manual;
else {
// For future variable fees
fee = 0;
// Apply the multiplier
fee = (fee * mint_fee_multiplier) / 1e6;
}
}
// In E6
function redeem_fee() public view returns (uint256 fee) {
if (use_manual_redeem_fee) fee = redeem_fee_manual;
else {
// For future variable fees
fee = 0;
// Apply the multiplier
fee = (fee * redeem_fee_multiplier) / 1e6;
}
}
// Get some info about the peg status
function pegStatusMntRdm() public view returns (uint256 cpi_peg_price, uint256 diff_frac_abs, bool within_range) {
uint256 fpi_price = getFPIPriceE18();
cpi_peg_price = cpiTracker.currPegPrice();
if (fpi_price > cpi_peg_price){
diff_frac_abs = ((fpi_price - cpi_peg_price) * PEG_BAND_PRECISION) / fpi_price;
}
else {
diff_frac_abs = ((cpi_peg_price - fpi_price) * PEG_BAND_PRECISION) / fpi_price;
}
within_range = (diff_frac_abs <= peg_band_mint_redeem);
}
// Get additional info about the peg status
function price_info() public view returns (
int256 collat_imbalance,
uint256 cpi_peg_price,
uint256 fpi_price,
uint256 price_diff_frac_abs
) {
fpi_price = getFPIPriceE18();
cpi_peg_price = cpiTracker.currPegPrice();
uint256 fpi_supply = FPI_TKN.totalSupply();
if (fpi_price > cpi_peg_price){
collat_imbalance = int256(((fpi_price - cpi_peg_price) * fpi_supply) / PRICE_PRECISION);
price_diff_frac_abs = ((fpi_price - cpi_peg_price) * PEG_BAND_PRECISION) / fpi_price;
}
else {
collat_imbalance = -1 * int256(((cpi_peg_price - fpi_price) * fpi_supply) / PRICE_PRECISION);
price_diff_frac_abs = ((cpi_peg_price - fpi_price) * PEG_BAND_PRECISION) / fpi_price;
}
}
/* ========== MUTATIVE ========== */
// Calculate Mint FPI with FRAX
function calcMintFPI(uint256 frax_in, uint256 min_fpi_out) public view returns (uint256 fpi_out) {
require(!mints_paused, "Mints paused");
// Fetch the CPI price and other info
(uint256 cpi_peg_price, , bool within_range) = pegStatusMntRdm();
// Make sure the peg is within range for minting
// Helps combat oracle errors and megadumping
require(within_range, "Peg band [Mint]");
// Calculate the amount of FPI that the incoming FRAX should give
fpi_out = (frax_in * PRICE_PRECISION) / cpi_peg_price;
// Apply the fee
fpi_out -= (fpi_out * mint_fee()) / FEE_PRECISION;
// Make sure enough FPI is generated
require(fpi_out >= min_fpi_out, "Slippage [Mint]");
// Check the mint cap
require(FPI_TKN.totalSupply() + fpi_out <= fpi_mint_cap, "FPI mint cap");
}
// Mint FPI with FRAX
function mintFPI(uint256 frax_in, uint256 min_fpi_out) external returns (uint256 fpi_out) {
fpi_out = calcMintFPI(frax_in, min_fpi_out);
// Pull in the FRAX
TransferHelper.safeTransferFrom(address(FRAX), msg.sender, address(this), frax_in);
// Mint FPI to the sender
FPI_TKN.minter_mint(msg.sender, fpi_out);
emit FPIMinted(frax_in, fpi_out);
}
// Calculate Redeem FPI for FRAX
function calcRedeemFPI(uint256 fpi_in, uint256 min_frax_out) public view returns (uint256 frax_out) {
require(!redeems_paused, "Redeems paused");
// Fetch the CPI price and other info
(uint256 cpi_peg_price, , bool within_range) = pegStatusMntRdm();
// Make sure the peg is within range for minting
// Helps combat oracle errors and megadumping
require(within_range, "Peg band [Redeem]");
// Calculate the amount of FRAX that the incoming FPI should give
frax_out = (fpi_in * cpi_peg_price) / PRICE_PRECISION;
// Apply the fee
frax_out -= (frax_out * redeem_fee()) / FEE_PRECISION;
// Make sure enough FRAX is generated
require(frax_out >= min_frax_out, "Slippage [Redeem]");
}
// Redeem FPI for FRAX
function redeemFPI(uint256 fpi_in, uint256 min_frax_out) external returns (uint256 frax_out) {
frax_out = calcRedeemFPI(fpi_in, min_frax_out);
// Pull in the FPI
TransferHelper.safeTransferFrom(address(FPI_TKN), msg.sender, address(this), fpi_in);
// Give FRAX to the sender
TransferHelper.safeTransfer(address(FRAX), msg.sender, frax_out);
emit FPIRedeemed(fpi_in, frax_out);
}
// Use the TWAMM for bulk peg corrections
function twammManual(uint256 frax_sell_amt, uint256 fpi_sell_amt, uint256 override_intervals) external onlyByOwnGov returns (uint256 frax_to_use, uint256 fpi_to_use) {
// Make sure only one direction occurs
require(!((frax_sell_amt > 0) && (fpi_sell_amt > 0)), "Can only sell in one direction");
// Update and get the reserves
// longTermSwapFrom0to1 and longTermSwapFrom1To0 do it automatically
// TWAMM.executeVirtualOrders(block.timestamp);
// Cancel the previous order (if any) and collect any leftover tokens
if (pending_twamm_order) TWAMM.cancelLongTermSwap(last_order_id_twamm);
// Now calculate the imbalance after the burn
(, , , uint256 price_diff_abs) = price_info();
// Make sure the FPI oracle price hasn't moved away too much from the target peg price
require(price_diff_abs <= peg_band_twamm, "Peg band [TWAMM]");
// Create a new order
last_order_id_twamm = TWAMM.getNextOrderID();
{
if (fpi_sell_amt > 0) {
// Mint FPI and sell for FRAX
// --------------------------------
fpi_to_use = fpi_sell_amt;
// Make sure nonzero
require(fpi_to_use > 0, "FPI sold must be nonzero");
// Safety check
require(fpi_to_use <= max_swap_fpi_amt_in, "Too much FPI sold");
// Mint some FPI
FPI_TKN.minter_mint(address(this), fpi_to_use);
// Approve FPI first
FPI_TKN.approve(address(TWAMM), fpi_to_use);
// Sell FPI for FRAX
if (frax_is_token0) {
TWAMM.longTermSwapFrom1To0(fpi_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
}
else {
TWAMM.longTermSwapFrom0To1(fpi_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
}
}
else {
// Use FRAX to buy FPI
// --------------------------------
frax_to_use = frax_sell_amt;
// Make sure nonzero
require(frax_to_use > 0, "FRAX sold must be nonzero");
// Safety check
require(frax_to_use <= max_swap_frax_amt_in, "Too much FRAX sold");
// Approve FRAX first
FRAX.approve(address(TWAMM), frax_to_use);
// Sell FRAX for FPI
if (frax_is_token0) {
TWAMM.longTermSwapFrom0To1(frax_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
}
else {
TWAMM.longTermSwapFrom1To0(frax_to_use, override_intervals > 0 ? override_intervals : num_twamm_intervals);
}
}
}
// Mark that there is a pending order
pending_twamm_order = true;
emit TWAMMedToPeg(last_order_id_twamm, frax_to_use, fpi_to_use, block.timestamp);
}
function cancelCurrTWAMMOrder(uint256 order_id_override) public onlyByOwnGov {
// Get the order id
uint256 order_id_to_use = (order_id_override == 0 ? last_order_id_twamm : order_id_override);
// Cancel the order
TWAMM.cancelLongTermSwap(order_id_to_use);
// Clear the pending order indicator
pending_twamm_order = false;
emit TWAMMOrderCancelled(order_id_to_use);
}
function collectCurrTWAMMProceeds(uint256 order_id_override) external onlyByOwnGov {
// Get the order id
uint256 order_id_to_use = (order_id_override == 0 ? last_order_id_twamm : order_id_override);
// Withdraw current proceeds
(bool is_expired, address rewardTkn, uint256 totalReward) = TWAMM.withdrawProceedsFromLongTermSwap(order_id_to_use);
// If using the last_order_id_twamm and it is expired, clear the pending order indicator
if (is_expired && (order_id_override == 0)) pending_twamm_order = false;
emit TWAMMProceedsCollected(order_id_to_use, rewardTkn, totalReward);
}
/* ========== Burns and givebacks ========== */
// Burn unneeded or excess FPI.
function burnFPI(bool burn_all, uint256 fpi_amount) public onlyByOwnGov {
uint256 amt_to_burn = burn_all ? FPI_TKN.balanceOf(address(this)) : fpi_amount;
// Burn
FPI_TKN.burn(amt_to_burn);
emit FPIBurned(amt_to_burn);
}
// ------------------------------------------------------------------
// ------------------------------ FRAX ------------------------------
// ------------------------------------------------------------------
// Lend the FRAX collateral to an AMO
function giveFRAXToAMO(address destination_amo, uint256 frax_amount) external onlyByOwnGov validAMO(destination_amo) {
require(frax_amount <= (2**255 - 1), "int256 overflow");
int256 frax_amount_i256 = int256(frax_amount);
// Update the balances first
require((frax_borrowed_sum + frax_amount_i256) <= frax_borrow_cap, "Borrow cap");
frax_borrowed_balances[destination_amo] += frax_amount_i256;
frax_borrowed_sum += frax_amount_i256;
// Give the FRAX to the AMO
TransferHelper.safeTransfer(address(FRAX), destination_amo, frax_amount);
emit FRAXGivenToAMO(destination_amo, frax_amount);
}
// AMO gives back FRAX. Needed for proper accounting
function receiveFRAXFromAMO(uint256 frax_amount) external validAMO(msg.sender) {
require(frax_amount <= (2**255 - 1), "int256 overflow");
int256 frax_amt_i256 = int256(frax_amount);
// Give back first
TransferHelper.safeTransferFrom(address(FRAX), msg.sender, address(this), frax_amount);
// Then update the balances
frax_borrowed_balances[msg.sender] -= frax_amt_i256;
frax_borrowed_sum -= frax_amt_i256;
emit FRAXReceivedFromAMO(msg.sender, frax_amount);
}
/* ========== RESTRICTED FUNCTIONS ========== */
// Adds an AMO
function addAMO(address amo_address) public onlyByOwnGov {
require(amo_address != address(0), "Zero address detected");
require(amos[amo_address] == false, "Address already exists");
amos[amo_address] = true;
amos_array.push(amo_address);
emit AMOAdded(amo_address);
}
// Removes an AMO
function removeAMO(address amo_address) public onlyByOwnGov {
require(amo_address != address(0), "Zero address detected");
require(amos[amo_address] == true, "Address nonexistant");
// Delete from the mapping
delete amos[amo_address];
// 'Delete' from the array by setting the address to 0x0
for (uint i = 0; i < amos_array.length; i++){
if (amos_array[i] == amo_address) {
amos_array[i] = address(0); // This will leave a null in the array and keep the indices the same
break;
}
}
emit AMORemoved(amo_address);
}
function setOracles(address _frax_oracle, address _fpi_oracle, address _cpi_oracle) external onlyByOwnGov {
priceFeedFRAXUSD = AggregatorV3Interface(_frax_oracle);
priceFeedFPIUSD = AggregatorV3Interface(_fpi_oracle);
cpiTracker = CPITrackerOracle(_cpi_oracle);
// Set the Chainlink oracle decimals
chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
chainlink_fpi_usd_decimals = priceFeedFPIUSD.decimals();
}
function setTWAMMAndSwapPeriod(address _twamm_addr, uint256 _swap_period) external onlyByOwnGov {
// Cancel an outstanding order, if present
if (pending_twamm_order) cancelCurrTWAMMOrder(last_order_id_twamm);
// Change the TWAMM parameters
TWAMM = IFraxswapPair(_twamm_addr);
swap_period = _swap_period;
num_twamm_intervals = _swap_period / TWAMM.orderTimeInterval();
}
function toggleMints() external onlyByOwnGov {
mints_paused = !mints_paused;
}
function toggleRedeems() external onlyByOwnGov {
redeems_paused = !redeems_paused;
}
function setFraxBorrowCap(int256 _frax_borrow_cap) external onlyByOwnGov {
require(_frax_borrow_cap >= 0, "int256 underflow");
require(_frax_borrow_cap <= (2**255 - 1), "int256 overflow");
frax_borrow_cap = _frax_borrow_cap;
}
function setMintCap(uint256 _fpi_mint_cap) external onlyByOwnGov {
fpi_mint_cap = _fpi_mint_cap;
}
function setPegBands(uint256 _peg_band_mint_redeem, uint256 _peg_band_twamm) external onlyByOwnGov {
peg_band_mint_redeem = _peg_band_mint_redeem;
peg_band_twamm = _peg_band_twamm;
}
function setMintRedeemFees(
bool _use_manual_mint_fee,
uint256 _mint_fee_manual,
uint256 _mint_fee_multiplier,
bool _use_manual_redeem_fee,
uint256 _redeem_fee_manual,
uint256 _redeem_fee_multiplier
) external onlyByOwnGov {
use_manual_mint_fee = _use_manual_mint_fee;
mint_fee_manual = _mint_fee_manual;
mint_fee_multiplier = _mint_fee_multiplier;
use_manual_redeem_fee = _use_manual_redeem_fee;
redeem_fee_manual = _redeem_fee_manual;
redeem_fee_multiplier = _redeem_fee_multiplier;
}
function setTWAMMMaxSwapIn(uint256 _max_swap_frax_amt_in, uint256 _max_swap_fpi_amt_in) external onlyByOwnGov {
max_swap_frax_amt_in = _max_swap_frax_amt_in;
max_swap_fpi_amt_in = _max_swap_fpi_amt_in;
}
function setTimelock(address _new_timelock_address) external onlyByOwnGov {
timelock_address = _new_timelock_address;
}
// Added to support recovering LP Rewards and other mistaken tokens from other systems to be distributed to holders
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
// Only the owner address can ever receive the recovery withdrawal
TransferHelper.safeTransfer(tokenAddress, owner, tokenAmount);
emit RecoveredERC20(tokenAddress, tokenAmount);
}
/* ========== EVENTS ========== */
event FPIMinted(uint256 frax_in, uint256 fpi_out);
event FPIRedeemed(uint256 fpi_in, uint256 frax_out);
event TWAMMedToPeg(uint256 order_id, uint256 frax_amt, uint256 fpi_amt, uint256 timestamp);
event TWAMMOrderCancelled(uint256 order_id);
event TWAMMProceedsCollected(uint256 order_id, address reward_tkn, uint256 ttl_reward);
event FPIBurned(uint256 amt_burned);
event FRAXGivenToAMO(address destination_amo, uint256 frax_amount);
event FRAXReceivedFromAMO(address source_amo, uint256 frax_amount);
event AMOAdded(address amo_address);
event AMORemoved(address amo_address);
event RecoveredERC20(address token, uint256 amount);
}
{
"compilationTarget": {
"FPIControllerPool.sol": "FPIControllerPool"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_creator_address","type":"address"},{"internalType":"address","name":"_timelock_address","type":"address"},{"internalType":"address[6]","name":"_address_pack","type":"address[6]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"amo_address","type":"address"}],"name":"AMOAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"amo_address","type":"address"}],"name":"AMORemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amt_burned","type":"uint256"}],"name":"FPIBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"frax_in","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fpi_out","type":"uint256"}],"name":"FPIMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fpi_in","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"frax_out","type":"uint256"}],"name":"FPIRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"destination_amo","type":"address"},{"indexed":false,"internalType":"uint256","name":"frax_amount","type":"uint256"}],"name":"FRAXGivenToAMO","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"source_amo","type":"address"},{"indexed":false,"internalType":"uint256","name":"frax_amount","type":"uint256"}],"name":"FRAXReceivedFromAMO","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":"RecoveredERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"order_id","type":"uint256"}],"name":"TWAMMOrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"order_id","type":"uint256"},{"indexed":false,"internalType":"address","name":"reward_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"ttl_reward","type":"uint256"}],"name":"TWAMMProceedsCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"order_id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"frax_amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fpi_amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"TWAMMedToPeg","type":"event"},{"inputs":[],"name":"FEE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FPI_TKN","outputs":[{"internalType":"contract FPI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FRAX","outputs":[{"internalType":"contract IFrax","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PEG_BAND_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TWAMM","outputs":[{"internalType":"contract IFraxswapPair","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"amo_address","type":"address"}],"name":"addAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"amos","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"amos_array","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"burn_all","type":"bool"},{"internalType":"uint256","name":"fpi_amount","type":"uint256"}],"name":"burnFPI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_in","type":"uint256"},{"internalType":"uint256","name":"min_fpi_out","type":"uint256"}],"name":"calcMintFPI","outputs":[{"internalType":"uint256","name":"fpi_out","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fpi_in","type":"uint256"},{"internalType":"uint256","name":"min_frax_out","type":"uint256"}],"name":"calcRedeemFPI","outputs":[{"internalType":"uint256","name":"frax_out","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"order_id_override","type":"uint256"}],"name":"cancelCurrTWAMMOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainlink_fpi_usd_decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlink_frax_usd_decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"order_id_override","type":"uint256"}],"name":"collectCurrTWAMMProceeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cpiTracker","outputs":[{"internalType":"contract CPITrackerOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dollarBalances","outputs":[{"internalType":"uint256","name":"frax_val_e18","type":"uint256"},{"internalType":"uint256","name":"collat_val_e18","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fpi_mint_cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"frax_borrow_cap","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"frax_borrowed_balances","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"frax_borrowed_sum","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"frax_is_token0","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFPIPriceE18","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFRAXPriceE18","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReservesAndFPISpot","outputs":[{"internalType":"uint256","name":"reserveFRAX","type":"uint256"},{"internalType":"uint256","name":"reserveFPI","type":"uint256"},{"internalType":"uint256","name":"fpi_price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"destination_amo","type":"address"},{"internalType":"uint256","name":"frax_amount","type":"uint256"}],"name":"giveFRAXToAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"last_order_id_twamm","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"max_swap_fpi_amt_in","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"max_swap_frax_amt_in","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_in","type":"uint256"},{"internalType":"uint256","name":"min_fpi_out","type":"uint256"}],"name":"mintFPI","outputs":[{"internalType":"uint256","name":"fpi_out","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mint_fee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint_fee_manual","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint_fee_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mints_paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"num_twamm_intervals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pegStatusMntRdm","outputs":[{"internalType":"uint256","name":"cpi_peg_price","type":"uint256"},{"internalType":"uint256","name":"diff_frac_abs","type":"uint256"},{"internalType":"bool","name":"within_range","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peg_band_mint_redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peg_band_twamm","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pending_twamm_order","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeedFPIUSD","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeedFRAXUSD","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_info","outputs":[{"internalType":"int256","name":"collat_imbalance","type":"int256"},{"internalType":"uint256","name":"cpi_peg_price","type":"uint256"},{"internalType":"uint256","name":"fpi_price","type":"uint256"},{"internalType":"uint256","name":"price_diff_frac_abs","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_amount","type":"uint256"}],"name":"receiveFRAXFromAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fpi_in","type":"uint256"},{"internalType":"uint256","name":"min_frax_out","type":"uint256"}],"name":"redeemFPI","outputs":[{"internalType":"uint256","name":"frax_out","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeem_fee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeem_fee_manual","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeem_fee_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeems_paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"amo_address","type":"address"}],"name":"removeAMO","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"_frax_borrow_cap","type":"int256"}],"name":"setFraxBorrowCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fpi_mint_cap","type":"uint256"}],"name":"setMintCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_use_manual_mint_fee","type":"bool"},{"internalType":"uint256","name":"_mint_fee_manual","type":"uint256"},{"internalType":"uint256","name":"_mint_fee_multiplier","type":"uint256"},{"internalType":"bool","name":"_use_manual_redeem_fee","type":"bool"},{"internalType":"uint256","name":"_redeem_fee_manual","type":"uint256"},{"internalType":"uint256","name":"_redeem_fee_multiplier","type":"uint256"}],"name":"setMintRedeemFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_frax_oracle","type":"address"},{"internalType":"address","name":"_fpi_oracle","type":"address"},{"internalType":"address","name":"_cpi_oracle","type":"address"}],"name":"setOracles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_peg_band_mint_redeem","type":"uint256"},{"internalType":"uint256","name":"_peg_band_twamm","type":"uint256"}],"name":"setPegBands","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_twamm_addr","type":"address"},{"internalType":"uint256","name":"_swap_period","type":"uint256"}],"name":"setTWAMMAndSwapPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_max_swap_frax_amt_in","type":"uint256"},{"internalType":"uint256","name":"_max_swap_fpi_amt_in","type":"uint256"}],"name":"setTWAMMMaxSwapIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_new_timelock_address","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swap_period","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelock_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleMints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleRedeems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_sell_amt","type":"uint256"},{"internalType":"uint256","name":"fpi_sell_amt","type":"uint256"},{"internalType":"uint256","name":"override_intervals","type":"uint256"}],"name":"twammManual","outputs":[{"internalType":"uint256","name":"frax_to_use","type":"uint256"},{"internalType":"uint256","name":"fpi_to_use","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"use_manual_mint_fee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"use_manual_redeem_fee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]