Overview
ETH Balance
ETH Value
$0.00Latest 8 from a total of 8 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Nominate New Own... | 3248422 | 1525 days ago | IN | 0 ETH | 0.00101892503 ETH | ||||
| Metapool Deposit | 3199513 | 1527 days ago | IN | 0 ETH | 0.001075477694 ETH | ||||
| Metapool Withdra... | 3191978 | 1527 days ago | IN | 0 ETH | 0.000858415563 ETH | ||||
| Metapool Withdra... | 3191973 | 1527 days ago | IN | 0 ETH | 0.000860133102 ETH | ||||
| Metapool Withdra... | 3191969 | 1527 days ago | IN | 0 ETH | 0.000828610685 ETH | ||||
| Metapool Deposit | 3191946 | 1527 days ago | IN | 0 ETH | 0.001114785609 ETH | ||||
| Metapool Withdra... | 3191927 | 1527 days ago | IN | 0 ETH | 0.000874351063 ETH | ||||
| Metapool Deposit | 3191887 | 1527 days ago | IN | 0 ETH | 0.001336090054 ETH |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 46697216 | 1136 days ago | 0 ETH | ||||
| 46697216 | 1136 days ago | 0 ETH | ||||
| 32273307 | 1189 days ago | 0 ETH | ||||
| 27047571 | 1216 days ago | 0 ETH | ||||
| 27047571 | 1216 days ago | 0 ETH | ||||
| 19329531 | 1271 days ago | 0 ETH | ||||
| 19329531 | 1271 days ago | 0 ETH | ||||
| 4127554 | 1489 days ago | 0 ETH | ||||
| 4127554 | 1489 days ago | 0 ETH | ||||
| 4127554 | 1489 days ago | 0 ETH | ||||
| 4127554 | 1489 days ago | 0 ETH | ||||
| 4127554 | 1489 days ago | 0 ETH | ||||
| 3741486 | 1507 days ago | 0 ETH | ||||
| 3741486 | 1507 days ago | 0 ETH | ||||
| 3741486 | 1507 days ago | 0 ETH | ||||
| 3741486 | 1507 days ago | 0 ETH | ||||
| 3741486 | 1507 days ago | 0 ETH | ||||
| 3684992 | 1509 days ago | 0 ETH | ||||
| 3684992 | 1509 days ago | 0 ETH | ||||
| 3684992 | 1509 days ago | 0 ETH | ||||
| 3684992 | 1509 days ago | 0 ETH | ||||
| 3684992 | 1509 days ago | 0 ETH | ||||
| 3441724 | 1518 days ago | 0 ETH | ||||
| 3441724 | 1518 days ago | 0 ETH | ||||
| 3441724 | 1518 days ago | 0 ETH |
Cross-Chain Transactions
Contract Source Code (Solidity)
/**
*Submitted for verification at Arbiscan.io on 2021-11-20
*/
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;
// Sources flattened with hardhat v2.6.7 https://hardhat.org
// File contracts/Common/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 GSN 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 payable) {
return payable(msg.sender);
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File contracts/Math/SafeMath.sol
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File contracts/ERC20/IERC20.sol
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, 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 contracts/Utils/Address.sol
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// File contracts/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 {ERC20Mintable}.
*
* 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 guidelines: functions revert instead
* of 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 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory __name, string memory __symbol) public {
_name = __name;
_symbol = __symbol;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view 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 {_setupDecimals} is
* called.
*
* 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 returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, 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}.
*
* Requirements:
*
* - `spender` cannot be the zero address.approve(address spender, uint256 amount)
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), 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};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @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) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(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) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is 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:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @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
*
* - `to` 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 = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @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 {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(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);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is 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 Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal virtual {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
/**
* @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 to 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:using-hooks.adoc[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
// File contracts/ERC20/ERC20Permit/IERC20Permit.sol
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// File contracts/ERC20/ERC20Permit/ECDSA.sol
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
// File contracts/ERC20/ERC20Permit/EIP712.sol
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (block.chainid == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}
// File contracts/ERC20/ERC20Permit/Counters.sol
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library Counters {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}
// File contracts/ERC20/ERC20Permit/ERC20Permit.sol
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* _Available since v3.4._
*/
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
using Counters for Counters.Counter;
mapping(address => Counters.Counter) private _nonces;
// solhint-disable-next-line var-name-mixedcase
bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
constructor(string memory name) EIP712(name, "1") {}
/**
* @dev See {IERC20Permit-permit}.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_approve(owner, spender, value);
}
/**
* @dev See {IERC20Permit-nonces}.
*/
function nonces(address owner) public view virtual override returns (uint256) {
return _nonces[owner].current();
}
/**
* @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
function PERMIT_TYPEHASH() external view returns (bytes32) {
return _PERMIT_TYPEHASH;
}
/**
* @dev "Consume a nonce": return the current value and increment.
*
* _Available since v4.1._
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
Counters.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
}
// 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/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/Utils/ReentrancyGuard.sol
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
// File contracts/ERC20/__CROSSCHAIN/CrossChainCanonical.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ======================== CrossChainCanonical =======================
// ====================================================================
// Cross-chain / non mainnet canonical token contract.
// Can accept any number of old non-canonical tokens. These will be
// withdrawable by the owner so they can de-bridge it and get back mainnet 'real' tokens
// Does not include any spurious mainnet logic
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Reviewer(s) / Contributor(s)
// Jason Huan: https://github.com/jasonhuan
// Sam Kazemian: https://github.com/samkazemian
// Dennis: github.com/denett
contract CrossChainCanonical is ERC20Permit, Owned, ReentrancyGuard {
using SafeMath for uint256;
/* ========== STATE VARIABLES ========== */
// Core
address public timelock_address; // Governance timelock address
address public custodian_address;
// Misc
uint256 public mint_cap;
mapping(address => uint256[2]) public swap_fees;
mapping(address => bool) public fee_exempt_list;
// Acceptable old tokens
address[] public bridge_tokens_array;
mapping(address => bool) public bridge_tokens;
// The addresses in this array are able to mint tokens
address[] public minters_array;
mapping(address => bool) public minters; // Mapping is also used for faster verification
// Constants for various precisions
uint256 private constant PRICE_PRECISION = 1e6;
// Administrative booleans
bool public exchangesPaused; // Pause old token exchanges in case of an emergency
mapping(address => bool) public canSwap;
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
_;
}
modifier onlyByOwnGovCust() {
require(msg.sender == timelock_address || msg.sender == owner || msg.sender == custodian_address, "Not owner, tlck, or custd");
_;
}
modifier onlyMinters() {
require(minters[msg.sender], "Not a minter");
_;
}
modifier onlyMintersOwnGov() {
require(_isMinterOwnGov(msg.sender), "Not minter, owner, or tlck");
_;
}
modifier validBridgeToken(address token_address) {
require(bridge_tokens[token_address], "Invalid old token");
_;
}
/* ========== CONSTRUCTOR ========== */
constructor (
string memory _name,
string memory _symbol,
address _creator_address,
uint256 _initial_mint_amt,
address _custodian_address,
address[] memory _bridge_tokens
) ERC20(_name, _symbol) ERC20Permit(_name) Owned(_creator_address) {
custodian_address = _custodian_address;
// Initialize the starting old tokens
for (uint256 i = 0; i < _bridge_tokens.length; i++){
// Mark as accepted
bridge_tokens[_bridge_tokens[i]] = true;
// Add to the array
bridge_tokens_array.push(_bridge_tokens[i]);
// Set a small swap fee initially of 0.04%
swap_fees[_bridge_tokens[i]] = [400, 400];
// Make sure swapping is on
canSwap[_bridge_tokens[i]] = true;
}
// Set the mint cap to the initial mint amount
mint_cap = _initial_mint_amt;
// Mint some canonical tokens to the creator
super._mint(_creator_address, _initial_mint_amt);
}
/* ========== VIEWS ========== */
// Helpful for UIs
function allBridgeTokens() external view returns (address[] memory) {
return bridge_tokens_array;
}
function _isMinterOwnGov(address the_address) internal view returns (bool) {
return (the_address == timelock_address || the_address == owner || minters[the_address]);
}
function _isFeeExempt(address the_address) internal view returns (bool) {
return (_isMinterOwnGov(the_address) || fee_exempt_list[the_address]);
}
/* ========== INTERNAL FUNCTIONS ========== */
// Enforce a minting cap
function _mint_capped(address account, uint256 amount) internal {
require(totalSupply() + amount <= mint_cap, "Mint cap");
super._mint(account, amount);
}
/* ========== PUBLIC FUNCTIONS ========== */
// Exchange old tokens for these canonical tokens
function exchangeOldForCanonical(address bridge_token_address, uint256 token_amount) external nonReentrant validBridgeToken(bridge_token_address) returns (uint256 canonical_tokens_out) {
require(!exchangesPaused && canSwap[bridge_token_address], "Exchanges paused");
// Pull in the old tokens
TransferHelper.safeTransferFrom(bridge_token_address, msg.sender, address(this), token_amount);
// Handle the fee, if applicable
canonical_tokens_out = token_amount;
if (!_isFeeExempt(msg.sender)) {
canonical_tokens_out -= ((canonical_tokens_out * swap_fees[bridge_token_address][0]) / PRICE_PRECISION);
}
// Mint canonical tokens and give it to the sender
_mint_capped(msg.sender, canonical_tokens_out);
}
// Exchange canonical tokens for old tokens
function exchangeCanonicalForOld(address bridge_token_address, uint256 token_amount) external nonReentrant validBridgeToken(bridge_token_address) returns (uint256 bridge_tokens_out) {
require(!exchangesPaused && canSwap[bridge_token_address], "Exchanges paused");
// Burn the canonical tokens
super._burn(msg.sender, token_amount);
// Handle the fee, if applicable
bridge_tokens_out = token_amount;
if (!_isFeeExempt(msg.sender)) {
bridge_tokens_out -= ((bridge_tokens_out * swap_fees[bridge_token_address][1]) / PRICE_PRECISION);
}
// Give old tokens to the sender
TransferHelper.safeTransfer(bridge_token_address, msg.sender, bridge_tokens_out);
}
/* ========== MINTERS OR GOVERNANCE FUNCTIONS ========== */
// Collect old tokens so you can de-bridge them back on mainnet
function withdrawBridgeTokens(address bridge_token_address, uint256 bridge_token_amount) external onlyMintersOwnGov validBridgeToken(bridge_token_address) {
TransferHelper.safeTransfer(bridge_token_address, msg.sender, bridge_token_amount);
}
/* ========== MINTERS ONLY ========== */
// This function is what other minters will call to mint new tokens
function minter_mint(address m_address, uint256 m_amount) external onlyMinters {
_mint_capped(m_address, m_amount);
emit TokenMinted(msg.sender, m_address, m_amount);
}
// This function is what other minters will call to burn tokens
function minter_burn(uint256 amount) external onlyMinters {
super._burn(msg.sender, amount);
emit TokenBurned(msg.sender, amount);
}
/* ========== RESTRICTED FUNCTIONS, BUT CUSTODIAN CAN CALL TOO ========== */
function toggleExchanges() external onlyByOwnGovCust {
exchangesPaused = !exchangesPaused;
}
/* ========== RESTRICTED FUNCTIONS ========== */
function addBridgeToken(address bridge_token_address) external onlyByOwnGov {
// Make sure the token is not already present
for (uint i = 0; i < bridge_tokens_array.length; i++){
if (bridge_tokens_array[i] == bridge_token_address){
revert("Token already present");
}
}
// Add the old token
bridge_tokens[bridge_token_address] = true;
bridge_tokens_array.push(bridge_token_address);
// Turn swapping on
canSwap[bridge_token_address] = true;
emit BridgeTokenAdded(bridge_token_address);
}
function toggleBridgeToken(address bridge_token_address) external onlyByOwnGov {
bridge_tokens[bridge_token_address] = !bridge_tokens[bridge_token_address];
// Toggle swapping
canSwap[bridge_token_address] = !canSwap[bridge_token_address];
emit BridgeTokenToggled(bridge_token_address, !bridge_tokens[bridge_token_address]);
}
// Adds a minter address
function addMinter(address minter_address) external 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) external 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);
}
function setMintCap(uint256 _mint_cap) external onlyByOwnGov {
mint_cap = _mint_cap;
emit MintCapSet(_mint_cap);
}
function setSwapFees(address bridge_token_address, uint256 _bridge_to_canonical, uint256 _canonical_to_old) external onlyByOwnGov {
swap_fees[bridge_token_address] = [_bridge_to_canonical, _canonical_to_old];
}
function toggleFeesForAddress(address the_address) external onlyByOwnGov {
fee_exempt_list[the_address] = !fee_exempt_list[the_address];
}
function setTimelock(address new_timelock) external onlyByOwnGov {
require(new_timelock != address(0), "Zero address detected");
timelock_address = new_timelock;
emit TimelockSet(new_timelock);
}
function setCustodian(address _custodian_address) external onlyByOwnGov {
require(_custodian_address != address(0), "Zero address detected");
custodian_address = _custodian_address;
emit CustodianSet(_custodian_address);
}
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
require(!bridge_tokens[tokenAddress], "Cannot withdraw bridge tokens");
require(tokenAddress != address(this), "Cannot withdraw these tokens");
TransferHelper.safeTransfer(address(tokenAddress), msg.sender, tokenAmount);
}
// // Generic proxy
// function execute(
// address _to,
// uint256 _value,
// bytes calldata _data
// ) external onlyByOwnGov returns (bool, bytes memory) {
// (bool success, bytes memory result) = _to.call{value:_value}(_data);
// return (success, result);
// }
/* ========== EVENTS ========== */
event TokenBurned(address indexed from, uint256 amount);
event TokenMinted(address indexed from, address indexed to, uint256 amount);
event BridgeTokenAdded(address indexed bridge_token_address);
event BridgeTokenToggled(address indexed bridge_token_address, bool state);
event CollateralRatioRefreshed(uint256 global_collateral_ratio);
event MinterAdded(address pool_address);
event MinterRemoved(address pool_address);
event MintCapSet(uint256 new_mint_cap);
event TimelockSet(address new_timelock);
event CustodianSet(address custodian_address);
}
// File contracts/ERC20/__CROSSCHAIN/CrossChainCanonicalFRAX.sol
contract CrossChainCanonicalFRAX is CrossChainCanonical {
constructor (
string memory _name,
string memory _symbol,
address _creator_address,
uint256 _initial_mint_amt,
address _custodian_address,
address[] memory _bridge_tokens
)
CrossChainCanonical(_name, _symbol, _creator_address, _initial_mint_amt, _custodian_address, _bridge_tokens)
{}
}
// File contracts/Math/Math.sol
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
// File contracts/ERC20/__CROSSCHAIN/IAnyswapV4ERC20.sol
interface IAnyswapV4ERC20 {
function DOMAIN_SEPARATOR() external view returns(bytes32);
function PERMIT_TYPEHASH() external view returns(bytes32);
function Swapin(bytes32 txhash, address account, uint256 amount) external returns(bool);
function Swapout(uint256 amount, address bindaddr) external returns(bool);
function TRANSFER_TYPEHASH() external view returns(bytes32);
function allowance(address, address) external view returns(uint256);
function applyMinter() external;
function applyVault() external;
function approve(address spender, uint256 value) external returns(bool);
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns(bool);
function balanceOf(address) external view returns(uint256);
function burn(address from, uint256 amount) external returns(bool);
function changeMPCOwner(address newVault) external returns(bool);
function changeVault(address newVault) external returns(bool);
function decimals() external view returns(uint8);
function delay() external view returns(uint256);
function delayDelay() external view returns(uint256);
function delayMinter() external view returns(uint256);
function delayVault() external view returns(uint256);
function deposit(uint256 amount, address to) external returns(uint256);
function deposit(uint256 amount) external returns(uint256);
function deposit() external returns(uint256);
function depositVault(uint256 amount, address to) external returns(uint256);
function depositWithPermit(address target, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s, address to) external returns(uint256);
function depositWithTransferPermit(address target, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s, address to) external returns(uint256);
function getAllMinters() external view returns(address[] memory);
function initVault(address _vault) external;
function isMinter(address) external view returns(bool);
function mint(address to, uint256 amount) external returns(bool);
function minters(uint256) external view returns(address);
function mpc() external view returns(address);
function name() external view returns(string memory);
function nonces(address) external view returns(uint256);
function owner() external view returns(address);
function pendingDelay() external view returns(uint256);
function pendingMinter() external view returns(address);
function pendingVault() external view returns(address);
function permit(address target, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function revokeMinter(address _auth) external;
function setMinter(address _auth) external;
function setVault(address _vault) external;
function setVaultOnly(bool enabled) external;
function symbol() external view returns(string memory);
function totalSupply() external view returns(uint256);
function transfer(address to, uint256 value) external returns(bool);
function transferAndCall(address to, uint256 value, bytes calldata data) external returns(bool);
function transferFrom(address from, address to, uint256 value) external returns(bool);
function transferWithPermit(address target, address to, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external returns(bool);
function underlying() external view returns(address);
function vault() external view returns(address);
function withdraw(uint256 amount, address to) external returns(uint256);
function withdraw(uint256 amount) external returns(uint256);
function withdraw() external returns(uint256);
function withdrawVault(address from, uint256 amount, address to) external returns(uint256);
}
// 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/FXS/IFxs.sol
interface IFxs {
function DEFAULT_ADMIN_ROLE() external view returns(bytes32);
function FRAXStablecoinAdd() external view returns(address);
function FXS_DAO_min() external view returns(uint256);
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 checkpoints(address, uint32) external view returns(uint32 fromBlock, uint96 votes);
function decimals() external view returns(uint8);
function decreaseAllowance(address spender, uint256 subtractedValue) external returns(bool);
function genesis_supply() external view returns(uint256);
function getCurrentVotes(address account) external view returns(uint96);
function getPriorVotes(address account, uint256 blockNumber) external view returns(uint96);
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 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 mint(address to, uint256 amount) external;
function name() external view returns(string memory);
function numCheckpoints(address) external view returns(uint32);
function oracle_address() external view returns(address);
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 renounceRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function setFRAXAddress(address frax_contract_address) external;
function setFXSMinDAO(uint256 min_FXS) external;
function setOracle(address new_oracle) external;
function setOwner(address _owner_address) external;
function setTimelock(address new_timelock) external;
function symbol() external view returns(string memory);
function timelock_address() external view returns(address);
function toggleVotes() external;
function totalSupply() external view returns(uint256);
function trackingVotes() external view returns(bool);
function transfer(address recipient, uint256 amount) external returns(bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns(bool);
}
// 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 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/Frax/Pools/FraxPoolV3.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ============================ FraxPoolV3 ============================
// ====================================================================
// Allows multiple stablecoins (fixed amount at initialization) as collateral
// LUSD, sUSD, USDP, Wrapped UST, and FEI initially
// For this pool, the goal is to accept crypto-backed / overcollateralized stablecoins to limit
// government / regulatory risk (e.g. USDC blacklisting until holders KYC)
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Reviewer(s) / Contributor(s)
// Jason Huan: https://github.com/jasonhuan
// Sam Kazemian: https://github.com/samkazemian
// Dennis: github.com/denett
// Hameed
contract FraxPoolV3 is Owned {
using SafeMath for uint256;
// SafeMath automatically included in Solidity >= 8.0.0
/* ========== STATE VARIABLES ========== */
// Core
address public timelock_address;
address public custodian_address; // Custodian is an EOA (or msig) with pausing privileges only, in case of an emergency
IFrax private FRAX = IFrax(0x853d955aCEf822Db058eb8505911ED77F175b99e);
IFxs private FXS = IFxs(0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0);
mapping(address => bool) public amo_minter_addresses; // minter address -> is it enabled
AggregatorV3Interface public priceFeedFRAXUSD = AggregatorV3Interface(0xB9E1E3A9feFf48998E45Fa90847ed4D467E8BcfD);
AggregatorV3Interface public priceFeedFXSUSD = AggregatorV3Interface(0x6Ebc52C8C1089be9eB3945C4350B68B8E4C2233f);
uint256 private chainlink_frax_usd_decimals;
uint256 private chainlink_fxs_usd_decimals;
// Collateral
address[] public collateral_addresses;
string[] public collateral_symbols;
uint256[] public missing_decimals; // Number of decimals needed to get to E18. collateral index -> missing_decimals
uint256[] public pool_ceilings; // Total across all collaterals. Accounts for missing_decimals
uint256[] public collateral_prices; // Stores price of the collateral, if price is paused
mapping(address => uint256) public collateralAddrToIdx; // collateral addr -> collateral index
mapping(address => bool) public enabled_collaterals; // collateral address -> is it enabled
// Redeem related
mapping (address => uint256) public redeemFXSBalances;
mapping (address => mapping(uint256 => uint256)) public redeemCollateralBalances; // Address -> collateral index -> balance
uint256[] public unclaimedPoolCollateral; // collateral index -> balance
uint256 public unclaimedPoolFXS;
mapping (address => uint256) public lastRedeemed; // Collateral independent
uint256 public redemption_delay = 2; // Number of blocks to wait before being able to collectRedemption()
uint256 public redeem_price_threshold = 990000; // $0.99
uint256 public mint_price_threshold = 1010000; // $1.01
// Buyback related
mapping(uint256 => uint256) public bbkHourlyCum; // Epoch hour -> Collat out in that hour (E18)
uint256 public bbkMaxColE18OutPerHour = 1000e18;
// Recollat related
mapping(uint256 => uint256) public rctHourlyCum; // Epoch hour -> FXS out in that hour
uint256 public rctMaxFxsOutPerHour = 1000e18;
// Fees and rates
// getters are in collateral_information()
uint256[] private minting_fee;
uint256[] private redemption_fee;
uint256[] private buyback_fee;
uint256[] private recollat_fee;
uint256 public bonus_rate; // Bonus rate on FXS minted during recollateralize(); 6 decimals of precision, set to 0.75% on genesis
// Constants for various precisions
uint256 private constant PRICE_PRECISION = 1e6;
// Pause variables
// getters are in collateral_information()
bool[] private mintPaused; // Collateral-specific
bool[] private redeemPaused; // Collateral-specific
bool[] private recollateralizePaused; // Collateral-specific
bool[] private buyBackPaused; // Collateral-specific
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
_;
}
modifier onlyByOwnGovCust() {
require(msg.sender == timelock_address || msg.sender == owner || msg.sender == custodian_address, "Not owner, tlck, or custd");
_;
}
modifier onlyAMOMinters() {
require(amo_minter_addresses[msg.sender], "Not an AMO Minter");
_;
}
modifier collateralEnabled(uint256 col_idx) {
require(enabled_collaterals[collateral_addresses[col_idx]], "Collateral disabled");
_;
}
/* ========== CONSTRUCTOR ========== */
constructor (
address _pool_manager_address,
address _custodian_address,
address _timelock_address,
address[] memory _collateral_addresses,
uint256[] memory _pool_ceilings,
uint256[] memory _initial_fees
) Owned(_pool_manager_address){
// Core
timelock_address = _timelock_address;
custodian_address = _custodian_address;
// Fill collateral info
collateral_addresses = _collateral_addresses;
for (uint256 i = 0; i < _collateral_addresses.length; i++){
// For fast collateral address -> collateral idx lookups later
collateralAddrToIdx[_collateral_addresses[i]] = i;
// Set all of the collaterals initially to disabled
enabled_collaterals[_collateral_addresses[i]] = false;
// Add in the missing decimals
missing_decimals.push(uint256(18).sub(ERC20(_collateral_addresses[i]).decimals()));
// Add in the collateral symbols
collateral_symbols.push(ERC20(_collateral_addresses[i]).symbol());
// Initialize unclaimed pool collateral
unclaimedPoolCollateral.push(0);
// Initialize paused prices to $1 as a backup
collateral_prices.push(PRICE_PRECISION);
// Handle the fees
minting_fee.push(_initial_fees[0]);
redemption_fee.push(_initial_fees[1]);
buyback_fee.push(_initial_fees[2]);
recollat_fee.push(_initial_fees[3]);
// Handle the pauses
mintPaused.push(false);
redeemPaused.push(false);
recollateralizePaused.push(false);
buyBackPaused.push(false);
}
// Pool ceiling
pool_ceilings = _pool_ceilings;
// Set the decimals
chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
chainlink_fxs_usd_decimals = priceFeedFXSUSD.decimals();
}
/* ========== STRUCTS ========== */
struct CollateralInformation {
uint256 index;
string symbol;
address col_addr;
bool is_enabled;
uint256 missing_decs;
uint256 price;
uint256 pool_ceiling;
bool mint_paused;
bool redeem_paused;
bool recollat_paused;
bool buyback_paused;
uint256 minting_fee;
uint256 redemption_fee;
uint256 buyback_fee;
uint256 recollat_fee;
}
/* ========== VIEWS ========== */
// Helpful for UIs
function collateral_information(address collat_address) external view returns (CollateralInformation memory return_data){
require(enabled_collaterals[collat_address], "Invalid collateral");
// Get the index
uint256 idx = collateralAddrToIdx[collat_address];
return_data = CollateralInformation(
idx, // [0]
collateral_symbols[idx], // [1]
collat_address, // [2]
enabled_collaterals[collat_address], // [3]
missing_decimals[idx], // [4]
collateral_prices[idx], // [5]
pool_ceilings[idx], // [6]
mintPaused[idx], // [7]
redeemPaused[idx], // [8]
recollateralizePaused[idx], // [9]
buyBackPaused[idx], // [10]
minting_fee[idx], // [11]
redemption_fee[idx], // [12]
buyback_fee[idx], // [13]
recollat_fee[idx] // [14]
);
}
function allCollaterals() external view returns (address[] memory) {
return collateral_addresses;
}
function getFRAXPrice() public view returns (uint256) {
( , int price, , , ) = priceFeedFRAXUSD.latestRoundData();
return uint256(price).mul(PRICE_PRECISION).div(10 ** chainlink_frax_usd_decimals);
}
function getFXSPrice() public view returns (uint256) {
( , int price, , , ) = priceFeedFXSUSD.latestRoundData();
return uint256(price).mul(PRICE_PRECISION).div(10 ** chainlink_fxs_usd_decimals);
}
// Returns the FRAX value in collateral tokens
function getFRAXInCollateral(uint256 col_idx, uint256 frax_amount) public view returns (uint256) {
return frax_amount.mul(PRICE_PRECISION).div(10 ** missing_decimals[col_idx]).div(collateral_prices[col_idx]);
}
// Used by some functions.
function freeCollatBalance(uint256 col_idx) public view returns (uint256) {
return ERC20(collateral_addresses[col_idx]).balanceOf(address(this)).sub(unclaimedPoolCollateral[col_idx]);
}
// Returns dollar value of collateral held in this Frax pool, in E18
function collatDollarBalance() external view returns (uint256 balance_tally) {
balance_tally = 0;
// Test 1
for (uint256 i = 0; i < collateral_addresses.length; i++){
balance_tally += freeCollatBalance(i).mul(10 ** missing_decimals[i]).mul(collateral_prices[i]).div(PRICE_PRECISION);
}
}
function comboCalcBbkRct(uint256 cur, uint256 max, uint256 theo) internal pure returns (uint256) {
if (cur >= max) {
// If the hourly limit has already been reached, return 0;
return 0;
}
else {
// Get the available amount
uint256 available = max.sub(cur);
if (theo >= available) {
// If the the theoretical is more than the available, return the available
return available;
}
else {
// Otherwise, return the theoretical amount
return theo;
}
}
}
// Returns the value of excess collateral (in E18) held globally, compared to what is needed to maintain the global collateral ratio
// Also has throttling to avoid dumps during large price movements
function buybackAvailableCollat() public view returns (uint256) {
uint256 total_supply = FRAX.totalSupply();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
uint256 global_collat_value = FRAX.globalCollateralValue();
if (global_collateral_ratio > PRICE_PRECISION) global_collateral_ratio = PRICE_PRECISION; // Handles an overcollateralized contract with CR > 1
uint256 required_collat_dollar_value_d18 = (total_supply.mul(global_collateral_ratio)).div(PRICE_PRECISION); // Calculates collateral needed to back each 1 FRAX with $1 of collateral at current collat ratio
if (global_collat_value > required_collat_dollar_value_d18) {
// Get the theoretical buyback amount
uint256 theoretical_bbk_amt = global_collat_value.sub(required_collat_dollar_value_d18);
// See how much has collateral has been issued this hour
uint256 current_hr_bbk = bbkHourlyCum[curEpochHr()];
// Account for the throttling
return comboCalcBbkRct(current_hr_bbk, bbkMaxColE18OutPerHour, theoretical_bbk_amt);
}
else return 0;
}
// Returns the missing amount of collateral (in E18) needed to maintain the collateral ratio
function recollatTheoColAvailableE18() public view returns (uint256) {
uint256 frax_total_supply = FRAX.totalSupply();
uint256 effective_collateral_ratio = FRAX.globalCollateralValue().mul(PRICE_PRECISION).div(frax_total_supply); // Returns it in 1e6
uint256 desired_collat_e24 = (FRAX.global_collateral_ratio()).mul(frax_total_supply);
uint256 effective_collat_e24 = effective_collateral_ratio.mul(frax_total_supply);
// Return 0 if already overcollateralized
// Otherwise, return the deficiency
if (effective_collat_e24 >= desired_collat_e24) return 0;
else {
return (desired_collat_e24.sub(effective_collat_e24)).div(PRICE_PRECISION);
}
}
// Returns the value of FXS available to be used for recollats
// Also has throttling to avoid dumps during large price movements
function recollatAvailableFxs() public view returns (uint256) {
uint256 fxs_price = getFXSPrice();
// Get the amount of collateral theoretically available
uint256 recollat_theo_available_e18 = recollatTheoColAvailableE18();
// Get the amount of FXS theoretically outputtable
uint256 fxs_theo_out = recollat_theo_available_e18.mul(PRICE_PRECISION).div(fxs_price);
// See how much FXS has been issued this hour
uint256 current_hr_rct = rctHourlyCum[curEpochHr()];
// Account for the throttling
return comboCalcBbkRct(current_hr_rct, rctMaxFxsOutPerHour, fxs_theo_out);
}
// Returns the current epoch hour
function curEpochHr() public view returns (uint256) {
return (block.timestamp / 3600); // Truncation desired
}
/* ========== PUBLIC FUNCTIONS ========== */
function mintFrax(
uint256 col_idx,
uint256 frax_amt,
uint256 frax_out_min,
bool one_to_one_override
) external collateralEnabled(col_idx) returns (
uint256 total_frax_mint,
uint256 collat_needed,
uint256 fxs_needed
) {
require(mintPaused[col_idx] == false, "Minting is paused");
// Prevent unneccessary mints
require(getFRAXPrice() >= mint_price_threshold, "Frax price too low");
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
if (one_to_one_override || global_collateral_ratio >= PRICE_PRECISION) {
// 1-to-1, overcollateralized, or user selects override
collat_needed = getFRAXInCollateral(col_idx, frax_amt);
fxs_needed = 0;
} else if (global_collateral_ratio == 0) {
// Algorithmic
collat_needed = 0;
fxs_needed = frax_amt.mul(PRICE_PRECISION).div(getFXSPrice());
} else {
// Fractional
uint256 frax_for_collat = frax_amt.mul(global_collateral_ratio).div(PRICE_PRECISION);
uint256 frax_for_fxs = frax_amt.sub(frax_for_collat);
collat_needed = getFRAXInCollateral(col_idx, frax_for_collat);
fxs_needed = frax_for_fxs.mul(PRICE_PRECISION).div(getFXSPrice());
}
// Subtract the minting fee
total_frax_mint = (frax_amt.mul(PRICE_PRECISION.sub(minting_fee[col_idx]))).div(PRICE_PRECISION);
// Checks
require((frax_out_min <= total_frax_mint), "FRAX slippage");
require(freeCollatBalance(col_idx).add(collat_needed) <= pool_ceilings[col_idx], "Pool ceiling");
// Take the FXS and collateral first
FXS.pool_burn_from(msg.sender, fxs_needed);
TransferHelper.safeTransferFrom(collateral_addresses[col_idx], msg.sender, address(this), collat_needed);
// Mint the FRAX
FRAX.pool_mint(msg.sender, total_frax_mint);
}
function redeemFrax(
uint256 col_idx,
uint256 frax_amount,
uint256 fxs_out_min,
uint256 col_out_min
) external collateralEnabled(col_idx) returns (
uint256 collat_out,
uint256 fxs_out
) {
require(redeemPaused[col_idx] == false, "Redeeming is paused");
// Prevent unneccessary redemptions that could adversely affect the FXS price
require(getFRAXPrice() <= redeem_price_threshold, "Frax price too high");
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
uint256 frax_after_fee = (frax_amount.mul(PRICE_PRECISION.sub(redemption_fee[col_idx]))).div(PRICE_PRECISION);
// Assumes $1 FRAX in all cases
if(global_collateral_ratio >= PRICE_PRECISION) {
// 1-to-1 or overcollateralized
collat_out = frax_after_fee
.mul(collateral_prices[col_idx])
.div(10 ** (6 + missing_decimals[col_idx])); // PRICE_PRECISION + missing decimals
fxs_out = 0;
} else if (global_collateral_ratio == 0) {
// Algorithmic
fxs_out = frax_after_fee
.mul(PRICE_PRECISION)
.div(getFXSPrice());
collat_out = 0;
} else {
// Fractional
collat_out = frax_after_fee
.mul(global_collateral_ratio)
.mul(collateral_prices[col_idx])
.div(10 ** (12 + missing_decimals[col_idx])); // PRICE_PRECISION ^2 + missing decimals
fxs_out = frax_after_fee
.mul(PRICE_PRECISION.sub(global_collateral_ratio))
.div(getFXSPrice()); // PRICE_PRECISIONS CANCEL OUT
}
// Checks
require(collat_out <= (ERC20(collateral_addresses[col_idx])).balanceOf(address(this)).sub(unclaimedPoolCollateral[col_idx]), "Insufficient pool collateral");
require(collat_out >= col_out_min, "Collateral slippage");
require(fxs_out >= fxs_out_min, "FXS slippage");
// Account for the redeem delay
redeemCollateralBalances[msg.sender][col_idx] = redeemCollateralBalances[msg.sender][col_idx].add(collat_out);
unclaimedPoolCollateral[col_idx] = unclaimedPoolCollateral[col_idx].add(collat_out);
redeemFXSBalances[msg.sender] = redeemFXSBalances[msg.sender].add(fxs_out);
unclaimedPoolFXS = unclaimedPoolFXS.add(fxs_out);
lastRedeemed[msg.sender] = block.number;
FRAX.pool_burn_from(msg.sender, frax_amount);
FXS.pool_mint(address(this), fxs_out);
}
// After a redemption happens, transfer the newly minted FXS and owed collateral from this pool
// contract to the user. Redemption is split into two functions to prevent flash loans from being able
// to take out FRAX/collateral from the system, use an AMM to trade the new price, and then mint back into the system.
function collectRedemption(uint256 col_idx) external returns (uint256 fxs_amount, uint256 collateral_amount) {
require(redeemPaused[col_idx] == false, "Redeeming is paused");
require((lastRedeemed[msg.sender].add(redemption_delay)) <= block.number, "Too soon");
bool sendFXS = false;
bool sendCollateral = false;
// Use Checks-Effects-Interactions pattern
if(redeemFXSBalances[msg.sender] > 0){
fxs_amount = redeemFXSBalances[msg.sender];
redeemFXSBalances[msg.sender] = 0;
unclaimedPoolFXS = unclaimedPoolFXS.sub(fxs_amount);
sendFXS = true;
}
if(redeemCollateralBalances[msg.sender][col_idx] > 0){
collateral_amount = redeemCollateralBalances[msg.sender][col_idx];
redeemCollateralBalances[msg.sender][col_idx] = 0;
unclaimedPoolCollateral[col_idx] = unclaimedPoolCollateral[col_idx].sub(collateral_amount);
sendCollateral = true;
}
// Send out the tokens
if(sendFXS){
TransferHelper.safeTransfer(address(FXS), msg.sender, fxs_amount);
}
if(sendCollateral){
TransferHelper.safeTransfer(collateral_addresses[col_idx], msg.sender, collateral_amount);
}
}
// Function can be called by an FXS holder to have the protocol buy back FXS with excess collateral value from a desired collateral pool
// This can also happen if the collateral ratio > 1
function buyBackFxs(uint256 col_idx, uint256 fxs_amount, uint256 col_out_min) external collateralEnabled(col_idx) returns (uint256 col_out) {
require(buyBackPaused[col_idx] == false, "Buyback is paused");
uint256 fxs_price = getFXSPrice();
uint256 available_excess_collat_dv = buybackAvailableCollat();
// If the total collateral value is higher than the amount required at the current collateral ratio then buy back up to the possible FXS with the desired collateral
require(available_excess_collat_dv > 0, "Insuf Collat Avail For BBK");
// Make sure not to take more than is available
uint256 fxs_dollar_value_d18 = fxs_amount.mul(fxs_price).div(PRICE_PRECISION);
require(fxs_dollar_value_d18 <= available_excess_collat_dv, "Insuf Collat Avail For BBK");
// Get the equivalent amount of collateral based on the market value of FXS provided
uint256 collateral_equivalent_d18 = fxs_dollar_value_d18.mul(PRICE_PRECISION).div(collateral_prices[col_idx]);
col_out = collateral_equivalent_d18.div(10 ** missing_decimals[col_idx]); // In its natural decimals()
// Subtract the buyback fee
col_out = (col_out.mul(PRICE_PRECISION.sub(buyback_fee[col_idx]))).div(PRICE_PRECISION);
// Check for slippage
require(col_out >= col_out_min, "Collateral slippage");
// Take in and burn the FXS, then send out the collateral
FXS.pool_burn_from(msg.sender, fxs_amount);
TransferHelper.safeTransfer(collateral_addresses[col_idx], msg.sender, col_out);
// Increment the outbound collateral, in E18, for that hour
// Used for buyback throttling
bbkHourlyCum[curEpochHr()] += collateral_equivalent_d18;
}
// When the protocol is recollateralizing, we need to give a discount of FXS to hit the new CR target
// Thus, if the target collateral ratio is higher than the actual value of collateral, minters get FXS for adding collateral
// This function simply rewards anyone that sends collateral to a pool with the same amount of FXS + the bonus rate
// Anyone can call this function to recollateralize the protocol and take the extra FXS value from the bonus rate as an arb opportunity
function recollateralize(uint256 col_idx, uint256 collateral_amount, uint256 fxs_out_min) external collateralEnabled(col_idx) returns (uint256 fxs_out) {
require(recollateralizePaused[col_idx] == false, "Recollat is paused");
uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals[col_idx]);
uint256 fxs_price = getFXSPrice();
// Get the amount of FXS actually available (accounts for throttling)
uint256 fxs_actually_available = recollatAvailableFxs();
// Calculated the attempted amount of FXS
fxs_out = collateral_amount_d18.mul(PRICE_PRECISION.add(bonus_rate).sub(recollat_fee[col_idx])).div(fxs_price);
// Make sure there is FXS available
require(fxs_out <= fxs_actually_available, "Insuf FXS Avail For RCT");
// Check slippage
require(fxs_out >= fxs_out_min, "FXS slippage");
// Don't take in more collateral than the pool ceiling for this token allows
require(freeCollatBalance(col_idx).add(collateral_amount) <= pool_ceilings[col_idx], "Pool ceiling");
// Take in the collateral and pay out the FXS
TransferHelper.safeTransferFrom(collateral_addresses[col_idx], msg.sender, address(this), collateral_amount);
FXS.pool_mint(msg.sender, fxs_out);
// Increment the outbound FXS, in E18
// Used for recollat throttling
rctHourlyCum[curEpochHr()] += fxs_out;
}
// Bypasses the gassy mint->redeem cycle for AMOs to borrow collateral
function amoMinterBorrow(uint256 collateral_amount) external onlyAMOMinters {
// Checks the col_idx of the minter as an additional safety check
uint256 minter_col_idx = IFraxAMOMinter(msg.sender).col_idx();
// Transfer
TransferHelper.safeTransfer(collateral_addresses[minter_col_idx], msg.sender, collateral_amount);
}
/* ========== RESTRICTED FUNCTIONS, CUSTODIAN CAN CALL TOO ========== */
function toggleMRBR(uint256 col_idx, uint8 tog_idx) external onlyByOwnGovCust {
if (tog_idx == 0) mintPaused[col_idx] = !mintPaused[col_idx];
else if (tog_idx == 1) redeemPaused[col_idx] = !redeemPaused[col_idx];
else if (tog_idx == 2) buyBackPaused[col_idx] = !buyBackPaused[col_idx];
else if (tog_idx == 3) recollateralizePaused[col_idx] = !recollateralizePaused[col_idx];
emit MRBRToggled(col_idx, tog_idx);
}
/* ========== RESTRICTED FUNCTIONS, GOVERNANCE ONLY ========== */
// Add an AMO Minter
function addAMOMinter(address amo_minter_addr) external onlyByOwnGov {
require(amo_minter_addr != address(0), "Zero address detected");
// Make sure the AMO Minter has collatDollarBalance()
uint256 collat_val_e18 = IFraxAMOMinter(amo_minter_addr).collatDollarBalance();
require(collat_val_e18 >= 0, "Invalid AMO");
amo_minter_addresses[amo_minter_addr] = true;
emit AMOMinterAdded(amo_minter_addr);
}
// Remove an AMO Minter
function removeAMOMinter(address amo_minter_addr) external onlyByOwnGov {
amo_minter_addresses[amo_minter_addr] = false;
emit AMOMinterRemoved(amo_minter_addr);
}
function setCollateralPrice(uint256 col_idx, uint256 _new_price) external onlyByOwnGov {
collateral_prices[col_idx] = _new_price;
emit CollateralPriceSet(col_idx, _new_price);
}
// Could also be called toggleCollateral
function toggleCollateral(uint256 col_idx) external onlyByOwnGov {
address col_address = collateral_addresses[col_idx];
enabled_collaterals[col_address] = !enabled_collaterals[col_address];
emit CollateralToggled(col_idx, enabled_collaterals[col_address]);
}
function setPoolCeiling(uint256 col_idx, uint256 new_ceiling) external onlyByOwnGov {
pool_ceilings[col_idx] = new_ceiling;
emit PoolCeilingSet(col_idx, new_ceiling);
}
function setFees(uint256 col_idx, uint256 new_mint_fee, uint256 new_redeem_fee, uint256 new_buyback_fee, uint256 new_recollat_fee) external onlyByOwnGov {
minting_fee[col_idx] = new_mint_fee;
redemption_fee[col_idx] = new_redeem_fee;
buyback_fee[col_idx] = new_buyback_fee;
recollat_fee[col_idx] = new_recollat_fee;
emit FeesSet(col_idx, new_mint_fee, new_redeem_fee, new_buyback_fee, new_recollat_fee);
}
function setPoolParameters(uint256 new_bonus_rate, uint256 new_redemption_delay) external onlyByOwnGov {
bonus_rate = new_bonus_rate;
redemption_delay = new_redemption_delay;
emit PoolParametersSet(new_bonus_rate, new_redemption_delay);
}
function setPriceThresholds(uint256 new_mint_price_threshold, uint256 new_redeem_price_threshold) external onlyByOwnGov {
mint_price_threshold = new_mint_price_threshold;
redeem_price_threshold = new_redeem_price_threshold;
emit PriceThresholdsSet(new_mint_price_threshold, new_redeem_price_threshold);
}
function setBbkRctPerHour(uint256 _bbkMaxColE18OutPerHour, uint256 _rctMaxFxsOutPerHour) external onlyByOwnGov {
bbkMaxColE18OutPerHour = _bbkMaxColE18OutPerHour;
rctMaxFxsOutPerHour = _rctMaxFxsOutPerHour;
emit BbkRctPerHourSet(_bbkMaxColE18OutPerHour, _rctMaxFxsOutPerHour);
}
// Set the Chainlink oracles
function setOracles(address _frax_usd_chainlink_addr, address _fxs_usd_chainlink_addr) external onlyByOwnGov {
// Set the instances
priceFeedFRAXUSD = AggregatorV3Interface(_frax_usd_chainlink_addr);
priceFeedFXSUSD = AggregatorV3Interface(_fxs_usd_chainlink_addr);
// Set the decimals
chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
chainlink_fxs_usd_decimals = priceFeedFXSUSD.decimals();
emit OraclesSet(_frax_usd_chainlink_addr, _fxs_usd_chainlink_addr);
}
function setCustodian(address new_custodian) external onlyByOwnGov {
custodian_address = new_custodian;
emit CustodianSet(new_custodian);
}
function setTimelock(address new_timelock) external onlyByOwnGov {
timelock_address = new_timelock;
emit TimelockSet(new_timelock);
}
/* ========== EVENTS ========== */
event CollateralToggled(uint256 col_idx, bool new_state);
event PoolCeilingSet(uint256 col_idx, uint256 new_ceiling);
event FeesSet(uint256 col_idx, uint256 new_mint_fee, uint256 new_redeem_fee, uint256 new_buyback_fee, uint256 new_recollat_fee);
event PoolParametersSet(uint256 new_bonus_rate, uint256 new_redemption_delay);
event PriceThresholdsSet(uint256 new_bonus_rate, uint256 new_redemption_delay);
event BbkRctPerHourSet(uint256 bbkMaxColE18OutPerHour, uint256 rctMaxFxsOutPerHour);
event AMOMinterAdded(address amo_minter_addr);
event AMOMinterRemoved(address amo_minter_addr);
event OraclesSet(address frax_usd_chainlink_addr, address fxs_usd_chainlink_addr);
event CustodianSet(address new_custodian);
event TimelockSet(address new_timelock);
event MRBRToggled(uint256 col_idx, uint8 tog_idx);
event CollateralPriceSet(uint256 col_idx, uint256 new_price);
}
// File contracts/Frax/Pools/IFraxPool.sol
interface IFraxPool {
function minting_fee() external returns (uint256);
function redeemCollateralBalances(address addr) external returns (uint256);
function redemption_fee() external returns (uint256);
function buyback_fee() external returns (uint256);
function recollat_fee() external returns (uint256);
function collatDollarBalance() external returns (uint256);
function availableExcessCollatDV() external returns (uint256);
function getCollateralPrice() external returns (uint256);
function setCollatETHOracle(address _collateral_weth_oracle_address, address _weth_address) external;
function mint1t1FRAX(uint256 collateral_amount, uint256 FRAX_out_min) external;
function mintAlgorithmicFRAX(uint256 fxs_amount_d18, uint256 FRAX_out_min) external;
function mintFractionalFRAX(uint256 collateral_amount, uint256 fxs_amount, uint256 FRAX_out_min) external;
function redeem1t1FRAX(uint256 FRAX_amount, uint256 COLLATERAL_out_min) external;
function redeemFractionalFRAX(uint256 FRAX_amount, uint256 FXS_out_min, uint256 COLLATERAL_out_min) external;
function redeemAlgorithmicFRAX(uint256 FRAX_amount, uint256 FXS_out_min) external;
function collectRedemption() external;
function recollateralizeFRAX(uint256 collateral_amount, uint256 FXS_out_min) external;
function buyBackFXS(uint256 FXS_amount, uint256 COLLATERAL_out_min) external;
function toggleMinting() external;
function toggleRedeeming() external;
function toggleRecollateralize() external;
function toggleBuyBack() external;
function toggleCollateralPrice(uint256 _new_price) external;
function setPoolParameters(uint256 new_ceiling, uint256 new_bonus_rate, uint256 new_redemption_delay, uint256 new_mint_fee, uint256 new_redeem_fee, uint256 new_buyback_fee, uint256 new_recollat_fee) external;
function setTimelock(address new_timelock) external;
function setOwner(address _owner_address) external;
}
// File contracts/Misc_AMOs/IAMO.sol
pragma experimental ABIEncoderV2;
interface IAMO {
function dollarBalances() external view returns (uint256 frax_val_e18, uint256 collat_val_e18);
}
// File contracts/Frax/FraxAMOMinter.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// =========================== FraxAMOMinter ==========================
// ====================================================================
// globalCollateralValue() in Frax.sol is gassy because of the loop and all of the AMOs attached to it.
// This minter would be single mint point for all of the AMOs, and would track the collatDollarBalance with a
// state variable after any mint occurs, or manually with a sync() call
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Reviewer(s) / Contributor(s)
// Jason Huan: https://github.com/jasonhuan
// Sam Kazemian: https://github.com/samkazemian
// Dennis: github.com/denett
// Hameed
contract FraxAMOMinter is Owned {
// SafeMath automatically included in Solidity >= 8.0.0
/* ========== STATE VARIABLES ========== */
// Core
IFrax public FRAX = IFrax(0x853d955aCEf822Db058eb8505911ED77F175b99e);
IFxs public FXS = IFxs(0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0);
ERC20 public collateral_token;
FraxPoolV3 public pool = FraxPoolV3(0x2fE065e6FFEf9ac95ab39E5042744d695F560729);
IFraxPool public old_pool = IFraxPool(0x1864Ca3d47AaB98Ee78D11fc9DCC5E7bADdA1c0d);
address public timelock_address;
address public custodian_address;
// Collateral related
address public collateral_address;
uint256 public col_idx;
// AMO addresses
address[] public amos_array;
mapping(address => bool) public amos; // Mapping is also used for faster verification
// Price constants
uint256 private constant PRICE_PRECISION = 1e6;
// Max amount of collateral the contract can borrow from the FraxPool
int256 public collat_borrow_cap = int256(10000000e6);
// Max amount of FRAX and FXS this contract can mint
int256 public frax_mint_cap = int256(100000000e18);
int256 public fxs_mint_cap = int256(100000000e18);
// Minimum collateral ratio needed for new FRAX minting
uint256 public min_cr = 810000;
// Frax mint balances
mapping(address => int256) public frax_mint_balances; // Amount of FRAX the contract minted, by AMO
int256 public frax_mint_sum = 0; // Across all AMOs
// Fxs mint balances
mapping(address => int256) public fxs_mint_balances; // Amount of FXS the contract minted, by AMO
int256 public fxs_mint_sum = 0; // Across all AMOs
// Collateral borrowed balances
mapping(address => int256) public collat_borrowed_balances; // Amount of collateral the contract borrowed, by AMO
int256 public collat_borrowed_sum = 0; // Across all AMOs
// FRAX balance related
uint256 public fraxDollarBalanceStored = 0;
// Collateral balance related
uint256 public missing_decimals;
uint256 public collatDollarBalanceStored = 0;
// AMO balance corrections
mapping(address => int256[2]) public correction_offsets_amos;
// [amo_address][0] = AMO's frax_val_e18
// [amo_address][1] = AMO's collat_val_e18
/* ========== CONSTRUCTOR ========== */
constructor (
address _owner_address,
address _custodian_address,
address _timelock_address,
address _collateral_address,
address _pool_address
) Owned(_owner_address) {
custodian_address = _custodian_address;
timelock_address = _timelock_address;
// Pool related
pool = FraxPoolV3(_pool_address);
// Collateral related
collateral_address = _collateral_address;
col_idx = pool.collateralAddrToIdx(_collateral_address);
collateral_token = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
missing_decimals = uint(18) - collateral_token.decimals();
}
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
_;
}
modifier validAMO(address amo_address) {
require(amos[amo_address], "Invalid AMO");
_;
}
/* ========== VIEWS ========== */
function collatDollarBalance() external view returns (uint256) {
(, uint256 collat_val_e18) = dollarBalances();
return collat_val_e18;
}
function dollarBalances() public view returns (uint256 frax_val_e18, uint256 collat_val_e18) {
frax_val_e18 = fraxDollarBalanceStored;
collat_val_e18 = collatDollarBalanceStored;
}
function allAMOAddresses() external view returns (address[] memory) {
return amos_array;
}
function allAMOsLength() external view returns (uint256) {
return amos_array.length;
}
function fraxTrackedGlobal() external view returns (int256) {
return int256(fraxDollarBalanceStored) - frax_mint_sum - (collat_borrowed_sum * int256(10 ** missing_decimals));
}
function fraxTrackedAMO(address amo_address) external view returns (int256) {
(uint256 frax_val_e18, ) = IAMO(amo_address).dollarBalances();
int256 frax_val_e18_corrected = int256(frax_val_e18) + correction_offsets_amos[amo_address][0];
return frax_val_e18_corrected - frax_mint_balances[amo_address] - ((collat_borrowed_balances[amo_address]) * int256(10 ** missing_decimals));
}
/* ========== PUBLIC FUNCTIONS ========== */
// Callable by anyone willing to pay the gas
function syncDollarBalances() public {
uint256 total_frax_value_d18 = 0;
uint256 total_collateral_value_d18 = 0;
for (uint i = 0; i < amos_array.length; i++){
// Exclude null addresses
address amo_address = amos_array[i];
if (amo_address != address(0)){
(uint256 frax_val_e18, uint256 collat_val_e18) = IAMO(amo_address).dollarBalances();
total_frax_value_d18 += uint256(int256(frax_val_e18) + correction_offsets_amos[amo_address][0]);
total_collateral_value_d18 += uint256(int256(collat_val_e18) + correction_offsets_amos[amo_address][1]);
}
}
fraxDollarBalanceStored = total_frax_value_d18;
collatDollarBalanceStored = total_collateral_value_d18;
}
/* ========== OLD POOL / BACKWARDS COMPATIBILITY ========== */
function oldPoolRedeem(uint256 frax_amount) external onlyByOwnGov {
uint256 redemption_fee = old_pool.redemption_fee();
uint256 col_price_usd = old_pool.getCollateralPrice();
uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
uint256 redeem_amount_E6 = ((frax_amount * (uint256(1e6) - redemption_fee)) / 1e6) / (10 ** missing_decimals);
uint256 expected_collat_amount = (redeem_amount_E6 * global_collateral_ratio) / 1e6;
expected_collat_amount = (expected_collat_amount * 1e6) / col_price_usd;
require((collat_borrowed_sum + int256(expected_collat_amount)) <= collat_borrow_cap, "Borrow cap");
collat_borrowed_sum += int256(expected_collat_amount);
// Mint the frax
FRAX.pool_mint(address(this), frax_amount);
// Redeem the frax
FRAX.approve(address(old_pool), frax_amount);
old_pool.redeemFractionalFRAX(frax_amount, 0, 0);
}
function oldPoolCollectAndGive(address destination_amo) external onlyByOwnGov validAMO(destination_amo) {
// Get the amount to be collected
uint256 collat_amount = old_pool.redeemCollateralBalances(address(this));
// Collect the redemption
old_pool.collectRedemption();
// Mark the destination amo's borrowed amount
collat_borrowed_balances[destination_amo] += int256(collat_amount);
// Give the collateral to the AMO
TransferHelper.safeTransfer(collateral_address, destination_amo, collat_amount);
// Sync
syncDollarBalances();
}
/* ========== OWNER / GOVERNANCE FUNCTIONS ONLY ========== */
// Only owner or timelock can call, to limit risk
// ------------------------------------------------------------------
// ------------------------------ FRAX ------------------------------
// ------------------------------------------------------------------
// This contract is essentially marked as a 'pool' so it can call OnlyPools functions like pool_mint and pool_burn_from
// on the main FRAX contract
function mintFraxForAMO(address destination_amo, uint256 frax_amount) external onlyByOwnGov validAMO(destination_amo) {
int256 frax_amt_i256 = int256(frax_amount);
// Make sure you aren't minting more than the mint cap
require((frax_mint_sum + frax_amt_i256) <= frax_mint_cap, "Mint cap reached");
frax_mint_balances[destination_amo] += frax_amt_i256;
frax_mint_sum += frax_amt_i256;
// Make sure the FRAX minting wouldn't push the CR down too much
// This is also a sanity check for the int256 math
uint256 current_collateral_E18 = FRAX.globalCollateralValue();
uint256 cur_frax_supply = FRAX.totalSupply();
uint256 new_frax_supply = cur_frax_supply + frax_amount;
uint256 new_cr = (current_collateral_E18 * PRICE_PRECISION) / new_frax_supply;
require(new_cr >= min_cr, "CR would be too low");
// Mint the FRAX to the AMO
FRAX.pool_mint(destination_amo, frax_amount);
// Sync
syncDollarBalances();
}
function burnFraxFromAMO(uint256 frax_amount) external validAMO(msg.sender) {
int256 frax_amt_i256 = int256(frax_amount);
// Burn first
FRAX.pool_burn_from(msg.sender, frax_amount);
// Then update the balances
frax_mint_balances[msg.sender] -= frax_amt_i256;
frax_mint_sum -= frax_amt_i256;
// Sync
syncDollarBalances();
}
// ------------------------------------------------------------------
// ------------------------------- FXS ------------------------------
// ------------------------------------------------------------------
function mintFxsForAMO(address destination_amo, uint256 fxs_amount) external onlyByOwnGov validAMO(destination_amo) {
int256 fxs_amt_i256 = int256(fxs_amount);
// Make sure you aren't minting more than the mint cap
require((fxs_mint_sum + fxs_amt_i256) <= fxs_mint_cap, "Mint cap reached");
fxs_mint_balances[destination_amo] += fxs_amt_i256;
fxs_mint_sum += fxs_amt_i256;
// Mint the FXS to the AMO
FXS.pool_mint(destination_amo, fxs_amount);
// Sync
syncDollarBalances();
}
function burnFxsFromAMO(uint256 fxs_amount) external validAMO(msg.sender) {
int256 fxs_amt_i256 = int256(fxs_amount);
// Burn first
FXS.pool_burn_from(msg.sender, fxs_amount);
// Then update the balances
fxs_mint_balances[msg.sender] -= fxs_amt_i256;
fxs_mint_sum -= fxs_amt_i256;
// Sync
syncDollarBalances();
}
// ------------------------------------------------------------------
// --------------------------- Collateral ---------------------------
// ------------------------------------------------------------------
function giveCollatToAMO(
address destination_amo,
uint256 collat_amount
) external onlyByOwnGov validAMO(destination_amo) {
int256 collat_amount_i256 = int256(collat_amount);
require((collat_borrowed_sum + collat_amount_i256) <= collat_borrow_cap, "Borrow cap");
collat_borrowed_balances[destination_amo] += collat_amount_i256;
collat_borrowed_sum += collat_amount_i256;
// Borrow the collateral
pool.amoMinterBorrow(collat_amount);
// Give the collateral to the AMO
TransferHelper.safeTransfer(collateral_address, destination_amo, collat_amount);
// Sync
syncDollarBalances();
}
function receiveCollatFromAMO(uint256 usdc_amount) external validAMO(msg.sender) {
int256 collat_amt_i256 = int256(usdc_amount);
// Give back first
TransferHelper.safeTransferFrom(collateral_address, msg.sender, address(pool), usdc_amount);
// Then update the balances
collat_borrowed_balances[msg.sender] -= collat_amt_i256;
collat_borrowed_sum -= collat_amt_i256;
// Sync
syncDollarBalances();
}
/* ========== RESTRICTED GOVERNANCE FUNCTIONS ========== */
// Adds an AMO
function addAMO(address amo_address, bool sync_too) public onlyByOwnGov {
require(amo_address != address(0), "Zero address detected");
(uint256 frax_val_e18, uint256 collat_val_e18) = IAMO(amo_address).dollarBalances();
require(frax_val_e18 >= 0 && collat_val_e18 >= 0, "Invalid AMO");
require(amos[amo_address] == false, "Address already exists");
amos[amo_address] = true;
amos_array.push(amo_address);
// Mint balances
frax_mint_balances[amo_address] = 0;
fxs_mint_balances[amo_address] = 0;
collat_borrowed_balances[amo_address] = 0;
// Offsets
correction_offsets_amos[amo_address][0] = 0;
correction_offsets_amos[amo_address][1] = 0;
if (sync_too) syncDollarBalances();
emit AMOAdded(amo_address);
}
// Removes an AMO
function removeAMO(address amo_address, bool sync_too) 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;
}
}
if (sync_too) syncDollarBalances();
emit AMORemoved(amo_address);
}
function setTimelock(address new_timelock) external onlyByOwnGov {
require(new_timelock != address(0), "Timelock address cannot be 0");
timelock_address = new_timelock;
}
function setCustodian(address _custodian_address) external onlyByOwnGov {
require(_custodian_address != address(0), "Custodian address cannot be 0");
custodian_address = _custodian_address;
}
function setFraxMintCap(uint256 _frax_mint_cap) external onlyByOwnGov {
frax_mint_cap = int256(_frax_mint_cap);
}
function setFxsMintCap(uint256 _fxs_mint_cap) external onlyByOwnGov {
fxs_mint_cap = int256(_fxs_mint_cap);
}
function setCollatBorrowCap(uint256 _collat_borrow_cap) external onlyByOwnGov {
collat_borrow_cap = int256(_collat_borrow_cap);
}
function setMinimumCollateralRatio(uint256 _min_cr) external onlyByOwnGov {
min_cr = _min_cr;
}
function setAMOCorrectionOffsets(address amo_address, int256 frax_e18_correction, int256 collat_e18_correction) external onlyByOwnGov {
correction_offsets_amos[amo_address][0] = frax_e18_correction;
correction_offsets_amos[amo_address][1] = collat_e18_correction;
syncDollarBalances();
}
function setFraxPool(address _pool_address) external onlyByOwnGov {
pool = FraxPoolV3(_pool_address);
// Make sure the collaterals match, or balances could get corrupted
require(pool.collateralAddrToIdx(collateral_address) == col_idx, "col_idx mismatch");
}
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
// Can only be triggered by owner or governance
TransferHelper.safeTransfer(tokenAddress, owner, tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
// Generic proxy
function execute(
address _to,
uint256 _value,
bytes calldata _data
) external onlyByOwnGov returns (bool, bytes memory) {
(bool success, bytes memory result) = _to.call{value:_value}(_data);
return (success, result);
}
/* ========== EVENTS ========== */
event AMOAdded(address amo_address);
event AMORemoved(address amo_address);
event Recovered(address token, uint256 amount);
}
// File contracts/ERC20/SafeERC20.sol
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// File contracts/Oracle/ICrossChainOracle.sol
interface ICrossChainOracle {
// Returns in USD E6
function getPrice(address token_address) external view returns (uint256 token_price);
}
// File contracts/Misc_AMOs/ICrossChainAMO.sol
interface ICrossChainAMO {
function allDollarBalances() external view returns (uint256 frax_val_e18, uint256 fxs_val_e18, uint256 collat_val_e18, uint256 total_val_e18);
}
// File contracts/Bridges/CrossChainBridgeBacker.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ====================== CrossChainBridgeBacker ======================
// ====================================================================
// Takes FRAX, FXS, and collateral and bridges it back to the Ethereum Mainnet
// Allows withdrawals to designated AMOs
// Tokens will need to be bridged to the contract first
// Frax Finance: https://github.com/FraxFinance
// Primary Author(s)
// Travis Moore: https://github.com/FortisFortuna
// Reviewer(s) / Contributor(s)
// Jason Huan: https://github.com/jasonhuan
// Sam Kazemian: https://github.com/samkazemian
contract CrossChainBridgeBacker is Owned {
// SafeMath automatically included in Solidity >= 8.0.0
using SafeERC20 for ERC20;
/* ========== STATE VARIABLES ========== */
// Core
IAnyswapV4ERC20 public anyFRAX;
CrossChainCanonical public canFRAX;
IAnyswapV4ERC20 public anyFXS;
CrossChainCanonical public canFXS;
ERC20 public collateral_token;
ICrossChainOracle public cross_chain_oracle;
// Admin addresses
address public timelock_address;
// AMO addresses
address[] public amos_array;
mapping(address => bool) public eoa_amos; // These need to be tracked so allBalances() skips them
mapping(address => bool) public amos; // Mapping is also used for faster verification
// Informational
string public name;
// Price constants
uint256 private constant PRICE_PRECISION = 1e6;
// Bridge related
address[3] public bridge_addresses;
address public destination_address_override;
string public non_evm_destination_address;
// Frax lent balances
mapping(address => uint256) public frax_lent_balances; // Amount of FRAX the contract lent, by AMO
uint256 public frax_lent_sum = 0; // Across all AMOs
uint256 public frax_bridged_back_sum = 0; // Across all AMOs
// Fxs lent balances
mapping(address => uint256) public fxs_lent_balances; // Amount of FXS the contract lent, by AMO
uint256 public fxs_lent_sum = 0; // Across all AMOs
uint256 public fxs_bridged_back_sum = 0; // Across all AMOs
// Collateral lent balances
mapping(address => uint256) public collat_lent_balances; // Amount of collateral the contract lent, by AMO
uint256 public collat_lent_sum = 0; // Across all AMOs
uint256 public collat_bridged_back_sum = 0; // Across all AMOs
// Collateral balance related
uint256 public missing_decimals;
/* ========== 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");
_;
}
modifier validCanonicalToken(address token_address) {
require (
token_address == address(canFRAX) ||
token_address == address(canFXS) ||
token_address == address(collateral_token), "Invalid canonical token"
);
_;
}
/* ========== CONSTRUCTOR ========== */
constructor (
address _owner,
address _timelock_address,
address _cross_chain_oracle_address,
address[5] memory _token_addresses,
address[3] memory _bridge_addresses,
address _destination_address_override,
string memory _non_evm_destination_address,
string memory _name
) Owned(_owner) {
// Core
timelock_address = _timelock_address;
cross_chain_oracle = ICrossChainOracle(_cross_chain_oracle_address);
anyFRAX = IAnyswapV4ERC20(_token_addresses[0]);
canFRAX = CrossChainCanonical(_token_addresses[1]);
anyFXS = IAnyswapV4ERC20(_token_addresses[2]);
canFXS = CrossChainCanonical(_token_addresses[3]);
collateral_token = ERC20(_token_addresses[4]);
missing_decimals = uint(18) - collateral_token.decimals();
// Bridge related
bridge_addresses = _bridge_addresses;
destination_address_override = _destination_address_override;
non_evm_destination_address = _non_evm_destination_address;
// Informational
name = _name;
// Add this bridger as an AMO. Cannot used the addAMO function
amos[address(this)] = true;
amos_array.push(address(this));
frax_lent_balances[address(this)] = 0;
fxs_lent_balances[address(this)] = 0;
collat_lent_balances[address(this)] = 0;
}
/* ========== VIEWS ========== */
function allAMOAddresses() external view returns (address[] memory) {
return amos_array;
}
function allAMOsLength() external view returns (uint256) {
return amos_array.length;
}
function getTokenType(address token_address) public view returns (uint256) {
// 0 = FRAX, 1 = FXS, 2 = Collateral
if (token_address == address(anyFRAX) || token_address == address(canFRAX)) return 0;
else if (token_address == address(anyFXS) || token_address == address(canFXS)) return 1;
else if (token_address == address(collateral_token)) return 2;
// Revert on invalid tokens
revert("getTokenType: Invalid token");
}
function showTokenBalances() public view returns (uint256[5] memory tkn_bals) {
tkn_bals[0] = anyFRAX.balanceOf(address(this)); // anyFRAX
tkn_bals[1] = canFRAX.balanceOf(address(this)); // canFRAX
tkn_bals[2] = anyFXS.balanceOf(address(this)); // anyFXS
tkn_bals[3] = canFXS.balanceOf(address(this)); // canFXS
tkn_bals[4] = collateral_token.balanceOf(address(this)); // anyFRAX
}
function showAllocations() public view returns (uint256[12] memory allocations) {
// All numbers given are in FRAX unless otherwise stated
// Get some token balances
uint256[5] memory tkn_bals = showTokenBalances();
// FRAX
allocations[0] = tkn_bals[0] + tkn_bals[1]; // Free FRAX
allocations[1] = frax_lent_sum; // Lent FRAX
allocations[2] = allocations[0] + allocations[1]; // Total FRAX
// FXS
allocations[3] = tkn_bals[2] + tkn_bals[3]; // Free FXS
allocations[4] = fxs_lent_sum; // Lent FXS
allocations[5] = allocations[3] + allocations[4]; // Total FXS
allocations[6] = (allocations[5] * (cross_chain_oracle.getPrice(address(canFXS)))) / PRICE_PRECISION; // Total FXS value in USD
// Collateral
allocations[7] = tkn_bals[4]; // Free Collateral
allocations[8] = collat_lent_sum; // Lent Collateral
allocations[9] = allocations[7] + allocations[8]; // Total Collateral, in native decimals()
allocations[10] = allocations[9] * (10 ** missing_decimals); // Total Collateral, in E18
// Total USD value of everything, in E18
allocations[11] = allocations[2] + allocations[6] + allocations[10];
}
function allBalances() public view returns (
uint256 frax_ttl,
uint256 fxs_ttl,
uint256 col_ttl, // in native decimals()
uint256 ttl_val_usd_e18
) {
// Handle this contract first (amos_array[0])
uint256[12] memory allocations = showAllocations();
frax_ttl = allocations[2];
fxs_ttl = allocations[5];
col_ttl = allocations[9];
ttl_val_usd_e18 = allocations[11];
// [0] will always be this address, so skip it to avoid an infinite loop
for (uint i = 1; i < amos_array.length; i++){
// Exclude null addresses and EOAs
if (amos_array[i] != address(0) && !eoa_amos[amos_array[i]]){
(
uint256 frax_bal,
uint256 fxs_bal,
uint256 collat_bal,
uint256 total_val_e18
) = ICrossChainAMO(amos_array[i]).allDollarBalances();
frax_ttl += frax_bal;
fxs_ttl += fxs_bal;
col_ttl += collat_bal;
ttl_val_usd_e18 += total_val_e18;
}
}
}
/* ========== BRIDGING / AMO FUNCTIONS ========== */
// Used for crumbs and drop-ins sitting in this contract
// Can also manually bridge back anyFRAX
// If do_swap is true, it will swap out canTokens in this contract for anyTokens in the canToken contracts
function selfBridge(uint256 token_type, uint256 token_amount, bool do_swap) external onlyByOwnGov {
require(token_type == 0 || token_type == 1 || token_type == 2, 'Invalid token type');
_receiveBack(address(this), token_type, token_amount, true, do_swap);
}
// AMOs should only be giving back canonical tokens
function receiveBackViaAMO(address canonical_token_address, uint256 token_amount, bool do_bridging) external validCanonicalToken(canonical_token_address) validAMO(msg.sender) {
// Pull in the tokens from the AMO
TransferHelper.safeTransferFrom(canonical_token_address, msg.sender, address(this), token_amount);
// Get the token type
uint256 token_type = getTokenType(canonical_token_address);
_receiveBack(msg.sender, token_type, token_amount, do_bridging, true);
}
// Optionally bridge
function _receiveBack(address from_address, uint256 token_type, uint256 token_amount, bool do_bridging, bool do_swap) internal {
if (do_bridging) {
// Swap canTokens for bridgeable anyTokens, if necessary
if (token_type == 0) {
// FRAX
// Swap the canonical tokens out for bridgeable anyTokens
if (do_swap) _swapCanonicalForAny(0, token_amount);
}
else if (token_type == 1){
// FXS
// Swap the canonical tokens out for bridgeable anyTokens
if (do_swap) _swapCanonicalForAny(1, token_amount);
}
// Defaults to sending to this contract's address on the other side
address address_to_send_to = address(this);
// See if there is an overriden destination
if (destination_address_override != address(0)) address_to_send_to = destination_address_override;
// Can be overridden
_bridgingLogic(token_type, address_to_send_to, token_amount);
}
// Account for the lent balances
if (token_type == 0){
if (token_amount >= frax_lent_balances[from_address]) frax_lent_balances[from_address] = 0;
else frax_lent_balances[from_address] -= token_amount;
if (token_amount >= frax_lent_sum) frax_lent_sum = 0;
else frax_lent_sum -= token_amount;
if (do_bridging) frax_bridged_back_sum += token_amount;
}
else if (token_type == 1){
if (token_amount >= fxs_lent_balances[from_address]) fxs_lent_balances[from_address] = 0;
else fxs_lent_balances[from_address] -= token_amount;
if (token_amount >= fxs_lent_sum) fxs_lent_sum = 0;
else fxs_lent_sum -= token_amount;
if (do_bridging) fxs_bridged_back_sum += token_amount;
}
else {
if (token_amount >= collat_lent_balances[from_address]) collat_lent_balances[from_address] = 0;
else collat_lent_balances[from_address] -= token_amount;
if (token_amount >= collat_lent_sum) collat_lent_sum = 0;
else collat_lent_sum -= token_amount;
if (do_bridging) collat_bridged_back_sum += token_amount;
}
}
// Meant to be overriden
function _bridgingLogic(uint256 token_type, address address_to_send_to, uint256 token_amount) internal virtual {
revert("Need bridging logic");
}
/* ========== LENDING FUNCTIONS ========== */
// Lend out canonical FRAX
function lendFraxToAMO(address destination_amo, uint256 frax_amount) external onlyByOwnGov validAMO(destination_amo) {
// Track the balances
frax_lent_balances[destination_amo] += frax_amount;
frax_lent_sum += frax_amount;
// Transfer
TransferHelper.safeTransfer(address(canFRAX), destination_amo, frax_amount);
}
// Lend out canonical FXS
function lendFxsToAMO(address destination_amo, uint256 fxs_amount) external onlyByOwnGov validAMO(destination_amo) {
// Track the balances
fxs_lent_balances[destination_amo] += fxs_amount;
fxs_lent_sum += fxs_amount;
// Transfer
TransferHelper.safeTransfer(address(canFXS), destination_amo, fxs_amount);
}
// Lend out collateral
function lendCollatToAMO(address destination_amo, uint256 collat_amount) external onlyByOwnGov validAMO(destination_amo) {
// Track the balances
collat_lent_balances[destination_amo] += collat_amount;
collat_lent_sum += collat_amount;
// Transfer
TransferHelper.safeTransfer(address(collateral_token), destination_amo, collat_amount);
}
/* ========== SWAPPING, GIVING, MINTING, AND BURNING ========== */
// ----------------- SWAPPING -----------------
// Swap anyToken for canToken [GOVERNANCE CALLABLE]
function swapAnyForCanonical(uint256 token_type, uint256 token_amount) external onlyByOwnGov {
_swapAnyForCanonical(token_type, token_amount);
}
// Swap anyToken for canToken [INTERNAL]
function _swapAnyForCanonical(uint256 token_type, uint256 token_amount) internal {
if (token_type == 0) {
// FRAX
// Approve and swap
anyFRAX.approve(address(canFRAX), token_amount);
canFRAX.exchangeOldForCanonical(address(anyFRAX), token_amount);
}
else {
// FXS
// Approve and swap
anyFXS.approve(address(canFXS), token_amount);
canFXS.exchangeOldForCanonical(address(anyFXS), token_amount);
}
}
// Swap canToken for anyToken [GOVERNANCE CALLABLE]
function swapCanonicalForAny(uint256 token_type, uint256 token_amount) external onlyByOwnGov {
_swapCanonicalForAny(token_type, token_amount);
}
// Swap anyToken for canToken [INTERNAL]
function _swapCanonicalForAny(uint256 token_type, uint256 token_amount) internal {
if (token_type == 0) {
// FRAX
// Approve and swap
canFRAX.approve(address(canFRAX), token_amount);
canFRAX.exchangeCanonicalForOld(address(anyFRAX), token_amount);
}
else {
// FXS
// Approve and swap
canFXS.approve(address(canFXS), token_amount);
canFXS.exchangeCanonicalForOld(address(anyFXS), token_amount);
}
}
// ----------------- GIVING -----------------
// Give anyToken to the canToken contract
function giveAnyToCan(uint256 token_type, uint256 token_amount) external onlyByOwnGov {
if (token_type == 0) {
// FRAX
// Transfer
TransferHelper.safeTransfer(address(anyFRAX), address(canFRAX), token_amount);
}
else {
// FXS
// Transfer
TransferHelper.safeTransfer(address(anyFXS), address(canFXS), token_amount);
}
}
// ----------------- FRAX -----------------
function mintCanonicalFrax(uint256 frax_amount) external onlyByOwnGov {
canFRAX.minter_mint(address(this), frax_amount);
}
function burnCanonicalFrax(uint256 frax_amount) external onlyByOwnGov {
canFRAX.minter_burn(frax_amount);
}
// ----------------- FXS -----------------
function mintCanonicalFxs(uint256 fxs_amount) external onlyByOwnGov {
canFXS.minter_mint(address(this), fxs_amount);
}
function burnCanonicalFxs(uint256 fxs_amount) external onlyByOwnGov {
canFXS.minter_burn(fxs_amount);
}
/* ========== RESTRICTED FUNCTIONS - Owner or timelock only ========== */
function collectBridgeTokens(uint256 token_type, address bridge_token_address, uint256 token_amount) external onlyByOwnGov {
if (token_type == 0) {
canFRAX.withdrawBridgeTokens(bridge_token_address, token_amount);
}
else if (token_type == 1) {
canFXS.withdrawBridgeTokens(bridge_token_address, token_amount);
}
else {
revert("Invalid token_type");
}
}
// Adds an AMO
function addAMO(address amo_address, bool is_eoa) external onlyByOwnGov {
require(amo_address != address(0), "Zero address detected");
if (is_eoa) {
eoa_amos[amo_address] = true;
}
else {
(uint256 frax_val_e18, uint256 fxs_val_e18, uint256 collat_val_e18, uint256 total_val_e18) = ICrossChainAMO(amo_address).allDollarBalances();
require(frax_val_e18 >= 0 && fxs_val_e18 >= 0 && collat_val_e18 >= 0 && total_val_e18 >= 0, "Invalid AMO");
}
require(amos[amo_address] == false, "Address already exists");
amos[amo_address] = true;
amos_array.push(amo_address);
frax_lent_balances[amo_address] = 0;
fxs_lent_balances[amo_address] = 0;
collat_lent_balances[amo_address] = 0;
emit AMOAdded(amo_address);
}
// Removes an AMO
function removeAMO(address amo_address) external 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);
}
// 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);
}
function setOracleAddress(address _new_cc_oracle_address) external onlyByOwnGov {
cross_chain_oracle = ICrossChainOracle(_new_cc_oracle_address);
}
function setTimelock(address _new_timelock) external onlyByOwnGov {
timelock_address = _new_timelock;
}
function setBridgeInfo(
address _frax_bridge_address,
address _fxs_bridge_address,
address _collateral_bridge_address,
address _destination_address_override,
string memory _non_evm_destination_address
) external onlyByOwnGov {
// Make sure there are valid bridges
require(
_frax_bridge_address != address(0) &&
_fxs_bridge_address != address(0) &&
_collateral_bridge_address != address(0)
, "Invalid bridge address");
// Set bridge addresses
bridge_addresses = [_frax_bridge_address, _fxs_bridge_address, _collateral_bridge_address];
// Overridden cross-chain destination address
destination_address_override = _destination_address_override;
// Set bytes32 / non-EVM address on the other chain, if applicable
non_evm_destination_address = _non_evm_destination_address;
emit BridgeInfoChanged(_frax_bridge_address, _fxs_bridge_address, _collateral_bridge_address, _destination_address_override, _non_evm_destination_address);
}
// Generic proxy
function execute(
address _to,
uint256 _value,
bytes calldata _data
) external onlyByOwnGov returns (bool, bytes memory) {
(bool success, bytes memory result) = _to.call{value:_value}(_data);
return (success, result);
}
/* ========== EVENTS ========== */
event AMOAdded(address amo_address);
event AMORemoved(address amo_address);
event RecoveredERC20(address token, uint256 amount);
event BridgeInfoChanged(address frax_bridge_address, address fxs_bridge_address, address collateral_bridge_address, address destination_address_override, string non_evm_destination_address);
}
// File contracts/Bridges/Fantom/CrossChainBridgeBacker_FTM_AnySwap.sol
contract CrossChainBridgeBacker_FTM_AnySwap is CrossChainBridgeBacker {
constructor (
address _owner,
address _timelock_address,
address _cross_chain_oracle_address,
address[5] memory _token_addresses,
address[3] memory _bridge_addresses,
address _destination_address_override,
string memory _non_evm_destination_address,
string memory _name
)
CrossChainBridgeBacker(_owner, _timelock_address, _cross_chain_oracle_address, _token_addresses, _bridge_addresses, _destination_address_override, _non_evm_destination_address, _name)
{}
// Override with logic specific to this chain
function _bridgingLogic(uint256 token_type, address address_to_send_to, uint256 token_amount) internal override {
// [Fantom]
if (token_type == 0){
// anyFRAX -> L1 FRAX
// Swapout
// AnySwap Bridge
anyFRAX.Swapout(token_amount, address_to_send_to);
}
else if (token_type == 1) {
// anyFXS -> L1 FXS
// Swapout
// AnySwap Bridge
anyFXS.Swapout(token_amount, address_to_send_to);
}
else {
// anyUSDC -> L1 USDC
// Swapout
// AnySwap Bridge
IAnyswapV4ERC20(address(collateral_token)).Swapout(token_amount, address_to_send_to);
}
}
}
// File contracts/Misc_AMOs/curve/IFRAX2pool.sol
interface IFRAX2pool {
function initialize(string memory _name, string memory _symbol, address _coin, uint256 _rate_multiplier, uint256 _A, uint256 _fee) external;
function decimals() external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function admin_fee() external view returns (uint256);
function A() external view returns (uint256);
function A_precise() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit) external view returns (uint256);
function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount) external returns (uint256);
function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount, address _receiver) external returns (uint256);
function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns (uint256);
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy, address _receiver) external returns (uint256);
function exchange_underlying(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
function exchange_underlying(int128 i, int128 j, uint256 _dx, uint256 _min_dy, address _receiver) external returns (uint256);
function remove_liquidity(uint256 _burn_amount, uint256[2] memory _min_amounts) external returns (uint256[2] memory);
function remove_liquidity(uint256 _burn_amount, uint256[2] memory _min_amounts, address _receiver) external returns (uint256[2] memory);
function remove_liquidity_imbalance(uint256[2] memory _amounts, uint256 _max_burn_amount) external returns (uint256);
function remove_liquidity_imbalance(uint256[2] memory _amounts, uint256 _max_burn_amount, address _receiver) external returns (uint256);
function calc_withdraw_one_coin(uint256 _burn_amount, int128 i) external view returns (uint256);
function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 _min_received) external returns (uint256);
function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 _min_received, address _receiver) external returns (uint256);
function ramp_A(uint256 _future_A, uint256 _future_time) external;
function stop_ramp_A() external;
function admin_balances(uint256 i) external view returns (uint256);
function withdraw_admin_fees() external;
function coins(uint256 arg0) external view returns (address);
function balances(uint256 arg0) external view returns (uint256);
function fee() external view returns (uint256);
function initial_A() external view returns (uint256);
function future_A() external view returns (uint256);
function initial_A_time() external view returns (uint256);
function future_A_time() external view returns (uint256);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function balanceOf(address arg0) external view returns (uint256);
function allowance(address arg0, address arg1) external view returns (uint256);
function totalSupply() external view returns (uint256);
function rewards_receiver() external view returns (address);
}
// File contracts/Misc_AMOs/curve/I2poolGaugeDeposit.sol
interface I2poolGaugeDeposit {
function decimals() external view returns (uint256);
function reward_contract() external view returns (address);
function last_claim() external view returns (uint256);
function claimed_reward(address _addr, address _token) external view returns (uint256);
function claimable_reward(address _addr, address _token) external view returns (uint256);
function claimable_reward_write(address _addr, address _token) external returns (uint256);
function set_rewards_receiver(address _receiver) external;
function claim_rewards() external;
function claim_rewards(address _addr) external;
function claim_rewards(address _addr, address _receiver) external;
function deposit(uint256 _value) external;
function deposit(uint256 _value, address _addr) external;
function deposit(uint256 _value, address _addr, bool _claim_rewards) external;
function withdraw(uint256 _value) external;
function withdraw(uint256 _value, bool _claim_rewards) external;
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function increaseAllowance(address _spender, uint256 _added_value) external returns (bool);
function decreaseAllowance(address _spender, uint256 _subtracted_value) external returns (bool);
function set_rewards(address _reward_contract, bytes32 _claim_sig, address[8] memory _reward_tokens) external;
function commit_transfer_ownership(address addr) external;
function accept_transfer_ownership() external;
function lp_token() external view returns (address);
function balanceOf(address arg0) external view returns (uint256);
function totalSupply() external view returns (uint256);
function allowance(address arg0, address arg1) external view returns (uint256);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function reward_tokens(uint256 arg0) external view returns (address);
function reward_balances(address arg0) external view returns (uint256);
function rewards_receiver(address arg0) external view returns (address);
function claim_sig() external view returns (bytes memory);
function reward_integral(address arg0) external view returns (uint256);
function reward_integral_for(address arg0, address arg1) external view returns (uint256);
function admin() external view returns (address);
function future_admin() external view returns (address);
}
// File contracts/Misc_AMOs/curve/I2pool.sol
interface I2pool {
function decimals() external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function A() external view returns (uint256);
function A_precise() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit) external view returns (uint256);
function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount) external returns (uint256);
function get_dy(int128 i, int128 j, uint256 _dx) external view returns (uint256);
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts) external returns (uint256[2] memory);
function remove_liquidity_imbalance(uint256[2] memory _amounts, uint256 _max_burn_amount) external returns (uint256);
function calc_withdraw_one_coin(uint256 _token_amount, int128 i) external view returns (uint256);
function remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 _min_amount) external returns (uint256);
function ramp_A(uint256 _future_A, uint256 _future_time) external;
function stop_ramp_A() external;
function commit_new_fee(uint256 _new_fee, uint256 _new_admin_fee) external;
function apply_new_fee() external;
function revert_new_parameters() external;
function commit_transfer_ownership(address _owner) external;
function apply_transfer_ownership() external;
function revert_transfer_ownership() external;
function admin_balances(uint256 i) external view returns (uint256);
function withdraw_admin_fees() external;
function donate_admin_fees() external;
function kill_me() external;
function unkill_me() external;
function coins(uint256 arg0) external view returns (address);
function balances(uint256 arg0) external view returns (uint256);
function fee() external view returns (uint256);
function admin_fee() external view returns (uint256);
function owner() external view returns (address);
function initial_A() external view returns (uint256);
function future_A() external view returns (uint256);
function initial_A_time() external view returns (uint256);
function future_A_time() external view returns (uint256);
function admin_actions_deadline() external view returns (uint256);
function transfer_ownership_deadline() external view returns (uint256);
function future_fee() external view returns (uint256);
function future_admin_fee() external view returns (uint256);
function future_owner() external view returns (address);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function balanceOf(address arg0) external view returns (uint256);
function allowance(address arg0, address arg1) external view returns (uint256);
function totalSupply() external view returns (uint256);
}
// File contracts/Misc_AMOs/curve/IZapDepositor.sol
interface IZapDepositor {
function add_liquidity(address _pool, uint256[3] memory _deposit_amounts, uint256 _min_mint_amount) external returns (uint256);
function add_liquidity(address _pool, uint256[3] memory _deposit_amounts, uint256 _min_mint_amount, address _receiver) external returns (uint256);
function remove_liquidity(address _pool, uint256 _burn_amount, uint256[3] memory _min_amounts) external returns (uint256[3] memory);
function remove_liquidity(address _pool, uint256 _burn_amount, uint256[3] memory _min_amounts, address _receiver) external returns (uint256[3] memory);
function remove_liquidity_one_coin(address _pool, uint256 _burn_amount, int128 i, uint256 _min_amount) external returns (uint256);
function remove_liquidity_one_coin(address _pool, uint256 _burn_amount, int128 i, uint256 _min_amount, address _receiver) external returns (uint256);
function remove_liquidity_imbalance(address _pool, uint256[3] memory _amounts, uint256 _max_burn_amount) external returns (uint256);
function remove_liquidity_imbalance(address _pool, uint256[3] memory _amounts, uint256 _max_burn_amount, address _receiver) external returns (uint256);
function calc_withdraw_one_coin(address _pool, uint256 _token_amount, int128 i) external view returns (uint256);
function calc_token_amount(address _pool, uint256[3] memory _amounts, bool _is_deposit) external view returns (uint256);
}
// File contracts/Misc_AMOs/__CROSSCHAIN/Arbitrum/CurveAMO_ARBI.sol
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ========================== CurveAMO_ARBI ===========================
// ====================================================================
// Uses Arbitrum Curve https://arbitrum.curve.fi/factory/9
// 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
// Jason Huan: https://github.com/jasonhuan
// Dennis: github.com/denett
contract CurveAMO_ARBI is Owned {
/* ========== STATE VARIABLES ========== */
// Core
CrossChainCanonicalFRAX public canFRAX;
ERC20 public USDC; // USDC: 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8
ERC20 public USDT; // USDT: 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9
CrossChainBridgeBacker_FTM_AnySwap public cc_bridge_backer;
// Pools
IFRAX2pool public frax2pool; // 0xf07d553B195080F84F582e88ecdD54bAa122b279
I2poolGaugeDeposit public two_pool_gauge; // 0xbF7E49483881C76487b0989CD7d9A8239B20CA41
I2pool public two_pool; // 0x7f90122BF0700F9E7e1F688fe926940E8839F353
IZapDepositor public zap_depositor; // 0x7544Fe3d184b6B55D6B36c3FCA1157eE0Ba30287
// Number of decimals under 18, for collateral token
uint256 public usdc_missing_decimals;
uint256 public usdt_missing_decimals;
// Precision related
uint256 public PRICE_PRECISION = 1e6;
uint256 public VIRTUAL_PRICE_PRECISION = 1e18;
// Min ratio of collat <-> 3crv conversions via add_liquidity / remove_liquidity; 1e6
uint256 public liq_slippage_3crv;
// Min ratio of (FRAX + 3CRV) <-> FRAX3CRV-f-2 metapool conversions via add_liquidity / remove_liquidity; 1e6
uint256 public slippage_metapool;
// Admins
address public timelock_address;
address public custodian_address;
/* ========== CONSTRUCTOR ========== */
constructor (
address _owner_address,
address _custodian_address,
address[4] memory _core_addresses, // 0: canFRAX, 1: USDC, 2: USDT, 3: CrossChainBridgeBacker
address[4] memory _pool_addresses // 0: FRAX2pool, 1: I2poolGaugeDeposit, 2: I2pool, 3: IZapDepositor
) Owned(_owner_address) {
// Owner
owner = _owner_address;
// Core
canFRAX = CrossChainCanonicalFRAX(_core_addresses[0]);
USDC = ERC20(_core_addresses[1]);
USDT = ERC20(_core_addresses[2]);
usdc_missing_decimals = uint(18) - USDC.decimals();
usdt_missing_decimals = uint(18) - USDT.decimals();
cc_bridge_backer = CrossChainBridgeBacker_FTM_AnySwap(_core_addresses[3]);
// Pools
frax2pool = IFRAX2pool(_pool_addresses[0]);
two_pool_gauge = I2poolGaugeDeposit(_pool_addresses[1]);
two_pool = I2pool(_pool_addresses[2]);
zap_depositor = IZapDepositor(_pool_addresses[3]);
// Other variable initializations
liq_slippage_3crv = 950000;
slippage_metapool = 950000;
// Set the custodian
custodian_address = _custodian_address;
// Get the timelock addresse from the minter
timelock_address = cc_bridge_backer.timelock_address();
}
/* ========== MODIFIERS ========== */
modifier onlyByOwnGov() {
require(msg.sender == timelock_address || msg.sender == owner, "Not owner or timelock");
_;
}
modifier onlyByOwnGovCust() {
require(msg.sender == timelock_address || msg.sender == owner || msg.sender == custodian_address, "Not owner, tlck, or custd");
_;
}
/* ========== VIEWS ========== */
function showAllocations() public view returns (uint256[12] memory allocations) {
// Get some LP token prices
uint256 two_pool_price = two_pool.get_virtual_price();
uint256 frax2pool_price = frax2pool.get_virtual_price();
// FRAX
allocations[0] = canFRAX.balanceOf(address(this)); // Free FRAX
// USDC
allocations[1] = USDC.balanceOf(address(this)); // Free USDC, native precision
allocations[2] = (allocations[1] * (10 ** usdc_missing_decimals)); // Free USDC USD value
// USDT
allocations[3] = USDT.balanceOf(address(this)); // Free USDT, native precision
allocations[4] = (allocations[3] * (10 ** usdt_missing_decimals)); // Free USDT USD value
// 2pool Gauge Deposit
allocations[5] = (two_pool_gauge.balanceOf(address(this))); // Free 2pool gauge
allocations[6] = (allocations[5] * two_pool_price) / VIRTUAL_PRICE_PRECISION; // Free 2pool gauge USD value (1-to-1 conversion with 2pool)
// 2pool
allocations[7] = (two_pool.balanceOf(address(this))); // Free 2pool
allocations[8] = (allocations[7] * two_pool_price) / VIRTUAL_PRICE_PRECISION; // Free 2pool USD value
// FRAX2pool LP
allocations[9] = (frax2pool.balanceOf(address(this))); // Free FRAX2pool
allocations[10] = (allocations[9] * frax2pool_price) / VIRTUAL_PRICE_PRECISION; // Free FRAX2pool USD value
// Total USD value
allocations[11] = allocations[0] + allocations[2] + allocations[4] + allocations[6] + allocations[8] + allocations[10];
}
// Needed by CrossChainBridgeBacker
function allDollarBalances() public view returns (
uint256 frax_ttl,
uint256 fxs_ttl,
uint256 col_ttl, // in native decimals()
uint256 ttl_val_usd_e18
) {
uint256[12] memory allocations = showAllocations();
return (allocations[0], 0, allocations[1], allocations[11]);
}
function borrowed_frax() public view returns (uint256) {
return cc_bridge_backer.frax_lent_balances(address(this));
}
function borrowed_collat() public view returns (uint256) {
return cc_bridge_backer.collat_lent_balances(address(this));
}
/* ========== RESTRICTED FUNCTIONS ========== */
function metapoolDeposit(uint256 _frax_amount, uint256 _usdc_amount, uint256 _usdt_amount) external onlyByOwnGov returns (uint256 metapool_LP_received) {
// Approve the FRAX to be zapped
if (_frax_amount > 0) {
canFRAX.approve(address(zap_depositor), _frax_amount);
}
// Approve the USDC to be zapped
if (_usdc_amount > 0) {
USDC.approve(address(zap_depositor), _usdc_amount);
}
// Approve the USDT to be zapped
if (_usdt_amount > 0) {
USDT.approve(address(zap_depositor), _usdt_amount);
}
// Calculate the min LP out expected
uint256 ttl_val_usd = _frax_amount + (_usdc_amount * (10 ** usdc_missing_decimals)) + (_usdt_amount * (10 ** usdt_missing_decimals));
ttl_val_usd = (ttl_val_usd * VIRTUAL_PRICE_PRECISION) / frax2pool.get_virtual_price();
uint256 min_3pool_out = (ttl_val_usd * liq_slippage_3crv) / PRICE_PRECISION;
// Zap the token(s)
metapool_LP_received = zap_depositor.add_liquidity(
address(frax2pool),
[
_frax_amount,
_usdc_amount,
_usdt_amount
],
min_3pool_out
);
}
function _metapoolWithdrawOneCoin(uint256 _metapool_lp_in, int128 tkn_idx) internal returns (uint256 tokens_received) {
// Approve the metapool LP tokens for zapper contract
frax2pool.approve(address(zap_depositor), _metapool_lp_in);
// Calculate the min FRAX out
uint256 lp_usd_value = (_metapool_lp_in * VIRTUAL_PRICE_PRECISION) / frax2pool.get_virtual_price();
uint256 min_tkn_out = (lp_usd_value * liq_slippage_3crv) / PRICE_PRECISION;
// Handle different decimals
if (tkn_idx == 1) min_tkn_out = min_tkn_out / (10 ** usdc_missing_decimals);
else if (tkn_idx == 2) min_tkn_out = min_tkn_out / (10 ** usdt_missing_decimals);
// Perform the liquidity swap
tokens_received = zap_depositor.remove_liquidity_one_coin(
address(frax2pool),
_metapool_lp_in,
tkn_idx,
min_tkn_out
);
}
function metapoolWithdrawFrax(uint256 _metapool_lp_in) external onlyByOwnGov returns (uint256) {
return _metapoolWithdrawOneCoin(_metapool_lp_in, 0);
}
function metapoolWithdrawUsdc(uint256 _metapool_lp_in) external onlyByOwnGov returns (uint256) {
return _metapoolWithdrawOneCoin(_metapool_lp_in, 1);
}
function metapoolWithdrawUsdt(uint256 _metapool_lp_in) external onlyByOwnGov returns (uint256) {
return _metapoolWithdrawOneCoin(_metapool_lp_in, 2);
}
function metapoolWithdrawAtCurRatio(
uint256 _metapool_lp_in,
uint256 min_frax,
uint256 min_usdc,
uint256 min_usdt
) external onlyByOwnGov returns (uint256 frax_received, uint256 usdc_received, uint256 usdt_received) {
// Approve the metapool LP tokens for zapper contract
frax2pool.approve(address(zap_depositor), _metapool_lp_in);
// Withdraw FRAX, USDC, and USDT from the metapool at the current balance
uint256[3] memory result_arr = zap_depositor.remove_liquidity(
address(frax2pool),
_metapool_lp_in,
[min_frax, min_usdc, min_usdt]
);
frax_received = result_arr[0];
usdc_received = result_arr[1];
usdt_received = result_arr[2];
}
/* ========== Burns and givebacks ========== */
// Give USDC profits back. Goes through the minter
function giveFRAXBack(uint256 frax_amount, bool do_bridging) external onlyByOwnGovCust {
canFRAX.approve(address(cc_bridge_backer), frax_amount);
cc_bridge_backer.receiveBackViaAMO(address(canFRAX), frax_amount, do_bridging);
}
function giveCollatBack(uint256 collat_amount, bool do_bridging) external onlyByOwnGovCust {
USDC.approve(address(cc_bridge_backer), collat_amount);
cc_bridge_backer.receiveBackViaAMO(address(USDC), collat_amount, do_bridging);
}
/* ========== RESTRICTED GOVERNANCE FUNCTIONS ========== */
function setCCBridgeBacker(address _cc_bridge_backer_address) external onlyByOwnGov {
cc_bridge_backer = CrossChainBridgeBacker_FTM_AnySwap(_cc_bridge_backer_address);
// Get the timelock addresses from the minter
timelock_address = cc_bridge_backer.timelock_address();
// Make sure the new addresse is not address(0)
require(timelock_address != address(0), "Invalid timelock");
}
function setSlippages(uint256 _liq_slippage_3crv, uint256 _slippage_metapool) external onlyByOwnGov {
liq_slippage_3crv = _liq_slippage_3crv;
slippage_metapool = _slippage_metapool;
}
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnGov {
// Can only be triggered by owner or governance, not custodian
// Tokens are sent to the custodian, as a sort of safeguard
TransferHelper.safeTransfer(address(tokenAddress), msg.sender, tokenAmount);
}
// Generic proxy
function execute(
address _to,
uint256 _value,
bytes calldata _data
) external onlyByOwnGov returns (bool, bytes memory) {
(bool success, bytes memory result) = _to.call{value:_value}(_data);
return (success, result);
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner_address","type":"address"},{"internalType":"address","name":"_custodian_address","type":"address"},{"internalType":"address[4]","name":"_core_addresses","type":"address[4]"},{"internalType":"address[4]","name":"_pool_addresses","type":"address[4]"}],"stateMutability":"nonpayable","type":"constructor"},{"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"},{"inputs":[],"name":"PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDT","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VIRTUAL_PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allDollarBalances","outputs":[{"internalType":"uint256","name":"frax_ttl","type":"uint256"},{"internalType":"uint256","name":"fxs_ttl","type":"uint256"},{"internalType":"uint256","name":"col_ttl","type":"uint256"},{"internalType":"uint256","name":"ttl_val_usd_e18","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowed_collat","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowed_frax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canFRAX","outputs":[{"internalType":"contract CrossChainCanonicalFRAX","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cc_bridge_backer","outputs":[{"internalType":"contract CrossChainBridgeBacker_FTM_AnySwap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"custodian_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"frax2pool","outputs":[{"internalType":"contract IFRAX2pool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"collat_amount","type":"uint256"},{"internalType":"bool","name":"do_bridging","type":"bool"}],"name":"giveCollatBack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"frax_amount","type":"uint256"},{"internalType":"bool","name":"do_bridging","type":"bool"}],"name":"giveFRAXBack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liq_slippage_3crv","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_frax_amount","type":"uint256"},{"internalType":"uint256","name":"_usdc_amount","type":"uint256"},{"internalType":"uint256","name":"_usdt_amount","type":"uint256"}],"name":"metapoolDeposit","outputs":[{"internalType":"uint256","name":"metapool_LP_received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_metapool_lp_in","type":"uint256"},{"internalType":"uint256","name":"min_frax","type":"uint256"},{"internalType":"uint256","name":"min_usdc","type":"uint256"},{"internalType":"uint256","name":"min_usdt","type":"uint256"}],"name":"metapoolWithdrawAtCurRatio","outputs":[{"internalType":"uint256","name":"frax_received","type":"uint256"},{"internalType":"uint256","name":"usdc_received","type":"uint256"},{"internalType":"uint256","name":"usdt_received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_metapool_lp_in","type":"uint256"}],"name":"metapoolWithdrawFrax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_metapool_lp_in","type":"uint256"}],"name":"metapoolWithdrawUsdc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_metapool_lp_in","type":"uint256"}],"name":"metapoolWithdrawUsdt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_cc_bridge_backer_address","type":"address"}],"name":"setCCBridgeBacker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_liq_slippage_3crv","type":"uint256"},{"internalType":"uint256","name":"_slippage_metapool","type":"uint256"}],"name":"setSlippages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"showAllocations","outputs":[{"internalType":"uint256[12]","name":"allocations","type":"uint256[12]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"slippage_metapool","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":"two_pool","outputs":[{"internalType":"contract I2pool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"two_pool_gauge","outputs":[{"internalType":"contract I2poolGaugeDeposit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdc_missing_decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdt_missing_decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zap_depositor","outputs":[{"internalType":"contract IZapDepositor","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052620f4240600c55670de0b6b3a7640000600d553480156200002457600080fd5b50604051620030d6380380620030d6833981016040819052620000479162000465565b836001600160a01b038116620000a35760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015260640160405180910390fd5b600080546001600160a01b0319166001600160a01b03831690811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a150600080546001600160a01b038681166001600160a01b031992831617909255835160028054918416918316919091179055602080850151600380549185169184168217905560408087015160048054919096169416939093178455825163313ce56760e01b81529251909363313ce5679380820193929190829003018186803b1580156200018857600080fd5b505afa1580156200019d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001c39190620004c5565b620001d39060ff166012620004ea565b600a55600480546040805163313ce56760e01b815290516001600160a01b039092169263313ce567928282019260209290829003018186803b1580156200021957600080fd5b505afa1580156200022e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002549190620004c5565b620002649060ff166012620004ea565b600b55606082810151600580546001600160a01b03199081166001600160a01b03938416908117909255845160068054831691851691909117905560208086015160078054841691861691909117905560408087015160088054851691871691909117905594860151600980548416918616919091179055620e7ef0600e819055600f5560118054909216938816939093179055825163dc6663c760e01b81529251909263dc6663c7926004828101939192829003018186803b1580156200032b57600080fd5b505afa15801562000340573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000366919062000440565b601080546001600160a01b0319166001600160a01b0392909216919091179055506200051092505050565b80516001600160a01b0381168114620003a957600080fd5b919050565b600082601f830112620003c057600080fd5b604051608081016001600160401b0381118282101715620003f157634e487b7160e01b600052604160045260246000fd5b6040528083608081018610156200040757600080fd5b60005b600481101562000435576200041f8262000391565b835260209283019291909101906001016200040a565b509195945050505050565b6000602082840312156200045357600080fd5b6200045e8262000391565b9392505050565b60008060008061014085870312156200047d57600080fd5b620004888562000391565b9350620004986020860162000391565b9250620004a98660408701620003ae565b9150620004ba8660c08701620003ae565b905092959194509250565b600060208284031215620004d857600080fd5b815160ff811681146200045e57600080fd5b6000828210156200050b57634e487b7160e01b600052601160045260246000fd5b500390565b612bb680620005206000396000f3fe608060405234801561001057600080fd5b50600436106102415760003560e01c80637ce6112b1161014557806392e51617116100bd578063bcb9d9c61161008c578063cc11da9011610071578063cc11da9014610541578063dc6663c71461056f578063de1c39851461058f57600080fd5b8063bcb9d9c614610518578063c54e44eb1461052157600080fd5b806392e51617146104bb57806395082d25146104db578063ac0482b2146104e4578063b61d27f6146104f757600080fd5b806387ce78341161011457806389a30271116100f957806389a302711461045b5780638d7732031461047b5780638da5cb5b1461049b57600080fd5b806387ce7834146104355780638980f11f1461044857600080fd5b80637ce6112b146103d75780637e34026e146103f75780637e3f8f171461040c578063868289bc1461041557600080fd5b80633b005ab4116101d85780635687b4aa116101a757806368d694401161018c57806368d69440146103b45780636b42c576146103bc57806379ba5097146103cf57600080fd5b80635687b4aa146103985780636162db06146103ab57600080fd5b80633b005ab41461033c57806345a90b10146103455780635015d8561461036557806353a47bb71461037857600080fd5b80631627540c116102145780631627540c146102ce5780631a2cf1f1146102e15780632c492a67146103015780632cc1ce531461031457600080fd5b8063017c68571461024657806303fa74c41461029057806308a515f3146102a657806314f2013e146102bb575b600080fd5b6008546102669073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b610298610598565b604051908152602001610287565b6102b96102b4366004612542565b610640565b005b6102986102c93660046126db565b61085f565b6102b96102dc366004612542565b610916565b6006546102669073ffffffffffffffffffffffffffffffffffffffff1681565b61029861030f3660046126db565b610a36565b61031c610ae7565b604080519485526020850193909352918301526060820152608001610287565b610298600f5481565b6009546102669073ffffffffffffffffffffffffffffffffffffffff1681565b61029861037336600461275f565b610b15565b6001546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6102b96103a636600461270d565b610fd1565b610298600e5481565b6102986111e0565b6102b96103ca36600461273d565b611237565b6102b96112e5565b6011546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6103ff611430565b6040516102879190612883565b610298600d5481565b6007546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6102986104433660046126db565b611a63565b6102b9610456366004612583565b611b14565b6003546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6002546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6000546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6005546102669073ffffffffffffffffffffffffffffffffffffffff1681565b610298600c5481565b6102b96104f236600461270d565b611bc6565b61050a6105053660046125af565b611da3565b6040516102879291906128b5565b610298600b5481565b6004546102669073ffffffffffffffffffffffffffffffffffffffff1681565b61055461054f36600461278b565b611ec9565b60408051938452602084019290925290820152606001610287565b6010546102669073ffffffffffffffffffffffffffffffffffffffff1681565b610298600a5481565b6005546040517f5242e71d00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff1690635242e71d906024015b60206040518083038186803b15801561060357600080fd5b505afa158015610617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063b91906126f4565b905090565b60105473ffffffffffffffffffffffffffffffffffffffff1633148061067d575060005473ffffffffffffffffffffffffffffffffffffffff1633145b6106e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064015b60405180910390fd5b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8316908117909155604080517fdc6663c7000000000000000000000000000000000000000000000000000000008152905163dc6663c791600480820192602092909190829003018186803b15801561077a57600080fd5b505afa15801561078e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b29190612566565b601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216918217905561085c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c69642074696d656c6f636b0000000000000000000000000000000060448201526064016106df565b50565b60105460009073ffffffffffffffffffffffffffffffffffffffff1633148061089f575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610905576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b61091082600261210b565b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201527f6f726d207468697320616374696f6e000000000000000000000000000000000060648201526084016106df565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b60105460009073ffffffffffffffffffffffffffffffffffffffff16331480610a76575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610adc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b61091082600161210b565b6000806000806000610af7611430565b80516020820151610160909201519097600097509195509350915050565b60105460009073ffffffffffffffffffffffffffffffffffffffff16331480610b55575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610bbb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b8315610c71576002546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810187905291169063095ea7b390604401602060405180830381600087803b158015610c3757600080fd5b505af1158015610c4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6f91906126be565b505b8215610d27576003546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810186905291169063095ea7b390604401602060405180830381600087803b158015610ced57600080fd5b505af1158015610d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2591906126be565b505b8115610ddf57600480546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182169381019390935260248301859052169063095ea7b390604401602060405180830381600087803b158015610da557600080fd5b505af1158015610db9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ddd91906126be565b505b6000600b54600a610df091906129c3565b610dfa9084612a89565b600a8054610e07916129c3565b610e119086612a89565b610e1b908761290f565b610e25919061290f565b9050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb7b8b806040518163ffffffff1660e01b815260040160206040518083038186803b158015610e8f57600080fd5b505afa158015610ea3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec791906126f4565b600d54610ed49083612a89565b610ede9190612927565b90506000600c54600e5483610ef39190612a89565b610efd9190612927565b600954600654604080516060810182528a8152602081018a905280820189905290517fa318517900000000000000000000000000000000000000000000000000000000815293945073ffffffffffffffffffffffffffffffffffffffff9283169363a318517993610f75931691908690600401612812565b602060405180830381600087803b158015610f8f57600080fd5b505af1158015610fa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc791906126f4565b9695505050505050565b60105473ffffffffffffffffffffffffffffffffffffffff1633148061100e575060005473ffffffffffffffffffffffffffffffffffffffff1633145b80611030575060115473ffffffffffffffffffffffffffffffffffffffff1633145b611096576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4e6f74206f776e65722c20746c636b2c206f722063757374640000000000000060448201526064016106df565b6003546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810185905291169063095ea7b390604401602060405180830381600087803b15801561110c57600080fd5b505af1158015611120573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114491906126be565b506005546003546040517f7565673a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018590528315156044820152911690637565673a906064015b600060405180830381600087803b1580156111c457600080fd5b505af11580156111d8573d6000803e3d6000fd5b505050505050565b6005546040517f3001f39800000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff1690633001f398906024016105eb565b60105473ffffffffffffffffffffffffffffffffffffffff16331480611274575060005473ffffffffffffffffffffffffffffffffffffffff1633145b6112da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b600e91909155600f55565b60015473ffffffffffffffffffffffffffffffffffffffff16331461138c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527f2063616e20616363657074206f776e657273686970000000000000000000000060648201526084016106df565b6000546001546040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b611438612523565b600854604080517fbb7b8b80000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163bb7b8b80916004808301926020929190829003018186803b1580156114a357600080fd5b505afa1580156114b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db91906126f4565b90506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb7b8b806040518163ffffffff1660e01b815260040160206040518083038186803b15801561154757600080fd5b505afa15801561155b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157f91906126f4565b6002546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291925073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b1580156115e957600080fd5b505afa1580156115fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162191906126f4565b83526003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b15801561168c57600080fd5b505afa1580156116a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c491906126f4565b6020840152600a80546116d6916129c3565b60208401516116e59190612a89565b8360026020020152600480546040517f70a08231000000000000000000000000000000000000000000000000000000008152309281019290925273ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561175757600080fd5b505afa15801561176b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178f91906126f4565b6060840152600b546117a290600a6129c3565b60608401516117b19190612a89565b60808401526007546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b15801561181f57600080fd5b505afa158015611833573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185791906126f4565b60a08401819052600d549061186d908490612a89565b6118779190612927565b60c08401526008546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b1580156118e557600080fd5b505afa1580156118f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191d91906126f4565b60e08401819052600d5490611933908490612a89565b61193d9190612927565b6101008401526006546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b1580156119ac57600080fd5b505afa1580156119c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e491906126f4565b6101208401819052600d54906119fb908390612a89565b611a059190612927565b610140840181905261010084015160c0850151608086015160408701518751611a2e919061290f565b611a38919061290f565b611a42919061290f565b611a4c919061290f565b611a56919061290f565b6101608401525090919050565b60105460009073ffffffffffffffffffffffffffffffffffffffff16331480611aa3575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611b09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b61091082600061210b565b60105473ffffffffffffffffffffffffffffffffffffffff16331480611b51575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611bb7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b611bc28233836123b3565b5050565b60105473ffffffffffffffffffffffffffffffffffffffff16331480611c03575060005473ffffffffffffffffffffffffffffffffffffffff1633145b80611c25575060115473ffffffffffffffffffffffffffffffffffffffff1633145b611c8b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4e6f74206f776e65722c20746c636b2c206f722063757374640000000000000060448201526064016106df565b6002546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810185905291169063095ea7b390604401602060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3991906126be565b506005546002546040517f7565673a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018590528315156044820152911690637565673a906064016111aa565b60105460009060609073ffffffffffffffffffffffffffffffffffffffff16331480611de6575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611e4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b6000808773ffffffffffffffffffffffffffffffffffffffff16878787604051611e779291906127e6565b60006040518083038185875af1925050503d8060008114611eb4576040519150601f19603f3d011682016040523d82523d6000602084013e611eb9565b606091505b5090999098509650505050505050565b6010546000908190819073ffffffffffffffffffffffffffffffffffffffff16331480611f0d575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611f73576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b6006546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018a905291169063095ea7b390604401602060405180830381600087803b158015611fe957600080fd5b505af1158015611ffd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202191906126be565b50600954600654604080516060810182528981526020810189905280820188905290517f8fa892a600000000000000000000000000000000000000000000000000000000815260009373ffffffffffffffffffffffffffffffffffffffff90811693638fa892a69361209c9391909216918d9160040161284a565b606060405180830381600087803b1580156120b657600080fd5b505af11580156120ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ee9190612638565b80516020820151604090920151909a919950975095505050505050565b6006546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015260248101859052600092919091169063095ea7b390604401602060405180830381600087803b15801561218657600080fd5b505af115801561219a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121be91906126be565b50600654604080517fbb7b8b80000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163bb7b8b80916004808301926020929190829003018186803b15801561222a57600080fd5b505afa15801561223e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061226291906126f4565b600d5461226f9086612a89565b6122799190612927565b90506000600c54600e548361228e9190612a89565b6122989190612927565b905083600f0b600114156122c457600a80546122b3916129c3565b6122bd9082612927565b90506122eb565b83600f0b600214156122eb57600b546122de90600a6129c3565b6122e89082612927565b90505b6009546006546040517f29ed286200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015260248101889052600f87900b6044820152606481018490529116906329ed286290608401602060405180830381600087803b15801561237257600080fd5b505af1158015612386573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123aa91906126f4565b95945050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052915160009283929087169161244a91906127f6565b6000604051808303816000865af19150503d8060008114612487576040519150601f19603f3d011682016040523d82523d6000602084013e61248c565b606091505b50915091508180156124b65750805115806124b65750808060200190518101906124b691906126be565b61251c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064016106df565b5050505050565b604051806101800160405280600c906020820280368337509192915050565b60006020828403121561255457600080fd5b813561255f81612b50565b9392505050565b60006020828403121561257857600080fd5b815161255f81612b50565b6000806040838503121561259657600080fd5b82356125a181612b50565b946020939093013593505050565b600080600080606085870312156125c557600080fd5b84356125d081612b50565b935060208501359250604085013567ffffffffffffffff808211156125f457600080fd5b818701915087601f83011261260857600080fd5b81358181111561261757600080fd5b88602082850101111561262957600080fd5b95989497505060200194505050565b60006060828403121561264a57600080fd5b82601f83011261265957600080fd5b6040516060810181811067ffffffffffffffff8211171561267c5761267c612b21565b60405280836060810186101561269157600080fd5b60005b60038110156126b3578151835260209283019290910190600101612694565b509195945050505050565b6000602082840312156126d057600080fd5b815161255f81612b72565b6000602082840312156126ed57600080fd5b5035919050565b60006020828403121561270657600080fd5b5051919050565b6000806040838503121561272057600080fd5b82359150602083013561273281612b72565b809150509250929050565b6000806040838503121561275057600080fd5b50508035926020909101359150565b60008060006060848603121561277457600080fd5b505081359360208301359350604090920135919050565b600080600080608085870312156127a157600080fd5b5050823594602084013594506040840135936060013592509050565b8060005b60038110156127e05781518452602093840193909101906001016127c1565b50505050565b8183823760009101908152919050565b60008251612808818460208701612ac6565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff8416815260a0810161283c60208301856127bd565b826080830152949350505050565b73ffffffffffffffffffffffffffffffffffffffff841681526020810183905260a0810161287b60408301846127bd565b949350505050565b6101808101818360005b600c8110156128ac57815183526020928301929091019060010161288d565b50505092915050565b821515815260406020820152600082518060408401526128dc816060850160208701612ac6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016060019392505050565b6000821982111561292257612922612af2565b500190565b60008261295d577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600181815b808511156129bb57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156129a1576129a1612af2565b808516156129ae57918102915b93841c9390800290612967565b509250929050565b600061255f83836000826129d957506001610910565b816129e657506000610910565b81600181146129fc5760028114612a0657612a22565b6001915050610910565b60ff841115612a1757612a17612af2565b50506001821b610910565b5060208310610133831016604e8410600b8410161715612a45575081810a610910565b612a4f8383612962565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612a8157612a81612af2565b029392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ac157612ac1612af2565b500290565b60005b83811015612ae1578181015183820152602001612ac9565b838111156127e05750506000910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461085c57600080fd5b801515811461085c57600080fdfea26469706673582212209cebc43e52fea3f5a0d73b335d0539d3096edab628b6e3922747a942f25e190564736f6c63430008060033000000000000000000000000bca9a9aab13a68d311160d4f997e3d783da865fb0000000000000000000000000d11148aa48abc566b256f0567ffa68bd2913f1f00000000000000000000000017fc002b466eec40dae837fc4be5c67993ddbd6f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000ddf6b5b2ba110a0267bffb86aeabfe2637cb8375000000000000000000000000f07d553b195080f84f582e88ecdd54baa122b279000000000000000000000000bf7e49483881c76487b0989cd7d9a8239b20ca410000000000000000000000007f90122bf0700f9e7e1f688fe926940e8839f3530000000000000000000000007544fe3d184b6b55d6b36c3fca1157ee0ba30287
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102415760003560e01c80637ce6112b1161014557806392e51617116100bd578063bcb9d9c61161008c578063cc11da9011610071578063cc11da9014610541578063dc6663c71461056f578063de1c39851461058f57600080fd5b8063bcb9d9c614610518578063c54e44eb1461052157600080fd5b806392e51617146104bb57806395082d25146104db578063ac0482b2146104e4578063b61d27f6146104f757600080fd5b806387ce78341161011457806389a30271116100f957806389a302711461045b5780638d7732031461047b5780638da5cb5b1461049b57600080fd5b806387ce7834146104355780638980f11f1461044857600080fd5b80637ce6112b146103d75780637e34026e146103f75780637e3f8f171461040c578063868289bc1461041557600080fd5b80633b005ab4116101d85780635687b4aa116101a757806368d694401161018c57806368d69440146103b45780636b42c576146103bc57806379ba5097146103cf57600080fd5b80635687b4aa146103985780636162db06146103ab57600080fd5b80633b005ab41461033c57806345a90b10146103455780635015d8561461036557806353a47bb71461037857600080fd5b80631627540c116102145780631627540c146102ce5780631a2cf1f1146102e15780632c492a67146103015780632cc1ce531461031457600080fd5b8063017c68571461024657806303fa74c41461029057806308a515f3146102a657806314f2013e146102bb575b600080fd5b6008546102669073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b610298610598565b604051908152602001610287565b6102b96102b4366004612542565b610640565b005b6102986102c93660046126db565b61085f565b6102b96102dc366004612542565b610916565b6006546102669073ffffffffffffffffffffffffffffffffffffffff1681565b61029861030f3660046126db565b610a36565b61031c610ae7565b604080519485526020850193909352918301526060820152608001610287565b610298600f5481565b6009546102669073ffffffffffffffffffffffffffffffffffffffff1681565b61029861037336600461275f565b610b15565b6001546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6102b96103a636600461270d565b610fd1565b610298600e5481565b6102986111e0565b6102b96103ca36600461273d565b611237565b6102b96112e5565b6011546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6103ff611430565b6040516102879190612883565b610298600d5481565b6007546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6102986104433660046126db565b611a63565b6102b9610456366004612583565b611b14565b6003546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6002546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6000546102669073ffffffffffffffffffffffffffffffffffffffff1681565b6005546102669073ffffffffffffffffffffffffffffffffffffffff1681565b610298600c5481565b6102b96104f236600461270d565b611bc6565b61050a6105053660046125af565b611da3565b6040516102879291906128b5565b610298600b5481565b6004546102669073ffffffffffffffffffffffffffffffffffffffff1681565b61055461054f36600461278b565b611ec9565b60408051938452602084019290925290820152606001610287565b6010546102669073ffffffffffffffffffffffffffffffffffffffff1681565b610298600a5481565b6005546040517f5242e71d00000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff1690635242e71d906024015b60206040518083038186803b15801561060357600080fd5b505afa158015610617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063b91906126f4565b905090565b60105473ffffffffffffffffffffffffffffffffffffffff1633148061067d575060005473ffffffffffffffffffffffffffffffffffffffff1633145b6106e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064015b60405180910390fd5b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8316908117909155604080517fdc6663c7000000000000000000000000000000000000000000000000000000008152905163dc6663c791600480820192602092909190829003018186803b15801561077a57600080fd5b505afa15801561078e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b29190612566565b601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216918217905561085c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c69642074696d656c6f636b0000000000000000000000000000000060448201526064016106df565b50565b60105460009073ffffffffffffffffffffffffffffffffffffffff1633148061089f575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610905576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b61091082600261210b565b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201527f6f726d207468697320616374696f6e000000000000000000000000000000000060648201526084016106df565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b60105460009073ffffffffffffffffffffffffffffffffffffffff16331480610a76575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610adc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b61091082600161210b565b6000806000806000610af7611430565b80516020820151610160909201519097600097509195509350915050565b60105460009073ffffffffffffffffffffffffffffffffffffffff16331480610b55575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610bbb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b8315610c71576002546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810187905291169063095ea7b390604401602060405180830381600087803b158015610c3757600080fd5b505af1158015610c4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6f91906126be565b505b8215610d27576003546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810186905291169063095ea7b390604401602060405180830381600087803b158015610ced57600080fd5b505af1158015610d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2591906126be565b505b8115610ddf57600480546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182169381019390935260248301859052169063095ea7b390604401602060405180830381600087803b158015610da557600080fd5b505af1158015610db9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ddd91906126be565b505b6000600b54600a610df091906129c3565b610dfa9084612a89565b600a8054610e07916129c3565b610e119086612a89565b610e1b908761290f565b610e25919061290f565b9050600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb7b8b806040518163ffffffff1660e01b815260040160206040518083038186803b158015610e8f57600080fd5b505afa158015610ea3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec791906126f4565b600d54610ed49083612a89565b610ede9190612927565b90506000600c54600e5483610ef39190612a89565b610efd9190612927565b600954600654604080516060810182528a8152602081018a905280820189905290517fa318517900000000000000000000000000000000000000000000000000000000815293945073ffffffffffffffffffffffffffffffffffffffff9283169363a318517993610f75931691908690600401612812565b602060405180830381600087803b158015610f8f57600080fd5b505af1158015610fa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc791906126f4565b9695505050505050565b60105473ffffffffffffffffffffffffffffffffffffffff1633148061100e575060005473ffffffffffffffffffffffffffffffffffffffff1633145b80611030575060115473ffffffffffffffffffffffffffffffffffffffff1633145b611096576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4e6f74206f776e65722c20746c636b2c206f722063757374640000000000000060448201526064016106df565b6003546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810185905291169063095ea7b390604401602060405180830381600087803b15801561110c57600080fd5b505af1158015611120573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114491906126be565b506005546003546040517f7565673a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018590528315156044820152911690637565673a906064015b600060405180830381600087803b1580156111c457600080fd5b505af11580156111d8573d6000803e3d6000fd5b505050505050565b6005546040517f3001f39800000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff1690633001f398906024016105eb565b60105473ffffffffffffffffffffffffffffffffffffffff16331480611274575060005473ffffffffffffffffffffffffffffffffffffffff1633145b6112da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b600e91909155600f55565b60015473ffffffffffffffffffffffffffffffffffffffff16331461138c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527f2063616e20616363657074206f776e657273686970000000000000000000000060648201526084016106df565b6000546001546040805173ffffffffffffffffffffffffffffffffffffffff93841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b611438612523565b600854604080517fbb7b8b80000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163bb7b8b80916004808301926020929190829003018186803b1580156114a357600080fd5b505afa1580156114b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db91906126f4565b90506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb7b8b806040518163ffffffff1660e01b815260040160206040518083038186803b15801561154757600080fd5b505afa15801561155b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157f91906126f4565b6002546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291925073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b1580156115e957600080fd5b505afa1580156115fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162191906126f4565b83526003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b15801561168c57600080fd5b505afa1580156116a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c491906126f4565b6020840152600a80546116d6916129c3565b60208401516116e59190612a89565b8360026020020152600480546040517f70a08231000000000000000000000000000000000000000000000000000000008152309281019290925273ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561175757600080fd5b505afa15801561176b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178f91906126f4565b6060840152600b546117a290600a6129c3565b60608401516117b19190612a89565b60808401526007546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b15801561181f57600080fd5b505afa158015611833573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185791906126f4565b60a08401819052600d549061186d908490612a89565b6118779190612927565b60c08401526008546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b1580156118e557600080fd5b505afa1580156118f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191d91906126f4565b60e08401819052600d5490611933908490612a89565b61193d9190612927565b6101008401526006546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b1580156119ac57600080fd5b505afa1580156119c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e491906126f4565b6101208401819052600d54906119fb908390612a89565b611a059190612927565b610140840181905261010084015160c0850151608086015160408701518751611a2e919061290f565b611a38919061290f565b611a42919061290f565b611a4c919061290f565b611a56919061290f565b6101608401525090919050565b60105460009073ffffffffffffffffffffffffffffffffffffffff16331480611aa3575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611b09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b61091082600061210b565b60105473ffffffffffffffffffffffffffffffffffffffff16331480611b51575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611bb7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b611bc28233836123b3565b5050565b60105473ffffffffffffffffffffffffffffffffffffffff16331480611c03575060005473ffffffffffffffffffffffffffffffffffffffff1633145b80611c25575060115473ffffffffffffffffffffffffffffffffffffffff1633145b611c8b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4e6f74206f776e65722c20746c636b2c206f722063757374640000000000000060448201526064016106df565b6002546005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810185905291169063095ea7b390604401602060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3991906126be565b506005546002546040517f7565673a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018590528315156044820152911690637565673a906064016111aa565b60105460009060609073ffffffffffffffffffffffffffffffffffffffff16331480611de6575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611e4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b6000808773ffffffffffffffffffffffffffffffffffffffff16878787604051611e779291906127e6565b60006040518083038185875af1925050503d8060008114611eb4576040519150601f19603f3d011682016040523d82523d6000602084013e611eb9565b606091505b5090999098509650505050505050565b6010546000908190819073ffffffffffffffffffffffffffffffffffffffff16331480611f0d575060005473ffffffffffffffffffffffffffffffffffffffff1633145b611f73576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f74206f776e6572206f722074696d656c6f636b000000000000000000000060448201526064016106df565b6006546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018a905291169063095ea7b390604401602060405180830381600087803b158015611fe957600080fd5b505af1158015611ffd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202191906126be565b50600954600654604080516060810182528981526020810189905280820188905290517f8fa892a600000000000000000000000000000000000000000000000000000000815260009373ffffffffffffffffffffffffffffffffffffffff90811693638fa892a69361209c9391909216918d9160040161284a565b606060405180830381600087803b1580156120b657600080fd5b505af11580156120ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ee9190612638565b80516020820151604090920151909a919950975095505050505050565b6006546009546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015260248101859052600092919091169063095ea7b390604401602060405180830381600087803b15801561218657600080fd5b505af115801561219a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121be91906126be565b50600654604080517fbb7b8b80000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163bb7b8b80916004808301926020929190829003018186803b15801561222a57600080fd5b505afa15801561223e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061226291906126f4565b600d5461226f9086612a89565b6122799190612927565b90506000600c54600e548361228e9190612a89565b6122989190612927565b905083600f0b600114156122c457600a80546122b3916129c3565b6122bd9082612927565b90506122eb565b83600f0b600214156122eb57600b546122de90600a6129c3565b6122e89082612927565b90505b6009546006546040517f29ed286200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015260248101889052600f87900b6044820152606481018490529116906329ed286290608401602060405180830381600087803b15801561237257600080fd5b505af1158015612386573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123aa91906126f4565b95945050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052915160009283929087169161244a91906127f6565b6000604051808303816000865af19150503d8060008114612487576040519150601f19603f3d011682016040523d82523d6000602084013e61248c565b606091505b50915091508180156124b65750805115806124b65750808060200190518101906124b691906126be565b61251c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064016106df565b5050505050565b604051806101800160405280600c906020820280368337509192915050565b60006020828403121561255457600080fd5b813561255f81612b50565b9392505050565b60006020828403121561257857600080fd5b815161255f81612b50565b6000806040838503121561259657600080fd5b82356125a181612b50565b946020939093013593505050565b600080600080606085870312156125c557600080fd5b84356125d081612b50565b935060208501359250604085013567ffffffffffffffff808211156125f457600080fd5b818701915087601f83011261260857600080fd5b81358181111561261757600080fd5b88602082850101111561262957600080fd5b95989497505060200194505050565b60006060828403121561264a57600080fd5b82601f83011261265957600080fd5b6040516060810181811067ffffffffffffffff8211171561267c5761267c612b21565b60405280836060810186101561269157600080fd5b60005b60038110156126b3578151835260209283019290910190600101612694565b509195945050505050565b6000602082840312156126d057600080fd5b815161255f81612b72565b6000602082840312156126ed57600080fd5b5035919050565b60006020828403121561270657600080fd5b5051919050565b6000806040838503121561272057600080fd5b82359150602083013561273281612b72565b809150509250929050565b6000806040838503121561275057600080fd5b50508035926020909101359150565b60008060006060848603121561277457600080fd5b505081359360208301359350604090920135919050565b600080600080608085870312156127a157600080fd5b5050823594602084013594506040840135936060013592509050565b8060005b60038110156127e05781518452602093840193909101906001016127c1565b50505050565b8183823760009101908152919050565b60008251612808818460208701612ac6565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff8416815260a0810161283c60208301856127bd565b826080830152949350505050565b73ffffffffffffffffffffffffffffffffffffffff841681526020810183905260a0810161287b60408301846127bd565b949350505050565b6101808101818360005b600c8110156128ac57815183526020928301929091019060010161288d565b50505092915050565b821515815260406020820152600082518060408401526128dc816060850160208701612ac6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016060019392505050565b6000821982111561292257612922612af2565b500190565b60008261295d577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600181815b808511156129bb57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156129a1576129a1612af2565b808516156129ae57918102915b93841c9390800290612967565b509250929050565b600061255f83836000826129d957506001610910565b816129e657506000610910565b81600181146129fc5760028114612a0657612a22565b6001915050610910565b60ff841115612a1757612a17612af2565b50506001821b610910565b5060208310610133831016604e8410600b8410161715612a45575081810a610910565b612a4f8383612962565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612a8157612a81612af2565b029392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ac157612ac1612af2565b500290565b60005b83811015612ae1578181015183820152602001612ac9565b838111156127e05750506000910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461085c57600080fd5b801515811461085c57600080fdfea26469706673582212209cebc43e52fea3f5a0d73b335d0539d3096edab628b6e3922747a942f25e190564736f6c63430008060033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000bca9a9aab13a68d311160d4f997e3d783da865fb0000000000000000000000000d11148aa48abc566b256f0567ffa68bd2913f1f00000000000000000000000017fc002b466eec40dae837fc4be5c67993ddbd6f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000ddf6b5b2ba110a0267bffb86aeabfe2637cb8375000000000000000000000000f07d553b195080f84f582e88ecdd54baa122b279000000000000000000000000bf7e49483881c76487b0989cd7d9a8239b20ca410000000000000000000000007f90122bf0700f9e7e1f688fe926940e8839f3530000000000000000000000007544fe3d184b6b55d6b36c3fca1157ee0ba30287
-----Decoded View---------------
Arg [0] : _owner_address (address): 0xbCa9a9Aab13a68d311160D4f997E3D783Da865Fb
Arg [1] : _custodian_address (address): 0x0d11148aa48AbC566B256f0567ffA68BD2913f1F
Arg [2] : _core_addresses (address[4]): 0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F,0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8,0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9,0xddf6b5B2BA110a0267BffB86AeAbFe2637cb8375
Arg [3] : _pool_addresses (address[4]): 0xf07d553B195080F84F582e88ecdD54bAa122b279,0xbF7E49483881C76487b0989CD7d9A8239B20CA41,0x7f90122BF0700F9E7e1F688fe926940E8839F353,0x7544Fe3d184b6B55D6B36c3FCA1157eE0Ba30287
-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 000000000000000000000000bca9a9aab13a68d311160d4f997e3d783da865fb
Arg [1] : 0000000000000000000000000d11148aa48abc566b256f0567ffa68bd2913f1f
Arg [2] : 00000000000000000000000017fc002b466eec40dae837fc4be5c67993ddbd6f
Arg [3] : 000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8
Arg [4] : 000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9
Arg [5] : 000000000000000000000000ddf6b5b2ba110a0267bffb86aeabfe2637cb8375
Arg [6] : 000000000000000000000000f07d553b195080f84f582e88ecdd54baa122b279
Arg [7] : 000000000000000000000000bf7e49483881c76487b0989cd7d9a8239b20ca41
Arg [8] : 0000000000000000000000007f90122bf0700f9e7e1f688fe926940e8839f353
Arg [9] : 0000000000000000000000007544fe3d184b6b55d6b36c3fca1157ee0ba30287
Deployed Bytecode Sourcemap
174716:11093:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;175270:22;;;;;;;;;;;;5408:42:1;5396:55;;;5378:74;;5366:2;5351:18;175270:22:0;;;;;;;;180053:135;;;:::i;:::-;;;13031:25:1;;;13019:2;13004:18;180053:135:0;12986:76:1;184524:434:0;;;;;;:::i;:::-;;:::i;:::-;;182845:165;;;;;;:::i;:::-;;:::i;50896:141::-;;;;;;:::i;:::-;;:::i;175097:27::-;;;;;;;;;182672:165;;;;;;:::i;:::-;;:::i;179570:336::-;;;:::i;:::-;;;;13622:25:1;;;13678:2;13663:18;;13656:34;;;;13706:18;;;13699:34;13764:2;13749:18;;13742:34;13609:3;13594:19;179570:336:0;13576:206:1;175950:32:0;;;;;;175345:34;;;;;;;;;180252:1290;;;;;;:::i;:::-;;:::i;50664:29::-;;;;;;;;;184194:252;;;;;;:::i;:::-;;:::i;175794:32::-;;;;;;179914:131;;;:::i;184966:206::-;;;;;;:::i;:::-;;:::i;51045:271::-;;;:::i;176044:32::-;;;;;;;;;177898:1623;;;:::i;:::-;;;;;;;:::i;175649:45::-;;;;;;175177:40;;;;;;;;;182499:165;;;;;;:::i;:::-;;:::i;185180:322::-;;;;;;:::i;:::-;;:::i;174864:17::-;;;;;;;;;174819:38;;;;;;;;;50637:20;;;;;;;;;175016:58;;;;;;;;;175606:36;;;;;;183936:250;;;;;;:::i;:::-;;:::i;185532:274::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;175535:36::-;;;;;;174940:17;;;;;;;;;183018:799;;;;;;:::i;:::-;;:::i;:::-;;;;13269:25:1;;;13325:2;13310:18;;13303:34;;;;13353:18;;;13346:34;13257:2;13242:18;183018:799:0;13224:162:1;176006:31:0;;;;;;;;;175492:36;;;;;;180053:135;180128:16;;:52;;;;;180174:4;180128:52;;;5378:74:1;180101:7:0;;180128:16;;;:37;;5351:18:1;;180128:52:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;180121:59;;180053:135;:::o;184524:434::-;177571:16;;;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;;;;;;;;;184619:16:::1;:80:::0;;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;184786:35:::1;::::0;;;;;;;:33:::1;::::0;:35:::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;184619:80;184786:35;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;184767:16;:54:::0;;;::::1;;::::0;;;::::1;::::0;;::::1;::::0;;184891:59:::1;;;::::0;::::1;::::0;;11622:2:1;184891:59:0::1;::::0;::::1;11604:21:1::0;11661:2;11641:18;;;11634:30;11700:18;11680;;;11673:46;11736:18;;184891:59:0::1;11594:166:1::0;184891:59:0::1;184524:434:::0;:::o;182845:165::-;177571:16;;182931:7;;177571:16;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;182958:44:::1;182983:15;183000:1;182958:24;:44::i;:::-;182951:51:::0;182845:165;-1:-1:-1;;182845:165:0:o;50896:141::-;51376:5;;;;51362:10;:19;51354:79;;;;;;;12671:2:1;51354:79:0;;;12653:21:1;12710:2;12690:18;;;12683:30;12749:34;12729:18;;;12722:62;12820:17;12800:18;;;12793:45;12855:19;;51354:79:0;12643:237:1;51354:79:0;50968:14:::1;:23:::0;;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;51007:22:::1;::::0;5378:74:1;;;51007:22:0::1;::::0;5366:2:1;5351:18;51007:22:0::1;;;;;;;50896:141:::0;:::o;182672:165::-;177571:16;;182758:7;;177571:16;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;182785:44:::1;182810:15;182827:1;182785:24;:44::i;179570:336::-:0;179630:16;179658:15;179684;179734:23;179776:30;179809:17;:15;:17::i;:::-;179847:14;;;179866;;;179882:15;;;;;179847:14;;179859:1;;-1:-1:-1;179866:14:0;;-1:-1:-1;179882:15:0;-1:-1:-1;179570:336:0;-1:-1:-1;;179570:336:0:o;180252:1290::-;177571:16;;180374:28;;177571:16;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;180461:16;;180457:102:::1;;180494:7;::::0;180518:13:::1;::::0;180494:53:::1;::::0;;;;:7:::1;180518:13:::0;;::::1;180494:53;::::0;::::1;6408:74:1::0;6498:18;;;6491:34;;;180494:7:0;::::1;::::0;:15:::1;::::0;6381:18:1;;180494:53:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;180457:102;180617:16:::0;;180613:99:::1;;180650:4;::::0;180671:13:::1;::::0;180650:50:::1;::::0;;;;:4:::1;180671:13:::0;;::::1;180650:50;::::0;::::1;6408:74:1::0;6498:18;;;6491:34;;;180650:4:0;::::1;::::0;:12:::1;::::0;6381:18:1;;180650:50:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;180613:99;180778:16:::0;;180774:99:::1;;180811:4;::::0;;180832:13:::1;::::0;180811:50:::1;::::0;;;;:4:::1;180832:13:::0;;::::1;180811:50:::0;;::::1;6408:74:1::0;;;;6498:18;;;6491:34;;;180811:4:0::1;::::0;:12:::1;::::0;6381:18:1;;180811:50:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;180774:99;180931:19;181040:21;;181034:2;:27;;;;:::i;:::-;181018:44;::::0;:12;:44:::1;:::i;:::-;180991:21;::::0;;180985:27:::1;::::0;::::1;:::i;:::-;180969:44;::::0;:12;:44:::1;:::i;:::-;180953:61;::::0;:12;:61:::1;:::i;:::-;:110;;;;:::i;:::-;180931:132;;181130:9;;;;;;;;;;;:27;;;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;181103:23;::::0;181089:37:::1;::::0;:11;:37:::1;:::i;:::-;181088:71;;;;:::i;:::-;181074:85;;181170:21;181230:15;;181209:17;;181195:11;:31;;;;:::i;:::-;181194:51;;;;:::i;:::-;181310:13;::::0;181360:9:::1;::::0;181310:224:::1;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;;;;;;;;;;181170:75;;-1:-1:-1;181310:13:0::1;::::0;;::::1;::::0;:27:::1;::::0;:224:::1;::::0;181360:9:::1;::::0;181310:224;181170:75;;181310:224:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;181287:247:::0;180252:1290;-1:-1:-1;;;;;;180252:1290:0:o;184194:252::-;177725:16;;;;177711:10;:30;;:53;;-1:-1:-1;177759:5:0;;;;177745:10;:19;177711:53;:88;;;-1:-1:-1;177782:17:0;;;;177768:10;:31;177711:88;177703:126;;;;;;;12317:2:1;177703:126:0;;;12299:21:1;12356:2;12336:18;;;12329:30;12395:27;12375:18;;;12368:55;12440:18;;177703:126:0;12289:175:1;177703:126:0;184296:4:::1;::::0;184317:16:::1;::::0;184296:54:::1;::::0;;;;:4:::1;184317:16:::0;;::::1;184296:54;::::0;::::1;6408:74:1::0;6498:18;;;6491:34;;;184296:4:0;::::1;::::0;:12:::1;::::0;6381:18:1;;184296:54:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;184361:16:0::1;::::0;184404:4:::1;::::0;184361:77:::1;::::0;;;;:16:::1;184404:4:::0;;::::1;184361:77;::::0;::::1;7170:74:1::0;7260:18;;;7253:34;;;7330:14;;7323:22;7303:18;;;7296:50;184361:16:0;::::1;::::0;:34:::1;::::0;7143:18:1;;184361:77:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;184194:252:::0;;:::o;179914:131::-;179987:16;;:50;;;;;180031:4;179987:50;;;5378:74:1;179960:7:0;;179987:16;;;:35;;5351:18:1;;179987:50:0;5333:125:1;184966:206:0;177571:16;;;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;185077:17:::1;:38:::0;;;;185126:17:::1;:38:::0;184966:206::o;51045:271::-;51114:14;;;;51100:10;:28;51092:94;;;;;;;11200:2:1;51092:94:0;;;11182:21:1;11239:2;11219:18;;;11212:30;11278:34;11258:18;;;11251:62;11349:23;11329:18;;;11322:51;11390:19;;51092:94:0;11172:243:1;51092:94:0;51215:5;;;51222:14;51202:35;;;51215:5;;;;5698:34:1;;51222:14:0;;;;5763:2:1;5748:18;;5741:43;51202:35:0;;5610:18:1;51202:35:0;;;;;;;51256:14;;;;51248:22;;;;;;51256:14;;;51248:22;;;;51281:27;;;51045:271::o;177898:1623::-;177946:30;;:::i;:::-;178051:8;;:28;;;;;;;;178026:22;;178051:8;;;:26;;:28;;;;;;;;;;;;;;:8;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178026:53;;178090:23;178116:9;;;;;;;;;;;:27;;;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178192:7;;:32;;;;;178218:4;178192:32;;;5378:74:1;178090:55:0;;-1:-1:-1;178192:7:0;;;:17;;5351:18:1;;178192:32:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178175:49;;178284:4;;:29;;;;;178307:4;178284:29;;;5378:74:1;178284:4:0;;;;;:14;;5351:18:1;;178284:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178267:14;;;:46;178397:21;;;178391:27;;;:::i;:::-;178373:14;;;;:46;;;;:::i;:::-;178355:11;178367:1;178355:14;;;:65;178490:4;;;:29;;;;;178513:4;178490:29;;;5378:74:1;;;;178490:4:0;;;:14;;5351:18:1;;178490:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178473:14;;;:46;178603:21;;178597:27;;:2;:27;:::i;:::-;178579:14;;;;:46;;;;:::i;:::-;178561:14;;;:65;178712:14;;:39;;;;;178745:4;178573:1;178712:39;;5378:74:1;178712:14:0;;;;;:24;;5351:18:1;;178712:39:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178694:14;;;:58;;;178836:23;;;178801:31;;178818:14;;178801:31;:::i;:::-;178800:59;;;;:::i;:::-;178783:14;;;:76;178969:8;;:33;;;;;178996:4;178969:33;;;5378:74:1;178969:8:0;;;;;:18;;5351::1;;178969:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;178951:14;;;:52;;;179081:23;;;179046:31;;179063:14;;179046:31;:::i;:::-;179045:59;;;;:::i;:::-;179028:14;;;:76;179184:9;;:34;;;;;179212:4;179184:34;;;5378:74:1;179184:9:0;;;;;:19;;5351:18:1;;179184:34:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;179166:14;;;:53;;;179303:23;;;179267:32;;179284:15;;179267:32;:::i;:::-;179266:60;;;;:::i;:::-;179248:15;;;:78;;;179481:14;;;;179464;;;;179447;;;;179430;;;;179413;;:31;;179430:14;179413:31;:::i;:::-;:48;;;;:::i;:::-;:65;;;;:::i;:::-;:82;;;;:::i;:::-;:100;;;;:::i;:::-;179395:15;;;:118;-1:-1:-1;179395:11:0;;177898:1623;-1:-1:-1;177898:1623:0:o;182499:165::-;177571:16;;182585:7;;177571:16;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;182612:44:::1;182637:15;182654:1;182612:24;:44::i;185180:322::-:0;177571:16;;;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;185419:75:::1;185455:12;185470:10;185482:11;185419:27;:75::i;:::-;185180:322:::0;;:::o;183936:250::-;177725:16;;;;177711:10;:30;;:53;;-1:-1:-1;177759:5:0;;;;177745:10;:19;177711:53;:88;;;-1:-1:-1;177782:17:0;;;;177768:10;:31;177711:88;177703:126;;;;;;;12317:2:1;177703:126:0;;;12299:21:1;12356:2;12336:18;;;12329:30;12395:27;12375:18;;;12368:55;12440:18;;177703:126:0;12289:175:1;177703:126:0;184034:7:::1;::::0;184058:16:::1;::::0;184034:55:::1;::::0;;;;:7:::1;184058:16:::0;;::::1;184034:55;::::0;::::1;6408:74:1::0;6498:18;;;6491:34;;;184034:7:0;::::1;::::0;:15:::1;::::0;6381:18:1;;184034:55:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;184100:16:0::1;::::0;184143:7:::1;::::0;184100:78:::1;::::0;;;;:16:::1;184143:7:::0;;::::1;184100:78;::::0;::::1;7170:74:1::0;7260:18;;;7253:34;;;7330:14;;7323:22;7303:18;;;7296:50;184100:16:0;::::1;::::0;:34:::1;::::0;7143:18:1;;184100:78:0::1;7125:227:1::0;185532:274:0;177571:16;;185665:4;;185671:12;;177571:16;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;185697:12:::1;185711:19:::0;185734:3:::1;:8;;185749:6;185757:5;;185734:29;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;185696:67:0;;;;-1:-1:-1;185532:274:0;-1:-1:-1;;;;;;;185532:274:0:o;183018:799::-;177571:16;;183210:21;;;;;;177571:16;;177557:10;:30;;:53;;-1:-1:-1;177605:5:0;;;;177591:10;:19;177557:53;177549:87;;;;;;;11967:2:1;177549:87:0;;;11949:21:1;12006:2;11986:18;;;11979:30;12045:23;12025:18;;;12018:51;12086:18;;177549:87:0;11939:171:1;177549:87:0;183353:9:::1;::::0;183379:13:::1;::::0;183353:58:::1;::::0;;;;:9:::1;183379:13:::0;;::::1;183353:58;::::0;::::1;6408:74:1::0;6498:18;;;6491:34;;;183353:9:0;::::1;::::0;:17:::1;::::0;6381:18:1;;183353:58:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;183538:13:0::1;::::0;183591:9:::1;::::0;183538:151:::1;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;;;;;;;;;;183507:28:::1;::::0;183538:13:::1;::::0;;::::1;::::0;:30:::1;::::0;:151:::1;::::0;183591:9;;;::::1;::::0;183617:15;;183538:151:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;183716:13:::0;;::::1;183756::::0;::::1;::::0;183796;;;::::1;::::0;183716;;183756;;-1:-1:-1;183796:13:0;-1:-1:-1;183018:799:0;-1:-1:-1;;;;;;183018:799:0:o;181550:941::-;181742:9;;181768:13;;181742:58;;;;;:9;181768:13;;;181742:58;;;6408:74:1;6498:18;;;6491:34;;;181643:23:0;;181742:9;;;;;:17;;6381:18:1;;181742:58:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;181921:9:0;;:29;;;;;;;;181852:20;;181921:9;;;:27;;:29;;;;;;;;;;;;;;:9;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;181894:23;;181876:41;;:15;:41;:::i;:::-;181875:75;;;;:::i;:::-;181852:98;;181961:19;182020:15;;181999:17;;181984:12;:32;;;;:::i;:::-;181983:52;;;;:::i;:::-;181961:74;;182090:7;:12;;182101:1;182090:12;182086:166;;;182139:21;;;182133:27;;;:::i;:::-;182118:43;;:11;:43;:::i;:::-;182104:57;;182086:166;;;182181:7;:12;;182192:1;182181:12;182177:75;;;182230:21;;182224:27;;:2;:27;:::i;:::-;182209:43;;:11;:43;:::i;:::-;182195:57;;182177:75;182322:13;;182384:9;;182322:161;;;;;:13;182384:9;;;182322:161;;;7586:74:1;7676:18;;;7669:34;;;7750:2;7739:22;;;7719:18;;;7712:50;7778:18;;;7771:34;;;182322:13:0;;;:39;;7558:19:1;;182322:161:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;182304:179;181550:941;-1:-1:-1;;;;;181550:941:0:o;49545:361::-;49740:45;;;49729:10;6426:55:1;;;49740:45:0;;;6408:74:1;6498:18;;;;6491:34;;;49740:45:0;;;;;;;;;;6381:18:1;;;;49740:45:0;;;;;;;;;;;;;49729:57;;-1:-1:-1;;;;49729:10:0;;;;:57;;49740:45;49729:57;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49693:93;;;;49805:7;:57;;;;-1:-1:-1;49817:11:0;;:16;;:44;;;49848:4;49837:24;;;;;;;;;;;;:::i;:::-;49797:101;;;;;;;10840:2:1;49797:101:0;;;10822:21:1;10879:2;10859:18;;;10852:30;10918:33;10898:18;;;10891:61;10969:18;;49797:101:0;10812:181:1;49797:101:0;49615:291;;49545:361;;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:247:1:-;73:6;126:2;114:9;105:7;101:23;97:32;94:2;;;142:1;139;132:12;94:2;181:9;168:23;200:31;225:5;200:31;:::i;:::-;250:5;84:177;-1:-1:-1;;;84:177:1:o;266:251::-;336:6;389:2;377:9;368:7;364:23;360:32;357:2;;;405:1;402;395:12;357:2;437:9;431:16;456:31;481:5;456:31;:::i;522:315::-;590:6;598;651:2;639:9;630:7;626:23;622:32;619:2;;;667:1;664;657:12;619:2;706:9;693:23;725:31;750:5;725:31;:::i;:::-;775:5;827:2;812:18;;;;799:32;;-1:-1:-1;;;609:228:1:o;842:794::-;930:6;938;946;954;1007:2;995:9;986:7;982:23;978:32;975:2;;;1023:1;1020;1013:12;975:2;1062:9;1049:23;1081:31;1106:5;1081:31;:::i;:::-;1131:5;-1:-1:-1;1183:2:1;1168:18;;1155:32;;-1:-1:-1;1238:2:1;1223:18;;1210:32;1261:18;1291:14;;;1288:2;;;1318:1;1315;1308:12;1288:2;1356:6;1345:9;1341:22;1331:32;;1401:7;1394:4;1390:2;1386:13;1382:27;1372:2;;1423:1;1420;1413:12;1372:2;1463;1450:16;1489:2;1481:6;1478:14;1475:2;;;1505:1;1502;1495:12;1475:2;1550:7;1545:2;1536:6;1532:2;1528:15;1524:24;1521:37;1518:2;;;1571:1;1568;1561:12;1518:2;965:671;;;;-1:-1:-1;;1602:2:1;1594:11;;-1:-1:-1;;;965:671:1:o;1641:798::-;1734:6;1787:2;1775:9;1766:7;1762:23;1758:32;1755:2;;;1803:1;1800;1793:12;1755:2;1852:7;1845:4;1834:9;1830:20;1826:34;1816:2;;1874:1;1871;1864:12;1816:2;1907;1901:9;1949:2;1941:6;1937:15;2018:6;2006:10;2003:22;1982:18;1970:10;1967:34;1964:62;1961:2;;;2029:18;;:::i;:::-;2065:2;2058:22;2100:6;2126:9;2165:2;2150:18;;2147:31;-1:-1:-1;2144:2:1;;;2191:1;2188;2181:12;2144:2;2213:1;2223:185;2237:4;2234:1;2231:11;2223:185;;;2296:10;;2284:23;;2330:4;2354:12;;;;2386;;;;2257:1;2250:9;2223:185;;;-1:-1:-1;2427:6:1;;1745:694;-1:-1:-1;;;;;1745:694:1:o;2444:245::-;2511:6;2564:2;2552:9;2543:7;2539:23;2535:32;2532:2;;;2580:1;2577;2570:12;2532:2;2612:9;2606:16;2631:28;2653:5;2631:28;:::i;2694:180::-;2753:6;2806:2;2794:9;2785:7;2781:23;2777:32;2774:2;;;2822:1;2819;2812:12;2774:2;-1:-1:-1;2845:23:1;;2764:110;-1:-1:-1;2764:110:1:o;2879:184::-;2949:6;3002:2;2990:9;2981:7;2977:23;2973:32;2970:2;;;3018:1;3015;3008:12;2970:2;-1:-1:-1;3041:16:1;;2960:103;-1:-1:-1;2960:103:1:o;3068:309::-;3133:6;3141;3194:2;3182:9;3173:7;3169:23;3165:32;3162:2;;;3210:1;3207;3200:12;3162:2;3246:9;3233:23;3223:33;;3306:2;3295:9;3291:18;3278:32;3319:28;3341:5;3319:28;:::i;:::-;3366:5;3356:15;;;3152:225;;;;;:::o;3382:248::-;3450:6;3458;3511:2;3499:9;3490:7;3486:23;3482:32;3479:2;;;3527:1;3524;3517:12;3479:2;-1:-1:-1;;3550:23:1;;;3620:2;3605:18;;;3592:32;;-1:-1:-1;3469:161:1:o;3635:316::-;3712:6;3720;3728;3781:2;3769:9;3760:7;3756:23;3752:32;3749:2;;;3797:1;3794;3787:12;3749:2;-1:-1:-1;;3820:23:1;;;3890:2;3875:18;;3862:32;;-1:-1:-1;3941:2:1;3926:18;;;3913:32;;3739:212;-1:-1:-1;3739:212:1:o;3956:385::-;4042:6;4050;4058;4066;4119:3;4107:9;4098:7;4094:23;4090:33;4087:2;;;4136:1;4133;4126:12;4087:2;-1:-1:-1;;4159:23:1;;;4229:2;4214:18;;4201:32;;-1:-1:-1;4280:2:1;4265:18;;4252:32;;4331:2;4316:18;4303:32;;-1:-1:-1;4077:264:1;-1:-1:-1;4077:264:1:o;4346:326::-;4439:5;4462:1;4472:194;4486:4;4483:1;4480:11;4472:194;;;4545:13;;4533:26;;4582:4;4606:12;;;;4641:15;;;;4506:1;4499:9;4472:194;;;4476:3;;4396:276;;:::o;4677:271::-;4860:6;4852;4847:3;4834:33;4816:3;4886:16;;4911:13;;;4886:16;4824:124;-1:-1:-1;4824:124:1:o;4953:274::-;5082:3;5120:6;5114:13;5136:53;5182:6;5177:3;5170:4;5162:6;5158:17;5136:53;:::i;:::-;5205:16;;;;;5090:137;-1:-1:-1;;5090:137:1:o;5795:434::-;6074:42;6062:55;;6044:74;;6031:3;6016:19;;6127:52;6175:2;6160:18;;6152:6;6127:52;:::i;:::-;6216:6;6210:3;6199:9;6195:19;6188:35;5998:231;;;;;;:::o;6536:433::-;6815:42;6803:55;;6785:74;;6890:2;6875:18;;6868:34;;;6772:3;6757:19;;6911:52;6959:2;6944:18;;6936:6;6911:52;:::i;:::-;6739:230;;;;;;:::o;7816:497::-;7998:3;7983:19;;7987:9;8079:6;7956:4;8113:194;8127:4;8124:1;8121:11;8113:194;;;8186:13;;8174:26;;8223:4;8247:12;;;;8282:15;;;;8147:1;8140:9;8113:194;;;8117:3;;;7965:348;;;;:::o;8318:521::-;8501:6;8494:14;8487:22;8476:9;8469:41;8546:2;8541;8530:9;8526:18;8519:30;8450:4;8578:6;8572:13;8621:6;8616:2;8605:9;8601:18;8594:34;8637:66;8696:6;8691:2;8680:9;8676:18;8671:2;8663:6;8659:15;8637:66;:::i;:::-;8755:2;8743:15;8760:66;8739:88;8724:104;;;;8830:2;8720:113;;8459:380;-1:-1:-1;;;8459:380:1:o;13787:128::-;13827:3;13858:1;13854:6;13851:1;13848:13;13845:2;;;13864:18;;:::i;:::-;-1:-1:-1;13900:9:1;;13835:80::o;13920:274::-;13960:1;13986;13976:2;;14021:77;14018:1;14011:88;14122:4;14119:1;14112:15;14150:4;14147:1;14140:15;13976:2;-1:-1:-1;14179:9:1;;13966:228::o;14199:482::-;14288:1;14331:5;14288:1;14345:330;14366:7;14356:8;14353:21;14345:330;;;14485:4;14417:66;14413:77;14407:4;14404:87;14401:2;;;14494:18;;:::i;:::-;14544:7;14534:8;14530:22;14527:2;;;14564:16;;;;14527:2;14643:22;;;;14603:15;;;;14345:330;;;14349:3;14263:418;;;;;:::o;14686:131::-;14746:5;14775:36;14802:8;14796:4;14871:5;14901:8;14891:2;;-1:-1:-1;14942:1:1;14956:5;;14891:2;14990:4;14980:2;;-1:-1:-1;15027:1:1;15041:5;;14980:2;15072:4;15090:1;15085:59;;;;15158:1;15153:130;;;;15065:218;;15085:59;15115:1;15106:10;;15129:5;;;15153:130;15190:3;15180:8;15177:17;15174:2;;;15197:18;;:::i;:::-;-1:-1:-1;;15253:1:1;15239:16;;15268:5;;15065:218;;15367:2;15357:8;15354:16;15348:3;15342:4;15339:13;15335:36;15329:2;15319:8;15316:16;15311:2;15305:4;15302:12;15298:35;15295:77;15292:2;;;-1:-1:-1;15404:19:1;;;15436:5;;15292:2;15483:34;15508:8;15502:4;15483:34;:::i;:::-;15613:6;15545:66;15541:79;15532:7;15529:92;15526:2;;;15624:18;;:::i;:::-;15662:20;;14881:807;-1:-1:-1;;;14881:807:1:o;15693:228::-;15733:7;15859:1;15791:66;15787:74;15784:1;15781:81;15776:1;15769:9;15762:17;15758:105;15755:2;;;15866:18;;:::i;:::-;-1:-1:-1;15906:9:1;;15745:176::o;15926:258::-;15998:1;16008:113;16022:6;16019:1;16016:13;16008:113;;;16098:11;;;16092:18;16079:11;;;16072:39;16044:2;16037:10;16008:113;;;16139:6;16136:1;16133:13;16130:2;;;-1:-1:-1;;16174:1:1;16156:16;;16149:27;15979:205::o;16189:184::-;16241:77;16238:1;16231:88;16338:4;16335:1;16328:15;16362:4;16359:1;16352:15;16567:184;16619:77;16616:1;16609:88;16716:4;16713:1;16706:15;16740:4;16737:1;16730:15;16756:154;16842:42;16835:5;16831:54;16824:5;16821:65;16811:2;;16900:1;16897;16890:12;16915:118;17001:5;16994:13;16987:21;16980:5;16977:32;16967:2;;17023:1;17020;17013:12
Swarm Source
ipfs://9cebc43e52fea3f5a0d73b335d0539d3096edab628b6e3922747a942f25e1905
Net Worth in USD
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.