Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
There are no matching entriesUpdate your filters to view other transactions | |||||||||
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
xSNX
Compiler Version
v0.5.15+commit.6a57276f
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2021-05-24
*/
// File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @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: @openzeppelin/upgrades/contracts/Initializable.sol
pragma solidity >=0.4.24 <0.7.0;
/**
* @title Initializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*/
contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to use in the initializer function of a contract.
*/
modifier initializer() {
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
address self = address(this);
uint256 cs;
assembly { cs := extcodesize(self) }
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
pragma solidity ^0.5.0;
/*
* @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.
*/
contract Context is Initializable {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
// solhint-disable-previous-line no-empty-blocks
function _msgSender() internal view returns (address payable) {
return msg.sender;
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: @openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Initializable, Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function initialize(address sender) public initializer {
_owner = sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return _msgSender() == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[50] private ______gap;
}
// File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @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: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.5.0;
/**
* @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 Initializable, Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view 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 returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public 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 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 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 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 {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_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 {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_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 {
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 {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
uint256[50] private ______gap;
}
// File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol
pragma solidity ^0.5.0;
/**
* @dev Optional functions from the ERC20 standard.
*/
contract ERC20Detailed is Initializable, IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
* these values are immutable: they can only be set once during
* construction.
*/
function initialize(string memory name, string memory symbol, uint8 decimals) public initializer {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
/**
* @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.
*
* 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;
}
uint256[50] private ______gap;
}
// File: synthetix/contracts/interfaces/ISynth.sol
pragma solidity >=0.4.24;
// https://docs.synthetix.io/contracts/source/interfaces/isynth
interface ISynth {
// Views
function currencyKey() external view returns (bytes32);
function transferableSynths(address account) external view returns (uint);
// Mutative functions
function transferAndSettle(address to, uint value) external returns (bool);
function transferFromAndSettle(
address from,
address to,
uint value
) external returns (bool);
// Restricted: used internally to Synthetix
function burn(address account, uint amount) external;
function issue(address account, uint amount) external;
}
// File: synthetix/contracts/interfaces/IVirtualSynth.sol
pragma solidity >=0.4.24;
interface IVirtualSynth {
// Views
function balanceOfUnderlying(address account) external view returns (uint);
function rate() external view returns (uint);
function readyToSettle() external view returns (bool);
function secsLeftInWaitingPeriod() external view returns (uint);
function settled() external view returns (bool);
function synth() external view returns (ISynth);
// Mutative functions
function settle(address account) external;
}
// File: synthetix/contracts/interfaces/ISynthetix.sol
pragma solidity >=0.4.24;
// https://docs.synthetix.io/contracts/source/interfaces/isynthetix
interface ISynthetix {
// Views
function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid);
function availableCurrencyKeys() external view returns (bytes32[] memory);
function availableSynthCount() external view returns (uint);
function availableSynths(uint index) external view returns (ISynth);
function collateral(address account) external view returns (uint);
function collateralisationRatio(address issuer) external view returns (uint);
function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint);
function isWaitingPeriod(bytes32 currencyKey) external view returns (bool);
function maxIssuableSynths(address issuer) external view returns (uint maxIssuable);
function remainingIssuableSynths(address issuer)
external
view
returns (
uint maxIssuable,
uint alreadyIssued,
uint totalSystemDebt
);
function synths(bytes32 currencyKey) external view returns (ISynth);
function synthsByAddress(address synthAddress) external view returns (bytes32);
function totalIssuedSynths(bytes32 currencyKey) external view returns (uint);
function totalIssuedSynthsExcludeEtherCollateral(bytes32 currencyKey) external view returns (uint);
function transferableSynthetix(address account) external view returns (uint transferable);
// Mutative Functions
function burnSynths(uint amount) external;
function burnSynthsOnBehalf(address burnForAddress, uint amount) external;
function burnSynthsToTarget() external;
function burnSynthsToTargetOnBehalf(address burnForAddress) external;
function exchange(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external returns (uint amountReceived);
function exchangeOnBehalf(
address exchangeForAddress,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external returns (uint amountReceived);
function exchangeWithTracking(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address originator,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeOnBehalfWithTracking(
address exchangeForAddress,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address originator,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeWithVirtual(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
bytes32 trackingCode
) external returns (uint amountReceived, IVirtualSynth vSynth);
function issueMaxSynths() external;
function issueMaxSynthsOnBehalf(address issueForAddress) external;
function issueSynths(uint amount) external;
function issueSynthsOnBehalf(address issueForAddress, uint amount) external;
function mint() external returns (bool);
function settle(bytes32 currencyKey)
external
returns (
uint reclaimed,
uint refunded,
uint numEntries
);
// Liquidations
function liquidateDelinquentAccount(address account, uint susdAmount) external returns (bool);
// Restricted Functions
function mintSecondary(address account, uint amount) external;
function mintSecondaryRewards(uint amount) external;
function burnSecondary(address account, uint amount) external;
}
// File: synthetix/contracts/interfaces/IRewardEscrowV2.sol
pragma solidity >=0.4.24;
pragma experimental ABIEncoderV2;
library VestingEntries {
struct VestingEntry {
uint64 endTime;
uint256 escrowAmount;
}
struct VestingEntryWithID {
uint64 endTime;
uint256 escrowAmount;
uint256 entryID;
}
}
interface IRewardEscrowV2 {
// Views
function balanceOf(address account) external view returns (uint);
function numVestingEntries(address account) external view returns (uint);
function totalEscrowedAccountBalance(address account) external view returns (uint);
function totalVestedAccountBalance(address account) external view returns (uint);
function getVestingQuantity(address account, uint256[] calldata entryIDs) external view returns (uint);
function getVestingSchedules(
address account,
uint256 index,
uint256 pageSize
) external view returns (VestingEntries.VestingEntryWithID[] memory);
function getAccountVestingEntryIDs(
address account,
uint256 index,
uint256 pageSize
) external view returns (uint256[] memory);
function getVestingEntryClaimable(address account, uint256 entryID) external view returns (uint);
function getVestingEntry(address account, uint256 entryID) external view returns (uint64, uint256);
// Mutative functions
function vest(uint256[] calldata entryIDs) external;
function createEscrowEntry(
address beneficiary,
uint256 deposit,
uint256 duration
) external;
function appendVestingEntry(
address account,
uint256 quantity,
uint256 duration
) external;
function migrateVestingSchedule(address _addressToMigrate) external;
function migrateAccountEscrowBalances(
address[] calldata accounts,
uint256[] calldata escrowBalances,
uint256[] calldata vestedBalances
) external;
// Account Merging
function startMergingWindow() external;
function mergeAccount(address accountToMerge, uint256[] calldata entryIDs) external;
function nominateAccountToMerge(address account) external;
function accountMergingIsOpen() external view returns (bool);
// L2 Migration
function importVestingEntries(
address account,
uint256 escrowedAmount,
VestingEntries.VestingEntry[] calldata vestingEntries
) external;
// Return amount of SNX transfered to SynthetixBridgeToOptimism deposit contract
function burnForMigration(address account, uint256[] calldata entryIDs)
external
returns (uint256 escrowedAccountBalance, VestingEntries.VestingEntry[] memory vestingEntries);
}
// File: synthetix/contracts/interfaces/IExchangeRates.sol
pragma solidity >=0.4.24;
// https://docs.synthetix.io/contracts/source/interfaces/iexchangerates
interface IExchangeRates {
// Structs
struct RateAndUpdatedTime {
uint216 rate;
uint40 time;
}
struct InversePricing {
uint entryPoint;
uint upperLimit;
uint lowerLimit;
bool frozenAtUpperLimit;
bool frozenAtLowerLimit;
}
// Views
function aggregators(bytes32 currencyKey) external view returns (address);
function aggregatorWarningFlags() external view returns (address);
function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool);
function canFreezeRate(bytes32 currencyKey) external view returns (bool);
function currentRoundForRate(bytes32 currencyKey) external view returns (uint);
function currenciesUsingAggregator(address aggregator) external view returns (bytes32[] memory);
function effectiveValue(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external view returns (uint value);
function effectiveValueAndRates(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint value,
uint sourceRate,
uint destinationRate
);
function effectiveValueAtRound(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
uint roundIdForSrc,
uint roundIdForDest
) external view returns (uint value);
function getCurrentRoundId(bytes32 currencyKey) external view returns (uint);
function getLastRoundIdBeforeElapsedSecs(
bytes32 currencyKey,
uint startingRoundId,
uint startingTimestamp,
uint timediff
) external view returns (uint);
function inversePricing(bytes32 currencyKey)
external
view
returns (
uint entryPoint,
uint upperLimit,
uint lowerLimit,
bool frozenAtUpperLimit,
bool frozenAtLowerLimit
);
function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256);
function oracle() external view returns (address);
function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time);
function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);
function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid);
function rateForCurrency(bytes32 currencyKey) external view returns (uint);
function rateIsFlagged(bytes32 currencyKey) external view returns (bool);
function rateIsFrozen(bytes32 currencyKey) external view returns (bool);
function rateIsInvalid(bytes32 currencyKey) external view returns (bool);
function rateIsStale(bytes32 currencyKey) external view returns (bool);
function rateStalePeriod() external view returns (uint);
function ratesAndUpdatedTimeForCurrencyLastNRounds(bytes32 currencyKey, uint numRounds)
external
view
returns (uint[] memory rates, uint[] memory times);
function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys)
external
view
returns (uint[] memory rates, bool anyRateInvalid);
function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory);
// Mutative functions
function freezeRate(bytes32 currencyKey) external;
}
// File: synthetix/contracts/interfaces/ISynthetixState.sol
pragma solidity >=0.4.24;
// https://docs.synthetix.io/contracts/source/interfaces/isynthetixstate
interface ISynthetixState {
// Views
function debtLedger(uint index) external view returns (uint);
function issuanceData(address account) external view returns (uint initialDebtOwnership, uint debtEntryIndex);
function debtLedgerLength() external view returns (uint);
function hasIssued(address account) external view returns (bool);
function lastDebtLedgerEntry() external view returns (uint);
// Mutative functions
function incrementTotalIssuerCount() external;
function decrementTotalIssuerCount() external;
function setCurrentIssuanceData(address account, uint initialDebtOwnership) external;
function appendDebtLedgerValue(uint value) external;
function clearIssuanceData(address account) external;
}
// File: synthetix/contracts/interfaces/IAddressResolver.sol
pragma solidity >=0.4.24;
// https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver
interface IAddressResolver {
function getAddress(bytes32 name) external view returns (address);
function getSynth(bytes32 key) external view returns (address);
function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address);
}
// File: contracts/interface/ISystemSettings.sol
pragma solidity 0.5.15;
interface ISystemSettings {
function issuanceRatio() external view returns(uint);
}
// File: contracts/interface/ICurveFi.sol
pragma solidity 0.5.15;
interface ICurveFi {
function exchange(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy
) external;
function exchange_underlying(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy
) external;
function get_dx_underlying(
int128 i,
int128 j,
uint256 dy
) external view returns (uint256);
function get_dy_underlying(
int128 i,
int128 j,
uint256 dx
) external view returns (uint256);
function get_dx(
int128 i,
int128 j,
uint256 dy
) external view returns (uint256);
function get_dy(
int128 i,
int128 j,
uint256 dx
) external view returns (uint256);
function get_virtual_price() external view returns (uint256);
}
// File: contracts/interface/ISetToken.sol
pragma solidity 0.5.15;
interface ISetToken {
function unitShares() external view returns(uint);
function naturalUnit() external view returns(uint);
function currentSet() external view returns(address);
// function getUnits() external view returns (uint256[] memory);
}
// File: contracts/interface/IKyberNetworkProxy.sol
pragma solidity 0.5.15;
contract IKyberNetworkProxy {
function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) external view returns (uint expectedRate, uint slippageRate);
function swapEtherToToken(ERC20 token, uint minConversionRate) external payable returns(uint);
function swapTokenToEther(ERC20 token, uint tokenQty, uint minRate) external payable returns(uint);
function swapTokenToToken(ERC20 src, uint srcAmount, ERC20 dest, uint minConversionRate) public returns(uint);
}
// File: contracts/interface/ISetAssetBaseCollateral.sol
pragma solidity 0.5.15;
interface ISetAssetBaseCollateral {
function getComponents() external view returns(address[] memory);
function naturalUnit() external view returns(uint);
function getUnits() external view returns (uint256[] memory);
}
// File: contracts/TradeAccounting.sol
pragma solidity 0.5.15;
/*
xSNX Target Allocation (assuming 800% C-RATIO)
----------------------
Allocation | NAV | % NAV
--------------------------------------
800 SNX @ $1/token | $800 | 100%
100 sUSD Debt | ($100) | (12.5%)
75 USD equiv Set | $75 | 9.375%
25 USD equiv ETH | $25 | 3.125%
--------------------------------------
Total $800 | 100%
*/
/*
Conditions for `isRebalanceTowardsHedgeRequired` to return true
Assuming 5% rebalance threshold
Allocation | NAV | % NAV
--------------------------------------
800 SNX @ $1/token | $800 | 100.63%
105 sUSD Debt | ($105) | (13.21%)
75 USD equiv Set | $75 | 9.43%
25 USD equiv ETH | $25 | 3.14%
--------------------------------------
Total $795 | 100%
Debt value | $105
Hedge Assets | $100
-------------------------
Debt/hedge ratio | 105%
*/
/*
Conditions for `isRebalanceTowardsSnxRequired` to return true
Assuming 5% rebalance threshold
Allocation | NAV | % NAV
--------------------------------------
800 SNX @ $1/token | $800 | 99.37%
100 sUSD Debt | ($100) | (12.42%)
75 USD equiv Set | $75 | 9.31%
30 USD equiv ETH | $30 | 3.72%
--------------------------------------
Total $805 | 100%
Hedge Assets | $105
Debt value | $100
-------------------------
Hedge/debt ratio | 105%
*/
contract TradeAccounting is Ownable {
using SafeMath for uint256;
uint256 private constant TEN = 10;
uint256 private constant DEC_18 = 1e18;
uint256 private constant PERCENT = 100;
uint256 private constant ETH_TARGET = 4; // targets 1/4th of hedge portfolio
uint256 private constant SLIPPAGE_RATE = 99;
uint256 private constant MAX_UINT = 2**256 - 1;
uint256 private constant RATE_STALE_TIME = 28800; // 8 hours
uint256 private constant REBALANCE_THRESHOLD = 105; // 5%
uint256 private constant INITIAL_SUPPLY_MULTIPLIER = 10;
int128 usdcIndex;
int128 susdIndex;
ICurveFi private curveFi;
ISynthetixState private synthetixState;
IAddressResolver private addressResolver;
IKyberNetworkProxy private kyberNetworkProxy;
address private xSNXAdminInstance;
address private addressValidator;
address private setAddress;
address private susdAddress;
address private usdcAddress;
address private nextCurveAddress;
bytes32 constant snx = "SNX";
bytes32 constant susd = "sUSD";
bytes32 constant seth = "sETH";
bytes32[2] synthSymbols;
address[2] setComponentAddresses;
bytes32 constant rewardEscrowName = "RewardEscrow";
bytes32 constant exchangeRatesName = "ExchangeRates";
bytes32 constant synthetixName = "Synthetix";
bytes32 constant systemSettingsName = "SystemSettings";
bytes32 constant rewardEscrowV2Name = "RewardEscrowV2";
uint256 private constant RATE_STALE_TIME_NEW = 86400; // 24 hours
function initialize(
address _setAddress,
address _kyberProxyAddress,
address _addressResolver,
address _susdAddress,
address _usdcAddress,
address _addressValidator,
bytes32[2] memory _synthSymbols,
address[2] memory _setComponentAddresses,
address _ownerAddress
) public initializer {
Ownable.initialize(_ownerAddress);
setAddress = _setAddress;
kyberNetworkProxy = IKyberNetworkProxy(_kyberProxyAddress);
addressResolver = IAddressResolver(_addressResolver);
susdAddress = _susdAddress;
usdcAddress = _usdcAddress;
addressValidator = _addressValidator;
synthSymbols = _synthSymbols;
setComponentAddresses = _setComponentAddresses;
}
modifier onlyXSNXAdmin {
require(
msg.sender == xSNXAdminInstance,
"Only xSNXAdmin contract can call"
);
_;
}
/* ========================================================================================= */
/* Kyber/Curve */
/* ========================================================================================= */
/*
* @dev Function that processes all token to token exchanges,
* sometimes via Kyber and sometimes via a combination of Kyber & Curve
* @dev Only callable by xSNXAdmin contract
*/
function swapTokenToToken(
address fromToken,
uint256 amount,
address toToken,
uint256 minKyberRate,
uint256 minCurveReturn
) public onlyXSNXAdmin {
if (fromToken == susdAddress) {
_exchangeUnderlying(susdIndex, usdcIndex, amount, minCurveReturn);
if (toToken != usdcAddress) {
uint256 usdcBal = getUsdcBalance();
_swapTokenToToken(usdcAddress, usdcBal, toToken, minKyberRate);
}
} else if (toToken == susdAddress) {
if (fromToken != usdcAddress) {
_swapTokenToToken(fromToken, amount, usdcAddress, minKyberRate);
}
uint256 usdcBal = getUsdcBalance();
_exchangeUnderlying(usdcIndex, susdIndex, usdcBal, minCurveReturn);
} else {
_swapTokenToToken(fromToken, amount, toToken, minKyberRate);
}
IERC20(toToken).transfer(
xSNXAdminInstance,
IERC20(toToken).balanceOf(address(this))
);
}
function _swapTokenToToken(
address _fromToken,
uint256 _amount,
address _toToken,
uint256 _minKyberRate
) private {
kyberNetworkProxy.swapTokenToToken(
ERC20(_fromToken),
_amount,
ERC20(_toToken),
_minKyberRate
);
}
/*
* @dev Function that processes all token to ETH exchanges,
* sometimes via Kyber and sometimes via a combination of Kyber & Curve
* @dev Only callable by xSNXAdmin contract
*/
function swapTokenToEther(
address fromToken,
uint256 amount,
uint256 minKyberRate,
uint256 minCurveReturn
) public onlyXSNXAdmin {
if (fromToken == susdAddress) {
_exchangeUnderlying(susdIndex, usdcIndex, amount, minCurveReturn);
uint256 usdcBal = getUsdcBalance();
_swapTokenToEther(usdcAddress, usdcBal, minKyberRate);
} else {
_swapTokenToEther(fromToken, amount, minKyberRate);
}
uint256 ethBal = address(this).balance;
(bool success, ) = msg.sender.call.value(ethBal)("");
require(success, "Transfer failed");
}
function _swapTokenToEther(
address _fromToken,
uint256 _amount,
uint256 _minKyberRate
) private {
kyberNetworkProxy.swapTokenToEther(
ERC20(_fromToken),
_amount,
_minKyberRate
);
}
function _exchangeUnderlying(
int128 _inputIndex,
int128 _outputIndex,
uint256 _amount,
uint256 _minReturn
) private {
curveFi.exchange_underlying(
_inputIndex,
_outputIndex,
_amount,
_minReturn
);
}
function getUsdcBalance() internal view returns (uint256) {
return IERC20(usdcAddress).balanceOf(address(this));
}
/* ========================================================================================= */
/* NAV */
/* ========================================================================================= */
function getEthBalance() public view returns (uint256) {
return address(xSNXAdminInstance).balance;
}
/*
* @dev Helper function for `xSNX.burn` that outputs NAV
* redemption value in ETH terms
* @param totalSupply: xSNX.totalSupply()
* @param tokensToRedeem: xSNX to burn
*/
function calculateRedemptionValue(
uint256 totalSupply,
uint256 tokensToRedeem
) public view returns (uint256 valueToRedeem) {
uint256 snxBalanceOwned = getSnxBalanceOwned();
uint256 contractDebtValue = getContractDebtValue();
uint256 pricePerToken = calculateRedeemTokenPrice(
totalSupply,
snxBalanceOwned,
contractDebtValue
);
valueToRedeem = pricePerToken.mul(tokensToRedeem).div(DEC_18);
}
/*
* @dev Helper function for `xSNX.mint` that
* 1) determines whether ETH contribution should be maintained in ETH or exchanged for SNX and
* 2) outputs the `nonSnxAssetValue` value to be used in NAV calculation
* @param totalSupply: xSNX.totalSupply()
*/
function getMintWithEthUtils(uint256 totalSupply)
public
view
returns (bool allocateToEth, uint256 nonSnxAssetValue)
{
uint256 setHoldingsInWei = getSetHoldingsValueInWei();
// called before eth transferred from xSNX to xSNXAdmin
uint256 ethBalBefore = getEthBalance();
allocateToEth = shouldAllocateEthToEthReserve(
setHoldingsInWei,
ethBalBefore,
totalSupply
);
nonSnxAssetValue = setHoldingsInWei.add(ethBalBefore);
}
/*
* @notice xSNX system targets 25% of hedge portfolio to be maintained in ETH
* @dev Function produces binary yes allocate/no allocate decision point
* determining whether ETH sent on xSNX.mint() is held or exchanged
* @param setHoldingsInWei: value of Set portfolio in ETH terms
* @param ethBalBefore: value of ETH reserve prior to tx
* @param totalSupply: xSNX.totalSupply()
*/
function shouldAllocateEthToEthReserve(
uint256 setHoldingsInWei,
uint256 ethBalBefore,
uint256 totalSupply
) public pure returns (bool allocateToEth) {
if (totalSupply == 0) return false;
if (ethBalBefore.mul(ETH_TARGET) < ethBalBefore.add(setHoldingsInWei)) {
// ETH reserve is under target
return true;
}
return false;
}
/*
* @dev Helper function for calculateIssueTokenPrice
* @dev Called indirectly by `xSNX.mint` and `xSNX.mintWithSnx`
* @dev Calculates NAV of the fund, including value of escrowed SNX, in ETH terms
* @param weiPerOneSnx: SNX price in ETH terms
* @param snxBalanceBefore: SNX balance pre-mint
* @param nonSnxAssetValue: NAV of non-SNX slice of fund
*/
function calculateNetAssetValueOnMint(
uint256 weiPerOneSnx,
uint256 snxBalanceBefore,
uint256 nonSnxAssetValue
) internal view returns (uint256) {
uint256 snxTokenValueInWei = snxBalanceBefore.mul(weiPerOneSnx).div(
DEC_18
);
uint256 contractDebtValue = getContractDebtValue();
uint256 contractDebtValueInWei = calculateDebtValueInWei(
contractDebtValue
);
return
snxTokenValueInWei.add(nonSnxAssetValue).sub(
contractDebtValueInWei
);
}
/*
* @dev Helper function for calculateRedeemTokenPrice
* @dev Called indirectly by `xSNX.burn`
* @dev Calculates NAV of the fund, excluding value of escrowed SNX, in ETH terms
* @param weiPerOneSnx: SNX price in ETH terms
* @param snxBalanceOwned: non-escrowed SNX balance
* @param contractDebtValueInWei: sUSD debt balance of fund in ETH terms
*/
function calculateNetAssetValueOnRedeem(
uint256 weiPerOneSnx,
uint256 snxBalanceOwned,
uint256 contractDebtValueInWei
) internal view returns (uint256) {
uint256 snxTokenValueInWei = snxBalanceOwned.mul(weiPerOneSnx).div(
DEC_18
);
uint256 nonSnxAssetValue = calculateNonSnxAssetValue();
return
snxTokenValueInWei.add(nonSnxAssetValue).sub(
contractDebtValueInWei
);
}
/*
* @dev NAV value of non-SNX assets, computed in ETH terms
*/
function calculateNonSnxAssetValue() internal view returns (uint256) {
return getSetHoldingsValueInWei().add(getEthBalance());
}
/*
* @dev SNX price in ETH terms, calculated for purposes of redemption NAV
* @notice Return value discounted slightly to better represent liquidation price
*/
function getWeiPerOneSnxOnRedeem()
internal
view
returns (uint256 weiPerOneSnx)
{
uint256 snxUsdPrice = getSnxPrice();
uint256 ethUsdPrice = getSynthPrice(seth);
weiPerOneSnx = snxUsdPrice
.mul(DEC_18)
.div(ethUsdPrice)
.mul(SLIPPAGE_RATE) // used to better represent liquidation price as volume scales
.div(PERCENT);
}
/*
* @dev Returns Synthetix synth symbol for asset currently held in TokenSet (e.g., sETH for WETH)
* @notice xSNX contract complex only compatible with Sets that hold a single asset at a time
*/
function getActiveAssetSynthSymbol()
internal
view
returns (bytes32 synthSymbol)
{
synthSymbol = getAssetCurrentlyActiveInSet() == setComponentAddresses[0]
? (synthSymbols[0])
: (synthSymbols[1]);
}
/*
* @dev Returns SNX price in ETH terms, calculated for purposes of issuance NAV (when allocateToEth)
*/
function getWeiPerOneSnxOnMint() internal view returns (uint256) {
uint256 snxUsd = getSynthPrice(snx);
uint256 ethUsd = getSynthPrice(seth);
return snxUsd.mul(DEC_18).div(ethUsd);
}
/*
* @dev Single use function to define initial xSNX issuance
*/
function getInitialSupply() internal view returns (uint256) {
return
IERC20(addressResolver.getAddress(synthetixName))
.balanceOf(xSNXAdminInstance)
.mul(INITIAL_SUPPLY_MULTIPLIER);
}
/*
* @dev Helper function for `xSNX.mint` that calculates token issuance
* @param snxBalanceBefore: SNX balance pre-mint
* @param ethContributed: ETH payable on mint, less fees
* @param nonSnxAssetValue: NAV of non-SNX slice of fund
* @param totalSupply: xSNX.totalSupply()
*/
function calculateTokensToMintWithEth(
uint256 snxBalanceBefore,
uint256 ethContributed,
uint256 nonSnxAssetValue,
uint256 totalSupply
) public view returns (uint256) {
if (totalSupply == 0) {
return getInitialSupply();
}
uint256 pricePerToken = calculateIssueTokenPrice(
getWeiPerOneSnxOnMint(),
snxBalanceBefore,
nonSnxAssetValue,
totalSupply
);
return ethContributed.mul(DEC_18).div(pricePerToken);
}
/*
* @dev Helper function for `xSNX.mintWithSnx` that calculates token issuance
* @param snxBalanceBefore: SNX balance pre-mint
* @param snxAddedToBalance: SNX contributed by mint
* @param totalSupply: xSNX.totalSupply()
*/
function calculateTokensToMintWithSnx(
uint256 snxBalanceBefore,
uint256 snxAddedToBalance,
uint256 totalSupply
) public view returns (uint256) {
if (totalSupply == 0) {
return getInitialSupply();
}
uint256 weiPerOneSnx = getWeiPerOneSnxOnMint();
// need to derive snx contribution in eth terms for NAV calc
uint256 proxyEthContribution = weiPerOneSnx.mul(snxAddedToBalance).div(
DEC_18
);
uint256 nonSnxAssetValue = calculateNonSnxAssetValue();
uint256 pricePerToken = calculateIssueTokenPrice(
weiPerOneSnx,
snxBalanceBefore,
nonSnxAssetValue,
totalSupply
);
return proxyEthContribution.mul(DEC_18).div(pricePerToken);
}
/*
* @dev Called indirectly by `xSNX.mint` and `xSNX.mintWithSnx`
* @dev Calculates token price on issuance, including value of escrowed SNX
* @param weiPerOneSnx: SNX price in ETH terms
* @param snxBalanceBefore: SNX balance pre-mint
* @param nonSnxAssetValue: Non-SNX slice of fund
* @param totalSupply: xSNX.totalSupply()
*/
function calculateIssueTokenPrice(
uint256 weiPerOneSnx,
uint256 snxBalanceBefore,
uint256 nonSnxAssetValue,
uint256 totalSupply
) public view returns (uint256 pricePerToken) {
pricePerToken = calculateNetAssetValueOnMint(
weiPerOneSnx,
snxBalanceBefore,
nonSnxAssetValue
)
.mul(DEC_18)
.div(totalSupply);
}
/*
* @dev Called indirectly by `xSNX.burn`
* @dev Calculates token price on redemption, excluding value of escrowed SNX
* @param totalSupply: xSNX.totalSupply()
* @param snxBalanceOwned: non-escrowed SNX balance
* @param contractDebtValue: sUSD debt in USD terms
*/
function calculateRedeemTokenPrice(
uint256 totalSupply,
uint256 snxBalanceOwned,
uint256 contractDebtValue
) public view returns (uint256 pricePerToken) {
// SNX won't actually be sold (burns are only distributed in available ETH) but
// this is a proxy for the return value of SNX that would be sold
uint256 weiPerOneSnx = getWeiPerOneSnxOnRedeem();
uint256 debtValueInWei = calculateDebtValueInWei(contractDebtValue);
pricePerToken = calculateNetAssetValueOnRedeem(
weiPerOneSnx,
snxBalanceOwned,
debtValueInWei
)
.mul(DEC_18)
.div(totalSupply);
}
/* ========================================================================================= */
/* Set */
/* ========================================================================================= */
/*
* @dev Balance of underlying asset "active" in Set (e.g., WETH or USDC)
*/
function getActiveSetAssetBalance() public view returns (uint256) {
return
IERC20(getAssetCurrentlyActiveInSet()).balanceOf(xSNXAdminInstance);
}
/*
* @dev Calculates quantity of Set Token equivalent to quantity of underlying asset token
* @notice rebalancingSetQuantity return value is reduced slightly to ensure successful execution
* @param componentQuantity: balance of underlying Set asset, e.g., WETH
*/
function calculateSetQuantity(uint256 componentQuantity)
public
view
returns (uint256 rebalancingSetQuantity)
{
uint256 baseSetNaturalUnit = getBaseSetNaturalUnit();
uint256 baseSetComponentUnits = getBaseSetComponentUnits();
uint256 baseSetIssuable = componentQuantity.mul(baseSetNaturalUnit).div(
baseSetComponentUnits
);
uint256 rebalancingSetNaturalUnit = getSetNaturalUnit();
uint256 unitShares = getSetUnitShares();
rebalancingSetQuantity = baseSetIssuable
.mul(rebalancingSetNaturalUnit)
.div(unitShares)
.mul(99) // ensure sufficient balance in underlying asset
.div(100)
.div(rebalancingSetNaturalUnit)
.mul(rebalancingSetNaturalUnit);
}
/*
* @dev Calculates mintable quantity of Set Token given asset holdings
*/
function calculateSetIssuanceQuantity()
public
view
returns (uint256 rebalancingSetIssuable)
{
uint256 componentQuantity = getActiveSetAssetBalance();
rebalancingSetIssuable = calculateSetQuantity(componentQuantity);
}
/*
* @dev Calculates Set token to sell given sUSD burn requirements
* @param totalSusdToBurn: sUSD to burn to fix ratio or unlock staked SNX
*/
function calculateSetRedemptionQuantity(uint256 totalSusdToBurn)
public
view
returns (uint256 rebalancingSetRedeemable)
{
address currentSetAsset = getAssetCurrentlyActiveInSet();
bytes32 activeAssetSynthSymbol = getActiveAssetSynthSymbol();
uint256 synthUsd = getSynthPrice(activeAssetSynthSymbol);
// expectedSetAssetRate = amount of current set asset needed to redeem for 1 sUSD
uint256 expectedSetAssetRate = DEC_18.mul(DEC_18).div(synthUsd);
uint256 setAssetCollateralToSell = expectedSetAssetRate
.mul(totalSusdToBurn)
.div(DEC_18)
.mul(103) // err on the high side
.div(PERCENT);
uint256 decimals = (TEN**ERC20Detailed(currentSetAsset).decimals());
setAssetCollateralToSell = setAssetCollateralToSell.mul(decimals).div(
DEC_18
);
rebalancingSetRedeemable = calculateSetQuantity(
setAssetCollateralToSell
);
}
/*
* @dev Calculates value of a single 1e18 Set unit in ETH terms
*/
function calculateEthValueOfOneSetUnit()
internal
view
returns (uint256 ethValue)
{
uint256 unitShares = getSetUnitShares();
uint256 rebalancingSetNaturalUnit = getSetNaturalUnit();
uint256 baseSetRequired = DEC_18.mul(unitShares).div(
rebalancingSetNaturalUnit
);
uint256 unitsOfUnderlying = getBaseSetComponentUnits();
uint256 baseSetNaturalUnit = getBaseSetNaturalUnit();
uint256 componentRequired = baseSetRequired.mul(unitsOfUnderlying).div(
baseSetNaturalUnit
);
address currentSetAsset = getAssetCurrentlyActiveInSet();
uint256 decimals = (TEN**ERC20Detailed(currentSetAsset).decimals());
componentRequired = componentRequired.mul(DEC_18).div(decimals);
bytes32 activeAssetSynthSymbol = getActiveAssetSynthSymbol();
uint256 synthUsd = getSynthPrice(activeAssetSynthSymbol);
uint256 ethUsd = getSynthPrice(seth);
ethValue = componentRequired.mul(synthUsd).div(ethUsd);
}
/*
* @dev Calculates value of Set Holdings in ETH terms
*/
function getSetHoldingsValueInWei()
public
view
returns (uint256 setValInWei)
{
uint256 setCollateralTokens = getSetCollateralTokens();
bytes32 synthSymbol = getActiveAssetSynthSymbol();
address currentSetAsset = getAssetCurrentlyActiveInSet();
uint256 synthUsd = getSynthPrice(synthSymbol);
uint256 ethUsd = getSynthPrice(seth);
uint256 decimals = (TEN**ERC20Detailed(currentSetAsset).decimals());
setCollateralTokens = setCollateralTokens.mul(DEC_18).div(decimals);
setValInWei = setCollateralTokens.mul(synthUsd).div(ethUsd);
}
function getBaseSetNaturalUnit() internal view returns (uint256) {
return getCurrentCollateralSet().naturalUnit();
}
/*
* @dev Outputs current active Set asset
* @notice xSNX contracts complex only compatible with Sets that hold a single asset at a time
*/
function getAssetCurrentlyActiveInSet() public view returns (address) {
address[] memory currentAllocation = getCurrentCollateralSet()
.getComponents();
return currentAllocation[0];
}
function getCurrentCollateralSet()
internal
view
returns (ISetAssetBaseCollateral)
{
return ISetAssetBaseCollateral(getCurrentSet());
}
function getCurrentSet() internal view returns (address) {
return ISetToken(setAddress).currentSet();
}
/*
* @dev Returns the number of underlying tokens in the current Set asset
* e.g., the contract's Set holdings are collateralized by 10.4 WETH
*/
function getSetCollateralTokens() internal view returns (uint256) {
return
getSetBalanceCollateral().mul(getBaseSetComponentUnits()).div(
getBaseSetNaturalUnit()
);
}
function getSetBalanceCollateral() internal view returns (uint256) {
uint256 unitShares = getSetUnitShares();
uint256 naturalUnit = getSetNaturalUnit();
return getContractSetBalance().mul(unitShares).div(naturalUnit);
}
function getSetUnitShares() internal view returns (uint256) {
return ISetToken(setAddress).unitShares();
}
function getSetNaturalUnit() internal view returns (uint256) {
return ISetToken(setAddress).naturalUnit();
}
function getContractSetBalance() internal view returns (uint256) {
return IERC20(setAddress).balanceOf(xSNXAdminInstance);
}
function getBaseSetComponentUnits() internal view returns (uint256) {
return ISetAssetBaseCollateral(getCurrentSet()).getUnits()[0];
}
/* ========================================================================================= */
/* Synthetix */
/* ========================================================================================= */
function getSusdBalance() public view returns (uint256) {
return IERC20(susdAddress).balanceOf(xSNXAdminInstance);
}
function getSnxBalance() public view returns (uint256) {
return getSnxBalanceOwned().add(getSnxBalanceEscrowed());
}
function getSnxBalanceOwned() internal view returns (uint256) {
return
IERC20(addressResolver.getAddress(synthetixName)).balanceOf(
xSNXAdminInstance
);
}
function getSnxBalanceEscrowed() internal view returns (uint256) {
return
IRewardEscrowV2(addressResolver.getAddress(rewardEscrowV2Name))
.balanceOf(xSNXAdminInstance);
}
function getContractEscrowedSnxValue() internal view returns (uint256) {
return getSnxBalanceEscrowed().mul(getSnxPrice()).div(DEC_18);
}
function getContractOwnedSnxValue() internal view returns (uint256) {
return getSnxBalanceOwned().mul(getSnxPrice()).div(DEC_18);
}
function getSnxPrice() internal view returns (uint256) {
(uint256 rate, uint256 time) = IExchangeRates(
addressResolver.getAddress(exchangeRatesName)
)
.rateAndUpdatedTime(snx);
require(time.add(RATE_STALE_TIME_NEW) > block.timestamp, "Rate stale");
return rate;
}
function getSynthPrice(bytes32 synth) internal view returns (uint256) {
(uint256 rate, uint256 time) = IExchangeRates(
addressResolver.getAddress(exchangeRatesName)
)
.rateAndUpdatedTime(synth);
if (synth != susd) {
require(time.add(RATE_STALE_TIME_NEW) > block.timestamp, "Rate stale");
}
return rate;
}
/*
* @dev Converts sUSD debt value into ETH terms
* @param debtValue: sUSD-denominated debt value
*/
function calculateDebtValueInWei(uint256 debtValue)
internal
view
returns (uint256 debtBalanceInWei)
{
uint256 ethUsd = getSynthPrice(seth);
debtBalanceInWei = debtValue.mul(DEC_18).div(ethUsd);
}
function getContractDebtValue() internal view returns (uint256) {
return
ISynthetix(addressResolver.getAddress(synthetixName)).debtBalanceOf(
xSNXAdminInstance,
susd
);
}
/*
* @notice Returns inverse of target C-RATIO
*/
function getIssuanceRatio() internal view returns (uint256) {
return
ISystemSettings(addressResolver.getAddress(systemSettingsName))
.issuanceRatio();
}
/*
* @notice Returns NAV contribution of SNX holdings in USD terms
*/
function getContractSnxValue() internal view returns (uint256) {
return getSnxBalance().mul(getSnxPrice()).div(DEC_18);
}
/* ========================================================================================= */
/* Burning sUSD */
/* ========================================================================================= */
/*
* @dev Calculates sUSD to burn to restore C-RATIO
* @param snxValueHeld: USD value of SNX
* @param contractDebtValue: USD value of sUSD debt
* @param issuanceRatio: Synthetix C-RATIO requirement
*/
function calculateSusdToBurnToFixRatio(
uint256 snxValueHeld,
uint256 contractDebtValue,
uint256 issuanceRatio
) internal pure returns (uint256) {
uint256 subtractor = issuanceRatio.mul(snxValueHeld).div(DEC_18);
if (subtractor > contractDebtValue) return 0;
return contractDebtValue.sub(subtractor);
}
/*
* @dev Calculates sUSD to burn to restore C-RATIO
*/
function calculateSusdToBurnToFixRatioExternal()
public
view
returns (uint256)
{
uint256 snxValueHeld = getContractSnxValue();
uint256 debtValue = getContractDebtValue();
uint256 issuanceRatio = getIssuanceRatio();
return
calculateSusdToBurnToFixRatio(
snxValueHeld,
debtValue,
issuanceRatio
);
}
/*
* @dev Calculates sUSD to burn to eclipse value of escrowed SNX
* @notice Synthetix system requires escrowed SNX to be "unlocked" first
* @param issuanceRatio: Synthetix C-RATIO requirement
*/
function calculateSusdToBurnToEclipseEscrowed(uint256 issuanceRatio)
public
view
returns (uint256)
{
uint256 escrowedSnxValue = getContractEscrowedSnxValue();
if (escrowedSnxValue == 0) return 0;
return escrowedSnxValue.mul(issuanceRatio).div(DEC_18);
}
/*
* @dev Helper function to calculate sUSD burn required for a potential redemption
* @param tokensToRedeem: potential tokens to burn
* @param totalSupply: xSNX.totalSupply()
* @param contractDebtValue: sUSD debt value
* @param issuanceRatio: Synthetix C-RATIO requirement
*/
function calculateSusdToBurnForRedemption(
uint256 tokensToRedeem,
uint256 totalSupply,
uint256 contractDebtValue,
uint256 issuanceRatio
) public view returns (uint256 susdToBurn) {
uint256 nonEscrowedSnxValue = getContractOwnedSnxValue();
uint256 lockedSnxValue = contractDebtValue.mul(DEC_18).div(
issuanceRatio
);
uint256 valueOfSnxToSell = nonEscrowedSnxValue.mul(tokensToRedeem).div(
totalSupply
);
susdToBurn = (
lockedSnxValue.add(valueOfSnxToSell).sub(nonEscrowedSnxValue)
)
.mul(issuanceRatio)
.div(DEC_18);
}
/* ========================================================================================= */
/* Rebalances */
/* ========================================================================================= */
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceTowardsHedge()
*/
function calculateAssetChangesForRebalanceToHedge()
internal
view
returns (uint256 totalSusdToBurn, uint256 snxToSell)
{
uint256 snxValueHeld = getContractSnxValue();
uint256 debtValueInUsd = getContractDebtValue();
uint256 issuanceRatio = getIssuanceRatio();
uint256 susdToBurnToFixRatio = calculateSusdToBurnToFixRatio(
snxValueHeld,
debtValueInUsd,
issuanceRatio
);
uint256 susdToBurnToEclipseEscrowed
= calculateSusdToBurnToEclipseEscrowed(issuanceRatio);
uint256 hedgeAssetsValueInUsd = calculateHedgeAssetsValueInUsd();
uint256 valueToUnlockInUsd = debtValueInUsd.sub(hedgeAssetsValueInUsd);
uint256 susdToBurnToUnlockTransfer = valueToUnlockInUsd
.mul(issuanceRatio)
.div(DEC_18);
totalSusdToBurn = (
susdToBurnToFixRatio.add(susdToBurnToEclipseEscrowed).add(
susdToBurnToUnlockTransfer
)
);
snxToSell = valueToUnlockInUsd.mul(DEC_18).div(getSnxPrice());
}
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceTowardsSnx()
*/
function calculateAssetChangesForRebalanceToSnx()
public
view
returns (uint256 setToSell)
{
(
uint256 debtValueInWei,
uint256 hedgeAssetsBalance
) = getRebalanceUtils();
uint256 setValueToSell = hedgeAssetsBalance.sub(debtValueInWei);
uint256 ethValueOfOneSet = calculateEthValueOfOneSetUnit();
setToSell = setValueToSell.mul(DEC_18).div(ethValueOfOneSet);
// Set quantity must be multiple of natural unit
uint256 naturalUnit = getSetNaturalUnit();
setToSell = setToSell.div(naturalUnit).mul(naturalUnit);
}
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceTowardsSnx()
*/
function getRebalanceTowardsSnxUtils()
public
view
returns (uint256 setToSell, address activeAsset)
{
setToSell = calculateAssetChangesForRebalanceToSnx();
activeAsset = getAssetCurrentlyActiveInSet();
}
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceTowardsSnx(), xSNXAdmin.rebalanceTowardsHedge()
* @dev Denominated in ETH terms
*/
function getRebalanceUtils()
public
view
returns (uint256 debtValueInWei, uint256 hedgeAssetsBalance)
{
uint256 setHoldingsInWei = getSetHoldingsValueInWei();
uint256 ethBalance = getEthBalance();
uint256 debtValue = getContractDebtValue();
debtValueInWei = calculateDebtValueInWei(debtValue);
hedgeAssetsBalance = setHoldingsInWei.add(ethBalance);
}
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceTowardsHedge()
* @dev Denominated in USD terms
*/
function calculateHedgeAssetsValueInUsd()
internal
view
returns (uint256 hedgeAssetsValueInUsd)
{
address currentSetAsset = getAssetCurrentlyActiveInSet();
uint256 decimals = (TEN**ERC20Detailed(currentSetAsset).decimals());
uint256 setCollateralTokens = getSetCollateralTokens();
setCollateralTokens = setCollateralTokens.mul(DEC_18).div(decimals);
bytes32 activeAssetSynthSymbol = getActiveAssetSynthSymbol();
uint256 synthUsd = getSynthPrice(activeAssetSynthSymbol);
uint256 setValueUsd = setCollateralTokens.mul(synthUsd).div(DEC_18);
uint256 ethBalance = getEthBalance();
uint256 ethUsd = getSynthPrice(seth);
uint256 ethValueUsd = ethBalance.mul(ethUsd).div(DEC_18);
hedgeAssetsValueInUsd = setValueUsd.add(ethValueUsd);
}
/*
* @dev Helper function to determine whether xSNXAdmin.rebalanceTowardsSnx() is required
*/
function isRebalanceTowardsSnxRequired() public view returns (bool) {
(
uint256 debtValueInWei,
uint256 hedgeAssetsBalance
) = getRebalanceUtils();
if (
debtValueInWei.mul(REBALANCE_THRESHOLD).div(PERCENT) <
hedgeAssetsBalance
) {
return true;
}
return false;
}
/*
* @dev Helper function to determine whether xSNXAdmin.rebalanceTowardsHedge() is required
*/
function isRebalanceTowardsHedgeRequired() public view returns (bool) {
(
uint256 debtValueInWei,
uint256 hedgeAssetsBalance
) = getRebalanceUtils();
if (
hedgeAssetsBalance.mul(REBALANCE_THRESHOLD).div(PERCENT) <
debtValueInWei
) {
return true;
}
return false;
}
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceTowardsHedge()
* @notice Will fail if !isRebalanceTowardsHedgeRequired()
*/
function getRebalanceTowardsHedgeUtils()
public
view
returns (
uint256,
uint256,
address
)
{
(
uint256 totalSusdToBurn,
uint256 snxToSell
) = calculateAssetChangesForRebalanceToHedge();
address activeAsset = getAssetCurrentlyActiveInSet();
return (totalSusdToBurn, snxToSell, activeAsset);
}
/*
* @dev Helper for `hedge` function
* @dev Determines share of sUSD to allocate to ETH
* @dev Implicitly determines Set allocation as well
* @param susdBal: sUSD balance post minting
*/
function getEthAllocationOnHedge(uint256 susdBal)
public
view
returns (uint256 ethAllocation)
{
uint256 ethUsd = getSynthPrice(seth);
uint256 setHoldingsInUsd = getSetHoldingsValueInWei().mul(ethUsd).div(
DEC_18
);
uint256 ethBalInUsd = getEthBalance().mul(ethUsd).div(DEC_18);
uint256 hedgeAssets = setHoldingsInUsd.add(ethBalInUsd);
if (ethBalInUsd.mul(ETH_TARGET) >= hedgeAssets.add(susdBal)) {
// full bal directed toward Set
// eth allocation is 0
} else if ((ethBalInUsd.add(susdBal)).mul(ETH_TARGET) < hedgeAssets) {
// full bal directed toward Eth
ethAllocation = susdBal;
} else {
// fractionate allocation
ethAllocation = ((hedgeAssets.add(susdBal)).div(ETH_TARGET)).sub(
ethBalInUsd
);
}
}
/*
* @dev Helper function to facilitate xSNXAdmin.rebalanceSetToEth()
*/
function calculateSetToSellForRebalanceSetToEth()
public
view
returns (uint256 setQuantityToSell)
{
uint256 setHoldingsInWei = getSetHoldingsValueInWei();
uint256 ethBal = getEthBalance();
uint256 hedgeAssets = setHoldingsInWei.add(ethBal);
require(
ethBal.mul(ETH_TARGET) < hedgeAssets,
"Rebalance not necessary"
);
uint256 ethToAdd = ((hedgeAssets.div(ETH_TARGET)).sub(ethBal));
setQuantityToSell = getContractSetBalance().mul(ethToAdd).div(
setHoldingsInWei
);
uint256 naturalUnit = getSetNaturalUnit();
setQuantityToSell = setQuantityToSell.div(naturalUnit).mul(naturalUnit);
}
/* ========================================================================================= */
/* Address Setters */
/* ========================================================================================= */
function setAdminInstanceAddress(address _xSNXAdminInstance)
public
onlyOwner
{
if (xSNXAdminInstance == address(0)) {
xSNXAdminInstance = _xSNXAdminInstance;
}
}
function setCurve(
address curvePoolAddress,
int128 _usdcIndex,
int128 _susdIndex
) public onlyOwner {
if (address(curveFi) == address(0)) {
// if initial set on deployment, immediately activate Curve address
curveFi = ICurveFi(curvePoolAddress);
nextCurveAddress = curvePoolAddress;
} else {
// if updating Curve address (i.e., not initial setting of address on deployment),
// store nextCurveAddress but don't activate until addressValidator has confirmed
nextCurveAddress = curvePoolAddress;
}
usdcIndex = _usdcIndex;
susdIndex = _susdIndex;
}
/* ========================================================================================= */
/* Utils */
/* ========================================================================================= */
// admin on deployment approve [snx, susd, setComponentA, setComponentB]
function approveKyber(address tokenAddress) public onlyOwner {
IERC20(tokenAddress).approve(address(kyberNetworkProxy), MAX_UINT);
}
// admin on deployment approve [susd, usdc]
function approveCurve(address tokenAddress) public onlyOwner {
IERC20(tokenAddress).approve(address(curveFi), MAX_UINT);
}
function confirmCurveAddress(address _nextCurveAddress) public {
require(msg.sender == addressValidator, "Incorrect caller");
require(nextCurveAddress == _nextCurveAddress, "Addresses don't match");
curveFi = ICurveFi(nextCurveAddress);
}
function() external payable {}
}
// File: contracts/helpers/Pausable.sol
pragma solidity ^0.5.15;
/* Adapted from OpenZeppelin */
contract Pausable is Initializable {
/**
* @dev Emitted when the pause is triggered by a pauser.
*/
event Paused();
/**
* @dev Emitted when the pause is lifted by a pauser.
*/
event Unpaused();
bool private _paused;
address public pauser;
/**
* @dev Initializes the contract in unpaused state. Assigns the Pauser role
* to the deployer.
*/
constructor () internal {
_paused = false;
pauser = msg.sender;
}
/**
* @dev Initializes the contract in unpaused state. Assigns the Pauser role
* to the deployer. This function is called when the contract is used in a upgradeable context.
*/
function initialize(address sender) public initializer {
_paused = false;
pauser = sender;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!_paused, "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(_paused, "Pausable: not paused");
_;
}
/**
* @dev Called by a pauser to pause, triggers stopped state.
*/
function pause() public onlyPauser whenNotPaused {
_paused = true;
emit Paused();
}
/**
* @dev Called by a pauser to unpause, returns to normal state.
*/
function unpause() public onlyPauser whenPaused {
_paused = false;
emit Unpaused();
}
modifier onlyPauser {
require(msg.sender == pauser, "Don't have rights");
_;
}
}
// File: contracts/interface/IxSNXAdmin.sol
pragma solidity 0.5.15;
contract IxSNXAdmin {
function sendEthOnRedemption(uint valueToRedeem) external;
}
// File: contracts/xSNX.sol
pragma solidity 0.5.15;
contract xSNX is ERC20, ERC20Detailed, Pausable, Ownable {
TradeAccounting private tradeAccounting;
IKyberNetworkProxy private kyberNetworkProxy;
address xsnxAdmin;
address snxAddress;
address susdAddress;
uint256 public withdrawableEthFees;
function initialize(
address payable _tradeAccountingAddress,
address _kyberProxyAddress,
address _snxAddress,
address _susdAddress,
address _xsnxAdmin,
address _ownerAddress,
uint256 _mintFeeDivisor,
uint256 _burnFeeDivisor,
uint256 _claimFeeDivisor,
uint256 _initialMint
) public initializer {
Ownable.initialize(_ownerAddress);
ERC20Detailed.initialize("xSNX", "xSNXa", 18);
Pausable.initialize(_ownerAddress);
tradeAccounting = TradeAccounting(_tradeAccountingAddress);
kyberNetworkProxy = IKyberNetworkProxy(_kyberProxyAddress);
snxAddress = _snxAddress;
susdAddress = _susdAddress;
xsnxAdmin = _xsnxAdmin;
_setFeeDivisors(_mintFeeDivisor, _burnFeeDivisor, _claimFeeDivisor);
_mint(msg.sender, _initialMint);
}
event Mint(
address indexed user,
uint256 timestamp,
uint256 valueSent,
uint256 mintAmount,
bool mintWithEth
);
event Burn(
address indexed user,
uint256 timestamp,
uint256 burnAmount,
uint256 valueToSend
);
event WithdrawFees(
uint256 ethAmount,
uint256 susdAmount,
uint256 snxAmount
);
struct FeeDivisors {
uint256 mintFee;
uint256 burnFee;
uint256 claimFee;
}
FeeDivisors public feeDivisors;
// addresses are locked from transfer after minting or burning
uint256 private constant BLOCK_LOCK_COUNT = 6;
// last block for which this address is timelocked
mapping(address => uint256) public lastLockedBlock;
/*
* @notice Mint new xSNX tokens from the contract by sending ETH
* @dev Exchanges ETH for SNX
* @dev Min rate ETH/SNX sourced from Kyber in JS
* @dev: Calculates overall fund NAV in ETH terms, using ETH/SNX implicit conversion rate
* or ETH/SNX price (via SNX oracle) in case of allocateToEth
* @dev: Mints/distributes new xSNX tokens based on contribution to NAV
* @param: minRate: kyberProxy.getExpectedRate eth=>snx
*/
function mint(uint256 minRate) external payable whenNotPaused notLocked(msg.sender) {
require(msg.value > 0, "Must send ETH");
lock(msg.sender);
uint256 fee = calculateFee(msg.value, feeDivisors.mintFee);
uint256 ethContribution = msg.value.sub(fee);
uint256 snxBalanceBefore = tradeAccounting.getSnxBalance();
uint256 totalSupply = totalSupply();
(bool allocateToEth, uint256 nonSnxAssetValue) = tradeAccounting
.getMintWithEthUtils(totalSupply);
if (!allocateToEth) {
uint256 snxAcquired = kyberNetworkProxy.swapEtherToToken.value(
ethContribution
)(ERC20(snxAddress), minRate);
require(
IERC20(snxAddress).transfer(xsnxAdmin, snxAcquired),
"Transfer failed"
);
} else {
(bool success, ) = xsnxAdmin.call.value(ethContribution)("");
require(success, "Transfer failed");
}
uint256 mintAmount = tradeAccounting.calculateTokensToMintWithEth(
snxBalanceBefore,
ethContribution,
nonSnxAssetValue,
totalSupply
);
emit Mint(msg.sender, block.timestamp, msg.value, mintAmount, true);
return super._mint(msg.sender, mintAmount);
}
/*
* @notice Mint new xSNX tokens from the contract by sending SNX
* @notice Won't run without ERC20 approval
* @dev: Calculates overall fund NAV in ETH terms, using ETH/SNX price (via SNX oracle)
* @dev: Mints/distributes new xSNX tokens based on contribution to NAV
* @param: snxAmount: SNX to contribute
*/
function mintWithSnx(uint256 snxAmount) external whenNotPaused notLocked(msg.sender) {
require(snxAmount > 0, "Must send SNX");
lock(msg.sender);
uint256 snxBalanceBefore = tradeAccounting.getSnxBalance();
uint256 fee = calculateFee(snxAmount, feeDivisors.mintFee);
uint256 snxContribution = snxAmount.sub(fee);
require(
IERC20(snxAddress).transferFrom(msg.sender, address(this), fee),
"Transfer failed"
);
require(
IERC20(snxAddress).transferFrom(
msg.sender,
xsnxAdmin,
snxContribution
),
"Transfer failed"
);
uint256 mintAmount = tradeAccounting.calculateTokensToMintWithSnx(
snxBalanceBefore,
snxContribution,
totalSupply()
);
emit Mint(
msg.sender,
block.timestamp,
snxContribution,
mintAmount,
false
);
return super._mint(msg.sender, mintAmount);
}
/*
* @notice Redeems and burns xSNX tokens and sends ETH to user
* @dev Checks if ETH reserve is sufficient to settle redeem obligation
* @dev Will only redeem if ETH reserve is sufficient
* @param tokensToRedeem
*/
function burn(uint256 tokensToRedeem) external notLocked(msg.sender) {
require(tokensToRedeem > 0, "Must burn tokens");
lock(msg.sender);
uint256 valueToRedeem = tradeAccounting.calculateRedemptionValue(
totalSupply(),
tokensToRedeem
);
require(
tradeAccounting.getEthBalance() >= valueToRedeem,
"Redeem amount exceeds available liquidity"
);
IxSNXAdmin(xsnxAdmin).sendEthOnRedemption(valueToRedeem);
uint256 valueToSend = valueToRedeem.sub(
calculateFee(valueToRedeem, feeDivisors.burnFee)
);
super._burn(msg.sender, tokensToRedeem);
emit Burn(msg.sender, block.timestamp, tokensToRedeem, valueToSend);
(bool success, ) = msg.sender.call.value(valueToSend)("");
require(success, "Burn transfer failed");
}
function transfer(address recipient, uint256 amount)
public
notLocked(msg.sender)
returns (bool)
{
return super.transfer(recipient, amount);
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public notLocked(sender) returns (bool) {
return super.transferFrom(sender, recipient, amount);
}
function calculateFee(uint256 _value, uint256 _feeDivisor)
internal
pure
returns (uint256 fee)
{
if (_feeDivisor > 0) {
fee = _value.div(_feeDivisor);
}
}
/*
* @notice Inverse of fee i.e., a fee divisor of 100 == 1%
* @notice Three fee types
* @dev Mint fee 0 or <= 2%
* @dev Burn fee 0 or <= 1%
* @dev Claim fee 0 <= 4%
*/
function setFeeDivisors(
uint256 mintFeeDivisor,
uint256 burnFeeDivisor,
uint256 claimFeeDivisor
) public onlyOwner {
_setFeeDivisors(mintFeeDivisor, burnFeeDivisor, claimFeeDivisor);
}
function _setFeeDivisors(
uint256 _mintFeeDivisor,
uint256 _burnFeeDivisor,
uint256 _claimFeeDivisor
) private {
require(_mintFeeDivisor == 0 || _mintFeeDivisor >= 50, "Invalid fee");
require(_burnFeeDivisor == 0 || _burnFeeDivisor >= 100, "Invalid fee");
require(_claimFeeDivisor >= 25, "Invalid fee");
feeDivisors.mintFee = _mintFeeDivisor;
feeDivisors.burnFee = _burnFeeDivisor;
feeDivisors.claimFee = _claimFeeDivisor;
}
/*
* @notice Withdraws ETH, sUSD and SNX fees to owner address
*/
function withdrawFees() public onlyOwner {
uint256 ethFeesToWithdraw = address(this).balance;
uint256 susdFeesToWithdraw = IERC20(susdAddress).balanceOf(
address(this)
);
uint256 snxFeesToWithdraw = IERC20(snxAddress).balanceOf(address(this));
(bool success, ) = msg.sender.call.value(ethFeesToWithdraw)("");
require(success, "Transfer failed");
IERC20(susdAddress).transfer(msg.sender, susdFeesToWithdraw);
IERC20(snxAddress).transfer(msg.sender, snxFeesToWithdraw);
emit WithdrawFees(
ethFeesToWithdraw,
susdFeesToWithdraw,
snxFeesToWithdraw
);
}
/*
* @notice Emergency function in case of errant transfer of
* xSNX token directly to contract
*/
function withdrawNativeToken() public onlyOwner {
uint256 tokenBal = balanceOf(address(this));
if (tokenBal > 0) {
IERC20(address(this)).transfer(msg.sender, tokenBal);
}
}
/*
* @dev Helper function for xSNXAdmin to calculate and
* transfer claim fees
*/
function getClaimFeeDivisor() public view returns (uint256) {
return feeDivisors.claimFee;
}
/**
* BlockLock logic: Implements locking of mint, burn, transfer and transferFrom
* functions via a notLocked modifier.
* Functions are locked per address.
*/
modifier notLocked(address lockedAddress) {
require(
lastLockedBlock[lockedAddress] <= block.number,
"Function is temporarily locked for this address"
);
_;
}
/**
* @dev Lock mint, burn, transfer and transferFrom functions
* for _address for BLOCK_LOCK_COUNT blocks
*/
function lock(address _address) private {
lastLockedBlock[_address] = block.number + BLOCK_LOCK_COUNT;
}
function() external payable {
require(msg.sender == xsnxAdmin, "Invalid send");
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"valueToSend","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"valueSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"mintWithEth","type":"bool"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"susdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"snxAmount","type":"uint256"}],"name":"WithdrawFees","type":"event"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokensToRedeem","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeDivisors","outputs":[{"internalType":"uint256","name":"mintFee","type":"uint256"},{"internalType":"uint256","name":"burnFee","type":"uint256"},{"internalType":"uint256","name":"claimFee","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getClaimFeeDivisor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_tradeAccountingAddress","type":"address"},{"internalType":"address","name":"_kyberProxyAddress","type":"address"},{"internalType":"address","name":"_snxAddress","type":"address"},{"internalType":"address","name":"_susdAddress","type":"address"},{"internalType":"address","name":"_xsnxAdmin","type":"address"},{"internalType":"address","name":"_ownerAddress","type":"address"},{"internalType":"uint256","name":"_mintFeeDivisor","type":"uint256"},{"internalType":"uint256","name":"_burnFeeDivisor","type":"uint256"},{"internalType":"uint256","name":"_claimFeeDivisor","type":"uint256"},{"internalType":"uint256","name":"_initialMint","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastLockedBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"minRate","type":"uint256"}],"name":"mint","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"snxAmount","type":"uint256"}],"name":"mintWithSnx","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pauser","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"mintFeeDivisor","type":"uint256"},{"internalType":"uint256","name":"burnFeeDivisor","type":"uint256"},{"internalType":"uint256","name":"claimFeeDivisor","type":"uint256"}],"name":"setFeeDivisors","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawFees","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawNativeToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"withdrawableEthFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052609d80546001600160a81b03191661010033021790556130f4806100296000396000f3fe6080604052600436106101e35760003560e01c8063715018a611610102578063a457c2d711610095578063e7654b3c11610064578063e7654b3c1461053f578063f00703331461055f578063f09ddae71461057f578063f2fde38b14610594576101e3565b8063a457c2d7146104bf578063a9059cbb146104df578063c4d66de8146104ff578063dd62ed3e1461051f576101e3565b806395d89b41116100d157806395d89b41146104625780639f3e8b34146104775780639fd0506d14610497578063a0712d68146104ac576101e3565b8063715018a6146104015780638456cb59146104165780638da5cb5b1461042b5780638f32d59b1461044d576101e3565b80633986de6a1161017a5780635a18664c116101495780635a18664c146103935780635c975abb146103a8578063629c577e146103bd57806370a08231146103e1576101e3565b80633986de6a146103295780633f4ba83a1461034957806342966c681461035e578063476343ee1461037e576101e3565b80631838d85c116101b65780631838d85c146102b257806323b872dd146102c7578063313ce567146102e75780633950935114610309576101e3565b806306fdde0314610218578063095ea7b3146102435780631624f6c61461027057806318160ddd14610290575b60d3546001600160a01b031633146102165760405162461bcd60e51b815260040161020d90612d27565b60405180910390fd5b005b34801561022457600080fd5b5061022d6105b4565b60405161023a9190612cf6565b60405180910390f35b34801561024f57600080fd5b5061026361025e36600461257f565b61064b565b60405161023a9190612cda565b34801561027c57600080fd5b5061021661028b3660046125fd565b610669565b34801561029c57600080fd5b506102a5610726565b60405161023a9190612e67565b3480156102be57600080fd5b506102a561072c565b3480156102d357600080fd5b506102636102e2366004612532565b610732565b3480156102f357600080fd5b506102fc610780565b60405161023a9190612f08565b34801561031557600080fd5b5061026361032436600461257f565b610789565b34801561033557600080fd5b5061021661034436600461241f565b6107e2565b34801561035557600080fd5b50610216610931565b34801561036a57600080fd5b5061021661037936600461266f565b6109b7565b34801561038a57600080fd5b50610216610c97565b34801561039f57600080fd5b50610216610f8f565b3480156103b457600080fd5b50610263611042565b3480156103c957600080fd5b506103d261104b565b60405161023a93929190612e83565b3480156103ed57600080fd5b506102a56103fc366004612401565b611057565b34801561040d57600080fd5b50610216611072565b34801561042257600080fd5b506102166110e0565b34801561043757600080fd5b5061044061116a565b60405161023a9190612c52565b34801561045957600080fd5b50610263611179565b34801561046e57600080fd5b5061022d61119f565b34801561048357600080fd5b506102a5610492366004612401565b611200565b3480156104a357600080fd5b50610440611212565b6102166104ba36600461266f565b611226565b3480156104cb57600080fd5b506102636104da36600461257f565b61168b565b3480156104eb57600080fd5b506102636104fa36600461257f565b6116f9565b34801561050b57600080fd5b5061021661051a366004612401565b61173d565b34801561052b57600080fd5b506102a561053a3660046124f8565b61180f565b34801561054b57600080fd5b5061021661055a3660046126ab565b61183a565b34801561056b57600080fd5b5061021661057a36600461266f565b61186e565b34801561058b57600080fd5b506102a5611bbf565b3480156105a057600080fd5b506102166105af366004612401565b611bc5565b60688054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106405780601f1061061557610100808354040283529160200191610640565b820191906000526020600020905b81548152906001019060200180831161062357829003601f168201915b505050505090505b90565b600061065f610658611bf2565b8484611bf6565b5060015b92915050565b600054610100900460ff16806106825750610682611caa565b80610690575060005460ff16155b6106ac5760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff161580156106d7576000805460ff1961ff0019909116610100171660011790555b83516106ea9060689060208701906122de565b5082516106fe9060699060208601906122de565b50606a805460ff191660ff84161790558015610720576000805461ff00191690555b50505050565b60355490565b60d65481565b6001600160a01b038316600090815260da6020526040812054849043101561076c5760405162461bcd60e51b815260040161020d90612da7565b610777858585611cb0565b95945050505050565b606a5460ff1690565b600061065f610796611bf2565b846107dd85603460006107a7611bf2565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff611d3916565b611bf6565b600054610100900460ff16806107fb57506107fb611caa565b80610809575060005460ff16155b6108255760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff16158015610850576000805460ff1961ff0019909116610100171660011790555b6108598661173d565b61089e604051806040016040528060048152602001630f0a69cb60e31b8152506040518060400160405280600581526020016478534e586160d81b8152506012610669565b6108a786611d5e565b60d180546001600160a01b03199081166001600160a01b038e81169190911790925560d2805482168d841617905560d4805482168c841617905560d5805482168b841617905560d38054909116918916919091179055610908858585611e00565b6109123383611e83565b8015610924576000805461ff00191690555b5050505050505050505050565b609d5461010090046001600160a01b031633146109605760405162461bcd60e51b815260040161020d90612d77565b609d5460ff166109825760405162461bcd60e51b815260040161020d90612d17565b609d805460ff191690556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a1565b33600081815260da60205260409020544310156109e65760405162461bcd60e51b815260040161020d90612da7565b60008211610a065760405162461bcd60e51b815260040161020d90612e27565b610a0f33611f43565b60d1546000906001600160a01b03166318d814ba610a2b610726565b856040518363ffffffff1660e01b8152600401610a49929190612e75565b60206040518083038186803b158015610a6157600080fd5b505afa158015610a75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a99919081019061268d565b90508060d160009054906101000a90046001600160a01b03166001600160a01b03166370ed0ada6040518163ffffffff1660e01b815260040160206040518083038186803b158015610aea57600080fd5b505afa158015610afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b22919081019061268d565b1015610b405760405162461bcd60e51b815260040161020d90612e47565b60d35460405163a965072760e01b81526001600160a01b039091169063a965072790610b70908490600401612e67565b600060405180830381600087803b158015610b8a57600080fd5b505af1158015610b9e573d6000803e3d6000fd5b505050506000610bc3610bb68360d760010154611f62565b839063ffffffff611f7a16565b9050610bcf3385611fbc565b336001600160a01b03167f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b4644428684604051610c0c93929190612e83565b60405180910390a26000336001600160a01b031682604051610c2d90612c47565b60006040518083038185875af1925050503d8060008114610c6a576040519150601f19603f3d011682016040523d82523d6000602084013e610c6f565b606091505b5050905080610c905760405162461bcd60e51b815260040161020d90612d87565b5050505050565b610c9f611179565b610cbb5760405162461bcd60e51b815260040161020d90612dc7565b60d5546040516370a0823160e01b815247916000916001600160a01b03909116906370a0823190610cf0903090600401612c60565b60206040518083038186803b158015610d0857600080fd5b505afa158015610d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d40919081019061268d565b60d4546040516370a0823160e01b81529192506000916001600160a01b03909116906370a0823190610d76903090600401612c60565b60206040518083038186803b158015610d8e57600080fd5b505afa158015610da2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610dc6919081019061268d565b90506000336001600160a01b031684604051610de190612c47565b60006040518083038185875af1925050503d8060008114610e1e576040519150601f19603f3d011682016040523d82523d6000602084013e610e23565b606091505b5050905080610e445760405162461bcd60e51b815260040161020d90612d57565b60d55460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90610e769033908790600401612cb1565b602060405180830381600087803b158015610e9057600080fd5b505af1158015610ea4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ec891908101906125af565b5060d45460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90610efb9033908690600401612cb1565b602060405180830381600087803b158015610f1557600080fd5b505af1158015610f29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f4d91908101906125af565b507fa80975b2ffacb3d3f1fcfd13863288991d27ae8e617c147e7c83d240b82bd1f6848484604051610f8193929190612e83565b60405180910390a150505050565b610f97611179565b610fb35760405162461bcd60e51b815260040161020d90612dc7565b6000610fbe30611057565b9050801561103f5760405163a9059cbb60e01b8152309063a9059cbb90610feb9033908590600401612cb1565b602060405180830381600087803b15801561100557600080fd5b505af1158015611019573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061103d91908101906125af565b505b50565b609d5460ff1690565b60d75460d85460d95483565b6001600160a01b031660009081526033602052604090205490565b61107a611179565b6110965760405162461bcd60e51b815260040161020d90612dc7565b609e546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3609e80546001600160a01b0319169055565b609d5461010090046001600160a01b0316331461110f5760405162461bcd60e51b815260040161020d90612d77565b609d5460ff16156111325760405162461bcd60e51b815260040161020d90612d97565b609d805460ff191660011790556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a1565b609e546001600160a01b031690565b609e546000906001600160a01b0316611190611bf2565b6001600160a01b031614905090565b60698054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106405780601f1061061557610100808354040283529160200191610640565b60da6020526000908152604090205481565b609d5461010090046001600160a01b031681565b609d5460ff16156112495760405162461bcd60e51b815260040161020d90612d97565b33600081815260da60205260409020544310156112785760405162461bcd60e51b815260040161020d90612da7565b600034116112985760405162461bcd60e51b815260040161020d90612e37565b6112a133611f43565b60006112b23460d760000154611f62565b905060006112c6348363ffffffff611f7a16565b9050600060d160009054906101000a90046001600160a01b03166001600160a01b03166389afb5b76040518163ffffffff1660e01b815260040160206040518083038186803b15801561131857600080fd5b505afa15801561132c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611350919081019061268d565b9050600061135c610726565b60d154604051634ebb2f5360e11b815291925060009182916001600160a01b031690639d765ea690611392908690600401612e67565b604080518083038186803b1580156113a957600080fd5b505afa1580156113bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113e191908101906125cd565b91509150816115225760d25460d454604051633d15022b60e11b81526000926001600160a01b0390811692637a2a0456928a926114249216908e90600401612ce8565b6020604051808303818588803b15801561143d57600080fd5b505af1158015611451573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250611476919081019061268d565b60d45460d35460405163a9059cbb60e01b81529293506001600160a01b039182169263a9059cbb926114ae9216908590600401612ccc565b602060405180830381600087803b1580156114c857600080fd5b505af11580156114dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061150091908101906125af565b61151c5760405162461bcd60e51b815260040161020d90612d57565b506115a5565b60d3546040516000916001600160a01b031690879061154090612c47565b60006040518083038185875af1925050503d806000811461157d576040519150601f19603f3d011682016040523d82523d6000602084013e611582565b606091505b50509050806115a35760405162461bcd60e51b815260040161020d90612d57565b505b60d154604051630748416d60e41b81526000916001600160a01b03169063748416d0906115dc9088908a9087908a90600401612ed3565b60206040518083038186803b1580156115f457600080fd5b505afa158015611608573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061162c919081019061268d565b9050336001600160a01b03167f5b5de75983fe8891c65234472ee1805018163815d4c146f10bf176b736093c09423484600160405161166e9493929190612e9e565b60405180910390a26116803382611e83565b505050505050505050565b600061065f611698611bf2565b846107dd8560405180606001604052806025815260200161308d60259139603460006116c2611bf2565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61209216565b33600081815260da602052604081205490919043101561172b5760405162461bcd60e51b815260040161020d90612da7565b61173584846120be565b949350505050565b600054610100900460ff16806117565750611756611caa565b80611764575060005460ff16155b6117805760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff161580156117ab576000805460ff1961ff0019909116610100171660011790555b609e80546001600160a01b0319166001600160a01b0384811691909117918290556040519116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3801561103d576000805461ff00191690555050565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b611842611179565b61185e5760405162461bcd60e51b815260040161020d90612dc7565b611869838383611e00565b505050565b609d5460ff16156118915760405162461bcd60e51b815260040161020d90612d97565b33600081815260da60205260409020544310156118c05760405162461bcd60e51b815260040161020d90612da7565b600082116118e05760405162461bcd60e51b815260040161020d90612e07565b6118e933611f43565b60d154604080516389afb5b760e01b815290516000926001600160a01b0316916389afb5b7916004808301926020929190829003018186803b15801561192e57600080fd5b505afa158015611942573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611966919081019061268d565b905060006119798460d760000154611f62565b9050600061198d858363ffffffff611f7a16565b60d4546040516323b872dd60e01b81529192506001600160a01b0316906323b872dd906119c290339030908790600401612c6e565b602060405180830381600087803b1580156119dc57600080fd5b505af11580156119f0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a1491908101906125af565b611a305760405162461bcd60e51b815260040161020d90612d57565b60d45460d3546040516323b872dd60e01b81526001600160a01b03928316926323b872dd92611a69923392909116908690600401612c96565b602060405180830381600087803b158015611a8357600080fd5b505af1158015611a97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611abb91908101906125af565b611ad75760405162461bcd60e51b815260040161020d90612d57565b60d1546000906001600160a01b0316632bdbe2198584611af5610726565b6040518463ffffffff1660e01b8152600401611b1393929190612e83565b60206040518083038186803b158015611b2b57600080fd5b505afa158015611b3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b63919081019061268d565b9050336001600160a01b03167f5b5de75983fe8891c65234472ee1805018163815d4c146f10bf176b736093c094284846000604051611ba59493929190612e9e565b60405180910390a2611bb73382611e83565b505050505050565b60d95490565b611bcd611179565b611be95760405162461bcd60e51b815260040161020d90612dc7565b61103f816120d2565b3390565b6001600160a01b038316611c1c5760405162461bcd60e51b815260040161020d90612e17565b6001600160a01b038216611c425760405162461bcd60e51b815260040161020d90612d47565b6001600160a01b0380841660008181526034602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590611c9d908590612e67565b60405180910390a3505050565b303b1590565b6000611cbd848484612154565b611d2e84611cc9611bf2565b6107dd85604051806060016040528060288152602001613065602891396001600160a01b038a16600090815260346020526040812090611d07611bf2565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61209216565b5060015b9392505050565b600082820183811015611d325760405162461bcd60e51b815260040161020d90612d67565b600054610100900460ff1680611d775750611d77611caa565b80611d85575060005460ff16155b611da15760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff16158015611dcc576000805460ff1961ff0019909116610100171660011790555b609d80546001600160a81b0319166101006001600160a01b03851602179055801561103d576000805461ff00191690555050565b821580611e0e575060328310155b611e2a5760405162461bcd60e51b815260040161020d90612db7565b811580611e38575060648210155b611e545760405162461bcd60e51b815260040161020d90612db7565b6019811015611e755760405162461bcd60e51b815260040161020d90612db7565b60d79290925560d85560d955565b6001600160a01b038216611ea95760405162461bcd60e51b815260040161020d90612e57565b603554611ebc908263ffffffff611d3916565b6035556001600160a01b038216600090815260336020526040902054611ee8908263ffffffff611d3916565b6001600160a01b0383166000818152603360205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611f37908590612e67565b60405180910390a35050565b6001600160a01b0316600090815260da60205260409020436006019055565b6000811561066357611d32838363ffffffff61226a16565b6000611d3283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612092565b6001600160a01b038216611fe25760405162461bcd60e51b815260040161020d90612de7565b6120258160405180606001604052806022815260200161301d602291396001600160a01b038516600090815260336020526040902054919063ffffffff61209216565b6001600160a01b038316600090815260336020526040902055603554612051908263ffffffff611f7a16565b6035556040516000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611f37908590612e67565b600081848411156120b65760405162461bcd60e51b815260040161020d9190612cf6565b505050900390565b600061065f6120cb611bf2565b8484612154565b6001600160a01b0381166120f85760405162461bcd60e51b815260040161020d90612d37565b609e546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3609e80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03831661217a5760405162461bcd60e51b815260040161020d90612df7565b6001600160a01b0382166121a05760405162461bcd60e51b815260040161020d90612d07565b6121e38160405180606001604052806026815260200161303f602691396001600160a01b038616600090815260336020526040902054919063ffffffff61209216565b6001600160a01b038085166000908152603360205260408082209390935590841681522054612218908263ffffffff611d3916565b6001600160a01b0380841660008181526033602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611c9d908590612e67565b6000611d3283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250600081836122c85760405162461bcd60e51b815260040161020d9190612cf6565b5060008385816122d457fe5b0495945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231f57805160ff191683800117855561234c565b8280016001018555821561234c579182015b8281111561234c578251825591602001919060010190612331565b5061235892915061235c565b5090565b61064891905b808211156123585760008155600101612362565b803561066381612fed565b805161066381613001565b600082601f83011261239d57600080fd5b81356123b06123ab82612f3d565b612f16565b915080825260208301602083018583830111156123cc57600080fd5b6123d7838284612fab565b50505092915050565b80356106638161300a565b80516106638161300a565b803561066381613013565b60006020828403121561241357600080fd5b60006117358484612376565b6000806000806000806000806000806101408b8d03121561243f57600080fd5b600061244b8d8d612376565b9a5050602061245c8d828e01612376565b995050604061246d8d828e01612376565b985050606061247e8d828e01612376565b975050608061248f8d828e01612376565b96505060a06124a08d828e01612376565b95505060c06124b18d828e016123e0565b94505060e06124c28d828e016123e0565b9350506101006124d48d828e016123e0565b9250506101206124e68d828e016123e0565b9150509295989b9194979a5092959850565b6000806040838503121561250b57600080fd5b60006125178585612376565b925050602061252885828601612376565b9150509250929050565b60008060006060848603121561254757600080fd5b60006125538686612376565b935050602061256486828701612376565b9250506040612575868287016123e0565b9150509250925092565b6000806040838503121561259257600080fd5b600061259e8585612376565b9250506020612528858286016123e0565b6000602082840312156125c157600080fd5b60006117358484612381565b600080604083850312156125e057600080fd5b60006125ec8585612381565b9250506020612528858286016123eb565b60008060006060848603121561261257600080fd5b833567ffffffffffffffff81111561262957600080fd5b6126358682870161238c565b935050602084013567ffffffffffffffff81111561265257600080fd5b61265e8682870161238c565b9250506040612575868287016123f6565b60006020828403121561268157600080fd5b600061173584846123e0565b60006020828403121561269f57600080fd5b600061173584846123eb565b6000806000606084860312156126c057600080fd5b60006126cc86866123e0565b9350506020612564868287016123e0565b6126e681612f99565b82525050565b6126e681612f77565b6126e681612f82565b6126e681612fa0565b600061271282612f65565b61271c8185612f6e565b935061272c818560208601612fb7565b61273581612fe3565b9093019392505050565b600061274c602383612f6e565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647281526265737360e81b602082015260400192915050565b6000612791601483612f6e565b7314185d5cd8589b194e881b9bdd081c185d5cd95960621b815260200192915050565b60006127c1600c83612f6e565b6b125b9d985b1a59081cd95b9960a21b815260200192915050565b60006127e9602683612f6e565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612831602283612f6e565b7f45524332303a20617070726f766520746f20746865207a65726f206164647265815261737360f01b602082015260400192915050565b6000612875600f83612f6e565b6e151c985b9cd9995c8819985a5b1959608a1b815260200192915050565b60006128a0601b83612f6e565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006128d9601183612f6e565b70446f6e277420686176652072696768747360781b815260200192915050565b6000612906601483612f6e565b73109d5c9b881d1c985b9cd9995c8819985a5b195960621b815260200192915050565b6000612936601083612f6e565b6f14185d5cd8589b194e881c185d5cd95960821b815260200192915050565b6000612962602f83612f6e565b7f46756e6374696f6e2069732074656d706f726172696c79206c6f636b6564206681526e6f722074686973206164647265737360881b602082015260400192915050565b60006129b3600b83612f6e565b6a496e76616c69642066656560a81b815260200192915050565b60006129da602083612f6e565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b6000612a13602e83612f6e565b7f436f6e747261637420696e7374616e63652068617320616c726561647920626581526d195b881a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612a63602183612f6e565b7f45524332303a206275726e2066726f6d20746865207a65726f206164647265738152607360f81b602082015260400192915050565b6000612aa6602583612f6e565b7f45524332303a207472616e736665722066726f6d20746865207a65726f206164815264647265737360d81b602082015260400192915050565b6000612aed600d83612f6e565b6c09aeae6e840e6cadcc840a69cb609b1b815260200192915050565b6000610663600083612f69565b6000612b23602483612f6e565b7f45524332303a20617070726f76652066726f6d20746865207a65726f206164648152637265737360e01b602082015260400192915050565b6000612b69601083612f6e565b6f4d757374206275726e20746f6b656e7360801b815260200192915050565b6000612b95600d83612f6e565b6c09aeae6e840e6cadcc8408aa89609b1b815260200192915050565b6000612bbe602983612f6e565b7f52656465656d20616d6f756e74206578636565647320617661696c61626c65208152686c697175696469747960b81b602082015260400192915050565b6000612c09601f83612f6e565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300815260200192915050565b6126e681610648565b6126e681612f93565b600061066382612b09565b6020810161066382846126ec565b6020810161066382846126dd565b60608101612c7c82866126dd565b612c8960208301856126dd565b6117356040830184612c35565b60608101612ca482866126dd565b612c8960208301856126ec565b60408101612cbf82856126dd565b611d326020830184612c35565b60408101612cbf82856126ec565b6020810161066382846126f5565b60408101612cbf82856126fe565b60208082528101611d328184612707565b602080825281016106638161273f565b6020808252810161066381612784565b60208082528101610663816127b4565b60208082528101610663816127dc565b6020808252810161066381612824565b6020808252810161066381612868565b6020808252810161066381612893565b60208082528101610663816128cc565b60208082528101610663816128f9565b6020808252810161066381612929565b6020808252810161066381612955565b60208082528101610663816129a6565b60208082528101610663816129cd565b6020808252810161066381612a06565b6020808252810161066381612a56565b6020808252810161066381612a99565b6020808252810161066381612ae0565b6020808252810161066381612b16565b6020808252810161066381612b5c565b6020808252810161066381612b88565b6020808252810161066381612bb1565b6020808252810161066381612bfc565b602081016106638284612c35565b60408101612cbf8285612c35565b60608101612e918286612c35565b612c896020830185612c35565b60808101612eac8287612c35565b612eb96020830186612c35565b612ec66040830185612c35565b61077760608301846126f5565b60808101612ee18287612c35565b612eee6020830186612c35565b612efb6040830185612c35565b6107776060830184612c35565b602081016106638284612c3e565b60405181810167ffffffffffffffff81118282101715612f3557600080fd5b604052919050565b600067ffffffffffffffff821115612f5457600080fd5b506020601f91909101601f19160190565b5190565b919050565b90815260200190565b600061066382612f87565b151590565b6001600160a01b031690565b60ff1690565b6000610663825b600061066382612f77565b82818337506000910152565b60005b83811015612fd2578181015183820152602001612fba565b838111156107205750506000910152565b601f01601f191690565b612ff681612f77565b811461103f57600080fd5b612ff681612f82565b612ff681610648565b612ff681612f9356fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa365627a7a723158201b55f4e85d25ec19534b6dce9285d84f0ee17accbec39864cf4ae689a3c46ae86c6578706572696d656e74616cf564736f6c634300050f0040
Deployed Bytecode
0x6080604052600436106101e35760003560e01c8063715018a611610102578063a457c2d711610095578063e7654b3c11610064578063e7654b3c1461053f578063f00703331461055f578063f09ddae71461057f578063f2fde38b14610594576101e3565b8063a457c2d7146104bf578063a9059cbb146104df578063c4d66de8146104ff578063dd62ed3e1461051f576101e3565b806395d89b41116100d157806395d89b41146104625780639f3e8b34146104775780639fd0506d14610497578063a0712d68146104ac576101e3565b8063715018a6146104015780638456cb59146104165780638da5cb5b1461042b5780638f32d59b1461044d576101e3565b80633986de6a1161017a5780635a18664c116101495780635a18664c146103935780635c975abb146103a8578063629c577e146103bd57806370a08231146103e1576101e3565b80633986de6a146103295780633f4ba83a1461034957806342966c681461035e578063476343ee1461037e576101e3565b80631838d85c116101b65780631838d85c146102b257806323b872dd146102c7578063313ce567146102e75780633950935114610309576101e3565b806306fdde0314610218578063095ea7b3146102435780631624f6c61461027057806318160ddd14610290575b60d3546001600160a01b031633146102165760405162461bcd60e51b815260040161020d90612d27565b60405180910390fd5b005b34801561022457600080fd5b5061022d6105b4565b60405161023a9190612cf6565b60405180910390f35b34801561024f57600080fd5b5061026361025e36600461257f565b61064b565b60405161023a9190612cda565b34801561027c57600080fd5b5061021661028b3660046125fd565b610669565b34801561029c57600080fd5b506102a5610726565b60405161023a9190612e67565b3480156102be57600080fd5b506102a561072c565b3480156102d357600080fd5b506102636102e2366004612532565b610732565b3480156102f357600080fd5b506102fc610780565b60405161023a9190612f08565b34801561031557600080fd5b5061026361032436600461257f565b610789565b34801561033557600080fd5b5061021661034436600461241f565b6107e2565b34801561035557600080fd5b50610216610931565b34801561036a57600080fd5b5061021661037936600461266f565b6109b7565b34801561038a57600080fd5b50610216610c97565b34801561039f57600080fd5b50610216610f8f565b3480156103b457600080fd5b50610263611042565b3480156103c957600080fd5b506103d261104b565b60405161023a93929190612e83565b3480156103ed57600080fd5b506102a56103fc366004612401565b611057565b34801561040d57600080fd5b50610216611072565b34801561042257600080fd5b506102166110e0565b34801561043757600080fd5b5061044061116a565b60405161023a9190612c52565b34801561045957600080fd5b50610263611179565b34801561046e57600080fd5b5061022d61119f565b34801561048357600080fd5b506102a5610492366004612401565b611200565b3480156104a357600080fd5b50610440611212565b6102166104ba36600461266f565b611226565b3480156104cb57600080fd5b506102636104da36600461257f565b61168b565b3480156104eb57600080fd5b506102636104fa36600461257f565b6116f9565b34801561050b57600080fd5b5061021661051a366004612401565b61173d565b34801561052b57600080fd5b506102a561053a3660046124f8565b61180f565b34801561054b57600080fd5b5061021661055a3660046126ab565b61183a565b34801561056b57600080fd5b5061021661057a36600461266f565b61186e565b34801561058b57600080fd5b506102a5611bbf565b3480156105a057600080fd5b506102166105af366004612401565b611bc5565b60688054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106405780601f1061061557610100808354040283529160200191610640565b820191906000526020600020905b81548152906001019060200180831161062357829003601f168201915b505050505090505b90565b600061065f610658611bf2565b8484611bf6565b5060015b92915050565b600054610100900460ff16806106825750610682611caa565b80610690575060005460ff16155b6106ac5760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff161580156106d7576000805460ff1961ff0019909116610100171660011790555b83516106ea9060689060208701906122de565b5082516106fe9060699060208601906122de565b50606a805460ff191660ff84161790558015610720576000805461ff00191690555b50505050565b60355490565b60d65481565b6001600160a01b038316600090815260da6020526040812054849043101561076c5760405162461bcd60e51b815260040161020d90612da7565b610777858585611cb0565b95945050505050565b606a5460ff1690565b600061065f610796611bf2565b846107dd85603460006107a7611bf2565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549063ffffffff611d3916565b611bf6565b600054610100900460ff16806107fb57506107fb611caa565b80610809575060005460ff16155b6108255760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff16158015610850576000805460ff1961ff0019909116610100171660011790555b6108598661173d565b61089e604051806040016040528060048152602001630f0a69cb60e31b8152506040518060400160405280600581526020016478534e586160d81b8152506012610669565b6108a786611d5e565b60d180546001600160a01b03199081166001600160a01b038e81169190911790925560d2805482168d841617905560d4805482168c841617905560d5805482168b841617905560d38054909116918916919091179055610908858585611e00565b6109123383611e83565b8015610924576000805461ff00191690555b5050505050505050505050565b609d5461010090046001600160a01b031633146109605760405162461bcd60e51b815260040161020d90612d77565b609d5460ff166109825760405162461bcd60e51b815260040161020d90612d17565b609d805460ff191690556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a1565b33600081815260da60205260409020544310156109e65760405162461bcd60e51b815260040161020d90612da7565b60008211610a065760405162461bcd60e51b815260040161020d90612e27565b610a0f33611f43565b60d1546000906001600160a01b03166318d814ba610a2b610726565b856040518363ffffffff1660e01b8152600401610a49929190612e75565b60206040518083038186803b158015610a6157600080fd5b505afa158015610a75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a99919081019061268d565b90508060d160009054906101000a90046001600160a01b03166001600160a01b03166370ed0ada6040518163ffffffff1660e01b815260040160206040518083038186803b158015610aea57600080fd5b505afa158015610afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b22919081019061268d565b1015610b405760405162461bcd60e51b815260040161020d90612e47565b60d35460405163a965072760e01b81526001600160a01b039091169063a965072790610b70908490600401612e67565b600060405180830381600087803b158015610b8a57600080fd5b505af1158015610b9e573d6000803e3d6000fd5b505050506000610bc3610bb68360d760010154611f62565b839063ffffffff611f7a16565b9050610bcf3385611fbc565b336001600160a01b03167f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b4644428684604051610c0c93929190612e83565b60405180910390a26000336001600160a01b031682604051610c2d90612c47565b60006040518083038185875af1925050503d8060008114610c6a576040519150601f19603f3d011682016040523d82523d6000602084013e610c6f565b606091505b5050905080610c905760405162461bcd60e51b815260040161020d90612d87565b5050505050565b610c9f611179565b610cbb5760405162461bcd60e51b815260040161020d90612dc7565b60d5546040516370a0823160e01b815247916000916001600160a01b03909116906370a0823190610cf0903090600401612c60565b60206040518083038186803b158015610d0857600080fd5b505afa158015610d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d40919081019061268d565b60d4546040516370a0823160e01b81529192506000916001600160a01b03909116906370a0823190610d76903090600401612c60565b60206040518083038186803b158015610d8e57600080fd5b505afa158015610da2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610dc6919081019061268d565b90506000336001600160a01b031684604051610de190612c47565b60006040518083038185875af1925050503d8060008114610e1e576040519150601f19603f3d011682016040523d82523d6000602084013e610e23565b606091505b5050905080610e445760405162461bcd60e51b815260040161020d90612d57565b60d55460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90610e769033908790600401612cb1565b602060405180830381600087803b158015610e9057600080fd5b505af1158015610ea4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ec891908101906125af565b5060d45460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90610efb9033908690600401612cb1565b602060405180830381600087803b158015610f1557600080fd5b505af1158015610f29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f4d91908101906125af565b507fa80975b2ffacb3d3f1fcfd13863288991d27ae8e617c147e7c83d240b82bd1f6848484604051610f8193929190612e83565b60405180910390a150505050565b610f97611179565b610fb35760405162461bcd60e51b815260040161020d90612dc7565b6000610fbe30611057565b9050801561103f5760405163a9059cbb60e01b8152309063a9059cbb90610feb9033908590600401612cb1565b602060405180830381600087803b15801561100557600080fd5b505af1158015611019573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061103d91908101906125af565b505b50565b609d5460ff1690565b60d75460d85460d95483565b6001600160a01b031660009081526033602052604090205490565b61107a611179565b6110965760405162461bcd60e51b815260040161020d90612dc7565b609e546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3609e80546001600160a01b0319169055565b609d5461010090046001600160a01b0316331461110f5760405162461bcd60e51b815260040161020d90612d77565b609d5460ff16156111325760405162461bcd60e51b815260040161020d90612d97565b609d805460ff191660011790556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a1565b609e546001600160a01b031690565b609e546000906001600160a01b0316611190611bf2565b6001600160a01b031614905090565b60698054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106405780601f1061061557610100808354040283529160200191610640565b60da6020526000908152604090205481565b609d5461010090046001600160a01b031681565b609d5460ff16156112495760405162461bcd60e51b815260040161020d90612d97565b33600081815260da60205260409020544310156112785760405162461bcd60e51b815260040161020d90612da7565b600034116112985760405162461bcd60e51b815260040161020d90612e37565b6112a133611f43565b60006112b23460d760000154611f62565b905060006112c6348363ffffffff611f7a16565b9050600060d160009054906101000a90046001600160a01b03166001600160a01b03166389afb5b76040518163ffffffff1660e01b815260040160206040518083038186803b15801561131857600080fd5b505afa15801561132c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611350919081019061268d565b9050600061135c610726565b60d154604051634ebb2f5360e11b815291925060009182916001600160a01b031690639d765ea690611392908690600401612e67565b604080518083038186803b1580156113a957600080fd5b505afa1580156113bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113e191908101906125cd565b91509150816115225760d25460d454604051633d15022b60e11b81526000926001600160a01b0390811692637a2a0456928a926114249216908e90600401612ce8565b6020604051808303818588803b15801561143d57600080fd5b505af1158015611451573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250611476919081019061268d565b60d45460d35460405163a9059cbb60e01b81529293506001600160a01b039182169263a9059cbb926114ae9216908590600401612ccc565b602060405180830381600087803b1580156114c857600080fd5b505af11580156114dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061150091908101906125af565b61151c5760405162461bcd60e51b815260040161020d90612d57565b506115a5565b60d3546040516000916001600160a01b031690879061154090612c47565b60006040518083038185875af1925050503d806000811461157d576040519150601f19603f3d011682016040523d82523d6000602084013e611582565b606091505b50509050806115a35760405162461bcd60e51b815260040161020d90612d57565b505b60d154604051630748416d60e41b81526000916001600160a01b03169063748416d0906115dc9088908a9087908a90600401612ed3565b60206040518083038186803b1580156115f457600080fd5b505afa158015611608573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061162c919081019061268d565b9050336001600160a01b03167f5b5de75983fe8891c65234472ee1805018163815d4c146f10bf176b736093c09423484600160405161166e9493929190612e9e565b60405180910390a26116803382611e83565b505050505050505050565b600061065f611698611bf2565b846107dd8560405180606001604052806025815260200161308d60259139603460006116c2611bf2565b6001600160a01b03908116825260208083019390935260409182016000908120918d1681529252902054919063ffffffff61209216565b33600081815260da602052604081205490919043101561172b5760405162461bcd60e51b815260040161020d90612da7565b61173584846120be565b949350505050565b600054610100900460ff16806117565750611756611caa565b80611764575060005460ff16155b6117805760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff161580156117ab576000805460ff1961ff0019909116610100171660011790555b609e80546001600160a01b0319166001600160a01b0384811691909117918290556040519116906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3801561103d576000805461ff00191690555050565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b611842611179565b61185e5760405162461bcd60e51b815260040161020d90612dc7565b611869838383611e00565b505050565b609d5460ff16156118915760405162461bcd60e51b815260040161020d90612d97565b33600081815260da60205260409020544310156118c05760405162461bcd60e51b815260040161020d90612da7565b600082116118e05760405162461bcd60e51b815260040161020d90612e07565b6118e933611f43565b60d154604080516389afb5b760e01b815290516000926001600160a01b0316916389afb5b7916004808301926020929190829003018186803b15801561192e57600080fd5b505afa158015611942573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611966919081019061268d565b905060006119798460d760000154611f62565b9050600061198d858363ffffffff611f7a16565b60d4546040516323b872dd60e01b81529192506001600160a01b0316906323b872dd906119c290339030908790600401612c6e565b602060405180830381600087803b1580156119dc57600080fd5b505af11580156119f0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a1491908101906125af565b611a305760405162461bcd60e51b815260040161020d90612d57565b60d45460d3546040516323b872dd60e01b81526001600160a01b03928316926323b872dd92611a69923392909116908690600401612c96565b602060405180830381600087803b158015611a8357600080fd5b505af1158015611a97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611abb91908101906125af565b611ad75760405162461bcd60e51b815260040161020d90612d57565b60d1546000906001600160a01b0316632bdbe2198584611af5610726565b6040518463ffffffff1660e01b8152600401611b1393929190612e83565b60206040518083038186803b158015611b2b57600080fd5b505afa158015611b3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b63919081019061268d565b9050336001600160a01b03167f5b5de75983fe8891c65234472ee1805018163815d4c146f10bf176b736093c094284846000604051611ba59493929190612e9e565b60405180910390a2611bb73382611e83565b505050505050565b60d95490565b611bcd611179565b611be95760405162461bcd60e51b815260040161020d90612dc7565b61103f816120d2565b3390565b6001600160a01b038316611c1c5760405162461bcd60e51b815260040161020d90612e17565b6001600160a01b038216611c425760405162461bcd60e51b815260040161020d90612d47565b6001600160a01b0380841660008181526034602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590611c9d908590612e67565b60405180910390a3505050565b303b1590565b6000611cbd848484612154565b611d2e84611cc9611bf2565b6107dd85604051806060016040528060288152602001613065602891396001600160a01b038a16600090815260346020526040812090611d07611bf2565b6001600160a01b03168152602081019190915260400160002054919063ffffffff61209216565b5060015b9392505050565b600082820183811015611d325760405162461bcd60e51b815260040161020d90612d67565b600054610100900460ff1680611d775750611d77611caa565b80611d85575060005460ff16155b611da15760405162461bcd60e51b815260040161020d90612dd7565b600054610100900460ff16158015611dcc576000805460ff1961ff0019909116610100171660011790555b609d80546001600160a81b0319166101006001600160a01b03851602179055801561103d576000805461ff00191690555050565b821580611e0e575060328310155b611e2a5760405162461bcd60e51b815260040161020d90612db7565b811580611e38575060648210155b611e545760405162461bcd60e51b815260040161020d90612db7565b6019811015611e755760405162461bcd60e51b815260040161020d90612db7565b60d79290925560d85560d955565b6001600160a01b038216611ea95760405162461bcd60e51b815260040161020d90612e57565b603554611ebc908263ffffffff611d3916565b6035556001600160a01b038216600090815260336020526040902054611ee8908263ffffffff611d3916565b6001600160a01b0383166000818152603360205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611f37908590612e67565b60405180910390a35050565b6001600160a01b0316600090815260da60205260409020436006019055565b6000811561066357611d32838363ffffffff61226a16565b6000611d3283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612092565b6001600160a01b038216611fe25760405162461bcd60e51b815260040161020d90612de7565b6120258160405180606001604052806022815260200161301d602291396001600160a01b038516600090815260336020526040902054919063ffffffff61209216565b6001600160a01b038316600090815260336020526040902055603554612051908263ffffffff611f7a16565b6035556040516000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611f37908590612e67565b600081848411156120b65760405162461bcd60e51b815260040161020d9190612cf6565b505050900390565b600061065f6120cb611bf2565b8484612154565b6001600160a01b0381166120f85760405162461bcd60e51b815260040161020d90612d37565b609e546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3609e80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03831661217a5760405162461bcd60e51b815260040161020d90612df7565b6001600160a01b0382166121a05760405162461bcd60e51b815260040161020d90612d07565b6121e38160405180606001604052806026815260200161303f602691396001600160a01b038616600090815260336020526040902054919063ffffffff61209216565b6001600160a01b038085166000908152603360205260408082209390935590841681522054612218908263ffffffff611d3916565b6001600160a01b0380841660008181526033602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90611c9d908590612e67565b6000611d3283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250600081836122c85760405162461bcd60e51b815260040161020d9190612cf6565b5060008385816122d457fe5b0495945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231f57805160ff191683800117855561234c565b8280016001018555821561234c579182015b8281111561234c578251825591602001919060010190612331565b5061235892915061235c565b5090565b61064891905b808211156123585760008155600101612362565b803561066381612fed565b805161066381613001565b600082601f83011261239d57600080fd5b81356123b06123ab82612f3d565b612f16565b915080825260208301602083018583830111156123cc57600080fd5b6123d7838284612fab565b50505092915050565b80356106638161300a565b80516106638161300a565b803561066381613013565b60006020828403121561241357600080fd5b60006117358484612376565b6000806000806000806000806000806101408b8d03121561243f57600080fd5b600061244b8d8d612376565b9a5050602061245c8d828e01612376565b995050604061246d8d828e01612376565b985050606061247e8d828e01612376565b975050608061248f8d828e01612376565b96505060a06124a08d828e01612376565b95505060c06124b18d828e016123e0565b94505060e06124c28d828e016123e0565b9350506101006124d48d828e016123e0565b9250506101206124e68d828e016123e0565b9150509295989b9194979a5092959850565b6000806040838503121561250b57600080fd5b60006125178585612376565b925050602061252885828601612376565b9150509250929050565b60008060006060848603121561254757600080fd5b60006125538686612376565b935050602061256486828701612376565b9250506040612575868287016123e0565b9150509250925092565b6000806040838503121561259257600080fd5b600061259e8585612376565b9250506020612528858286016123e0565b6000602082840312156125c157600080fd5b60006117358484612381565b600080604083850312156125e057600080fd5b60006125ec8585612381565b9250506020612528858286016123eb565b60008060006060848603121561261257600080fd5b833567ffffffffffffffff81111561262957600080fd5b6126358682870161238c565b935050602084013567ffffffffffffffff81111561265257600080fd5b61265e8682870161238c565b9250506040612575868287016123f6565b60006020828403121561268157600080fd5b600061173584846123e0565b60006020828403121561269f57600080fd5b600061173584846123eb565b6000806000606084860312156126c057600080fd5b60006126cc86866123e0565b9350506020612564868287016123e0565b6126e681612f99565b82525050565b6126e681612f77565b6126e681612f82565b6126e681612fa0565b600061271282612f65565b61271c8185612f6e565b935061272c818560208601612fb7565b61273581612fe3565b9093019392505050565b600061274c602383612f6e565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647281526265737360e81b602082015260400192915050565b6000612791601483612f6e565b7314185d5cd8589b194e881b9bdd081c185d5cd95960621b815260200192915050565b60006127c1600c83612f6e565b6b125b9d985b1a59081cd95b9960a21b815260200192915050565b60006127e9602683612f6e565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612831602283612f6e565b7f45524332303a20617070726f766520746f20746865207a65726f206164647265815261737360f01b602082015260400192915050565b6000612875600f83612f6e565b6e151c985b9cd9995c8819985a5b1959608a1b815260200192915050565b60006128a0601b83612f6e565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006128d9601183612f6e565b70446f6e277420686176652072696768747360781b815260200192915050565b6000612906601483612f6e565b73109d5c9b881d1c985b9cd9995c8819985a5b195960621b815260200192915050565b6000612936601083612f6e565b6f14185d5cd8589b194e881c185d5cd95960821b815260200192915050565b6000612962602f83612f6e565b7f46756e6374696f6e2069732074656d706f726172696c79206c6f636b6564206681526e6f722074686973206164647265737360881b602082015260400192915050565b60006129b3600b83612f6e565b6a496e76616c69642066656560a81b815260200192915050565b60006129da602083612f6e565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b6000612a13602e83612f6e565b7f436f6e747261637420696e7374616e63652068617320616c726561647920626581526d195b881a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612a63602183612f6e565b7f45524332303a206275726e2066726f6d20746865207a65726f206164647265738152607360f81b602082015260400192915050565b6000612aa6602583612f6e565b7f45524332303a207472616e736665722066726f6d20746865207a65726f206164815264647265737360d81b602082015260400192915050565b6000612aed600d83612f6e565b6c09aeae6e840e6cadcc840a69cb609b1b815260200192915050565b6000610663600083612f69565b6000612b23602483612f6e565b7f45524332303a20617070726f76652066726f6d20746865207a65726f206164648152637265737360e01b602082015260400192915050565b6000612b69601083612f6e565b6f4d757374206275726e20746f6b656e7360801b815260200192915050565b6000612b95600d83612f6e565b6c09aeae6e840e6cadcc8408aa89609b1b815260200192915050565b6000612bbe602983612f6e565b7f52656465656d20616d6f756e74206578636565647320617661696c61626c65208152686c697175696469747960b81b602082015260400192915050565b6000612c09601f83612f6e565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300815260200192915050565b6126e681610648565b6126e681612f93565b600061066382612b09565b6020810161066382846126ec565b6020810161066382846126dd565b60608101612c7c82866126dd565b612c8960208301856126dd565b6117356040830184612c35565b60608101612ca482866126dd565b612c8960208301856126ec565b60408101612cbf82856126dd565b611d326020830184612c35565b60408101612cbf82856126ec565b6020810161066382846126f5565b60408101612cbf82856126fe565b60208082528101611d328184612707565b602080825281016106638161273f565b6020808252810161066381612784565b60208082528101610663816127b4565b60208082528101610663816127dc565b6020808252810161066381612824565b6020808252810161066381612868565b6020808252810161066381612893565b60208082528101610663816128cc565b60208082528101610663816128f9565b6020808252810161066381612929565b6020808252810161066381612955565b60208082528101610663816129a6565b60208082528101610663816129cd565b6020808252810161066381612a06565b6020808252810161066381612a56565b6020808252810161066381612a99565b6020808252810161066381612ae0565b6020808252810161066381612b16565b6020808252810161066381612b5c565b6020808252810161066381612b88565b6020808252810161066381612bb1565b6020808252810161066381612bfc565b602081016106638284612c35565b60408101612cbf8285612c35565b60608101612e918286612c35565b612c896020830185612c35565b60808101612eac8287612c35565b612eb96020830186612c35565b612ec66040830185612c35565b61077760608301846126f5565b60808101612ee18287612c35565b612eee6020830186612c35565b612efb6040830185612c35565b6107776060830184612c35565b602081016106638284612c3e565b60405181810167ffffffffffffffff81118282101715612f3557600080fd5b604052919050565b600067ffffffffffffffff821115612f5457600080fd5b506020601f91909101601f19160190565b5190565b919050565b90815260200190565b600061066382612f87565b151590565b6001600160a01b031690565b60ff1690565b6000610663825b600061066382612f77565b82818337506000910152565b60005b83811015612fd2578181015183820152602001612fba565b838111156107205750506000910152565b601f01601f191690565b612ff681612f77565b811461103f57600080fd5b612ff681612f82565b612ff681610648565b612ff681612f9356fe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa365627a7a723158201b55f4e85d25ec19534b6dce9285d84f0ee17accbec39864cf4ae689a3c46ae86c6578706572696d656e74616cf564736f6c634300050f0040
Deployed Bytecode Sourcemap
84398:10251:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;94612:9;;-1:-1:-1;;;;;94612:9:0;94598:10;:23;94590:48;;;;-1:-1:-1;;;94590:48:0;;;;;;;;;;;;;;;;;84398:10251;23559:83;;8:9:-1;5:2;;;30:1;27;20:12;5:2;23559:83:0;;;:::i;:::-;;;;;;;;;;;;;;;;17002:152;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;17002:152:0;;;;;;;;:::i;:::-;;;;;;;;23303:186;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;23303:186:0;;;;;;;;:::i;16023:91::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;16023:91:0;;;:::i;:::-;;;;;;;;84638:34;;8:9:-1;5:2;;;30:1;27;20:12;5:2;84638:34:0;;;:::i;91085:218::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;91085:218:0;;;;;;;;:::i;24411:83::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;24411:83:0;;;:::i;:::-;;;;;;;;18339:210;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;18339:210:0;;;;;;;;:::i;84681:909::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;84681:909:0;;;;;;;;:::i;83932:108::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;83932:108:0;;;:::i;89980:901::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;89980:901:0;;;;;;;;:::i;92596:701::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92596:701:0;;;:::i;93427:217::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93427:217:0;;;:::i;83151:78::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;83151:78:0;;;:::i;86140:30::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;86140:30:0;;;:::i;:::-;;;;;;;;;;16177:110;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16177:110:0;;;;;;;;:::i;10717:140::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;10717:140:0;;;:::i;83731:106::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;83731:106:0;;;:::i;9904:79::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9904:79:0;;;:::i;:::-;;;;;;;;10270:94;;8:9:-1;5:2;;;30:1;27;20:12;5:2;10270:94:0;;;:::i;23761:87::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;23761:87:0;;;:::i;86355:50::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;86355:50:0;;;;;;;;:::i;82486:21::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;82486:21:0;;;:::i;86891:1358::-;;;;;;;;;:::i;19052:261::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;19052:261:0;;;;;;;;:::i;90889:188::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;90889:188:0;;;;;;;;:::i;9678:145::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9678:145:0;;;;;;;;:::i;16721:134::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16721:134:0;;;;;;;;:::i;91751:231::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;91751:231:0;;;;;;;;:::i;88608:1113::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;88608:1113:0;;;;;;;;:::i;93757:106::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93757:106:0;;;:::i;11012:109::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;11012:109:0;;;;;;;;:::i;23559:83::-;23629:5;23622:12;;;;;;;;-1:-1:-1;;23622:12:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23596:13;;23622:12;;23629:5;;23622:12;;23629:5;23622:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23559:83;;:::o;17002:152::-;17068:4;17085:39;17094:12;:10;:12::i;:::-;17108:7;17117:6;17085:8;:39::i;:::-;-1:-1:-1;17142:4:0;17002:152;;;;;:::o;23303:186::-;6655:12;;;;;;;;:31;;;6671:15;:13;:15::i;:::-;6655:47;;;-1:-1:-1;6691:11:0;;;;6690:12;6655:47;6647:106;;;;-1:-1:-1;;;6647:106:0;;;;;;;;;6762:19;6785:12;;;;;;6784:13;6804:83;;;;6833:12;:19;;-1:-1:-1;;;;6833:19:0;;;;;6861:18;6848:4;6861:18;;;6804:83;23411:12;;;;:5;;:12;;;;;:::i;:::-;-1:-1:-1;23434:16:0;;;;:7;;:16;;;;;:::i;:::-;-1:-1:-1;23461:9:0;:20;;-1:-1:-1;;23461:20:0;;;;;;;6905:57;;;;6949:5;6934:20;;-1:-1:-1;;6934:20:0;;;6905:57;23303:186;;;;:::o;16023:91::-;16094:12;;16023:91;:::o;84638:34::-;;;;:::o;91085:218::-;-1:-1:-1;;;;;94138:30:0;;91226:4;94138:30;;;:15;:30;;;;;;91209:6;;94172:12;-1:-1:-1;94138:46:0;94116:143;;;;-1:-1:-1;;;94116:143:0;;;;;;;;;91250:45;91269:6;91277:9;91288:6;91250:18;:45::i;:::-;91243:52;91085:218;-1:-1:-1;;;;;91085:218:0:o;24411:83::-;24477:9;;;;24411:83;:::o;18339:210::-;18419:4;18436:83;18445:12;:10;:12::i;:::-;18459:7;18468:50;18507:10;18468:11;:25;18480:12;:10;:12::i;:::-;-1:-1:-1;;;;;18468:25:0;;;;;;;;;;;;;;;;;-1:-1:-1;18468:25:0;;;:34;;;;;;;;;;;:50;:38;:50;:::i;:::-;18436:8;:83::i;84681:909::-;6655:12;;;;;;;;:31;;;6671:15;:13;:15::i;:::-;6655:47;;;-1:-1:-1;6691:11:0;;;;6690:12;6655:47;6647:106;;;;-1:-1:-1;;;6647:106:0;;;;;;;;;6762:19;6785:12;;;;;;6784:13;6804:83;;;;6833:12;:19;;-1:-1:-1;;;;6833:19:0;;;;;6861:18;6848:4;6861:18;;;6804:83;85081:33;85100:13;85081:18;:33::i;:::-;85125:45;;;;;;;;;;;;;;-1:-1:-1;;;85125:45:0;;;;;;;;;;;;;;;;-1:-1:-1;;;85125:45:0;;;85167:2;85125:24;:45::i;:::-;85181:34;85201:13;85181:19;:34::i;:::-;85228:15;:58;;-1:-1:-1;;;;;;85228:58:0;;;-1:-1:-1;;;;;85228:58:0;;;;;;;;;;85297:17;:58;;;;;;;;;;85366:10;:24;;;;;;;;;;85401:11;:26;;;;;;;;;;85438:9;:22;;;;;;;;;;;;;;85473:67;85489:15;85506;85523:16;85473:15;:67::i;:::-;85551:31;85557:10;85569:12;85551:5;:31::i;:::-;6909:14;6905:57;;;6949:5;6934:20;;-1:-1:-1;;6934:20:0;;;6905:57;84681:909;;;;;;;;;;;:::o;83932:108::-;84101:6;;;;;-1:-1:-1;;;;;84101:6:0;84087:10;:20;84079:50;;;;-1:-1:-1;;;84079:50:0;;;;;;;;;83587:7;;;;83579:40;;;;-1:-1:-1;;;83579:40:0;;;;;;;;;83991:7;:15;;-1:-1:-1;;83991:15:0;;;84022:10;;;;84001:5;;84022:10;83932:108::o;89980:901::-;90037:10;94138:30;;;;:15;:30;;;;;;94172:12;-1:-1:-1;94138:46:0;94116:143;;;;-1:-1:-1;;;94116:143:0;;;;;;;;;90085:1;90068:14;:18;90060:47;;;;-1:-1:-1;;;90060:47:0;;;;;;;;;90118:16;90123:10;90118:4;:16::i;:::-;90171:15;;90147:21;;-1:-1:-1;;;;;90171:15:0;:40;90226:13;:11;:13::i;:::-;90254:14;90171:108;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;90171:108:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;90171:108:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;90171:108:0;;;;;;;;;90147:132;;90349:13;90314:15;;;;;;;;;-1:-1:-1;;;;;90314:15:0;-1:-1:-1;;;;;90314:29:0;;:31;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;90314:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;90314:31:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;90314:31:0;;;;;;;;;:48;;90292:139;;;;-1:-1:-1;;;90292:139:0;;;;;;;;;90455:9;;90444:56;;-1:-1:-1;;;90444:56:0;;-1:-1:-1;;;;;90455:9:0;;;;90444:41;;:56;;90486:13;;90444:56;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;90444:56:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;90444:56:0;;;;90511:19;90533:91;90565:48;90578:13;90593:11;:19;;;90565:12;:48::i;:::-;90533:13;;:91;:17;:91;:::i;:::-;90511:113;;90635:39;90647:10;90659:14;90635:11;:39::i;:::-;90695:10;-1:-1:-1;;;;;90690:62:0;;90707:15;90724:14;90740:11;90690:62;;;;;;;;;;;;;;;;;90766:12;90784:10;-1:-1:-1;;;;;90784:15:0;90806:11;90784:38;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;90765:57:0;;;90841:7;90833:40;;;;-1:-1:-1;;;90833:40:0;;;;;;;;;94270:1;;;89980:901;;:::o;92596:701::-;10116:9;:7;:9::i;:::-;10108:54;;;;-1:-1:-1;;;10108:54:0;;;;;;;;;92744:11;;92737:68;;-1:-1:-1;;;92737:68:0;;92676:21;;92648:25;;-1:-1:-1;;;;;92744:11:0;;;;92737:29;;:68;;92789:4;;92737:68;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92737:68:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;92737:68:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;92737:68:0;;;;;;;;;92851:10;;92844:43;;-1:-1:-1;;;92844:43:0;;92708:97;;-1:-1:-1;92816:25:0;;-1:-1:-1;;;;;92851:10:0;;;;92844:28;;:43;;92881:4;;92844:43;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92844:43:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;92844:43:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;92844:43:0;;;;;;;;;92816:71;;92901:12;92919:10;-1:-1:-1;;;;;92919:15:0;92941:17;92919:44;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;92900:63:0;;;92982:7;92974:35;;;;-1:-1:-1;;;92974:35:0;;;;;;;;;93029:11;;93022:60;;-1:-1:-1;;;93022:60:0;;-1:-1:-1;;;;;93029:11:0;;;;93022:28;;:60;;93051:10;;93063:18;;93022:60;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93022:60:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;93022:60:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;93022:60:0;;;;;;;;;-1:-1:-1;93100:10:0;;93093:58;;-1:-1:-1;;;93093:58:0;;-1:-1:-1;;;;;93100:10:0;;;;93093:27;;:58;;93121:10;;93133:17;;93093:58;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93093:58:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;93093:58:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;93093:58:0;;;;;;;;;;93169:120;93196:17;93228:18;93261:17;93169:120;;;;;;;;;;;;;;;;;10173:1;;;;92596:701::o;93427:217::-;10116:9;:7;:9::i;:::-;10108:54;;;;-1:-1:-1;;;10108:54:0;;;;;;;;;93486:16;93505:24;93523:4;93505:9;:24::i;:::-;93486:43;-1:-1:-1;93544:12:0;;93540:97;;93573:52;;-1:-1:-1;;;93573:52:0;;93588:4;;93573:30;;:52;;93604:10;;93616:8;;93573:52;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93573:52:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;93573:52:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;93573:52:0;;;;;;;;;;93540:97;10173:1;93427:217::o;83151:78::-;83214:7;;;;83151:78;:::o;86140:30::-;;;;;;;;:::o;16177:110::-;-1:-1:-1;;;;;16261:18:0;16234:7;16261:18;;;:9;:18;;;;;;;16177:110::o;10717:140::-;10116:9;:7;:9::i;:::-;10108:54;;;;-1:-1:-1;;;10108:54:0;;;;;;;;;10800:6;;10779:40;;10816:1;;-1:-1:-1;;;;;10800:6:0;;10779:40;;10816:1;;10779:40;10830:6;:19;;-1:-1:-1;;;;;;10830:19:0;;;10717:140::o;83731:106::-;84101:6;;;;;-1:-1:-1;;;;;84101:6:0;84087:10;:20;84079:50;;;;-1:-1:-1;;;84079:50:0;;;;;;;;;83388:7;;;;83387:8;83379:37;;;;-1:-1:-1;;;83379:37:0;;;;;;;;;83791:7;:14;;-1:-1:-1;;83791:14:0;83801:4;83791:14;;;83821:8;;;;83791:7;;83821:8;83731:106::o;9904:79::-;9969:6;;-1:-1:-1;;;;;9969:6:0;9904:79;:::o;10270:94::-;10350:6;;10310:4;;-1:-1:-1;;;;;10350:6:0;10334:12;:10;:12::i;:::-;-1:-1:-1;;;;;10334:22:0;;10327:29;;10270:94;:::o;23761:87::-;23833:7;23826:14;;;;;;;;-1:-1:-1;;23826:14:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23800:13;;23826:14;;23833:7;;23826:14;;23833:7;23826:14;;;;;;;;;;;;;;;;;;;;;;;;86355:50;;;;;;;;;;;;;:::o;82486:21::-;;;;;;-1:-1:-1;;;;;82486:21:0;;:::o;86891:1358::-;83388:7;;;;83387:8;83379:37;;;;-1:-1:-1;;;83379:37:0;;;;;;;;;86963:10;94138:30;;;;:15;:30;;;;;;94172:12;-1:-1:-1;94138:46:0;94116:143;;;;-1:-1:-1;;;94116:143:0;;;;;;;;;87006:1;86994:9;:13;86986:39;;;;-1:-1:-1;;;86986:39:0;;;;;;;;;87036:16;87041:10;87036:4;:16::i;:::-;87065:11;87079:44;87092:9;87103:11;:19;;;87079:12;:44::i;:::-;87065:58;-1:-1:-1;87134:23:0;87160:18;:9;87065:58;87160:18;:13;:18;:::i;:::-;87134:44;;87189:24;87216:15;;;;;;;;;-1:-1:-1;;;;;87216:15:0;-1:-1:-1;;;;;87216:29:0;;:31;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87216:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87216:31:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;87216:31:0;;;;;;;;;87189:58;;87260:19;87282:13;:11;:13::i;:::-;87355:15;;:62;;-1:-1:-1;;;87355:62:0;;87260:35;;-1:-1:-1;87307:18:0;;;;-1:-1:-1;;;;;87355:15:0;;:49;;:62;;87260:35;;87355:62;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87355:62:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87355:62:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;87355:62:0;;;;;;;;;87306:111;;;;87435:13;87430:472;;87487:17;;87583:10;;87487:117;;-1:-1:-1;;;87487:117:0;;87465:19;;-1:-1:-1;;;;;87487:17:0;;;;:34;;87546:15;;87487:117;;87583:10;;87596:7;;87487:117;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87487:117:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87487:117:0;;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;87487:117:0;;;;;;;;;87652:10;;87673:9;;87645:51;;-1:-1:-1;;;87645:51:0;;87465:139;;-1:-1:-1;;;;;;87652:10:0;;;;87645:27;;:51;;87673:9;;87465:139;;87645:51;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87645:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87645:51:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;87645:51:0;;;;;;;;;87619:128;;;;-1:-1:-1;;;87619:128:0;;;;;;;;;87430:472;;;;87799:9;;:41;;87781:12;;-1:-1:-1;;;;;87799:9:0;;87820:15;;87799:41;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;87780:60:0;;;87863:7;87855:35;;;;-1:-1:-1;;;87855:35:0;;;;;;;;;87430:472;;87935:15;;:173;;-1:-1:-1;;;87935:173:0;;87914:18;;-1:-1:-1;;;;;87935:15:0;;:44;;:173;;87994:16;;88025:15;;88055:16;;88086:11;;87935:173;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87935:173:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87935:173:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;87935:173:0;;;;;;;;;87914:194;;88131:10;-1:-1:-1;;;;;88126:62:0;;88143:15;88160:9;88171:10;88183:4;88126:62;;;;;;;;;;;;;;;;;;88206:35;88218:10;88230;88206:11;:35::i;:::-;88199:42;;;;;;;83427:1;86891:1358;:::o;19052:261::-;19137:4;19154:129;19163:12;:10;:12::i;:::-;19177:7;19186:96;19225:15;19186:96;;;;;;;;;;;;;;;;;:11;:25;19198:12;:10;:12::i;:::-;-1:-1:-1;;;;;19186:25:0;;;;;;;;;;;;;;;;;-1:-1:-1;19186:25:0;;;:34;;;;;;;;;;;:96;;:38;:96;:::i;90889:188::-;90977:10;91007:4;94138:30;;;:15;:30;;;;;;91007:4;;90977:10;94172:12;-1:-1:-1;94138:46:0;94116:143;;;;-1:-1:-1;;;94116:143:0;;;;;;;;;91036:33;91051:9;91062:6;91036:14;:33::i;:::-;91029:40;90889:188;-1:-1:-1;;;;90889:188:0:o;9678:145::-;6655:12;;;;;;;;:31;;;6671:15;:13;:15::i;:::-;6655:47;;;-1:-1:-1;6691:11:0;;;;6690:12;6655:47;6647:106;;;;-1:-1:-1;;;6647:106:0;;;;;;;;;6762:19;6785:12;;;;;;6784:13;6804:83;;;;6833:12;:19;;-1:-1:-1;;;;6833:19:0;;;;;6861:18;6848:4;6861:18;;;6804:83;9744:6;:15;;-1:-1:-1;;;;;;9744:15:0;-1:-1:-1;;;;;9744:15:0;;;;;;;;;;;9775:40;;9808:6;;;-1:-1:-1;;9775:40:0;;-1:-1:-1;;9775:40:0;6909:14;6905:57;;;6949:5;6934:20;;-1:-1:-1;;6934:20:0;;;9678:145;;:::o;16721:134::-;-1:-1:-1;;;;;16820:18:0;;;16793:7;16820:18;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;16721:134::o;91751:231::-;10116:9;:7;:9::i;:::-;10108:54;;;;-1:-1:-1;;;10108:54:0;;;;;;;;;91910:64;91926:14;91942;91958:15;91910;:64::i;:::-;91751:231;;;:::o;88608:1113::-;83388:7;;;;83387:8;83379:37;;;;-1:-1:-1;;;83379:37:0;;;;;;;;;88681:10;94138:30;;;;:15;:30;;;;;;94172:12;-1:-1:-1;94138:46:0;94116:143;;;;-1:-1:-1;;;94116:143:0;;;;;;;;;88724:1;88712:9;:13;88704:39;;;;-1:-1:-1;;;88704:39:0;;;;;;;;;88754:16;88759:10;88754:4;:16::i;:::-;88808:15;;:31;;;-1:-1:-1;;;88808:31:0;;;;88781:24;;-1:-1:-1;;;;;88808:15:0;;:29;;:31;;;;;;;;;;;;;;:15;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;88808:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;88808:31:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;88808:31:0;;;;;;;;;88781:58;;88852:11;88866:44;88879:9;88890:11;:19;;;88866:12;:44::i;:::-;88852:58;-1:-1:-1;88921:23:0;88947:18;:9;88852:58;88947:18;:13;:18;:::i;:::-;89007:10;;89000:63;;-1:-1:-1;;;89000:63:0;;88921:44;;-1:-1:-1;;;;;;89007:10:0;;89000:31;;:63;;89032:10;;89052:4;;89059:3;;89000:63;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;89000:63:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;89000:63:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;89000:63:0;;;;;;;;;88978:128;;;;-1:-1:-1;;;88978:128:0;;;;;;;;;89146:10;;89218:9;;89139:137;;-1:-1:-1;;;89139:137:0;;-1:-1:-1;;;;;89146:10:0;;;;89139:31;;:137;;89189:10;;89218:9;;;;89246:15;;89139:137;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;89139:137:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;89139:137:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;89139:137:0;;;;;;;;;89117:202;;;;-1:-1:-1;;;89117:202:0;;;;;;;;;89353:15;;89332:18;;-1:-1:-1;;;;;89353:15:0;:44;89412:16;89443:15;89473:13;:11;:13::i;:::-;89353:144;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;89353:144:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;89353:144:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;89353:144:0;;;;;;;;;89332:165;;89534:10;-1:-1:-1;;;;;89515:145:0;;89559:15;89589;89619:10;89644:5;89515:145;;;;;;;;;;;;;;;;;;89678:35;89690:10;89702;89678:11;:35::i;:::-;89671:42;;;;83427:1;88608:1113;:::o;93757:106::-;93835:20;;93757:106;:::o;11012:109::-;10116:9;:7;:9::i;:::-;10108:54;;;;-1:-1:-1;;;10108:54:0;;;;;;;;;11085:28;11104:8;11085:18;:28::i;8583:98::-;8663:10;8583:98;:::o;21983:338::-;-1:-1:-1;;;;;22077:19:0;;22069:68;;;;-1:-1:-1;;;22069:68:0;;;;;;;;;-1:-1:-1;;;;;22156:21:0;;22148:68;;;;-1:-1:-1;;;22148:68:0;;;;;;;;;-1:-1:-1;;;;;22229:18:0;;;;;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;;:36;;;22281:32;;;;;22259:6;;22281:32;;;;;;;;;;21983:338;;;:::o;7056:508::-;7473:4;7519:17;7551:7;7056:508;:::o;17626:304::-;17715:4;17732:36;17742:6;17750:9;17761:6;17732:9;:36::i;:::-;17779:121;17788:6;17796:12;:10;:12::i;:::-;17810:89;17848:6;17810:89;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17810:19:0;;;;;;:11;:19;;;;;;17830:12;:10;:12::i;:::-;-1:-1:-1;;;;;17810:33:0;;;;;;;;;;;;-1:-1:-1;17810:33:0;;;:89;;:37;:89;:::i;17779:121::-;-1:-1:-1;17918:4:0;17626:304;;;;;;:::o;940:181::-;998:7;1030:5;;;1054:6;;;;1046:46;;;;-1:-1:-1;;;1046:46:0;;;;;;;;82936:115;6655:12;;;;;;;;:31;;;6671:15;:13;:15::i;:::-;6655:47;;;-1:-1:-1;6691:11:0;;;;6690:12;6655:47;6647:106;;;;-1:-1:-1;;;6647:106:0;;;;;;;;;6762:19;6785:12;;;;;;6784:13;6804:83;;;;6833:12;:19;;-1:-1:-1;;;;6833:19:0;;;;;6861:18;6848:4;6861:18;;;6804:83;83002:7;:15;;-1:-1:-1;;;;;;83028:15:0;83002;-1:-1:-1;;;;;83028:15:0;;;;;;6905:57;;;;6949:5;6934:20;;-1:-1:-1;;6934:20:0;;;82936:115;;:::o;91990:515::-;92152:20;;;:45;;;92195:2;92176:15;:21;;92152:45;92144:69;;;;-1:-1:-1;;;92144:69:0;;;;;;;;;92232:20;;;:46;;;92275:3;92256:15;:22;;92232:46;92224:70;;;;-1:-1:-1;;;92224:70:0;;;;;;;;;92333:2;92313:16;:22;;92305:46;;;;-1:-1:-1;;;92305:46:0;;;;;;;;;92362:11;:37;;;;92410:19;:37;92458:20;:39;91990:515::o;20555:308::-;-1:-1:-1;;;;;20631:21:0;;20623:65;;;;-1:-1:-1;;;20623:65:0;;;;;;;;;20716:12;;:24;;20733:6;20716:24;:16;:24;:::i;:::-;20701:12;:39;-1:-1:-1;;;;;20772:18:0;;;;;;:9;:18;;;;;;:30;;20795:6;20772:30;:22;:30;:::i;:::-;-1:-1:-1;;;;;20751:18:0;;;;;;:9;:18;;;;;;:51;;;;20818:37;;20751:18;;;20818:37;;;;20848:6;;20818:37;;;;;;;;;;20555:308;;:::o;94425:118::-;-1:-1:-1;;;;;94476:25:0;;;;;:15;:25;;;;;94504:12;86291:1;94504:31;94476:59;;94425:118::o;91311:222::-;91420:11;91453:15;;91449:77;;91491:23;:6;91502:11;91491:23;:10;:23;:::i;1396:136::-;1454:7;1481:43;1485:1;1488;1481:43;;;;;;;;;;;;;;;;;:3;:43::i;21195:348::-;-1:-1:-1;;;;;21271:21:0;;21263:67;;;;-1:-1:-1;;;21263:67:0;;;;;;;;;21364:68;21387:6;21364:68;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21364:18:0;;;;;;:9;:18;;;;;;;:68;;:22;:68;:::i;:::-;-1:-1:-1;;;;;21343:18:0;;;;;;:9;:18;;;;;:89;21458:12;;:24;;21475:6;21458:24;:16;:24;:::i;:::-;21443:12;:39;21498:37;;21524:1;;-1:-1:-1;;;;;21498:37:0;;;;;;;21528:6;;21498:37;;1869:192;1955:7;1991:12;1983:6;;;;1975:29;;;;-1:-1:-1;;;1975:29:0;;;;;;;;;;-1:-1:-1;;;2027:5:0;;;1869:192::o;16500:158::-;16569:4;16586:42;16596:12;:10;:12::i;:::-;16610:9;16621:6;16586:9;:42::i;11227:229::-;-1:-1:-1;;;;;11301:22:0;;11293:73;;;;-1:-1:-1;;;11293:73:0;;;;;;;;;11403:6;;11382:38;;-1:-1:-1;;;;;11382:38:0;;;;11403:6;;11382:38;;11403:6;;11382:38;11431:6;:17;;-1:-1:-1;;;;;;11431:17:0;-1:-1:-1;;;;;11431:17:0;;;;;;;;;;11227:229::o;19803:471::-;-1:-1:-1;;;;;19901:20:0;;19893:70;;;;-1:-1:-1;;;19893:70:0;;;;;;;;;-1:-1:-1;;;;;19982:23:0;;19974:71;;;;-1:-1:-1;;;19974:71:0;;;;;;;;;20078;20100:6;20078:71;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;20078:17:0;;;;;;:9;:17;;;;;;;:71;;:21;:71;:::i;:::-;-1:-1:-1;;;;;20058:17:0;;;;;;;:9;:17;;;;;;:91;;;;20183:20;;;;;;;:32;;20208:6;20183:32;:24;:32;:::i;:::-;-1:-1:-1;;;;;20160:20:0;;;;;;;:9;:20;;;;;;;:55;;;;20231:35;;;;;;;;;;20259:6;;20231:35;;3251:132;3309:7;3336:39;3340:1;3343;3336:39;;;;;;;;;;;;;;;;;3999:7;4101:12;4094:5;4086:28;;;;-1:-1:-1;;;4086:28:0;;;;;;;;;;;4125:9;4141:1;4137;:5;;;;;;;3913:345;-1:-1:-1;;;;;3913:345:0:o;84398:10251::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;84398:10251:0;;;-1:-1:-1;84398:10251:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;5:130:-1;72:20;;97:33;72:20;97:33;;295:128;370:13;;388:30;370:13;388:30;;431:442;;533:3;526:4;518:6;514:17;510:27;500:2;;551:1;548;541:12;500:2;588:6;575:20;610:65;625:49;667:6;625:49;;;610:65;;;601:74;;695:6;688:5;681:21;731:4;723:6;719:17;764:4;757:5;753:16;799:3;790:6;785:3;781:16;778:25;775:2;;;816:1;813;806:12;775:2;826:41;860:6;855:3;850;826:41;;;493:380;;;;;;;;881:130;948:20;;973:33;948:20;973:33;;1018:134;1096:13;;1114:33;1096:13;1114:33;;1159:126;1224:20;;1249:31;1224:20;1249:31;;1292:241;;1396:2;1384:9;1375:7;1371:23;1367:32;1364:2;;;1412:1;1409;1402:12;1364:2;1447:1;1464:53;1509:7;1489:9;1464:53;;1540:1389;;;;;;;;;;;1805:3;1793:9;1784:7;1780:23;1776:33;1773:2;;;1822:1;1819;1812:12;1773:2;1857:1;1874:61;1927:7;1907:9;1874:61;;;1864:71;;1836:105;1972:2;1990:53;2035:7;2026:6;2015:9;2011:22;1990:53;;;1980:63;;1951:98;2080:2;2098:53;2143:7;2134:6;2123:9;2119:22;2098:53;;;2088:63;;2059:98;2188:2;2206:53;2251:7;2242:6;2231:9;2227:22;2206:53;;;2196:63;;2167:98;2296:3;2315:53;2360:7;2351:6;2340:9;2336:22;2315:53;;;2305:63;;2275:99;2405:3;2424:53;2469:7;2460:6;2449:9;2445:22;2424:53;;;2414:63;;2384:99;2514:3;2533:53;2578:7;2569:6;2558:9;2554:22;2533:53;;;2523:63;;2493:99;2623:3;2642:53;2687:7;2678:6;2667:9;2663:22;2642:53;;;2632:63;;2602:99;2732:3;2751:53;2796:7;2787:6;2776:9;2772:22;2751:53;;;2741:63;;2711:99;2841:3;2860:53;2905:7;2896:6;2885:9;2881:22;2860:53;;;2850:63;;2820:99;1767:1162;;;;;;;;;;;;;;2936:366;;;3057:2;3045:9;3036:7;3032:23;3028:32;3025:2;;;3073:1;3070;3063:12;3025:2;3108:1;3125:53;3170:7;3150:9;3125:53;;;3115:63;;3087:97;3215:2;3233:53;3278:7;3269:6;3258:9;3254:22;3233:53;;;3223:63;;3194:98;3019:283;;;;;;3309:491;;;;3447:2;3435:9;3426:7;3422:23;3418:32;3415:2;;;3463:1;3460;3453:12;3415:2;3498:1;3515:53;3560:7;3540:9;3515:53;;;3505:63;;3477:97;3605:2;3623:53;3668:7;3659:6;3648:9;3644:22;3623:53;;;3613:63;;3584:98;3713:2;3731:53;3776:7;3767:6;3756:9;3752:22;3731:53;;;3721:63;;3692:98;3409:391;;;;;;3807:366;;;3928:2;3916:9;3907:7;3903:23;3899:32;3896:2;;;3944:1;3941;3934:12;3896:2;3979:1;3996:53;4041:7;4021:9;3996:53;;;3986:63;;3958:97;4086:2;4104:53;4149:7;4140:6;4129:9;4125:22;4104:53;;4180:257;;4292:2;4280:9;4271:7;4267:23;4263:32;4260:2;;;4308:1;4305;4298:12;4260:2;4343:1;4360:61;4413:7;4393:9;4360:61;;4444:393;;;4573:2;4561:9;4552:7;4548:23;4544:32;4541:2;;;4589:1;4586;4579:12;4541:2;4624:1;4641:61;4694:7;4674:9;4641:61;;;4631:71;;4603:105;4739:2;4757:64;4813:7;4804:6;4793:9;4789:22;4757:64;;4844:699;;;;5000:2;4988:9;4979:7;4975:23;4971:32;4968:2;;;5016:1;5013;5006:12;4968:2;5051:31;;5102:18;5091:30;;5088:2;;;5134:1;5131;5124:12;5088:2;5154:63;5209:7;5200:6;5189:9;5185:22;5154:63;;;5144:73;;5030:193;5282:2;5271:9;5267:18;5254:32;5306:18;5298:6;5295:30;5292:2;;;5338:1;5335;5328:12;5292:2;5358:63;5413:7;5404:6;5393:9;5389:22;5358:63;;;5348:73;;5233:194;5458:2;5476:51;5519:7;5510:6;5499:9;5495:22;5476:51;;5550:241;;5654:2;5642:9;5633:7;5629:23;5625:32;5622:2;;;5670:1;5667;5660:12;5622:2;5705:1;5722:53;5767:7;5747:9;5722:53;;5798:263;;5913:2;5901:9;5892:7;5888:23;5884:32;5881:2;;;5929:1;5926;5919:12;5881:2;5964:1;5981:64;6037:7;6017:9;5981:64;;6068:491;;;;6206:2;6194:9;6185:7;6181:23;6177:32;6174:2;;;6222:1;6219;6212:12;6174:2;6257:1;6274:53;6319:7;6299:9;6274:53;;;6264:63;;6236:97;6364:2;6382:53;6427:7;6418:6;6407:9;6403:22;6382:53;;6566:142;6657:45;6696:5;6657:45;;;6652:3;6645:58;6639:69;;;6715:113;6798:24;6816:5;6798:24;;6835:104;6912:21;6927:5;6912:21;;6946:152;7042:50;7086:5;7042:50;;7105:347;;7217:39;7250:5;7217:39;;;7268:71;7332:6;7327:3;7268:71;;;7261:78;;7344:52;7389:6;7384:3;7377:4;7370:5;7366:16;7344:52;;;7417:29;7439:6;7417:29;;;7408:39;;;;7197:255;-1:-1;;;7197:255;7460:372;;7620:67;7684:2;7679:3;7620:67;;;7720:34;7700:55;;-1:-1;;;7784:2;7775:12;;7768:27;7823:2;7814:12;;7606:226;-1:-1;;7606:226;7841:320;;8001:67;8065:2;8060:3;8001:67;;;-1:-1;;;8081:43;;8152:2;8143:12;;7987:174;-1:-1;;7987:174;8170:312;;8330:67;8394:2;8389:3;8330:67;;;-1:-1;;;8410:35;;8473:2;8464:12;;8316:166;-1:-1;;8316:166;8491:375;;8651:67;8715:2;8710:3;8651:67;;;8751:34;8731:55;;-1:-1;;;8815:2;8806:12;;8799:30;8857:2;8848:12;;8637:229;-1:-1;;8637:229;8875:371;;9035:67;9099:2;9094:3;9035:67;;;9135:34;9115:55;;-1:-1;;;9199:2;9190:12;;9183:26;9237:2;9228:12;;9021:225;-1:-1;;9021:225;9255:315;;9415:67;9479:2;9474:3;9415:67;;;-1:-1;;;9495:38;;9561:2;9552:12;;9401:169;-1:-1;;9401:169;9579:327;;9739:67;9803:2;9798:3;9739:67;;;9839:29;9819:50;;9897:2;9888:12;;9725:181;-1:-1;;9725:181;9915:317;;10075:67;10139:2;10134:3;10075:67;;;-1:-1;;;10155:40;;10223:2;10214:12;;10061:171;-1:-1;;10061:171;10241:320;;10401:67;10465:2;10460:3;10401:67;;;-1:-1;;;10481:43;;10552:2;10543:12;;10387:174;-1:-1;;10387:174;10570:316;;10730:67;10794:2;10789:3;10730:67;;;-1:-1;;;10810:39;;10877:2;10868:12;;10716:170;-1:-1;;10716:170;10895:384;;11055:67;11119:2;11114:3;11055:67;;;11155:34;11135:55;;-1:-1;;;11219:2;11210:12;;11203:39;11270:2;11261:12;;11041:238;-1:-1;;11041:238;11288:311;;11448:67;11512:2;11507:3;11448:67;;;-1:-1;;;11528:34;;11590:2;11581:12;;11434:165;-1:-1;;11434:165;11608:332;;11768:67;11832:2;11827:3;11768:67;;;11868:34;11848:55;;11931:2;11922:12;;11754:186;-1:-1;;11754:186;11949:383;;12109:67;12173:2;12168:3;12109:67;;;12209:34;12189:55;;-1:-1;;;12273:2;12264:12;;12257:38;12323:2;12314:12;;12095:237;-1:-1;;12095:237;12341:370;;12501:67;12565:2;12560:3;12501:67;;;12601:34;12581:55;;-1:-1;;;12665:2;12656:12;;12649:25;12702:2;12693:12;;12487:224;-1:-1;;12487:224;12720:374;;12880:67;12944:2;12939:3;12880:67;;;12980:34;12960:55;;-1:-1;;;13044:2;13035:12;;13028:29;13085:2;13076:12;;12866:228;-1:-1;;12866:228;13103:313;;13263:67;13327:2;13322:3;13263:67;;;-1:-1;;;13343:36;;13407:2;13398:12;;13249:167;-1:-1;;13249:167;13425:296;;13602:83;13683:1;13678:3;13602:83;;13730:373;;13890:67;13954:2;13949:3;13890:67;;;13990:34;13970:55;;-1:-1;;;14054:2;14045:12;;14038:28;14094:2;14085:12;;13876:227;-1:-1;;13876:227;14112:316;;14272:67;14336:2;14331:3;14272:67;;;-1:-1;;;14352:39;;14419:2;14410:12;;14258:170;-1:-1;;14258:170;14437:313;;14597:67;14661:2;14656:3;14597:67;;;-1:-1;;;14677:36;;14741:2;14732:12;;14583:167;-1:-1;;14583:167;14759:378;;14919:67;14983:2;14978:3;14919:67;;;15019:34;14999:55;;-1:-1;;;15083:2;15074:12;;15067:33;15128:2;15119:12;;14905:232;-1:-1;;14905:232;15146:331;;15306:67;15370:2;15365:3;15306:67;;;15406:33;15386:54;;15468:2;15459:12;;15292:185;-1:-1;;15292:185;15485:113;15568:24;15586:5;15568:24;;15605:107;15684:22;15700:5;15684:22;;15719:370;;15917:147;16060:3;15917:147;;16096:213;16214:2;16199:18;;16228:71;16203:9;16272:6;16228:71;;16316:229;16442:2;16427:18;;16456:79;16431:9;16508:6;16456:79;;16552:467;16742:2;16727:18;;16756:79;16731:9;16808:6;16756:79;;;16846:80;16922:2;16911:9;16907:18;16898:6;16846:80;;;16937:72;17005:2;16994:9;16990:18;16981:6;16937:72;;17026:451;17208:2;17193:18;;17222:79;17197:9;17274:6;17222:79;;;17312:72;17380:2;17369:9;17365:18;17356:6;17312:72;;17484:340;17638:2;17623:18;;17652:79;17627:9;17704:6;17652:79;;;17742:72;17810:2;17799:9;17795:18;17786:6;17742:72;;17831:324;17977:2;17962:18;;17991:71;17966:9;18035:6;17991:71;;18162:201;18274:2;18259:18;;18288:65;18263:9;18326:6;18288:65;;18370:350;18529:2;18514:18;;18543:84;18518:9;18600:6;18543:84;;18727:301;18865:2;18879:47;;;18850:18;;18940:78;18850:18;19004:6;18940:78;;19035:407;19226:2;19240:47;;;19211:18;;19301:131;19211:18;19301:131;;19449:407;19640:2;19654:47;;;19625:18;;19715:131;19625:18;19715:131;;19863:407;20054:2;20068:47;;;20039:18;;20129:131;20039:18;20129:131;;20277:407;20468:2;20482:47;;;20453:18;;20543:131;20453:18;20543:131;;20691:407;20882:2;20896:47;;;20867:18;;20957:131;20867:18;20957:131;;21105:407;21296:2;21310:47;;;21281:18;;21371:131;21281:18;21371:131;;21519:407;21710:2;21724:47;;;21695:18;;21785:131;21695:18;21785:131;;21933:407;22124:2;22138:47;;;22109:18;;22199:131;22109:18;22199:131;;22347:407;22538:2;22552:47;;;22523:18;;22613:131;22523:18;22613:131;;22761:407;22952:2;22966:47;;;22937:18;;23027:131;22937:18;23027:131;;23175:407;23366:2;23380:47;;;23351:18;;23441:131;23351:18;23441:131;;23589:407;23780:2;23794:47;;;23765:18;;23855:131;23765:18;23855:131;;24003:407;24194:2;24208:47;;;24179:18;;24269:131;24179:18;24269:131;;24417:407;24608:2;24622:47;;;24593:18;;24683:131;24593:18;24683:131;;24831:407;25022:2;25036:47;;;25007:18;;25097:131;25007:18;25097:131;;25245:407;25436:2;25450:47;;;25421:18;;25511:131;25421:18;25511:131;;25659:407;25850:2;25864:47;;;25835:18;;25925:131;25835:18;25925:131;;26073:407;26264:2;26278:47;;;26249:18;;26339:131;26249:18;26339:131;;26487:407;26678:2;26692:47;;;26663:18;;26753:131;26663:18;26753:131;;26901:407;27092:2;27106:47;;;27077:18;;27167:131;27077:18;27167:131;;27315:407;27506:2;27520:47;;;27491:18;;27581:131;27491:18;27581:131;;27729:407;27920:2;27934:47;;;27905:18;;27995:131;27905:18;27995:131;;28143:213;28261:2;28246:18;;28275:71;28250:9;28319:6;28275:71;;28363:324;28509:2;28494:18;;28523:71;28498:9;28567:6;28523:71;;28694:435;28868:2;28853:18;;28882:71;28857:9;28926:6;28882:71;;;28964:72;29032:2;29021:9;29017:18;29008:6;28964:72;;29136:535;29332:3;29317:19;;29347:71;29321:9;29391:6;29347:71;;;29429:72;29497:2;29486:9;29482:18;29473:6;29429:72;;;29512;29580:2;29569:9;29565:18;29556:6;29512:72;;;29595:66;29657:2;29646:9;29642:18;29633:6;29595:66;;29678:547;29880:3;29865:19;;29895:71;29869:9;29939:6;29895:71;;;29977:72;30045:2;30034:9;30030:18;30021:6;29977:72;;;30060;30128:2;30117:9;30113:18;30104:6;30060:72;;;30143;30211:2;30200:9;30196:18;30187:6;30143:72;;30232:205;30346:2;30331:18;;30360:67;30335:9;30400:6;30360:67;;30444:256;30506:2;30500:9;30532:17;;;30607:18;30592:34;;30628:22;;;30589:62;30586:2;;;30664:1;30661;30654:12;30586:2;30680;30673:22;30484:216;;-1:-1;30484:216;30707:322;;30851:18;30843:6;30840:30;30837:2;;;30883:1;30880;30873:12;30837:2;-1:-1;31014:4;30950;30927:17;;;;-1:-1;;30923:33;31004:15;;30774:255;31036:122;31124:12;;31095:63;31166:144;31301:3;31279:31;-1:-1;31279:31;31319:163;31422:19;;;31471:4;31462:14;;31415:67;31490:91;;31552:24;31570:5;31552:24;;31694:85;31760:13;31753:21;;31736:43;31786:121;-1:-1;;;;;31848:54;;31831:76;31993:81;32064:4;32053:16;;32036:38;32081:129;;32168:37;32199:5;32217:147;;32309:50;32353:5;32309:50;;32743:145;32824:6;32819:3;32814;32801:30;-1:-1;32880:1;32862:16;;32855:27;32794:94;32897:268;32962:1;32969:101;32983:6;32980:1;32977:13;32969:101;;;33050:11;;;33044:18;33031:11;;;33024:39;33005:2;32998:10;32969:101;;;33085:6;33082:1;33079:13;33076:2;;;-1:-1;;33150:1;33132:16;;33125:27;32946:219;33173:97;33261:2;33241:14;-1:-1;;33237:28;;33221:49;33278:117;33347:24;33365:5;33347:24;;;33340:5;33337:35;33327:2;;33386:1;33383;33376:12;33542:111;33608:21;33623:5;33608:21;;33660:117;33729:24;33747:5;33729:24;;33784:113;33851:22;33867:5;33851:22;
Swarm Source
bzzr://1b55f4e85d25ec19534b6dce9285d84f0ee17accbec39864cf4ae689a3c46ae8
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.