Source Code
View more zero value Internal Transactions in Advanced View mode
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
HoprChannels
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity)
/**
*Submitted for verification at gnosisscan.io on 2023-08-30
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Multicall.sol)
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
abstract contract Multicall {
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
return results;
}
}
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/IERC1820Registry.sol)
/**
* @dev Interface of the global ERC1820 Registry, as defined in the
* https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
* implementers for interfaces in this registry, as well as query support.
*
* Implementers may be shared by multiple accounts, and can also implement more
* than a single interface for each account. Contracts can implement interfaces
* for themselves, but externally-owned accounts (EOA) must delegate this to a
* contract.
*
* {IERC165} interfaces can also be queried via the registry.
*
* For an in-depth explanation and source code analysis, see the EIP text.
*/
interface IERC1820Registry {
event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
event ManagerChanged(address indexed account, address indexed newManager);
/**
* @dev Sets `newManager` as the manager for `account`. A manager of an
* account is able to set interface implementers for it.
*
* By default, each account is its own manager. Passing a value of `0x0` in
* `newManager` will reset the manager to this initial state.
*
* Emits a {ManagerChanged} event.
*
* Requirements:
*
* - the caller must be the current manager for `account`.
*/
function setManager(address account, address newManager) external;
/**
* @dev Returns the manager for `account`.
*
* See {setManager}.
*/
function getManager(address account) external view returns (address);
/**
* @dev Sets the `implementer` contract as ``account``'s implementer for
* `interfaceHash`.
*
* `account` being the zero address is an alias for the caller's address.
* The zero address can also be used in `implementer` to remove an old one.
*
* See {interfaceHash} to learn how these are created.
*
* Emits an {InterfaceImplementerSet} event.
*
* Requirements:
*
* - the caller must be the current manager for `account`.
* - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
* end in 28 zeroes).
* - `implementer` must implement {IERC1820Implementer} and return true when
* queried for support, unless `implementer` is the caller. See
* {IERC1820Implementer-canImplementInterfaceForAddress}.
*/
function setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer) external;
/**
* @dev Returns the implementer of `interfaceHash` for `account`. If no such
* implementer is registered, returns the zero address.
*
* If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
* zeroes), `account` will be queried for support of it.
*
* `account` being the zero address is an alias for the caller's address.
*/
function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);
/**
* @dev Returns the interface hash for an `interfaceName`, as defined in the
* corresponding
* https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
*/
function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
/**
* @notice Updates the cache with whether the contract implements an ERC165 interface or not.
* @param account Address of the contract for which to update the cache.
* @param interfaceId ERC165 interface for which to update the cache.
*/
function updateERC165Cache(address account, bytes4 interfaceId) external;
/**
* @notice Checks whether a contract implements an ERC165 interface or not.
* If the result is not cached a direct lookup on the contract address is performed.
* If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
* {updateERC165Cache} with the contract address.
* @param account Address of the contract to check.
* @param interfaceId ERC165 interface to check.
* @return True if `account` implements `interfaceId`, false otherwise.
*/
function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
/**
* @notice Checks whether a contract implements an ERC165 interface or not without using or updating the cache.
* @param account Address of the contract to check.
* @param interfaceId ERC165 interface to check.
* @return True if `account` implements `interfaceId`, false otherwise.
*/
function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
}
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC1820Implementer.sol)
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Implementer.sol)
/**
* @dev Interface for an ERC1820 implementer, as defined in the
* https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP].
* Used by contracts that will be registered as implementers in the
* {IERC1820Registry}.
*/
interface IERC1820Implementer {
/**
* @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract
* implements `interfaceHash` for `account`.
*
* See {IERC1820Registry-setInterfaceImplementer}.
*/
function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32);
}
/**
* @dev Implementation of the {IERC1820Implementer} interface.
*
* Contracts may inherit from this and call {_registerInterfaceForAddress} to
* declare their willingness to be implementers.
* {IERC1820Registry-setInterfaceImplementer} should then be called for the
* registration to be complete.
*
* CAUTION: This file is deprecated as of v4.9 and will be removed in the next major release.
*/
contract ERC1820Implementer is IERC1820Implementer {
bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC");
mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces;
/**
* @dev See {IERC1820Implementer-canImplementInterfaceForAddress}.
*/
function canImplementInterfaceForAddress(
bytes32 interfaceHash,
address account
) public view virtual override returns (bytes32) {
return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00);
}
/**
* @dev Declares the contract as willing to be an implementer of
* `interfaceHash` for `account`.
*
* See {IERC1820Registry-setInterfaceImplementer} and
* {IERC1820Registry-interfaceHash}.
*/
function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual {
_supportedInterfaces[interfaceHash][account] = true;
}
}
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol)
/**
* @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
*
* Accounts can be notified of {IERC777} tokens being sent to them by having a
* contract implement this interface (contract holders can be their own
* implementer) and registering it on the
* https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
*
* See {IERC1820Registry} and {ERC1820Implementer}.
*/
interface IERC777Recipient {
/**
* @dev Called by an {IERC777} token contract whenever tokens are being
* moved or created into a registered account (`to`). The type of operation
* is conveyed by `from` being the zero address or not.
*
* This call occurs _after_ the token contract's state is updated, so
* {IERC777-balanceOf}, etc., can be used to query the post-operation state.
*
* This function may revert to prevent the operation from being executed.
*/
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external;
}
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
/**
* &&&&
* &&&&
* &&&&
* &&&& &&&&&&&&& &&&&&&&&&&&& &&&&&&&&&&/ &&&&.&&&&&&&&&
* &&&&&&&&& &&&&& &&&&&& &&&&&, &&&&& &&&&& &&&&&&&& &&&&
* &&&&&& &&&& &&&&# &&&& &&&&& &&&&& &&&&&& &&&&&
* &&&&& &&&&/ &&&& &&&& #&&&& &&&& &&&&&
* &&&& &&&& &&&&& &&&& &&&& &&&&& &&&&&
* %%%% /%%%% %%%%%% %%%%%% %%%% %%%%%%%%% %%%%%
* %%%%% %%%% %%%%%%%%%%% %%%% %%%%%% %%%%
* %%%%
* %%%%
* %%%%
*
* Bundles cryptographic primitives used by the HOPR protocol
*
*/
abstract contract HoprCrypto {
error InvalidFieldElement();
error InvalidCurvePoint();
error InvalidPointWitness();
// secp256k1: y^2 = x^3 + b (mod F_p)
uint256 internal constant SECP256K1_B = 0x0000000000000000000000000000000000000000000000000000000000000007;
// Field order created by secp256k1 curve
// solhint-disable-next-line max-line-length
uint256 internal constant SECP256K1_FIELD_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
// Order of the underlying field used for secp256k1
uint256 internal constant SECP256K1_BASE_FIELD_ORDER =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
// x-component of base point of secp256k1 curve
uint256 internal constant SECP256K1_BASE_POINT_X_COMPONENT =
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
// encoded sign of y-component of base point of secp256k1 curve
uint8 internal constant SECP256K1_BASE_POINT_Y_COMPONENT_SIGN = 27;
// E': y^2 = x^3 + A_PRIME + B_PRIME (mod F_p)
// used by `hash_to_curve` function
uint256 private constant A_PRIME = 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533;
uint256 private constant B_PRIME = 1771;
// Coefficients used for isogeneous mapping from E' to secp256k1
// see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#appx-iso-secp256k1
//
// used by `hash_to_curve` function
uint256 private constant K_10 = 0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7;
uint256 private constant K_11 = 0x07d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581;
uint256 private constant K_12 = 0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262;
uint256 private constant K_13 = 0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c;
uint256 private constant K_20 = 0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b;
uint256 private constant K_21 = 0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14;
uint256 private constant K_30 = 0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c;
uint256 private constant K_31 = 0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3;
uint256 private constant K_32 = 0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931;
uint256 private constant K_33 = 0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84;
uint256 private constant K_40 = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b;
uint256 private constant K_41 = 0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573;
uint256 private constant K_42 = 0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f;
// Coefficients used for simplified SWU mapping
// see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-suites-for-secp256k1
//
// used by `hash_to_curve` function
uint256 private constant Z = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC24;
uint256 private constant C_1 = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0b;
uint256 private constant C_2 = 0x31fdf302724013e57ad13fb38f842afeec184f00a74789dd286729c8303c4a59; // sqrt(-Z)
uint256 private constant KECCAK256_BLOCKSIZE = 136;
/**
* Holds a compact ECDSA signature, following ERC-2098
*/
struct CompactSignature {
bytes32 r;
bytes32 vs;
}
/**
* Checks whether given value is an element of the secp256k1 field
*
* @param el element to check
*/
function isFieldElementInternal(uint256 el) internal pure returns (bool) {
return 0 == el || el < SECP256K1_FIELD_ORDER;
}
/**
* Checks whether given coordinates of P fulfill the secp256k1 curve equation
*
* @param pX first component of P
* @param pY second component of P
*/
function isCurvePointInternal(uint256 pX, uint256 pY) internal pure returns (bool r) {
// solhint-disable-next-line no-inline-assembly
assembly {
r :=
eq(
mulmod(pY, pY, SECP256K1_BASE_FIELD_ORDER),
addmod(
SECP256K1_B,
mulmod(mulmod(pX, pX, SECP256K1_BASE_FIELD_ORDER), pX, SECP256K1_BASE_FIELD_ORDER),
SECP256K1_BASE_FIELD_ORDER
)
)
}
}
modifier isCurvePoint(uint256 pX, uint256 pY) {
if (!isCurvePointInternal(pX, pY)) {
revert InvalidCurvePoint();
}
_;
}
modifier isFieldElement(uint256 el) {
if (!isFieldElementInternal(el)) {
revert InvalidFieldElement();
}
_;
}
/**
* Takes a `scalar` and returns the Ethereum address associated to
* `scalar * G` where `G` is the base point of the secp256k1 curve.
*
* This function is necessary due to the missing ECMUL operation in Ethereum. It misuses the
* ECRECOVER precompile to perform the scalar multiplication in a gas-efficient way
*
* For more information see
* https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384
*
* @param scalar to multiply with secp256k1 base point
*/
function scalarTimesBasepoint(uint256 scalar) internal pure returns (address) {
return ecrecover(
0,
SECP256K1_BASE_POINT_Y_COMPONENT_SIGN,
bytes32(SECP256K1_BASE_POINT_X_COMPONENT),
bytes32(mulmod(scalar, SECP256K1_BASE_POINT_X_COMPONENT, SECP256K1_FIELD_ORDER))
);
}
/**
* Takes a curve point `P = (pX, pY)` and a scalar and returns the Ethereum address associated
* to the point `scalar * P` on the secp256k1 curve.
*
* This function is necessary due to the missing ECMUL operation in Ethereum. It misuses the
* ECRECOVER precompile to perform the scalar multiplication in a gas-efficient way
*
* For more information see
* https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384
*
* @param scalar values to multiply P with
* @param pX first component of P
* @param pY second component of P
*/
function scalarPointMultiplication(uint256 scalar, uint256 pX, uint256 pY) internal pure returns (address) {
uint8 sign;
if (pY % 2 == 0) {
sign = 27;
} else {
sign = 28;
}
return ecrecover(0, sign, bytes32(pX), bytes32(mulmod(scalar, pX, SECP256K1_FIELD_ORDER)));
}
/**
* Converts a curve point P to an Ethereum address.
*
* This function can be used to witness the result of a scalar
* multiplication.
*
* @param pX first component of P
* @param pY second component of P
*/
function pointToAddress(uint256 pX, uint256 pY) internal pure returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(pX, pY)))));
}
/**
* Adds two elliptic curve points P and Q using the general implementation.
*
* This function is optimized to perform one single point addition, e.g.
* when using in a VRF or hash_to_curve scheme.
*
* @dev Throws if Q = -P since Infinity point is not supported.
*
* @dev This function is meant to be part of another function and thus does
* not perform any sanity checks, such as if any of the given points
* fulfill the curve equation. These checks are left to the caller of
* the function.
*
* Optimizations:
* - solidity assembly
* - optimize for a single point addition
* - inline modular inversion
*
* @param pX first component of P
* @param pY second component of P
* @param qX first component of Q
* @param qY second component of Q
* @param a curve parameter, y^2 = x^3 + a*x + b (mod p)
*/
function ecAdd(
uint256 pX,
uint256 pY,
uint256 qX,
uint256 qY,
uint256 a
)
internal
view
returns (uint256 rx, uint256 ry)
{
// solhint-disable-next-line no-inline-assembly
assembly {
if and(eq(pX, qX), not(eq(pY, qY))) {
// Q = -P
// which means P + Q = P - P = 0 which is not supported
revert(0, 0)
}
let lambda
let toInvert
switch and(eq(pX, qX), eq(pY, qY))
// P == Q ?
case true {
// Point double
toInvert := addmod(mulmod(2, pY, SECP256K1_BASE_FIELD_ORDER), a, SECP256K1_BASE_FIELD_ORDER) // 2 * p.y
// compute (2 * p.y) ^ -1 using expmod precompile
let payload := mload(0x40)
mstore(payload, 0x20) // Length of Base
mstore(add(payload, 0x20), 0x20) // Length of Exponent
mstore(add(payload, 0x40), 0x20) // Length of Modulus
mstore(add(payload, 0x60), toInvert) // Base
mstore(add(payload, 0x80), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D) // p - 1
mstore(add(payload, 0xa0), SECP256K1_BASE_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, payload, 0xC0, payload, 0x20)) {
// 0x05 == expmod precompile
revert(0, 0)
}
lambda :=
mulmod( // (3 * p.x ^ 2) * (2 * p.y) ^ -1
mulmod( // 3 * p.x ^ 2
3, mulmod(pX, pX, SECP256K1_BASE_FIELD_ORDER), SECP256K1_BASE_FIELD_ORDER),
mload(payload),
SECP256K1_BASE_FIELD_ORDER
)
}
case false {
// Point addition
toInvert :=
addmod( // q.x - p.x
qX, // q.x
sub(SECP256K1_BASE_FIELD_ORDER, pX), // - p.x
SECP256K1_BASE_FIELD_ORDER
)
// compute (q.x - p.x) ^ -1 using expmod precompile
let payload := mload(0x40)
mstore(payload, 0x20) // Length of Base
mstore(add(payload, 0x20), 0x20) // Length of Exponent
mstore(add(payload, 0x40), 0x20) // Length of Modulus
mstore(add(payload, 0x60), toInvert) // Base
mstore(add(payload, 0x80), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D) // p - 1
mstore(add(payload, 0xa0), SECP256K1_BASE_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, payload, 0xC0, payload, 0x20)) {
// 0x05 == expmod precompile
revert(0, 0)
}
lambda :=
mulmod( // (q.y - p.y) * (q.x - p.x) ^ -1
addmod( // q.y - p.y
qY, // q.y
sub(SECP256K1_BASE_FIELD_ORDER, pY), // - p.y
SECP256K1_BASE_FIELD_ORDER
),
mload(payload), // (q.x - p.x) ^ -1
SECP256K1_BASE_FIELD_ORDER
)
}
rx :=
addmod( // lambda^2 - q.x - p.x
mulmod(lambda, lambda, SECP256K1_BASE_FIELD_ORDER), // lambda^2
addmod( // - q.x - p.x
sub(SECP256K1_BASE_FIELD_ORDER, qX), // - q.x
sub(SECP256K1_BASE_FIELD_ORDER, pX), // - p.x
SECP256K1_BASE_FIELD_ORDER
),
SECP256K1_BASE_FIELD_ORDER
)
ry :=
addmod( // lambda * (p.x - r.x) - p.y
mulmod( // lambda * (p.x - r.x)
lambda,
addmod( // p.x - r.x
pX, // p.x
sub(SECP256K1_BASE_FIELD_ORDER, rx), // - r.x
SECP256K1_BASE_FIELD_ORDER
),
SECP256K1_BASE_FIELD_ORDER
),
sub(SECP256K1_BASE_FIELD_ORDER, pY),
SECP256K1_BASE_FIELD_ORDER
)
}
}
/**
* Consumes a byte string and returns a pseudo-random secp256k1 curvepoint.
*
* Implements secp256k1_XMD:KECCAK_256_SSWU_RO_, see
* https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html
*
* @dev DSTs longer than 255 bytes are considered unsound.
* see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-domain-separation
*
* @param payload values "to hash"
* @param dst domain separation tag, used to makes protocol instantiations unique
*/
function hashToCurve(bytes memory payload, bytes memory dst) internal view returns (uint256 rx, uint256 ry) {
(uint256 u0, uint256 u1) = hash_to_field(payload, dst);
(uint256 q0x, uint256 q0y) = mapToCurveSimpleSWU(uint256(u0)); // on isogenous curve
(uint256 q1x, uint256 q1y) = mapToCurveSimpleSWU(uint256(u1)); // on isogenous curve
// P + Q on isogenous curve
(uint256 sx, uint256 sy) = ecAdd(q0x, q0y, q1x, q1y, A_PRIME);
return mapPoint(sx, sy);
}
/**
* Maps a curve point on E': y^2 = A'x^3 + B' to secp256k1. This function is necessary because
* A*B = 0 for secp256k1 which is why the simplified SWU mapping is not directly applicable.
*
* A' := 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533
* B' := 1771
* modulus 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F (same as secp256k1)
*
* see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#appx-iso-secp256k1
*
* Optimizations:
* - mathematical optimization: reduce expmod / mulmod / addmod operations
* - few temporary values to reduce memory expansion
* - use Solidity assembly
*
* @param pX first component of P
* @param pY second component of P
*/
function mapPoint(uint256 pX, uint256 pY) internal view returns (uint256 rx, uint256 ry) {
// solhint-disable-next-line no-inline-assembly
assembly {
let pxSquare := mulmod(pX, pX, SECP256K1_BASE_FIELD_ORDER) // p.x * p.x
let pxCubic := mulmod(pX, pxSquare, SECP256K1_BASE_FIELD_ORDER) // p.x * pxSquare
// xNum = k_(1,3) * x'^3 + k_(1,2) * x'^2 + k_(1,1) * x' + k_(1,0)
let xNum :=
addmod(
addmod(
mulmod(K_13, pxCubic, SECP256K1_BASE_FIELD_ORDER),
mulmod(K_12, pxSquare, SECP256K1_BASE_FIELD_ORDER),
SECP256K1_BASE_FIELD_ORDER
),
addmod(mulmod(K_11, pX, SECP256K1_BASE_FIELD_ORDER), K_10, SECP256K1_BASE_FIELD_ORDER),
SECP256K1_BASE_FIELD_ORDER
)
// xDen = x'^2 + k_(2,1) * x' + k_(2,0)
let xDen :=
addmod(
addmod(pxSquare, mulmod(K_21, pX, SECP256K1_BASE_FIELD_ORDER), SECP256K1_BASE_FIELD_ORDER),
K_20,
SECP256K1_BASE_FIELD_ORDER
)
// computes xDen ^ -1 using expmod precompile
let payload := mload(0x40)
mstore(payload, 0x20) // Length of Base
mstore(add(payload, 0x20), 0x20) // Length of Exponent
mstore(add(payload, 0x40), 0x20) // Length of Modulus
mstore(add(payload, 0x60), xDen) // Base
mstore(add(payload, 0x80), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D) // p - 1
mstore(add(payload, 0xa0), SECP256K1_BASE_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, payload, 0xC0, payload, 0x20)) {
// 0x05 == expmod precompile
revert(0, 0)
}
xDen := mload(payload)
// x = xNum / xDen
rx := mulmod(xNum, xDen, SECP256K1_BASE_FIELD_ORDER)
// y_num = k_(3,3) * x'^3 + k_(3,2) * x'^2 + k_(3,1) * x' + k_(3,0)
let y_num :=
addmod(
addmod(
mulmod(K_33, pxCubic, SECP256K1_BASE_FIELD_ORDER),
mulmod(K_32, pxSquare, SECP256K1_BASE_FIELD_ORDER),
SECP256K1_BASE_FIELD_ORDER
),
addmod(mulmod(K_31, pX, SECP256K1_BASE_FIELD_ORDER), K_30, SECP256K1_BASE_FIELD_ORDER),
SECP256K1_BASE_FIELD_ORDER
)
// y_den = x'^3 + k_(4,2) * x'^2 + k_(4,1) * x' + k_(4,0)
let y_den :=
addmod(
addmod(pxCubic, mulmod(K_42, pxSquare, SECP256K1_BASE_FIELD_ORDER), SECP256K1_BASE_FIELD_ORDER),
addmod(mulmod(K_41, pX, SECP256K1_BASE_FIELD_ORDER), K_40, SECP256K1_BASE_FIELD_ORDER),
SECP256K1_BASE_FIELD_ORDER
)
// Computes (y_den ^ -1) using expmod precompile
payload := mload(0x40)
mstore(payload, 0x20) // Length of Base
mstore(add(payload, 0x20), 0x20) // Length of Exponent
mstore(add(payload, 0x40), 0x20) // Length of Modulus
mstore(add(payload, 0x60), y_den) // Base
mstore(add(payload, 0x80), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D) // p - 1
mstore(add(payload, 0xa0), SECP256K1_BASE_FIELD_ORDER) // p
if iszero(staticcall(not(0), 0x05, payload, 0xC0, payload, 0x20)) {
// 0x05 == expmod precompile
revert(0, 0)
}
y_den := mload(payload)
// y = y' * y_num / y_den
ry := mulmod(mulmod(pY, y_num, SECP256K1_BASE_FIELD_ORDER), y_den, SECP256K1_BASE_FIELD_ORDER)
}
}
/**
* Takes a field element and returns a curve point on an elliptic curve that is 3-isogenous
* to secp256k1.
*
* Implements the simplified SWU mapping. Uses the optimized sample implementation from
* https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method
*
* Optimizations:
* - mathematical optimization: reduce expmod / mulmod / addmod operations
* - few temporary values to reduce memory expansion
* - Solidity assembly
*
* @param u the field element to map to a secp256k1 curve point
*/
function mapToCurveSimpleSWU(uint256 u) internal view returns (uint256 rx, uint256 ry) {
// solhint-disable-next-line no-inline-assembly
assembly {
let tv1 := mulmod(u, u, SECP256K1_BASE_FIELD_ORDER) // 1. tv1 = u^2
tv1 := mulmod(Z, tv1, SECP256K1_BASE_FIELD_ORDER) // 2. tv1 = Z * tv1
let tv2 := mulmod(tv1, tv1, SECP256K1_BASE_FIELD_ORDER) // 3. tv2 = tv1^2
tv2 := addmod(tv2, tv1, SECP256K1_BASE_FIELD_ORDER) // 4. tv2 = tv2 + tv1
let tv3 := addmod(tv2, 1, SECP256K1_BASE_FIELD_ORDER) // 5. tv3 = tv2 + 1
tv3 := mulmod(tv3, B_PRIME, SECP256K1_BASE_FIELD_ORDER) // 6. tv3 = B * tv3
let tv4
switch eq(tv2, 0)
// 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
case true { tv4 := Z }
case false { tv4 := sub(SECP256K1_BASE_FIELD_ORDER, tv2) }
tv4 := mulmod(A_PRIME, tv4, SECP256K1_BASE_FIELD_ORDER) // 8. tv4 = A * tv4
tv2 := mulmod(tv3, tv3, SECP256K1_BASE_FIELD_ORDER) // 9. tv2 = tv3^2
let tv6 := mulmod(tv4, tv4, SECP256K1_BASE_FIELD_ORDER) // 10. tv6 = tv4^2
let tv5 := mulmod(A_PRIME, tv6, SECP256K1_BASE_FIELD_ORDER) // 11. tv5 = A * tv6
tv2 := addmod(tv2, tv5, SECP256K1_BASE_FIELD_ORDER) // 12. tv2 = tv2 + tv5
tv2 := mulmod(tv2, tv3, SECP256K1_BASE_FIELD_ORDER) // 13. tv2 = tv2 * tv3
tv6 := mulmod(tv6, tv4, SECP256K1_BASE_FIELD_ORDER) // 14. tv6 = tv6 * tv4
tv5 := mulmod(B_PRIME, tv6, SECP256K1_BASE_FIELD_ORDER) // 15. tv5 = B * tv6
tv2 := addmod(tv2, tv5, SECP256K1_BASE_FIELD_ORDER) // 16. tv2 = tv2 + tv5
rx := mulmod(tv1, tv3, SECP256K1_BASE_FIELD_ORDER) // 17. x = tv1 * tv3
// 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
let y1
let isSquare
// sqrt_ratio_3mod4(u,v) subroutine
// tv1 -> tv7
// tv2 -> tv8
// u -> tv2
// v -> tv6
//
// Algorithm from:
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q-
// ===================================
{
let tv7 := mulmod(tv6, tv6, SECP256K1_BASE_FIELD_ORDER) // 1. tv1 = v^2
let tv8 := mulmod(tv2, tv6, SECP256K1_BASE_FIELD_ORDER) // 2. tv2 = u * v
tv7 := mulmod(tv7, tv8, SECP256K1_BASE_FIELD_ORDER) // 3. tv1 = tv1 * tv2
// 4. y1 = tv1^c1 (using expmod precompile)
let p := mload(0x40)
mstore(p, 0x20) // Length of Base
mstore(add(p, 0x20), 0x20) // Length of Exponent
mstore(add(p, 0x40), 0x20) // Length of Modulus
mstore(add(p, 0x60), tv7) // Base
mstore(add(p, 0x80), C_1) // Exponent
mstore(add(p, 0xa0), SECP256K1_BASE_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, p, 0xC0, p, 0x20)) {
// 0x05 == expmod precompile
revert(0, 0)
}
let y1Inner := mulmod(mload(p), tv8, SECP256K1_BASE_FIELD_ORDER) // 5. y1 = y1 * tv2
let y2Inner := mulmod(y1Inner, C_2, SECP256K1_BASE_FIELD_ORDER) // 6. y2 = y1 * c2
let tv9 := mulmod(y1Inner, y1Inner, SECP256K1_BASE_FIELD_ORDER) // 7. tv3 = y1^2
tv9 := mulmod(tv9, tv6, SECP256K1_BASE_FIELD_ORDER) // 8. tv3 = tv3 * v
switch eq(tv9, tv2)
// 9. isQR = tv3 == u
case true {
// 10. y = CMOV(y2, y1, isQR)
isSquare := true
y1 := y1Inner
}
case false {
isSquare := false
y1 := y2Inner
}
}
// =====================================
ry := mulmod(tv1, u, SECP256K1_BASE_FIELD_ORDER) // 19. y = tv1 * u
ry := mulmod(ry, y1, SECP256K1_BASE_FIELD_ORDER) // 20. y = y * y1
if isSquare {
rx := tv3 // 21. x = CMOV(x, tv3, is_gx1_square)
ry := y1 // 22. y = CMOV(y, y1, is_gx1_square)
}
// 23. e1 = sgn0(u) == sgn0(y)
if iszero(eq(mod(u, 2), mod(ry, 2))) {
// sgn0(x) ~= x % 2
ry := sub(SECP256K1_BASE_FIELD_ORDER, ry) // 24. y = CMOV(-y, y, e1)
}
// compute tv4 ^ -1
let payload := mload(0x40)
mstore(payload, 0x20) // Length of Base
mstore(add(payload, 0x20), 0x20) // Length of Exponent
mstore(add(payload, 0x40), 0x20) // Length of Modulus
mstore(add(payload, 0x60), tv4) // Base
mstore(add(payload, 0x80), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D) // p-2
mstore(add(payload, 0xa0), SECP256K1_BASE_FIELD_ORDER) // p
if iszero(staticcall(not(0), 0x05, payload, 0xC0, payload, 0x20)) {
// 0x05 == expmod precompile
revert(0, 0)
}
rx := mulmod(rx, mload(payload), SECP256K1_BASE_FIELD_ORDER) // 25. x = x / tv4
}
}
/**
* Takes an arbitrary byte-string and a domain seperation tag (dst) and returns
* two elements of the field used to create the secp256k1 curve.
*
* @dev DSTs longer than 255 bytes are considered unsound.
* see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-domain-separation
*
* @param message the message to hash
* @param dst domain separation tag, used to make protocol instantiations unique
*/
function hash_to_field(bytes memory message, bytes memory dst) internal view returns (uint256 u0, uint256 u1) {
(bytes32 b1, bytes32 b2, bytes32 b3) = expand_message_xmd_keccak256(message, dst);
// computes [...b1[..], ...b2[0..16]] ^ 1 mod n
// solhint-disable-next-line no-inline-assembly
assembly {
let p := mload(0x40) // next free memory slot
mstore(p, 0x30) // Length of Base
mstore(add(p, 0x20), 0x20) // Length of Exponent
mstore(add(p, 0x40), 0x20) // Length of Modulus
mstore(add(p, 0x60), b1) // Base
mstore(add(p, 0x80), b2)
mstore(add(p, 0x90), 1) // Exponent
mstore(add(p, 0xb0), SECP256K1_BASE_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, p, 0xD0, p, 0x20)) { revert(0, 0) }
u0 := mload(p)
}
// computes [...b2[16..32], ...b3[..]] ^ 1 mod n
// solhint-disable-next-line no-inline-assembly
assembly {
let p := mload(0x40)
mstore(p, 0x30) // Length of Base
mstore(add(p, 0x20), 0x20) // Length of Exponent
mstore(add(p, 0x50), b2)
mstore(add(p, 0x40), 0x20) // Length of Modulus
mstore(add(p, 0x70), b3) // Base
mstore(add(p, 0x90), 1) // Exponent
mstore(add(p, 0xb0), SECP256K1_BASE_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, p, 0xD0, p, 0x20)) { revert(0, 0) }
u1 := mload(p)
}
}
/**
* Takes an arbitrary bytestring and a domain seperation tag and returns a
* pseudo-random scalar in the secp256k1 curve field.
*
* @dev DSTs longer than 255 bytes are considered unsound.
* see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-domain-separation
*
* @param message the message to hash
* @param dst domain separation tag, used to make protocol instantiations unique
*/
function hashToScalar(bytes memory message, bytes memory dst) internal view returns (uint256 u) {
(bytes32 b1, bytes32 b2) = expand_message_xmd_keccak256_single(message, dst);
// computes [...b1[0..32], ...b2[0..16]] ^ 1 mod n
// solhint-disable-next-line no-inline-assembly
assembly {
let p := mload(0x40) // next free memory slot
mstore(p, 0x30) // Length of Base
mstore(add(p, 0x20), 0x20) // Length of Exponent
mstore(add(p, 0x40), 0x20) // Length of Modulus
mstore(add(p, 0x60), b1) // Base
mstore(add(p, 0x80), b2)
mstore(add(p, 0x90), 1) // Exponent
mstore(add(p, 0xb0), SECP256K1_FIELD_ORDER) // Modulus
if iszero(staticcall(not(0), 0x05, p, 0xD0, p, 0x20)) { revert(0, 0) }
u := mload(p)
}
}
/**
* Expands an arbitrary byte-string to 96 bytes using the `expand_message_xmd` method described in
* https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html
*
* Used for hash_to_curve functionality.
*
* @dev This is not a general implementation as the output length fixed. It is tailor-made
* for secp256k1_XMD:KECCAK_256_SSWU_RO_ hash_to_curve implementation.
*
* @dev DSTs longer than 255 bytes are considered unsound.
* see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-domain-separation
*
* @param message the message to hash
* @param dst domain separation tag, used to make protocol instantiations unique
*/
function expand_message_xmd_keccak256(
bytes memory message,
bytes memory dst
)
internal
pure
returns (bytes32 b1, bytes32 b2, bytes32 b3)
{
// solhint-disable-next-line no-inline-assembly
assembly {
if gt(mload(dst), 255) { revert(0, 0) }
let b0
{
// create payload for b0 hash
let b0Payload := mload(0x40)
// payload[0..KECCAK256_BLOCKSIZE] = 0
let b0PayloadO := KECCAK256_BLOCKSIZE // leave first block empty
let msg_o := 0x20 // skip length prefix
// payload[KECCAK256_BLOCKSIZE..KECCAK256_BLOCKSIZE+message.len()] = message[0..message.len()]
for { let i := 0 } lt(i, mload(message)) { i := add(i, 0x20) } {
mstore(add(b0Payload, b0PayloadO), mload(add(message, msg_o)))
b0PayloadO := add(b0PayloadO, 0x20)
msg_o := add(msg_o, 0x20)
}
// payload[KECCAK256_BLOCKSIZE+message.len()+1..KECCAK256_BLOCKSIZE+message.len()+2] = 96
b0PayloadO := add(mload(message), 137)
mstore8(add(b0Payload, b0PayloadO), 0x60) // only support for 96 bytes output length
let dstO := 0x20
b0PayloadO := add(b0PayloadO, 2)
// payload[KECCAK256_BLOCKSIZE+message.len()+3..KECCAK256_BLOCKSIZE+message.len()+dst.len()]
// = dst[0..dst.len()]
for { let i := 0 } lt(i, mload(dst)) { i := add(i, 0x20) } {
mstore(add(b0Payload, b0PayloadO), mload(add(dst, dstO)))
b0PayloadO := add(b0PayloadO, 0x20)
dstO := add(dstO, 0x20)
}
// payload[KECCAK256_BLOCKSIZE+message.len()+dst.len()..KECCAK256_BLOCKSIZE+message.len()+dst.len()+1]
// = dst.len()
b0PayloadO := add(add(mload(message), mload(dst)), 139)
mstore8(add(b0Payload, b0PayloadO), mload(dst))
b0 := keccak256(b0Payload, add(140, add(mload(dst), mload(message))))
}
// create payload for b1, b2 ... hashes
let bIPayload := mload(0x40)
mstore(bIPayload, b0)
// payload[32..33] = 1
mstore8(add(bIPayload, 0x20), 1)
let payloadO := 0x21
let dstO := 0x20
// payload[33..33+dst.len()] = dst[0..dst.len()]
for { let i := 0 } lt(i, mload(dst)) { i := add(i, 0x20) } {
mstore(add(bIPayload, payloadO), mload(add(dst, dstO)))
payloadO := add(payloadO, 0x20)
dstO := add(dstO, 0x20)
}
// payload[65+dst.len()..66+dst.len()] = dst.len()
mstore8(add(bIPayload, add(0x21, mload(dst))), mload(dst))
b1 := keccak256(bIPayload, add(34, mload(dst)))
// payload[0..32] = b0 XOR b1
mstore(bIPayload, xor(b0, b1))
// payload[32..33] = 2
mstore8(add(bIPayload, 0x20), 2)
b2 := keccak256(bIPayload, add(34, mload(dst)))
// payload[0..32] = b0 XOR b2
mstore(bIPayload, xor(b0, b2))
// payload[32..33] = 2
mstore8(add(bIPayload, 0x20), 3)
b3 := keccak256(bIPayload, add(34, mload(dst)))
}
}
/**
* Expands an arbitrary byte-string to 48 bytes using the `expand_message_xmd` method described in
* https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html
*
* Used for the VRF functionality.
*
* @dev This is not a general implementation as the output length fixed. It is tailor-made
* for secp256k1_XMD:KECCAK_256_SSWU_RO_ hash_to_curve implementation.
*
* @dev DSTs longer than 255 bytes are considered unsound.
* see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-domain-separation
*
* @param message the message to hash
* @param dst domain separation tag, used to make protocol instantiations unique
*/
function expand_message_xmd_keccak256_single(
bytes memory message,
bytes memory dst
)
internal
pure
returns (bytes32 b1, bytes32 b2)
{
// solhint-disable-next-line no-inline-assembly
assembly {
if gt(mload(dst), 255) { revert(0, 0) }
let b0
{
// create payload for b0 hash
let b0Payload := mload(0x40)
// payload[0..KECCAK256_BLOCKSIZE] = 0
let b0PayloadO := KECCAK256_BLOCKSIZE // leave first block empty
let msg_o := 0x20 // skip length prefix
// payload[KECCAK256_BLOCKSIZE..KECCAK256_BLOCKSIZE+message.len()] = message[0..message.len()]
for { let i := 0 } lt(i, mload(message)) { i := add(i, 0x20) } {
mstore(add(b0Payload, b0PayloadO), mload(add(message, msg_o)))
b0PayloadO := add(b0PayloadO, 0x20)
msg_o := add(msg_o, 0x20)
}
// payload[KECCAK256_BLOCKSIZE+message.len()+1..KECCAK256_BLOCKSIZE+message.len()+2] = 48
b0PayloadO := add(mload(message), 137)
mstore8(add(b0Payload, b0PayloadO), 0x30) // only support for 48 bytes output length
let dstO := 0x20
b0PayloadO := add(b0PayloadO, 2)
// payload[KECCAK256_BLOCKSIZE+message.len()+3..KECCAK256_BLOCKSIZE+message.len()+dst.len()]
// = dst[0..dst.len()]
for { let i := 0 } lt(i, mload(dst)) { i := add(i, 0x20) } {
mstore(add(b0Payload, b0PayloadO), mload(add(dst, dstO)))
b0PayloadO := add(b0PayloadO, 0x20)
dstO := add(dstO, 0x20)
}
// payload[KECCAK256_BLOCKSIZE+message.len()+dst.len()..KECCAK256_BLOCKSIZE+message.len()+dst.len()+1]
// = dst.len()
b0PayloadO := add(add(mload(message), mload(dst)), 139)
mstore8(add(b0Payload, b0PayloadO), mload(dst))
b0 := keccak256(b0Payload, add(140, add(mload(dst), mload(message))))
}
// create payload for b1, b2 ... hashes
let bIPayload := mload(0x40)
mstore(bIPayload, b0)
// payload[32..33] = 1
mstore8(add(bIPayload, 0x20), 1)
let payloadO := 0x21
let dstO := 0x20
// payload[33..33+dst.len()] = dst[0..dst.len()]
for { let i := 0 } lt(i, mload(dst)) { i := add(i, 0x20) } {
mstore(add(bIPayload, payloadO), mload(add(dst, dstO)))
payloadO := add(payloadO, 0x20)
dstO := add(dstO, 0x20)
}
// payload[65+dst.len()..66+dst.len()] = dst.len()
mstore8(add(bIPayload, add(0x21, mload(dst))), mload(dst))
b1 := keccak256(bIPayload, add(34, mload(dst)))
// payload[0..32] = b0 XOR b1
mstore(bIPayload, xor(b0, b1))
// payload[32..33] = 2
mstore8(add(bIPayload, 0x20), 2)
b2 := keccak256(bIPayload, add(34, mload(dst)))
}
}
/**
* Bundles values to verify the validity of the VRF
*/
struct VRFParameters {
// the main deterministic pseudo-random values
uint256 vx;
uint256 vy;
// s = r + h * a, where r, a are kept hidden
uint256 s;
// hash over computed values
uint256 h;
// Ethereum only supports scalar multiplication to address
// so we provide witnesses that are checked against
// computed values
uint256 sBx; // s * B
uint256 sBy;
uint256 hVx; // h * V
uint256 hVy;
}
/**
* Bundles payload used to create a VRF-generated deterministic
* pseudo-random value.
*/
struct VRFPayload {
// the main message, e.g. ticket Hash
bytes32 message;
// the "public key" of the signer,
// necessary to make VRF individual for each Ethereum account
address signer;
// domain separation tag, make each protocol instantiation,
// unique, such as staging and production environment,
// must be at most 255 bytes, otherwise considered unsound
// see https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-domain-separation
bytes dst;
}
/**
* Implements a VRF based on public-key cryptography using hash_to_curve primitive.
*
* Algorithm highly inspired by:
* https://www.signal.org/docs/specifications/xeddsa/#vxeddsa
*
* @param params necessary values verify validity of VRF
* @param payload values over which the VRF was computed, e.g. ticketHash
*/
function vrfVerify(VRFParameters memory params, VRFPayload memory payload) internal view returns (bool) {
if (params.h >= SECP256K1_BASE_FIELD_ORDER || params.s >= SECP256K1_BASE_FIELD_ORDER) {
revert InvalidFieldElement();
}
if (!isCurvePointInternal(params.vx, params.vy)) {
revert InvalidCurvePoint();
}
// we get a pseudo-random curve point
(uint256 bX, uint256 bY) = hashToCurve(abi.encodePacked(payload.signer, payload.message), payload.dst);
// Mitigate missing ECMUL operation by using precomputed values and verify
// against computed Ethereum address.
address sBvMaybe = scalarPointMultiplication(params.s, bX, bY);
if (sBvMaybe != pointToAddress(params.sBx, params.sBy)) {
revert InvalidPointWitness();
}
address hVMaybe = scalarPointMultiplication(params.h, params.vx, params.vy);
if (hVMaybe != pointToAddress(params.hVx, params.hVy)) {
revert InvalidPointWitness();
}
// We checked the validity of precomputed sB and hV values,
// now use them as if they were intermediate results.
// R = sB - hV
// solhint-disable-next-line max-line-length
(uint256 rx, uint256 ry) = ecAdd(params.sBx, params.sBy, params.hVx, SECP256K1_BASE_FIELD_ORDER - params.hVy, 0);
uint256 hCheck =
hashToScalar(abi.encodePacked(payload.signer, params.vx, params.vy, rx, ry, payload.message), payload.dst);
return hCheck == params.h;
}
}
abstract contract HoprLedgerEvents {
/**
* Emitted once the ledger domain separator is updated.
*/
event LedgerDomainSeparatorUpdated(bytes32 indexed ledgerDomainSeparator);
}
/**
* &&&&
* &&&&
* &&&&
* &&&& &&&&&&&&& &&&&&&&&&&&& &&&&&&&&&&/ &&&&.&&&&&&&&&
* &&&&&&&&& &&&&& &&&&&& &&&&&, &&&&& &&&&& &&&&&&&& &&&&
* &&&&&& &&&& &&&&# &&&& &&&&& &&&&& &&&&&& &&&&&
* &&&&& &&&&/ &&&& &&&& #&&&& &&&& &&&&&
* &&&& &&&& &&&&& &&&& &&&& &&&&& &&&&&
* %%%% /%%%% %%%%%% %%%%%% %%%% %%%%%%%%% %%%%%
* %%%%% %%%% %%%%%%%%%%% %%%% %%%%%% %%%%
* %%%%
* %%%%
* %%%%
*
* Indexes data trustlessly to allow a fast-sync for nodes in the network.
*/
abstract contract HoprLedger is HoprLedgerEvents {
string public constant LEDGER_VERSION = "1.0.0";
uint256 immutable snapshotInterval;
/**
* Stores the last indexer state
*
* Aligned to 1 EVM word
*/
struct RootStruct {
bytes28 rootHash;
// Overflow at year 2105
uint32 timestamp;
}
RootStruct latestRoot;
RootStruct latestSnapshotRoot;
bytes32 public ledgerDomainSeparator;
/**
* @param _snapshotInterval time in miliseconds to create a new snapshot
*/
constructor(uint256 _snapshotInterval) {
snapshotInterval = _snapshotInterval;
// take first 28 bytes
latestRoot.rootHash = bytes28(keccak256(abi.encodePacked(address(this))));
latestRoot.timestamp = uint32(block.timestamp);
latestSnapshotRoot = latestRoot;
// compute the domain separator on deployment
updateLedgerDomainSeparator();
}
/**
* @dev recompute the domain seperator in case of a fork
* This function should be called by anyone when required.
* An event is emitted when the domain separator is updated
*/
function updateLedgerDomainSeparator() public {
// following encoding guidelines of EIP712
bytes32 newLedgerDomainSeparator = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("HoprLedger")),
keccak256(bytes(LEDGER_VERSION)),
block.chainid,
address(this)
)
);
if (newLedgerDomainSeparator != ledgerDomainSeparator) {
ledgerDomainSeparator = newLedgerDomainSeparator;
emit LedgerDomainSeparatorUpdated(ledgerDomainSeparator);
}
}
function indexEvent(bytes memory payload) internal {
bool createSnapshot = false;
if (block.timestamp > latestRoot.timestamp + snapshotInterval) {
createSnapshot = true;
}
// take first 28 bytes
latestRoot.rootHash = bytes28(
keccak256(
// keep hashed data minimal
abi.encodePacked(
// ledger feed must be unique
ledgerDomainSeparator,
// Allows the verifier to detect up until which block the snapshot includes state changes
uint32(block.number),
// Bind result to previous root
latestRoot.rootHash,
// Information about the happened state change
keccak256(payload)
)
)
);
latestRoot.timestamp = uint32(block.timestamp);
if (createSnapshot) {
latestSnapshotRoot = latestRoot;
}
}
}
/**
* @title Interface for Avatar (Safe)
* Adapted from `IAvatar.sol` at commit 8a77e7b224af8004bd9f2ff4e2919642e93ffd85, which
* was audited https://github.com/gnosis/zodiac/tree/master/audits
* Added an additional function `getOwners()`
*/
/**
* @title Enum - Collection of enums used in Safe contracts.
* @author Richard Meissner - @rmeissner
*/
abstract contract Enum {
enum Operation {
Call,
DelegateCall
}
}
interface IAvatar {
function getOwners() external view returns (address[] memory);
/// @dev Enables a module on the avatar.
/// @notice Can only be called by the avatar.
/// @notice Modules should be stored as a linked list.
/// @notice Must emit EnabledModule(address module) if successful.
/// @param module Module to be enabled.
function enableModule(address module) external;
/// @dev Disables a module on the avatar.
/// @notice Can only be called by the avatar.
/// @notice Must emit DisabledModule(address module) if successful.
/// @param prevModule Address that pointed to the module to be removed in the linked list
/// @param module Module to be removed.
function disableModule(address prevModule, address module) external;
/// @dev Allows a Module to execute a transaction.
/// @notice Can only be called by an enabled module.
/// @notice Must emit ExecutionFromModuleSuccess(address module) if successful.
/// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful.
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.
function execTransactionFromModule(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation
)
external
returns (bool success);
/// @dev Allows a Module to execute a transaction and return data
/// @notice Can only be called by an enabled module.
/// @notice Must emit ExecutionFromModuleSuccess(address module) if successful.
/// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful.
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction: 0 == call, 1 == delegate call.
function execTransactionFromModuleReturnData(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation
)
external
returns (bool success, bytes memory returnData);
/// @dev Returns if an module is enabled
/// @return True if the module is enabled
function isModuleEnabled(address module) external view returns (bool);
/// @dev Returns array of modules.
/// @param start Start of the page.
/// @param pageSize Maximum number of modules that should be returned.
/// @return array Array of modules.
/// @return next Start of the next page.
function getModulesPaginated(
address start,
uint256 pageSize
)
external
view
returns (address[] memory array, address next);
}
/**
* @title HoprNodeManagementModule interface
*/
interface IHoprNodeManagementModule {
function isHoprNodeManagementModule() external view returns (bool);
function isNode(address nodeAddress) external view returns (bool);
}
abstract contract HoprNodeSafeRegistryEvents {
/**
* Emitted once a safe and node pair gets registered
*/
event RegisteredNodeSafe(address indexed safeAddress, address indexed nodeAddress);
/**
* Emitted once a safe and node pair gets deregistered
*/
event DergisteredNodeSafe(address indexed safeAddress, address indexed nodeAddress);
/**
* Emitted once the domain separator is updated.
*/
event DomainSeparatorUpdated(bytes32 indexed domainSeparator);
}
/**
* &&&&
* &&&&
* &&&&
* &&&& &&&&&&&&& &&&&&&&&&&&& &&&&&&&&&&/ &&&&.&&&&&&&&&
* &&&&&&&&& &&&&& &&&&&& &&&&&, &&&&& &&&&& &&&&&&&& &&&&
* &&&&&& &&&& &&&&# &&&& &&&&& &&&&& &&&&&& &&&&&
* &&&&& &&&&/ &&&& &&&& #&&&& &&&& &&&&&
* &&&& &&&& &&&&& &&&& &&&& &&&&& &&&&&
* %%%% /%%%% %%%%%% %%%%%% %%%% %%%%%%%%% %%%%%
* %%%%% %%%% %%%%%%%%%%% %%%% %%%%%% %%%%
* %%%%
* %%%%
* %%%%
*
* @title HoprNodeSafeRegistry
* @dev Node safe must prove that the Safe is the only authorized controller of
* the CHAIN_KEY address. This link between the Safe and node's chain-key address
* should be registered upon successful verification
*
* The CHAIN_KEY address should not be a contract
* The Safe addres should be a contract
* This implies that Safe and CHAIN_KEY address cannot be the same.
*
* This contract is meant to be deployed as a standalone contract
*/
contract HoprNodeSafeRegistry is HoprNodeSafeRegistryEvents {
using Address for address;
// Node already has mapped to Safe
error NodeHasSafe();
// Not a valid Safe address;
error NotValidSafe();
// Not a valid signature from node;
error NotValidSignatureFromNode();
// Safe address is zero
error SafeAddressZero();
// Node address is zero
error NodeAddressZero();
// Node address is a contract
error NodeIsContract();
// Provided address is not a member of an enabled NodeManagementModule
error NodeNotModuleMember();
// Structure to store the mapping between nodes and their associated Safe contracts
struct NodeSafeRecord {
address safeAddress;
uint96 nodeSigNonce;
}
// Structure to represent a node-safe pair with a nonce
struct NodeSafeNonce {
address safeAddress;
address nodeChainKeyAddress;
uint256 nodeSigNonce;
}
// Currently deployed version, starting with 1.0.0
string public constant VERSION = "1.0.0";
bytes32 public domainSeparator;
mapping(address => NodeSafeRecord) _nodeToSafe;
// NodeSafeNonce struct type hash.
// keccak256("NodeSafeNonce(address safeAddress,address nodeChainKeyAddress,uint256 nodeSigNonce)");
bytes32 public constant NODE_SAFE_TYPEHASH = hex"a8ac7aed128d1a2da0773fecc80b6265d15f7e62bf4401eb23bd46c3fcf5d2f8";
// start and end point for linked list of modules
address private constant SENTINEL_MODULES = address(0x1);
// page size of querying modules
uint256 private constant pageSize = 100;
/**
* @dev Constructor function to initialize the contract state.
* Computes the domain separator for EIP-712 verification.
*/
constructor() {
// compute the domain separator on deployment
updateDomainSeparator();
}
/**
* @dev Returns the Safe address associated with a specific node address.
* @param nodeAddress The address of the Hopr node.
* @return safeAddress The associated Safe address.
*/
function nodeToSafe(address nodeAddress) external view returns (address) {
return _nodeToSafe[nodeAddress].safeAddress;
}
/**
* @dev Returns the nonce of the signature for a specific node address.
* @param nodeAddress The address of the Hopr node.
* @return nodeSigNonce The nonce of the node's signature.
*/
function nodeSigNonce(address nodeAddress) external view returns (uint256) {
return _nodeToSafe[nodeAddress].nodeSigNonce;
}
/**
* @dev Checks whether a specific node-safe combination is registered.
* @param safeAddress Address of safe
* @param nodeChainKeyAddress Address of node
* @return registered Whether the node-safe combination is registered.
*/
function isNodeSafeRegistered(address safeAddress, address nodeChainKeyAddress) external view returns (bool) {
// If node is not registered to any safe, return false
if (_nodeToSafe[nodeChainKeyAddress].safeAddress == address(0)) {
return false;
}
return _nodeToSafe[nodeChainKeyAddress].safeAddress == safeAddress;
}
/**
* @dev Register the Safe with a signature from the node.
* This function can be called by any party.
* @param safeAddress Address of safe
* @param nodeChainKeyAddress Address of node
* @param sig The signature provided by the node.
*/
function registerSafeWithNodeSig(address safeAddress, address nodeChainKeyAddress, bytes calldata sig) external {
// check adminKeyAddress has added HOPR tokens to the staking contract.
// Compute the hash of the struct according to EIP712 guidelines
bytes32 hashStruct = keccak256(
abi.encode(
NODE_SAFE_TYPEHASH, safeAddress, nodeChainKeyAddress, _nodeToSafe[nodeChainKeyAddress].nodeSigNonce
)
);
// Build the typed digest for signature verification
bytes32 registerHash = keccak256(abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, hashStruct));
// Verify that the signature is from nodeChainKeyAddress
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(registerHash, sig);
if (error != ECDSA.RecoverError.NoError || recovered != nodeChainKeyAddress) {
revert NotValidSignatureFromNode();
}
// store those state, emit events etc.
addNodeSafe(safeAddress, nodeChainKeyAddress);
}
/**
* @dev Deregisters a Hopr node from its associated Safe and emits relevant events.
* This function can only be called by the associated Safe.
* @param nodeAddr The address of the Hopr node to be deregistered.
*/
function deregisterNodeBySafe(address nodeAddr) external {
// check this node was registered to the caller
if (_nodeToSafe[nodeAddr].safeAddress != msg.sender) {
revert NotValidSafe();
}
// Ensure that node is a member of the module
ensureNodeIsSafeModuleMember(msg.sender, nodeAddr);
// Update the state and emit the event
_nodeToSafe[nodeAddr].safeAddress = address(0);
emit DergisteredNodeSafe(msg.sender, nodeAddr);
}
/**
* @dev Registers a Safe by the node through a direct function call.
* This function is meant to be called by the Hopr node itself.
* @param safeAddr The address of the Safe to be registered.
*/
function registerSafeByNode(address safeAddr) external {
addNodeSafe(safeAddr, msg.sender);
}
/**
* @dev Recomputes the domain separator in case of a network fork or update.
* This function should be called by anyone when required.
* An event is emitted when the domain separator is updated
*/
function updateDomainSeparator() public {
// following encoding guidelines of EIP712
bytes32 newDomainSeparator = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("NodeSafeRegistry")),
keccak256(bytes(VERSION)),
block.chainid,
address(this)
)
);
if (newDomainSeparator != domainSeparator) {
domainSeparator = newDomainSeparator;
emit DomainSeparatorUpdated(domainSeparator);
}
}
/**
* @dev Internal function to store a node-safe pair and emit relevant events.
* @param safeAddress Address of safe
* @param nodeChainKeyAddress Address of node
*/
function addNodeSafe(address safeAddress, address nodeChainKeyAddress) internal {
// Safe address cannot be zero
if (safeAddress == address(0)) {
revert SafeAddressZero();
}
// Safe address cannot be zero
if (nodeChainKeyAddress == address(0)) {
revert NodeAddressZero();
}
// Ensure that the node address is not a contract address
if (nodeChainKeyAddress.isContract()) {
revert NodeIsContract();
}
// check this node hasn't been registered ower
if (_nodeToSafe[nodeChainKeyAddress].safeAddress != address(0)) {
revert NodeHasSafe();
}
// ensure that node is a member of the (enabled) NodeManagementModule
ensureNodeIsSafeModuleMember(safeAddress, nodeChainKeyAddress);
NodeSafeRecord storage record = _nodeToSafe[nodeChainKeyAddress];
// update record
record.safeAddress = safeAddress;
record.nodeSigNonce = record.nodeSigNonce + 1; // as of Solidity 0.8, this reverts on overflows
// update and emit event
emit RegisteredNodeSafe(safeAddress, nodeChainKeyAddress);
}
/**
* @dev Ensure that the node address is a member of
* the enabled node management module of the safe
* @param safeAddress Address of safe
* @param nodeChainKeyAddress Address of node
*/
function ensureNodeIsSafeModuleMember(address safeAddress, address nodeChainKeyAddress) internal view {
// nodeChainKeyAddress must be a member of the enabled node management module
address nextModule;
address[] memory modules;
// there may be many modules, loop through them. Stop at the end point of the linked list
while (nextModule != SENTINEL_MODULES) {
// get modules for safe
(modules, nextModule) = IAvatar(safeAddress).getModulesPaginated(SENTINEL_MODULES, pageSize);
for (uint256 i = 0; i < modules.length; i++) {
if (
IHoprNodeManagementModule(modules[i]).isHoprNodeManagementModule()
&& IHoprNodeManagementModule(modules[i]).isNode(nodeChainKeyAddress)
) {
return;
}
}
}
// if nodeChainKeyAddress is not a member of a valid HoprNodeManagementModule to the safe, revert
revert NodeNotModuleMember();
}
}
/**
* &&&&
* &&&&
* &&&&
* &&&& &&&&&&&&& &&&&&&&&&&&& &&&&&&&&&&/ &&&&.&&&&&&&&&
* &&&&&&&&& &&&&& &&&&&& &&&&&, &&&&& &&&&& &&&&&&&& &&&&
* &&&&&& &&&& &&&&# &&&& &&&&& &&&&& &&&&&& &&&&&
* &&&&& &&&&/ &&&& &&&& #&&&& &&&& &&&&&
* &&&& &&&& &&&&& &&&& &&&& &&&&& &&&&&
* %%%% /%%%% %%%%%% %%%%%% %%%% %%%%%%%%% %%%%%
* %%%%% %%%% %%%%%%%%%%% %%%% %%%%%% %%%%
* %%%%
* %%%%
* %%%%
*
* Provides modifiers to enforce usage of a MultiSig contract
*/
abstract contract HoprMultiSig {
error AlreadyInitialized();
error MultiSigUninitialized();
error ContractNotResponsible();
error InvalidSafeAddress();
HoprNodeSafeRegistry registry;
bool initialized = false;
/**
* Sets address of NodeSafeRegistry contract.
*
* @dev Must be called exactly once
*/
function setNodeSafeRegistry(HoprNodeSafeRegistry _registry) internal {
if (initialized) {
revert AlreadyInitialized();
}
if (address(_registry) == address(0)) {
revert InvalidSafeAddress();
}
initialized = true;
registry = _registry;
}
/**
* Enforces usage of Safe contract specified in NodeSafeRegistry
*/
modifier onlySafe(address self) {
if (!initialized) {
revert MultiSigUninitialized();
}
if (registry.nodeToSafe(self) != msg.sender) {
revert ContractNotResponsible();
}
_;
}
/**
* Only permits operation if no Safe contract has been specified
* in NodeSafeRegistry
*/
modifier noSafeSet() {
if (!initialized) {
revert MultiSigUninitialized();
}
if (registry.nodeToSafe(msg.sender) != address(0)) {
revert ContractNotResponsible();
}
_;
}
}
uint256 constant TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000; // in milliseconds
uint256 constant INDEX_SNAPSHOT_INTERVAL = TWENTY_FOUR_HOURS;
abstract contract HoprChannelsEvents {
/**
* Emitted once a channel is opened.
*
* Includes source and destination separately because mapping
* (source, destination) -> channelId destroys information.
*/
event ChannelOpened(address indexed source, address indexed destination);
/**
* Emitted once balance of a channel is increased, e.g. after opening a
* channel or redeeming a ticket.
*/
event ChannelBalanceIncreased(bytes32 indexed channelId, HoprChannels.Balance newBalance);
/**
* Emitted once balance of a channel is decreased, e.g. when redeeming
* a ticket or closing a channel.
*/
event ChannelBalanceDecreased(bytes32 indexed channelId, HoprChannels.Balance newBalance);
/**
* Emitted once a party initiates the closure of an outgoing
* channel. Includes the timestamp when the notice period is due.
*/
event OutgoingChannelClosureInitiated(bytes32 indexed channelId, HoprChannels.Timestamp closureTime);
/**
* Emitted once a channel closure is finalized.
*/
event ChannelClosed(bytes32 indexed channelId);
/**
* Emitted once a ticket is redeemed. Includes latest ticketIndex
* since this value is necessary for issuing and validating tickets.
*/
event TicketRedeemed(bytes32 indexed channelId, HoprChannels.TicketIndex newTicketIndex);
/**
* Emitted once the domain separator is updated.
*/
event DomainSeparatorUpdated(bytes32 indexed domainSeparator);
}
/**
* &&&&
* &&&&
* &&&&
* &&&& &&&&&&&&& &&&&&&&&&&&& &&&&&&&&&&/ &&&&.&&&&&&&&&
* &&&&&&&&& &&&&& &&&&&& &&&&&, &&&&& &&&&& &&&&&&&& &&&&
* &&&&&& &&&& &&&&# &&&& &&&&& &&&&& &&&&&& &&&&&
* &&&&& &&&&/ &&&& &&&& #&&&& &&&& &&&&&
* &&&& &&&& &&&&& &&&& &&&& &&&&& &&&&&
* %%%% /%%%% %%%%%% %%%%%% %%%% %%%%%%%%% %%%%%
* %%%%% %%%% %%%%%%%%%%% %%%% %%%%%% %%%%
* %%%%
* %%%%
* %%%%
*
* Manages mixnet incentives in the hopr network.
*
*/
contract HoprChannels is
IERC777Recipient,
ERC1820Implementer,
Multicall,
HoprLedger(INDEX_SNAPSHOT_INTERVAL),
HoprMultiSig,
HoprCrypto,
HoprChannelsEvents
{
// required by ERC1820 spec
IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
// required by ERC777 spec
bytes32 public constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
type Balance is uint96;
type TicketIndex is uint48;
type TicketIndexOffset is uint32;
type ChannelEpoch is uint24;
type Timestamp is uint32; // overflows in year 2105
// Using IEEE 754 double precision -> 53 significant bits
type WinProb is uint56;
error InvalidBalance();
error BalanceExceedsGlobalPerChannelAllowance();
error SourceEqualsDestination();
error ZeroAddress(string reason);
error TokenTransferFailed();
error InvalidNoticePeriod();
error NoticePeriodNotDue();
error WrongChannelState(string reason);
error InvalidTicketSignature();
error InvalidVRFProof();
error InsufficientChannelBalance();
error TicketIsNotAWin();
error InvalidAggregatedTicketInterval();
error WrongToken();
error InvalidTokenRecipient();
error InvalidTokensReceivedUsage();
Balance public constant MAX_USED_BALANCE = Balance.wrap(10 ** 25); // 1% of total supply, staking more is not sound
Balance public constant MIN_USED_BALANCE = Balance.wrap(1); // no empty token transactions
// ERC-777 tokensReceived hook, fundChannelMulti
uint256 public immutable ERC777_HOOK_FUND_CHANNEL_MULTI_SIZE =
abi.encodePacked(address(0), Balance.wrap(0), address(0), Balance.wrap(0)).length;
// ERC-777 tokensReceived hook, fundChannel
uint256 public immutable ERC777_HOOK_FUND_CHANNEL_SIZE = abi.encodePacked(address(0), address(0)).length;
string public constant VERSION = "2.0.0";
bytes32 public domainSeparator; // depends on chainId
/**
* @dev Channel state machine
* redeemTicket()
* ┌──────┐
* finalizeOutgoingChannelClosure() v │
* (after notice period), or ┌──────────────────────┐
* closeIncomingChannel() │ │ initiateOutgoingChannelClosure()
* ┌────────────────│ Pending To Close │<─────────────────┐
* │ │ │ │
* │ └──────────────────────┘ │
* v │
* ┌────────────┐ tokensReceived() / fundChannel() ┌──────────┐
* │ │──────────────────────────────────────────────>│ │
* │ Closed │ closeIncomingChannel() │ Open │
* │ │<──────────────────────────────────────────────│ │
* └────────────┘ └──────────┘
* │ ^
* └──────┘
* redeemTicket()
*/
enum ChannelStatus {
CLOSED,
OPEN,
PENDING_TO_CLOSE
}
/**
* Represents the state of a channel
*
* Aligned to 2 EVM words
*/
struct Channel {
// latest balance of the channel, changes whenever a ticket gets redeemed
Balance balance;
// prevents tickets from being replayed, increased with every redeemed ticket
TicketIndex ticketIndex;
// if set, timestamp once we can pull all funds from the channel
Timestamp closureTime;
// prevents tickets issued for older instantions to be replayed
ChannelEpoch epoch;
// current state of the channel
ChannelStatus status;
}
/**
* Represents a ticket that can be redeemed using `redeemTicket` function.
*
* Aligned to 2 EVM words
*/
struct TicketData {
// ticket is valid in this channel
bytes32 channelId;
// amount of tokens to transfer if ticket is a win
Balance amount;
// highest channel.ticketIndex to accept when redeeming
// ticket, used to aggregate tickets off-chain
TicketIndex ticketIndex;
// delta by which channel.ticketIndex gets increased when redeeming
// the ticket, should be set to 1 if ticket is not aggregated, and >1 if
// it is aggregated. Must never be <1.
TicketIndexOffset indexOffset;
// replay protection, invalidates all tickets once payment channel
// gets closed
ChannelEpoch epoch;
// encoded winning probability of the ticket
WinProb winProb;
}
/**
* Bundles data that is necessary to redeem a ticket
*/
struct RedeemableTicket {
// gives each ticket a unique identity and defines what this
// ticket is worth
TicketData data;
// signature by the ticket issuer
HoprCrypto.CompactSignature signature;
// proof-of-relay secret computed by ticket redeemer, after
// receiving keying material from next downstream node
uint256 porSecret;
}
/**
* Stores channels, indexed by their channelId
*/
mapping(bytes32 => Channel) public channels;
/**
* Token that will be used for all interactions.
*/
IERC20 public immutable token;
/**
* Notice period before fund from an outgoing channel can be pulled out.
*/
Timestamp public immutable noticePeriodChannelClosure; // in seconds
/**
* @param _token HoprToken address
* @param _noticePeriodChannelClosure seconds until a channel can be closed
* @param _safeRegistry address of the contract that maps from accounts to deployed Gnosis Safe instances
*/
constructor(address _token, Timestamp _noticePeriodChannelClosure, HoprNodeSafeRegistry _safeRegistry) {
if (Timestamp.unwrap(_noticePeriodChannelClosure) == 0) {
revert InvalidNoticePeriod();
}
require(_token != address(0), "token must not be empty");
setNodeSafeRegistry(_safeRegistry);
token = IERC20(_token);
noticePeriodChannelClosure = _noticePeriodChannelClosure;
_ERC1820_REGISTRY.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
updateDomainSeparator();
}
/**
* Assert that source and destination are good addresses, and distinct.
*/
modifier validateChannelParties(address source, address destination) {
if (source == destination) {
revert SourceEqualsDestination();
}
if (source == address(0)) {
revert ZeroAddress({ reason: "source must not be empty" });
}
if (destination == address(0)) {
revert ZeroAddress({ reason: "destination must not be empty" });
}
_;
}
modifier validateBalance(Balance balance) {
if (Balance.unwrap(balance) < Balance.unwrap(MIN_USED_BALANCE)) {
revert InvalidBalance();
}
if (Balance.unwrap(balance) > Balance.unwrap(MAX_USED_BALANCE)) {
revert BalanceExceedsGlobalPerChannelAllowance();
}
_;
}
/**
* @dev recompute the domain seperator in case of a fork
* This function should be called by anyone when required.
* An event is emitted when the domain separator is updated
*/
function updateDomainSeparator() public {
// following encoding guidelines of EIP712
bytes32 newDomainSeparator = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("HoprChannels")),
keccak256(bytes(VERSION)),
block.chainid,
address(this)
)
);
if (newDomainSeparator != domainSeparator) {
domainSeparator = newDomainSeparator;
emit DomainSeparatorUpdated(domainSeparator);
}
}
/**
* See `_redeemTicketInternal`, entrypoint for MultiSig contract
*/
function redeemTicketSafe(
address self,
RedeemableTicket calldata redeemable,
HoprCrypto.VRFParameters calldata params
)
external
HoprMultiSig.onlySafe(self)
{
_redeemTicketInternal(self, redeemable, params);
}
/**
* See `_redeemTicketInternal`
*/
function redeemTicket(
RedeemableTicket calldata redeemable,
HoprCrypto.VRFParameters calldata params
)
external
HoprMultiSig.noSafeSet()
{
_redeemTicketInternal(msg.sender, redeemable, params);
}
/**
* ~51k gas execution cost
* Claims the incentive for relaying a mixnet packet using probabilistic payments.
*
* Verifies the outcome of a 3-to-4-party protocol: creator of the packet, ticket
* issuer and ticket and next downstream node that acknowledges the reception and
* the validity of the relayed mixnet packet. In many cases, the creator of the
* packet and ticket redeemer is the same party.
*
* The packet creator states the challenge which gets fulfilled by presenting
* `porSecret` (Proof-Of-Relay). The ticket issuer creates the ticket and signs
* it. The provided signature acts as a source of entropy given by the ticket
* issuer. The ticket redeemer ultimately receives a packet with a ticket next
* to it. Once the ticket redeemer receives the acknowledgement from the next
* downstream node, it can compute `porSecret`.
*
* When submitting the ticket, the ticket redeemer creates a deterministic
* pseudo-random value that is verifiable by using its public key. This value is
* unique for each ticket and adds entropy that can only be known by the ticket
* redeemer.
*
* Tickets embed the incentive for relaying a single packet. To reduce on-chain
* state changes, they can get aggregated before submitting to this function.
*
* Aggregated tickets define an validity interval such that the redemption of any
* individual ticket invalidates the aggregated ticket and vice-versa.
*
* Used cryptographic primitives:
* - ECDSA signature
* - secp256k1 group homomorphism and DLP property
* - hash_to_curve using simplified Shallue, van de Woestijne method
* - Verifiable random function based on hash_to_curve
* - pseudorandomness of keccak256 function
*
* @dev This method makes use of several methods to reduce stack height.
*
* @param self account address of the ticket redeemer
* @param redeemable ticket, signature of ticket issuer, porSecret
* @param params pseudo-random VRF value + proof that it was correctly using
* ticket redeemer's private key
*/
function _redeemTicketInternal(
address self,
RedeemableTicket calldata redeemable,
HoprCrypto.VRFParameters calldata params
)
internal
validateBalance(redeemable.data.amount)
HoprCrypto.isFieldElement(redeemable.porSecret)
{
Channel storage spendingChannel = channels[redeemable.data.channelId];
if (spendingChannel.status != ChannelStatus.OPEN && spendingChannel.status != ChannelStatus.PENDING_TO_CLOSE) {
revert WrongChannelState({ reason: "spending channel must be OPEN or PENDING_TO_CLOSE" });
}
if (ChannelEpoch.unwrap(spendingChannel.epoch) != ChannelEpoch.unwrap(redeemable.data.epoch)) {
revert WrongChannelState({ reason: "channel epoch must match" });
}
// Aggregatable Tickets - validity interval:
// A ticket has a base index and an offset. The offset must be > 0,
// while the base index must be >= the currently set ticket index in the
// channel.
uint48 baseIndex = TicketIndex.unwrap(redeemable.data.ticketIndex);
uint32 baseIndexOffset = TicketIndexOffset.unwrap(redeemable.data.indexOffset);
uint48 currentIndex = TicketIndex.unwrap(spendingChannel.ticketIndex);
if (baseIndexOffset < 1 || baseIndex < currentIndex) {
revert InvalidAggregatedTicketInterval();
}
if (Balance.unwrap(spendingChannel.balance) < Balance.unwrap(redeemable.data.amount)) {
revert InsufficientChannelBalance();
}
// Deviates from EIP712 due to computed property and non-standard struct property encoding
bytes32 ticketHash = _getTicketHash(redeemable);
if (!_isWinningTicket(ticketHash, redeemable, params)) {
revert TicketIsNotAWin();
}
HoprCrypto.VRFPayload memory payload =
HoprCrypto.VRFPayload(ticketHash, self, abi.encodePacked(domainSeparator));
if (!vrfVerify(params, payload)) {
revert InvalidVRFProof();
}
address source = ECDSA.recover(ticketHash, redeemable.signature.r, redeemable.signature.vs);
if (_getChannelId(source, self) != redeemable.data.channelId) {
revert InvalidTicketSignature();
}
spendingChannel.ticketIndex = TicketIndex.wrap(baseIndex + baseIndexOffset);
spendingChannel.balance =
Balance.wrap(Balance.unwrap(spendingChannel.balance) - Balance.unwrap(redeemable.data.amount));
indexEvent(
abi.encodePacked(ChannelBalanceDecreased.selector, redeemable.data.channelId, spendingChannel.balance)
);
emit ChannelBalanceDecreased(redeemable.data.channelId, spendingChannel.balance);
bytes32 outgoingChannelId = _getChannelId(self, source);
Channel storage earningChannel = channels[outgoingChannelId];
// Informs about new ticketIndex
indexEvent(abi.encodePacked(TicketRedeemed.selector, redeemable.data.channelId, spendingChannel.ticketIndex));
emit TicketRedeemed(redeemable.data.channelId, spendingChannel.ticketIndex);
if (earningChannel.status == ChannelStatus.CLOSED) {
// The other channel does not exist, so we need to transfer funds directly
if (token.transfer(msg.sender, Balance.unwrap(redeemable.data.amount)) != true) {
revert TokenTransferFailed();
}
} else {
// this CAN produce channels with more stake than MAX_USED_AMOUNT - which does not lead
// to overflows since total supply < type(uin96).max
earningChannel.balance =
Balance.wrap(Balance.unwrap(earningChannel.balance) + Balance.unwrap(redeemable.data.amount));
indexEvent(abi.encodePacked(ChannelBalanceIncreased.selector, outgoingChannelId, earningChannel.balance));
emit ChannelBalanceIncreased(outgoingChannelId, earningChannel.balance);
}
}
/**
* See `_initiateOutgoingChannelClosureInternal`, entrypoint for MultiSig contract
*/
function initiateOutgoingChannelClosureSafe(
address self,
address destination
)
external
HoprMultiSig.onlySafe(self)
{
_initiateOutgoingChannelClosureInternal(self, destination);
}
/**
* See `_initiateOutgoingChannelClosureInternal`
*/
function initiateOutgoingChannelClosure(address destination) external HoprMultiSig.noSafeSet() {
_initiateOutgoingChannelClosureInternal(msg.sender, destination);
}
/**
* Prepares a channel to pull out funds from an outgoing channel.
*
* There is a notice period to give the other end, `destination`, the
* opportunity to redeem their collected tickets.
*
* @param destination destination end of the channel to close
*/
function _initiateOutgoingChannelClosureInternal(address self, address destination) internal {
// We can only initiate closure to outgoing channels
bytes32 channelId = _getChannelId(self, destination);
Channel storage channel = channels[channelId];
// calling initiateClosure on a PENDING_TO_CLOSE channel extends the noticePeriod
if (channel.status == ChannelStatus.CLOSED) {
revert WrongChannelState({ reason: "channel must have state OPEN or PENDING_TO_CLOSE" });
}
channel.closureTime =
Timestamp.wrap(Timestamp.unwrap(_currentBlockTimestamp()) + Timestamp.unwrap(noticePeriodChannelClosure));
channel.status = ChannelStatus.PENDING_TO_CLOSE;
// Inform others at which time the notice period is due
indexEvent(abi.encodePacked(OutgoingChannelClosureInitiated.selector, channelId, channel.closureTime));
emit OutgoingChannelClosureInitiated(channelId, channel.closureTime);
}
/**
* See `_closeIncomingChannelInternal`, entrypoint for MultiSig contract
*/
function closeIncomingChannelSafe(address self, address source) external HoprMultiSig.onlySafe(self) {
_closeIncomingChannelInternal(self, source);
}
/**
* See `_closeIncomingChannelInternal`
*/
function closeIncomingChannel(address source) external HoprMultiSig.noSafeSet() {
_closeIncomingChannelInternal(msg.sender, source);
}
/**
* Closes an incoming channel.
*
* This can happen immediately since it is up to the caller to
* redeem their collected tickets.
*
* @param source source end of the channel to close
*/
function _closeIncomingChannelInternal(address self, address source) internal {
// We can only close incoming channels directly
bytes32 channelId = _getChannelId(source, self);
Channel storage channel = channels[channelId];
if (channel.status == ChannelStatus.CLOSED) {
revert WrongChannelState({ reason: "channel must have state OPEN or PENDING_TO_CLOSE" });
}
uint256 balance = Balance.unwrap(channel.balance);
channel.status = ChannelStatus.CLOSED; // ChannelStatus.CLOSED == 0
channel.closureTime = Timestamp.wrap(0);
channel.ticketIndex = TicketIndex.wrap(0);
channel.balance = Balance.wrap(0);
// channel.epoch must be kept
indexEvent(abi.encodePacked(ChannelClosed.selector, channelId));
emit ChannelClosed(channelId);
if (balance > 0) {
if (token.transfer(source, balance) != true) {
revert TokenTransferFailed();
}
}
}
/**
* See `_finalizeOutgoingChannelClosureInternal`, entrypoint for MultiSig contract
*/
function finalizeOutgoingChannelClosureSafe(
address self,
address destination
)
external
HoprMultiSig.onlySafe(self)
{
_finalizeOutgoingChannelClosureInternal(self, destination);
}
/**
* See `_finalizeOutgoingChannelClosureInternal`
*/
function finalizeOutgoingChannelClosure(address destination) external HoprMultiSig.noSafeSet() {
_finalizeOutgoingChannelClosureInternal(msg.sender, destination);
}
/**
* Pulls out funds from an outgoing channel. Can be called once
* notice period is due.
*
* @param destination the address of the counterparty
*/
function _finalizeOutgoingChannelClosureInternal(address self, address destination) internal {
// We can only finalize closure to outgoing channels
bytes32 channelId = _getChannelId(self, destination);
Channel storage channel = channels[channelId];
if (channel.status != ChannelStatus.PENDING_TO_CLOSE) {
revert WrongChannelState({ reason: "channel state must be PENDING_TO_CLOSE" });
}
if (Timestamp.unwrap(channel.closureTime) >= Timestamp.unwrap(_currentBlockTimestamp())) {
revert NoticePeriodNotDue();
}
uint256 balance = Balance.unwrap(channel.balance);
channel.status = ChannelStatus.CLOSED; // ChannelStatus.CLOSED == 0
channel.closureTime = Timestamp.wrap(0);
channel.ticketIndex = TicketIndex.wrap(0);
channel.balance = Balance.wrap(0);
// channel.epoch must be kept
indexEvent(abi.encodePacked(ChannelClosed.selector, channelId));
emit ChannelClosed(channelId);
if (balance > 0) {
if (token.transfer(msg.sender, balance) != true) {
revert TokenTransferFailed();
}
}
}
/**
* ERC777.tokensReceived() hook, triggered by ERC777 token contract after
* transfering tokens.
*
* Depending on the userData payload, this method either funds one
* channel, or a bidirectional channel, consisting of two unidirectional
* channels.
*
* Channel source and destination are specified by the userData payload.
*
* @dev This function reverts if it results in a no-op, i.e., no state change occurs.
* @notice The opening of bidirectional channels is currently implemented for internal
* and community testing purposes only, and is not intended for production use.
* @param from The account from which the tokens have been transferred
* @param to The account to which the tokens have been transferred
* @param amount The amount of tokens transferred
* @param userData The payload that determines the intended action
*/
function tokensReceived(
address, // operator not needed
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata // operatorData not needed
)
external
override
{
// don't accept any other tokens ;-)
if (msg.sender != address(token)) {
revert WrongToken();
}
if (to != address(this)) {
revert InvalidTokenRecipient();
}
if (userData.length == 0) {
// ERC777.tokensReceived() hook got called by `ERC777.send()` or
// `ERC777.transferFrom()` which we can ignore at this point
return;
}
// Opens an outgoing channel
if (userData.length == ERC777_HOOK_FUND_CHANNEL_SIZE) {
if (amount > type(uint96).max) {
revert BalanceExceedsGlobalPerChannelAllowance();
}
address src;
address dest;
assembly {
src := shr(96, calldataload(userData.offset))
dest := shr(96, calldataload(add(userData.offset, 20)))
}
address safeAddress = registry.nodeToSafe(src);
// skip the check between `from` and `src` on node-safe registry
if (from == src) {
// node if opening an outgoing channel
if (safeAddress != address(0)) {
revert ContractNotResponsible();
}
} else {
if (safeAddress != from) {
revert ContractNotResponsible();
}
}
_fundChannelInternal(src, dest, Balance.wrap(uint96(amount)));
// Opens two channels, donating msg.sender's tokens
} else if (userData.length == ERC777_HOOK_FUND_CHANNEL_MULTI_SIZE) {
address account1;
Balance amount1;
address account2;
Balance amount2;
assembly {
account1 := shr(96, calldataload(userData.offset))
amount1 := shr(160, calldataload(add(0x14, userData.offset)))
account2 := shr(96, calldataload(add(0x20, userData.offset)))
amount2 := shr(160, calldataload(add(0x34, userData.offset)))
}
if (amount == 0 || amount != uint256(Balance.unwrap(amount1)) + uint256(Balance.unwrap(amount2))) {
revert InvalidBalance();
}
// fund channel in direction of: account1 -> account2
if (Balance.unwrap(amount1) > 0) {
_fundChannelInternal(account1, account2, amount1);
}
// fund channel in direction of: account2 -> account1
if (Balance.unwrap(amount2) > 0) {
_fundChannelInternal(account2, account1, amount2);
}
} else {
revert InvalidTokensReceivedUsage();
}
}
/**
* Fund an outgoing channel
* Used in channel operation with Safe
*
* @param self address of the source
* @param account address of the destination
* @param amount amount to fund for channel
*/
function fundChannelSafe(address self, address account, Balance amount) external HoprMultiSig.onlySafe(self) {
_fundChannelInternal(self, account, amount);
// pull tokens from Safe and handle result
if (token.transferFrom(msg.sender, address(this), Balance.unwrap(amount)) != true) {
// sth. went wrong, we need to revert here
revert TokenTransferFailed();
}
}
/**
* Fund an outgoing channel by a node
* @param account address of the destination
* @param amount amount to fund for channel
*/
function fundChannel(address account, Balance amount) external HoprMultiSig.noSafeSet() {
_fundChannelInternal(msg.sender, account, amount);
// pull tokens from funder and handle result
if (token.transferFrom(msg.sender, address(this), Balance.unwrap(amount)) != true) {
// sth. went wrong, we need to revert here
revert TokenTransferFailed();
}
}
/**
* @dev Internal function to fund an outgoing channel from self to account with amount token
* @notice only balance above zero can execute
*
* @param self source address
* @param account destination address
* @param amount token amount
*/
function _fundChannelInternal(
address self,
address account,
Balance amount
)
internal
validateBalance(amount)
validateChannelParties(self, account)
{
bytes32 channelId = _getChannelId(self, account);
Channel storage channel = channels[channelId];
if (channel.status == ChannelStatus.PENDING_TO_CLOSE) {
revert WrongChannelState({ reason: "cannot fund a channel that will close soon" });
}
channel.balance = Balance.wrap(Balance.unwrap(channel.balance) + Balance.unwrap(amount));
if (channel.status == ChannelStatus.CLOSED) {
// We are opening or reoping a channel
channel.epoch = ChannelEpoch.wrap(ChannelEpoch.unwrap(channel.epoch) + 1);
channel.ticketIndex = TicketIndex.wrap(0);
channel.status = ChannelStatus.OPEN;
indexEvent(abi.encodePacked(ChannelOpened.selector, self, account));
emit ChannelOpened(self, account);
}
indexEvent(abi.encodePacked(ChannelBalanceIncreased.selector, channelId, channel.balance));
emit ChannelBalanceIncreased(channelId, channel.balance);
}
// utility functions, no state changes involved
/**
* Computes the channel identifier
*
* @param source the address of source
* @param destination the address of destination
* @return the channel id
*/
function _getChannelId(address source, address destination) public pure returns (bytes32) {
return keccak256(abi.encodePacked(source, destination));
}
/**
* Gets the current block timestamp correctly sliced to uint32
*/
function _currentBlockTimestamp() public view returns (Timestamp) {
// solhint-disable-next-line
return Timestamp.wrap(uint32(block.timestamp));
}
/**
* Gets the hash of a ticket upon which the signature has been
* created. Also used by the VRF.
*
* Tickets come with a signature from the ticket issuer and state a
* challenge to be fulfilled when redeeming the ticket. As the validity
* of the signature need to be checked before being able reconstruct
* the response to the stated challenge, the signature includes the
* challenge - rather the response, whereas the smart contract
* requires the response.
*
* @param redeemable ticket data
*/
function _getTicketHash(RedeemableTicket calldata redeemable) public view returns (bytes32) {
address challenge = HoprCrypto.scalarTimesBasepoint(redeemable.porSecret);
// TicketData is aligned to exactly 2 EVM words, from which channelId
// takes one. Removing channelId can thus be encoded in 1 EVM word.
//
// Tickets get signed and transferred in packed encoding, consuming
// 148 bytes, including signature and challenge. Using tight encoding
// for ticket hash unifies on-chain and off-chain usage of tickets.
uint256 secondPart = (uint256(Balance.unwrap(redeemable.data.amount)) << 160)
| (uint256(TicketIndex.unwrap(redeemable.data.ticketIndex)) << 112)
| (uint256(TicketIndexOffset.unwrap(redeemable.data.indexOffset)) << 80)
| (uint256(ChannelEpoch.unwrap(redeemable.data.epoch)) << 56) | uint256(WinProb.unwrap(redeemable.data.winProb));
// Deviates from EIP712 due to computed property and non-standard struct property encoding
bytes32 hashStruct = keccak256(
abi.encode(
this.redeemTicket.selector,
keccak256(abi.encodePacked(redeemable.data.channelId, secondPart, challenge))
)
);
return keccak256(abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator, hashStruct));
}
/**
* Determines whether a ticket is considered a win.
*
* This is done by hashing values that must be revealed when redeeming tickets with
* a property stated in the signed ticket.
*
* @param ticketHash hash of the ticket to check
* @param redeemable ticket, opening, porSecret, signature
* @param params VRF values, entropy given by ticket redeemer
*/
function _isWinningTicket(
bytes32 ticketHash,
RedeemableTicket calldata redeemable,
HoprCrypto.VRFParameters calldata params
)
public
pure
returns (bool)
{
// hash function produces 256 bits output but we require only first 56 bits (IEEE 754 double precision means 53
// signifcant bits)
uint56 ticketProb = (
uint56(
bytes7(
keccak256(
abi.encodePacked(
// unique due to ticketIndex + ticketEpoch
ticketHash,
// use deterministic pseudo-random VRF output generated by redeemer
params.vx,
params.vy,
// challenge-response packet sender + next downstream node
redeemable.porSecret,
// entropy by ticket issuer, only ticket issuer can generate a valid signature
redeemable.signature.r,
redeemable.signature.vs
)
)
)
)
);
return ticketProb <= WinProb.unwrap(redeemable.data.winProb);
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"HoprChannels.Timestamp","name":"_noticePeriodChannelClosure","type":"uint32"},{"internalType":"contract HoprNodeSafeRegistry","name":"_safeRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceExceedsGlobalPerChannelAllowance","type":"error"},{"inputs":[],"name":"ContractNotResponsible","type":"error"},{"inputs":[],"name":"InsufficientChannelBalance","type":"error"},{"inputs":[],"name":"InvalidAggregatedTicketInterval","type":"error"},{"inputs":[],"name":"InvalidBalance","type":"error"},{"inputs":[],"name":"InvalidCurvePoint","type":"error"},{"inputs":[],"name":"InvalidFieldElement","type":"error"},{"inputs":[],"name":"InvalidNoticePeriod","type":"error"},{"inputs":[],"name":"InvalidPointWitness","type":"error"},{"inputs":[],"name":"InvalidSafeAddress","type":"error"},{"inputs":[],"name":"InvalidTicketSignature","type":"error"},{"inputs":[],"name":"InvalidTokenRecipient","type":"error"},{"inputs":[],"name":"InvalidTokensReceivedUsage","type":"error"},{"inputs":[],"name":"InvalidVRFProof","type":"error"},{"inputs":[],"name":"MultiSigUninitialized","type":"error"},{"inputs":[],"name":"NoticePeriodNotDue","type":"error"},{"inputs":[],"name":"SourceEqualsDestination","type":"error"},{"inputs":[],"name":"TicketIsNotAWin","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"WrongChannelState","type":"error"},{"inputs":[],"name":"WrongToken","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"HoprChannels.Balance","name":"newBalance","type":"uint96"}],"name":"ChannelBalanceDecreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"HoprChannels.Balance","name":"newBalance","type":"uint96"}],"name":"ChannelBalanceIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"channelId","type":"bytes32"}],"name":"ChannelClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":true,"internalType":"address","name":"destination","type":"address"}],"name":"ChannelOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"domainSeparator","type":"bytes32"}],"name":"DomainSeparatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"ledgerDomainSeparator","type":"bytes32"}],"name":"LedgerDomainSeparatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"HoprChannels.Timestamp","name":"closureTime","type":"uint32"}],"name":"OutgoingChannelClosureInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"HoprChannels.TicketIndex","name":"newTicketIndex","type":"uint48"}],"name":"TicketRedeemed","type":"event"},{"inputs":[],"name":"ERC777_HOOK_FUND_CHANNEL_MULTI_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC777_HOOK_FUND_CHANNEL_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LEDGER_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_USED_BALANCE","outputs":[{"internalType":"HoprChannels.Balance","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_USED_BALANCE","outputs":[{"internalType":"HoprChannels.Balance","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKENS_RECIPIENT_INTERFACE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_currentBlockTimestamp","outputs":[{"internalType":"HoprChannels.Timestamp","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"source","type":"address"},{"internalType":"address","name":"destination","type":"address"}],"name":"_getChannelId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"HoprChannels.Balance","name":"amount","type":"uint96"},{"internalType":"HoprChannels.TicketIndex","name":"ticketIndex","type":"uint48"},{"internalType":"HoprChannels.TicketIndexOffset","name":"indexOffset","type":"uint32"},{"internalType":"HoprChannels.ChannelEpoch","name":"epoch","type":"uint24"},{"internalType":"HoprChannels.WinProb","name":"winProb","type":"uint56"}],"internalType":"struct HoprChannels.TicketData","name":"data","type":"tuple"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct HoprCrypto.CompactSignature","name":"signature","type":"tuple"},{"internalType":"uint256","name":"porSecret","type":"uint256"}],"internalType":"struct HoprChannels.RedeemableTicket","name":"redeemable","type":"tuple"}],"name":"_getTicketHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"ticketHash","type":"bytes32"},{"components":[{"components":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"HoprChannels.Balance","name":"amount","type":"uint96"},{"internalType":"HoprChannels.TicketIndex","name":"ticketIndex","type":"uint48"},{"internalType":"HoprChannels.TicketIndexOffset","name":"indexOffset","type":"uint32"},{"internalType":"HoprChannels.ChannelEpoch","name":"epoch","type":"uint24"},{"internalType":"HoprChannels.WinProb","name":"winProb","type":"uint56"}],"internalType":"struct HoprChannels.TicketData","name":"data","type":"tuple"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct HoprCrypto.CompactSignature","name":"signature","type":"tuple"},{"internalType":"uint256","name":"porSecret","type":"uint256"}],"internalType":"struct HoprChannels.RedeemableTicket","name":"redeemable","type":"tuple"},{"components":[{"internalType":"uint256","name":"vx","type":"uint256"},{"internalType":"uint256","name":"vy","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"uint256","name":"h","type":"uint256"},{"internalType":"uint256","name":"sBx","type":"uint256"},{"internalType":"uint256","name":"sBy","type":"uint256"},{"internalType":"uint256","name":"hVx","type":"uint256"},{"internalType":"uint256","name":"hVy","type":"uint256"}],"internalType":"struct HoprCrypto.VRFParameters","name":"params","type":"tuple"}],"name":"_isWinningTicket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"interfaceHash","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"canImplementInterfaceForAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"channels","outputs":[{"internalType":"HoprChannels.Balance","name":"balance","type":"uint96"},{"internalType":"HoprChannels.TicketIndex","name":"ticketIndex","type":"uint48"},{"internalType":"HoprChannels.Timestamp","name":"closureTime","type":"uint32"},{"internalType":"HoprChannels.ChannelEpoch","name":"epoch","type":"uint24"},{"internalType":"enum HoprChannels.ChannelStatus","name":"status","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"source","type":"address"}],"name":"closeIncomingChannel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"self","type":"address"},{"internalType":"address","name":"source","type":"address"}],"name":"closeIncomingChannelSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"destination","type":"address"}],"name":"finalizeOutgoingChannelClosure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"self","type":"address"},{"internalType":"address","name":"destination","type":"address"}],"name":"finalizeOutgoingChannelClosureSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"HoprChannels.Balance","name":"amount","type":"uint96"}],"name":"fundChannel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"self","type":"address"},{"internalType":"address","name":"account","type":"address"},{"internalType":"HoprChannels.Balance","name":"amount","type":"uint96"}],"name":"fundChannelSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"destination","type":"address"}],"name":"initiateOutgoingChannelClosure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"self","type":"address"},{"internalType":"address","name":"destination","type":"address"}],"name":"initiateOutgoingChannelClosureSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ledgerDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"noticePeriodChannelClosure","outputs":[{"internalType":"HoprChannels.Timestamp","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"HoprChannels.Balance","name":"amount","type":"uint96"},{"internalType":"HoprChannels.TicketIndex","name":"ticketIndex","type":"uint48"},{"internalType":"HoprChannels.TicketIndexOffset","name":"indexOffset","type":"uint32"},{"internalType":"HoprChannels.ChannelEpoch","name":"epoch","type":"uint24"},{"internalType":"HoprChannels.WinProb","name":"winProb","type":"uint56"}],"internalType":"struct HoprChannels.TicketData","name":"data","type":"tuple"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct HoprCrypto.CompactSignature","name":"signature","type":"tuple"},{"internalType":"uint256","name":"porSecret","type":"uint256"}],"internalType":"struct HoprChannels.RedeemableTicket","name":"redeemable","type":"tuple"},{"components":[{"internalType":"uint256","name":"vx","type":"uint256"},{"internalType":"uint256","name":"vy","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"uint256","name":"h","type":"uint256"},{"internalType":"uint256","name":"sBx","type":"uint256"},{"internalType":"uint256","name":"sBy","type":"uint256"},{"internalType":"uint256","name":"hVx","type":"uint256"},{"internalType":"uint256","name":"hVy","type":"uint256"}],"internalType":"struct HoprCrypto.VRFParameters","name":"params","type":"tuple"}],"name":"redeemTicket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"self","type":"address"},{"components":[{"components":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"HoprChannels.Balance","name":"amount","type":"uint96"},{"internalType":"HoprChannels.TicketIndex","name":"ticketIndex","type":"uint48"},{"internalType":"HoprChannels.TicketIndexOffset","name":"indexOffset","type":"uint32"},{"internalType":"HoprChannels.ChannelEpoch","name":"epoch","type":"uint24"},{"internalType":"HoprChannels.WinProb","name":"winProb","type":"uint56"}],"internalType":"struct HoprChannels.TicketData","name":"data","type":"tuple"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct HoprCrypto.CompactSignature","name":"signature","type":"tuple"},{"internalType":"uint256","name":"porSecret","type":"uint256"}],"internalType":"struct HoprChannels.RedeemableTicket","name":"redeemable","type":"tuple"},{"components":[{"internalType":"uint256","name":"vx","type":"uint256"},{"internalType":"uint256","name":"vy","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"uint256","name":"h","type":"uint256"},{"internalType":"uint256","name":"sBx","type":"uint256"},{"internalType":"uint256","name":"sBy","type":"uint256"},{"internalType":"uint256","name":"hVx","type":"uint256"},{"internalType":"uint256","name":"hVy","type":"uint256"}],"internalType":"struct HoprCrypto.VRFParameters","name":"params","type":"tuple"}],"name":"redeemTicketSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"userData","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"tokensReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateDomainSeparator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateLedgerDomainSeparator","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6004805460ff60a01b191690556000610140819052610154819052610160819052610174819052604061012081905260a08190526101a08290526101b49190915260286101808190526101c890915260c0523480156200005e57600080fd5b5060405162004927380380620049278339810160408190526200008191620004e9565b6305265c0060808190526040516001600160601b03193060601b16602082015260340160408051808303601f190181529190528051602091820120901c600160e01b4263ffffffff90811682029290921760018190556001600160e01b038082169183900490931690910217600255620000fc906200024f16565b508163ffffffff16600003620001255760405163f9ee910760e01b815260040160405180910390fd5b6001600160a01b038316620001805760405162461bcd60e51b815260206004820152601760248201527f746f6b656e206d757374206e6f7420626520656d707479000000000000000000604482015260640160405180910390fd5b6200018b816200034e565b6001600160a01b03831660e05263ffffffff8216610100526040516329965a1d60e01b815230600482018190527fb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b60248301526044820152731820a4b7618bde71dce8cdc73aab6c95905fad24906329965a1d90606401600060405180830381600087803b1580156200021d57600080fd5b505af115801562000232573d6000803e3d6000fd5b5050505062000246620003ca60201b60201c565b50505062000545565b604080518082018252600a8152692437b8392632b233b2b960b11b6020918201528151808301835260058152640312e302e360dc1b90820152815160008051602062004907833981519152818301527f6cd681790c78c220517b099a737f8e85f69e797abe4e2910fb189b61db4bf2cd818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c0909101909252815191012060035481146200034b57600381905560405181907fa43fad83920fd09445855e854e73c9c532e17402c9ceb09993a2392843a5bdb990600090a25b50565b600454600160a01b900460ff1615620003795760405162dc149f60e41b815260040160405180910390fd5b6001600160a01b038116620003a15760405163474ebe2f60e11b815260040160405180910390fd5b600480546001600160a01b039092166001600160a81b031990921691909117600160a01b179055565b604080518082018252600c81526b486f70724368616e6e656c7360a01b6020918201528151808301835260058152640322e302e360dc1b90820152815160008051602062004907833981519152918101919091527f84e6908f343601d9ce9fb60d8250394eb8a51c56f1876bc1e017c97acd6567f2918101919091527fb4bcb154e38601c389396fa918314da42d4626f13ef6d0ceb07e5f5d26b2fbc360608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905060055481146200034b57600581905560405181907f771f5240ae5fd8a7640d3fb82fa70aab2fb1dbf35f2ef464f8509946717664c590600090a250565b6001600160a01b03811681146200034b57600080fd5b600080600060608486031215620004ff57600080fd5b83516200050c81620004d3565b602085015190935063ffffffff811681146200052757600080fd5b60408501519092506200053a81620004d3565b809150509250925092565b60805160a05160c05160e0516101005161433e620005c9600039600081816103d9015261262d0152600081816104d701528181610566015281816109120152818161160101528181612089015281816122f801526124dc0152600081816102a801526105d501526000818161032e015261073001526000612751015261433e6000f3fe608060405234801561001057600080fd5b50600436106101e45760003560e01c80637c8e28da1161010f578063c966c4fe116100a2578063fc0c546a11610071578063fc0c546a146104d2578063fc55309a14610511578063fcb7796f14610524578063ffa1ad741461053757600080fd5b8063c966c4fe14610487578063dc96fd5014610490578063ddad190214610498578063f698da25146104c957600080fd5b8063ac9650d8116100de578063ac9650d81461043b578063b920deed1461045b578063bda65f4514610461578063be9babdc1461047457600080fd5b80637c8e28da146103c157806387352d65146103d457806389ccfe89146104105780638c3710c91461041857600080fd5b806329392e3211610187578063651514bf11610156578063651514bf146102ef57806372581cc01461030257806378d8016d146103295780637a7ebd7b1461035057600080fd5b806329392e321461028357806344dae6f8146102a357806354a2edf5146102ca5780635d2f07c5146102dd57600080fd5b80631a7ffe7a116101c35780631a7ffe7a1461022457806323cb3ac01461023757806324086cc21461024a578063249cb3fa1461027057600080fd5b806223de29146101e95780630abec58f146101fe5780630cd88d7214610211575b600080fd5b6101fc6101f7366004613a87565b61055b565b005b6101fc61020c366004613b54565b610817565b6101fc61021f366004613bc7565b6109af565b6101fc610232366004613c07565b610a80565b6101fc610245366004613c07565b610b50565b61025d610258366004613c2b565b610c1d565b6040519081526020015b60405180910390f35b61025d61027e366004613c48565b610d8a565b61028b600181565b6040516001600160601b039091168152602001610267565b61025d7f000000000000000000000000000000000000000000000000000000000000000081565b6101fc6102d8366004613c78565b610de4565b61028b6a084595161401484a00000081565b6101fc6102fd366004613c78565b610eb9565b61025d7fb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b81565b61025d7f000000000000000000000000000000000000000000000000000000000000000081565b6103b061035e366004613ca6565b6006602052600090815260409020546001600160601b03811690600160601b810465ffffffffffff1690600160901b810463ffffffff1690600160b01b810462ffffff1690600160c81b900460ff1685565b604051610267959493929190613cd5565b6101fc6103cf366004613c07565b610f89565b6103fb7f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610267565b6101fc611056565b61042b610426366004613d38565b61116f565b6040519015158152602001610267565b61044e610449366004613d5f565b6111f1565b6040516102679190613e24565b426103fb565b6101fc61046f366004613c78565b6112e6565b61025d610482366004613c78565b6113b6565b61025d60035481565b6101fc6113fb565b6104bc604051806040016040528060058152602001640312e302e360dc1b81525081565b6040516102679190613e86565b61025d60055481565b6104f97f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610267565b6101fc61051f366004613e99565b611509565b6101fc610532366004613ece565b61169c565b6104bc604051806040016040528060058152602001640322e302e360dc1b81525081565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105a457604051635079ff7560e11b815260040160405180910390fd5b6001600160a01b03861630146105cd57604051631738922160e31b815260040160405180910390fd5b821561080d577f0000000000000000000000000000000000000000000000000000000000000000830361072e576001600160601b038511156106225760405163293ceef960e21b815260040160405180910390fd5b600480546040516302265e3160e61b81528635606090811c9382018490526014880135901c916000916001600160a01b03909116906389978c4090602401602060405180830381865afa15801561067d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a19190613efc565b9050826001600160a01b03168a6001600160a01b0316036106e9576001600160a01b038116156106e45760405163acd5a82360e01b815260040160405180910390fd5b61071b565b896001600160a01b0316816001600160a01b03161461071b5760405163acd5a82360e01b815260040160405180910390fd5b61072683838a61176a565b50505061080d565b7f000000000000000000000000000000000000000000000000000000000000000083036107f4578335606090811c90601486013560a090811c916020880135901c906034880135901c88158061079957506107956001600160601b03808316908516613f2f565b8914155b156107b75760405163c52e3eff60e01b815260040160405180910390fd5b6001600160601b038316156107d1576107d184838561176a565b6001600160601b038116156107eb576107eb82858361176a565b5050505061080d565b604051630d3dcde560e31b815260040160405180910390fd5b5050505050505050565b6004548390600160a01b900460ff16610843576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b69190613efc565b6001600160a01b0316146108dd5760405163acd5a82360e01b815260040160405180910390fd5b6108e884848461176a565b6040516323b872dd60e01b81523360048201523060248201526001600160601b03831660448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610963573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109879190613f42565b15156001146109a95760405163022e258160e11b815260040160405180910390fd5b50505050565b6004548390600160a01b900460ff166109db576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e9190613efc565b6001600160a01b031614610a755760405163acd5a82360e01b815260040160405180910390fd5b6109a9848484611b16565b600454600160a01b900460ff16610aaa576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015610af8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1c9190613efc565b6001600160a01b031614610b435760405163acd5a82360e01b815260040160405180910390fd5b610b4d3382612213565b50565b600454600160a01b900460ff16610b7a576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015610bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bec9190613efc565b6001600160a01b031614610c135760405163acd5a82360e01b815260040160405180910390fd5b610b4d338261238f565b600080610c2e836101000135612513565b90506000610c4260c0850160a08601613f64565b66ffffffffffffff166038610c5d60a0870160808801613f8d565b62ffffff16901b6050610c766080880160608901613fb2565b63ffffffff16901b6070610c906060890160408a01613fd8565b65ffffffffffff16901b60a0610cac60408a0160208b01614000565b6001600160601b0316901b171717179050600063fcb7796f60e01b85600001600001358385604051602001610d0193929190928352602083019190915260601b6001600160601b031916604082015260540190565b60408051808303601f1901815282825280516020918201206001600160e01b0319949094168184015282820193909352805180830382018152606083018252805190840120600554601960f81b6080850152600160f81b6081850152608284015260a2808401919091528151808403909101815260c29092019052805191012095945050505050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610db9576000610ddb565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b90505b92915050565b6004548290600160a01b900460ff16610e10576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610e5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e839190613efc565b6001600160a01b031614610eaa5760405163acd5a82360e01b815260040160405180910390fd5b610eb48383612213565b505050565b6004548290600160a01b900460ff16610ee5576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190613efc565b6001600160a01b031614610f7f5760405163acd5a82360e01b815260040160405180910390fd5b610eb4838361238f565b600454600160a01b900460ff16610fb3576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015611001573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110259190613efc565b6001600160a01b03161461104c5760405163acd5a82360e01b815260040160405180910390fd5b610b4d33826125d0565b604080518082018252600c81526b486f70724368616e6e656c7360a01b6020918201528151808301835260058152640322e302e360dc1b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f918101919091527f84e6908f343601d9ce9fb60d8250394eb8a51c56f1876bc1e017c97acd6567f2918101919091527fb4bcb154e38601c389396fa918314da42d4626f13ef6d0ceb07e5f5d26b2fbc360608201524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090506005548114610b4d57600581905560405181907f771f5240ae5fd8a7640d3fb82fa70aab2fb1dbf35f2ef464f8509946717664c590600090a250565b604080516020808201869052833582840152838101356060830152610100850135608083015260c0808601803560a08086019190915260e0808901358487015286518087039094018452909401909452805191012060009260c89190911c916111da91908601613f64565b66ffffffffffffff90811691161115949350505050565b60608167ffffffffffffffff81111561120c5761120c61401b565b60405190808252806020026020018201604052801561123f57816020015b606081526020019060019003908161122a5790505b50905060005b828110156112df576112af3085858481811061126357611263614031565b90506020028101906112759190614047565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061272092505050565b8282815181106112c1576112c1614031565b602002602001018190525080806112d79061408e565b915050611245565b5092915050565b6004548290600160a01b900460ff16611312576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015611361573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113859190613efc565b6001600160a01b0316146113ac5760405163acd5a82360e01b815260040160405180910390fd5b610eb483836125d0565b6040516001600160601b0319606084811b8216602084015283901b16603482015260009060480160405160208183030381529060405280519060200120905092915050565b604080518082018252600a8152692437b8392632b233b2b960b11b6020918201528151808301835260058152640312e302e360dc1b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f6cd681790c78c220517b099a737f8e85f69e797abe4e2910fb189b61db4bf2cd818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c090910190925281519101206003548114610b4d57600381905560405181907fa43fad83920fd09445855e854e73c9c532e17402c9ceb09993a2392843a5bdb990600090a250565b600454600160a01b900460ff16611533576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015611581573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a59190613efc565b6001600160a01b0316146115cc5760405163acd5a82360e01b815260040160405180910390fd5b6115d733838361176a565b6040516323b872dd60e01b81523360048201523060248201526001600160601b03821660448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af1158015611652573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116769190613f42565b15156001146116985760405163022e258160e11b815260040160405180910390fd5b5050565b600454600160a01b900460ff166116c6576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015611714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117389190613efc565b6001600160a01b03161461175f5760405163acd5a82360e01b815260040160405180910390fd5b611698338383611b16565b8060016001600160601b03821610156117965760405163c52e3eff60e01b815260040160405180910390fd5b6a084595161401484a0000006001600160601b03821611156117cb5760405163293ceef960e21b815260040160405180910390fd5b8383806001600160a01b0316826001600160a01b0316036117ff57604051634bd1d76960e11b815260040160405180910390fd5b6001600160a01b03821661185b5760405163eac0d38960e01b815260206004820152601860248201527f736f75726365206d757374206e6f7420626520656d707479000000000000000060448201526064015b60405180910390fd5b6001600160a01b0381166118b25760405163eac0d38960e01b815260206004820152601d60248201527f64657374696e6174696f6e206d757374206e6f7420626520656d7074790000006044820152606401611852565b60006118be87876113b6565b600081815260066020526040902090915060028154600160c81b900460ff1660028111156118ee576118ee613cbf565b0361194f5760405163499463c160e01b815260206004820152602a60248201527f63616e6e6f742066756e642061206368616e6e656c20746861742077696c6c2060448201526931b637b9b29039b7b7b760b11b6064820152608401611852565b80546119659087906001600160601b03166140a7565b81546001600160601b0319166001600160601b039190911617815560008154600160c81b900460ff16600281111561199f5761199f613cbf565b03611aaa5780546119bd90600160b01b900462ffffff1660016140c7565b815462ffffff91909116600160b01b026dff00000000000000ffffffffffff60601b19166dffffffff00000000ffffffffffff60601b1990911617600160c81b178155604080517fdd90f938230335e59dc925c57ecb0e27a28c2d87356e31f00cd5554abd6c1b2d602082015260608a811b6001600160601b03199081169383019390935289901b9091166054820152611a69906068015b604051602081830303815290604052612745565b866001600160a01b0316886001600160a01b03167fdd90f938230335e59dc925c57ecb0e27a28c2d87356e31f00cd5554abd6c1b2d60405160405180910390a35b8054604051611add91611a55916000805160206142c98339815191529186916001600160601b03909116906020016140e3565b80546040516001600160601b03909116815282906000805160206142c98339815191529060200160405180910390a25050505050505050565b611b266040830160208401614000565b60016001600160601b0382161015611b515760405163c52e3eff60e01b815260040160405180910390fd5b6a084595161401484a0000006001600160601b0382161115611b865760405163293ceef960e21b815260040160405180910390fd5b826101000135611b958161282b565b611bb257604051633ae4ed6b60e01b815260040160405180910390fd5b8335600090815260066020526040902060018154600160c81b900460ff166002811115611be157611be1613cbf565b14158015611c0c575060028154600160c81b900460ff166002811115611c0957611c09613cbf565b14155b15611c745760405163499463c160e01b815260206004820152603160248201527f7370656e64696e67206368616e6e656c206d757374206265204f50454e206f726044820152702050454e44494e475f544f5f434c4f534560781b6064820152608401611852565b611c8460a0860160808701613f8d565b8154600160b01b900462ffffff908116911614611ce45760405163499463c160e01b815260206004820152601860248201527f6368616e6e656c2065706f6368206d757374206d6174636800000000000000006044820152606401611852565b6000611cf66060870160408801613fd8565b90506000611d0a6080880160608901613fb2565b8354909150600160601b900465ffffffffffff16600163ffffffff83161080611d4257508065ffffffffffff168365ffffffffffff16105b15611d605760405163686e1e0f60e11b815260040160405180910390fd5b611d706040890160208a01614000565b84546001600160601b0391821691161015611d9e57604051632c51d8db60e21b815260040160405180910390fd5b6000611da989610c1d565b9050611db6818a8a61116f565b611dd35760405163ee835c8960e01b815260040160405180910390fd5b600060405180606001604052808381526020018c6001600160a01b03168152602001600554604051602001611e0a91815260200190565b60408051601f1981840301815291905290529050611e36611e30368b90038b018b614106565b8261284d565b611e53576040516312bfb7b760e31b815260040160405180910390fd5b6000611e688360c08d013560e08e0135612ad6565b90508a35611e76828e6113b6565b14611e94576040516366eea9ab60e11b815260040160405180910390fd5b611ea463ffffffff8616876141a4565b875465ffffffffffff91909116600160601b0265ffffffffffff60601b19909116178755611ed860408c0160208d01614000565b8754611eed91906001600160601b03166141c3565b87546001600160601b0319166001600160601b03919091169081178855604051611f4291611a55917f22e2a422a8860656a3a33cfa1daf771e76798ce5649747957235025de12e0b24918f35916020016140e3565b86546040516001600160601b0390911681528b35907f22e2a422a8860656a3a33cfa1daf771e76798ce5649747957235025de12e0b249060200160405180910390a26000611f908d836113b6565b9050600060066000838152602001908152602001600020905061201c7f7165e2ebc7ce35cc98cb7666f9945b3617f3f36326b76d18937ba5fecf18739a8e600001600001358b600001600c9054906101000a900465ffffffffffff16604051602001611a5593929190928352602083019190915260d01b6001600160d01b031916604082015260460190565b8854604051600160601b90910465ffffffffffff1681528d35907f7165e2ebc7ce35cc98cb7666f9945b3617f3f36326b76d18937ba5fecf18739a9060200160405180910390a260008154600160c81b900460ff16600281111561208257612082613cbf565b0361216c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb338f60000160200160208101906120cd9190614000565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526001600160601b031660248201526044016020604051808303816000875af1158015612121573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121459190613f42565b15156001146121675760405163022e258160e11b815260040160405180910390fd5b612203565b61217c60408e0160208f01614000565b815461219191906001600160601b03166140a7565b81546001600160601b0319166001600160601b039190911690811782556040516121d391611a55916000805160206142c98339815191529186916020016140e3565b80546040516001600160601b03909116815282906000805160206142c98339815191529060200160405180910390a25b5050505050505050505050505050565b600061221f82846113b6565b60008181526006602052604081209192508154600160c81b900460ff16600281111561224d5761224d613cbf565b0361226b5760405163499463c160e01b8152600401611852906141e3565b8054600163ff00000160b01b031981168255604080516000805160206142e983398151915260208201529081018490526001600160601b03909116906122b390606001611a55565b60405183906000805160206142e983398151915290600090a280156123885760405163a9059cbb60e01b81526001600160a01b038581166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044015b6020604051808303816000875af1158015612342573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123669190613f42565b15156001146123885760405163022e258160e11b815260040160405180910390fd5b5050505050565b600061239b83836113b6565b600081815260066020526040902090915060028154600160c81b900460ff1660028111156123cb576123cb613cbf565b146124285760405163499463c160e01b815260206004820152602660248201527f6368616e6e656c207374617465206d7573742062652050454e44494e475f544f6044820152655f434c4f534560d01b6064820152608401611852565b805463ffffffff428116600160901b9092041610612459576040516338b2019560e11b815260040160405180910390fd5b8054600163ff00000160b01b031981168255604080516000805160206142e983398151915260208201529081018490526001600160601b03909116906124a190606001611a55565b60405183906000805160206142e983398151915290600090a280156123885760405163a9059cbb60e01b8152336004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb90604401612323565b6000600181601b7f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179870014551231950b75fc4402da1732fc9bebe197f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179887096040805160008152602081018083529590955260ff909316928401929092526060830152608082015260a0016020604051602081039080840390855afa1580156125bf573d6000803e3d6000fd5b5050604051601f1901519392505050565b60006125dc83836113b6565b60008181526006602052604081209192508154600160c81b900460ff16600281111561260a5761260a613cbf565b036126285760405163499463c160e01b8152600401611852906141e3565b6126527f000000000000000000000000000000000000000000000000000000000000000042614233565b8154600160c91b67ff000000ffffffff60901b1990911660ff60c81b19600160901b63ffffffff949094168402161717808355604080517f07b5c950597fc3bed92e2ad37fa84f701655acb372982e486f5fad3607f04a5c602082015290810185905291900460e01b6001600160e01b03191660608201526126d690606401611a55565b8054604051600160901b90910463ffffffff16815282907f07b5c950597fc3bed92e2ad37fa84f701655acb372982e486f5fad3607f04a5c9060200160405180910390a250505050565b6060610ddb83836040518060600160405280602781526020016142a260279139612afc565b600154600090612783907f000000000000000000000000000000000000000000000000000000000000000090600160e01b900463ffffffff16613f2f565b42111561278e575060015b600354600154835160208086019190912060408051808401959095524360e01b6001600160e01b0319169085015291901b63ffffffff19166044830152606082015260800160408051601f19818403018152919052805160209182012063ffffffff4216600160e01b02911c1760015580156116985750506001546001600160e01b038116600160e01b9182900463ffffffff1690910217600255565b6000811580610dde57505070014551231950b75fc4402da1732fc9bebe191190565b60006401000003d019836060015110158061287257506401000003d019836040015110155b1561289057604051633ae4ed6b60e01b815260040160405180910390fd5b6128a283600001518460200151612b74565b6128bf57604051633922a54160e11b815260040160405180910390fd5b600080612911846020015185600001516040516020016128f892919060609290921b6001600160601b0319168252601482015260340190565b6040516020818303038152906040528560400151612b9f565b91509150600061292686604001518484612c25565b905061296186608001518760a00151604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6001600160a01b0316816001600160a01b03161461299257604051631dbfb9b360e31b815260040160405180910390fd5b60006129ab876060015188600001518960200151612c25565b90506129e68760c001518860e00151604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6001600160a01b0316816001600160a01b031614612a1757604051631dbfb9b360e31b815260040160405180910390fd5b600080612a4989608001518a60a001518b60c001518c60e001516401000003d019612a429190614250565b6000612cc4565b6020808b01518c518d8301518d51604051969850949650600095612ac195612aa8958a928a92910160609690961b6001600160601b03191686526014860194909452603485019290925260548401526074830152609482015260b40190565b6040516020818303038152906040528a60400151612e4b565b60608b01511497505050505050505092915050565b6000806000612ae6868686612ebc565b91509150612af381612ef5565b50949350505050565b6060600080856001600160a01b031685604051612b199190614263565b600060405180830381855af49150503d8060008114612b54576040519150601f19603f3d011682016040523d82523d6000602084013e612b59565b606091505b5091509150612b6a8683838761303f565b9695505050505050565b60006401000003d01980846401000003d019868709096007086401000003d019838409149392505050565b600080600080612baf86866130c0565b91509150600080612bbf8461317c565b91509150600080612bcf8561317c565b91509150600080612c03868686867f3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533612cc4565b91509150612c11828261343e565b9950995050505050505050505b9250929050565b600080612c3360028461427f565b600003612c425750601b612c46565b50601c5b60016000828670014551231950b75fc4402da1732fc9bebe19888a096040805160008152602081018083529590955260ff909316928401929092526060830152608082015260a0016020604051602081039080840390855afa158015612cb0573d6000803e3d6000fd5b5050604051601f1901519695505050505050565b600080838614198588141615612cd957600080fd5b600080858814878a141660018114612cf6578015612d7357612dee565b6401000003d019866401000003d0198b60020908915060405160208152602080820152602060408201528260608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa612d5357600080fd5b6401000003d01981516401000003d019808e8f0960030909935050612dee565b6401000003d0198a6401000003d019038908915060405160208152602080820152602060408201528260608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa612dce57600080fd5b6401000003d01981516401000003d0198c6401000003d019038b08099350505b50506401000003d01980896401000003d01903886401000003d01903086401000003d0198384090892506401000003d019876401000003d019036401000003d01980866401000003d019038c088409089150509550959350505050565b6000806000612e5a858561372b565b9150915060405160308152602080820152602060408201528260608201528160808201526001609082015270014551231950b75fc4402da1732fc9bebe1960b082015260208160d0836005600019fa612eb257600080fd5b5195945050505050565b6000806001600160ff1b03831681612ed960ff86901c601b613f2f565b9050612ee78782888561382b565b935093505050935093915050565b6000816004811115612f0957612f09613cbf565b03612f115750565b6001816004811115612f2557612f25613cbf565b03612f725760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611852565b6002816004811115612f8657612f86613cbf565b03612fd35760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611852565b6003816004811115612fe757612fe7613cbf565b03610b4d5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401611852565b606083156130ae5782516000036130a7576001600160a01b0385163b6130a75760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611852565b50816130b8565b6130b883836138ef565b949350505050565b60008060008060006130d28787613919565b9250925092506040516030815260208082015260206040820152836060820152826080820152600160908201526401000003d01960b082015260208160d0836005600019fa61312057600080fd5b80519550506040516030815260208082015282605082015260206040820152816070820152600160908201526401000003d01960b082015260208160d0836005600019fa61316d57600080fd5b80519450505050509250929050565b6000806401000003d0198384096401000003d019816401000003db190990506401000003d0198182096401000003d01982820890506401000003d019600182086401000003d0196106eb8209905060008215600181146131e15780156131ef576131fb565b6401000003db1991506131fb565b836401000003d0190391505b506401000003d019817f3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a4445330990506401000003d01982830992506401000003d0198182096401000003d019817f3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533096401000003d01981860894506401000003d01984860994506401000003d01983830991506401000003d019826106eb0990506401000003d0198186089450506401000003d01983860996506000806401000003d0198384096401000003d0198488096401000003d0198183099150604051602081526020808201526020604082015282606082015263400000f5600160fe1b0360808201526401000003d01960a082015260208160c0836005600019fa61332157600080fd5b6401000003d01982825109925050506401000003d0197f31fdf302724013e57ad13fb38f842afeec184f00a74789dd286729c8303c4a5982096401000003d0198283096401000003d0198682099050888114600181146133865780156133925761339a565b6001945083955061339a565b600094508295505b505050506401000003d0198a880997506401000003d019828909975080156133c3578498508197505b5050506002850660028806146133df57846401000003d0190394505b604051935060208452602080850152602060408501528060608501525050506401000003d21960808201526401000003d01960a082015260208160c0836005600019fa61342b57600080fd5b6401000003d01981518409925050915091565b6000806401000003d0198485096401000003d0198186096401000003d019807f8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c76401000003d019897f07d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c658109086401000003d01980857f534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262096401000003d019857f8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c0908086401000003d0197fd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b6401000003d019808a7fedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d140986080860405160208152602080820152602060408201528160608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa61359d57600080fd5b805191506401000003d01982840996506401000003d019807f4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c6401000003d0198c7fc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a309086401000003d01980887f29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931096401000003d019887f2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d8409080892506401000003d019806401000006c4196401000003d0198c7f7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c257309086401000003d01980887f6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f098708089450604051905060208152602080820152602060408201528460608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa61370d57600080fd5b5193506401000003d019905083818389090993505050509250929050565b60008060ff8351111561373d57600080fd5b60006040516088602060005b885181101561376a5788820151848401526020928301929182019101613749565b505060898751019050603081830153600201602060005b87518110156137a25787820151848401526020928301929182019101613781565b5050608b8651885101019050855181830153508551855101608c018120915050604051818152600160208201536021602060005b87518110156137f757878201518484015260209283019291820191016137d6565b5050508451855160210182015384516022018120935083821881526002602082015384516022018120925050509250929050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561386257506000905060036138e6565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156138b6573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166138df576000600192509250506138e6565b9150600090505b94509492505050565b8151156138ff5781518083602001fd5b8060405162461bcd60e51b81526004016118529190613e86565b600080600060ff8451111561392d57600080fd5b60006040516088602060005b895181101561395a5789820151848401526020928301929182019101613939565b505060898851019050606081830153600201602060005b88518110156139925788820151848401526020928301929182019101613971565b5050608b8751895101019050865181830153508651865101608c018120915050604051818152600160208201536021602060005b88518110156139e757888201518484015260209283019291820191016139c6565b5050508551865160210182015385516022018120945084821881526002602082015385516022018120935083821881526003602082015385516022018120925050509250925092565b6001600160a01b0381168114610b4d57600080fd5b60008083601f840112613a5757600080fd5b50813567ffffffffffffffff811115613a6f57600080fd5b602083019150836020828501011115612c1e57600080fd5b60008060008060008060008060c0898b031215613aa357600080fd5b8835613aae81613a30565b97506020890135613abe81613a30565b96506040890135613ace81613a30565b955060608901359450608089013567ffffffffffffffff80821115613af257600080fd5b613afe8c838d01613a45565b909650945060a08b0135915080821115613b1757600080fd5b50613b248b828c01613a45565b999c989b5096995094979396929594505050565b80356001600160601b0381168114613b4f57600080fd5b919050565b600080600060608486031215613b6957600080fd5b8335613b7481613a30565b92506020840135613b8481613a30565b9150613b9260408501613b38565b90509250925092565b60006101208284031215613bae57600080fd5b50919050565b60006101008284031215613bae57600080fd5b60008060006102408486031215613bdd57600080fd5b8335613be881613a30565b9250613bf78560208601613b9b565b9150613b92856101408601613bb4565b600060208284031215613c1957600080fd5b8135613c2481613a30565b9392505050565b60006101208284031215613c3e57600080fd5b610ddb8383613b9b565b60008060408385031215613c5b57600080fd5b823591506020830135613c6d81613a30565b809150509250929050565b60008060408385031215613c8b57600080fd5b8235613c9681613a30565b91506020830135613c6d81613a30565b600060208284031215613cb857600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b6001600160601b038616815265ffffffffffff8516602082015263ffffffff8416604082015262ffffff8316606082015260a0810160038310613d2857634e487b7160e01b600052602160045260246000fd5b8260808301529695505050505050565b60008060006102408486031215613d4e57600080fd5b83359250613bf78560208601613b9b565b60008060208385031215613d7257600080fd5b823567ffffffffffffffff80821115613d8a57600080fd5b818501915085601f830112613d9e57600080fd5b813581811115613dad57600080fd5b8660208260051b8501011115613dc257600080fd5b60209290920196919550909350505050565b60005b83811015613def578181015183820152602001613dd7565b50506000910152565b60008151808452613e10816020860160208601613dd4565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613e7957603f19888603018452613e67858351613df8565b94509285019290850190600101613e4b565b5092979650505050505050565b602081526000610ddb6020830184613df8565b60008060408385031215613eac57600080fd5b8235613eb781613a30565b9150613ec560208401613b38565b90509250929050565b6000806102208385031215613ee257600080fd5b613eec8484613b9b565b9150613ec5846101208501613bb4565b600060208284031215613f0e57600080fd5b8151613c2481613a30565b634e487b7160e01b600052601160045260246000fd5b80820180821115610dde57610dde613f19565b600060208284031215613f5457600080fd5b81518015158114613c2457600080fd5b600060208284031215613f7657600080fd5b813566ffffffffffffff81168114613c2457600080fd5b600060208284031215613f9f57600080fd5b813562ffffff81168114613c2457600080fd5b600060208284031215613fc457600080fd5b813563ffffffff81168114613c2457600080fd5b600060208284031215613fea57600080fd5b813565ffffffffffff81168114613c2457600080fd5b60006020828403121561401257600080fd5b610ddb82613b38565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261405e57600080fd5b83018035915067ffffffffffffffff82111561407957600080fd5b602001915036819003821315612c1e57600080fd5b6000600182016140a0576140a0613f19565b5060010190565b6001600160601b038181168382160190808211156112df576112df613f19565b62ffffff8181168382160190808211156112df576112df613f19565b928352602083019190915260a01b6001600160a01b0319166040820152604c0190565b600061010080838503121561411a57600080fd5b6040519081019067ffffffffffffffff8211818310171561414b57634e487b7160e01b600052604160045260246000fd5b81604052833581526020840135602082015260408401356040820152606084013560608201526080840135608082015260a084013560a082015260c084013560c082015260e084013560e0820152809250505092915050565b65ffffffffffff8181168382160190808211156112df576112df613f19565b6001600160601b038281168282160390808211156112df576112df613f19565b60208082526030908201527f6368616e6e656c206d7573742068617665207374617465204f50454e206f722060408201526f50454e44494e475f544f5f434c4f534560801b606082015260800190565b63ffffffff8181168382160190808211156112df576112df613f19565b81810381811115610dde57610dde613f19565b60008251614275818460208701613dd4565b9190910192915050565b60008261429c57634e487b7160e01b600052601260045260246000fd5b50069056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65645fa17246d3a5d68d42baa94cde33042180b783a399c02bf63ac2076e0f708738ceeab2eef998c17fe96f30f83fbf3c55fc5047f6e40c55a0cf72d236e9d2ba72a26469706673582212209ded07965f6328884d9e87e9794963325b6e7136a1a2366eeac2078a7a5d0fde64736f6c634300081300338b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c1000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000e15c24a0910311c83ac78b5930d771089e93077b
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101e45760003560e01c80637c8e28da1161010f578063c966c4fe116100a2578063fc0c546a11610071578063fc0c546a146104d2578063fc55309a14610511578063fcb7796f14610524578063ffa1ad741461053757600080fd5b8063c966c4fe14610487578063dc96fd5014610490578063ddad190214610498578063f698da25146104c957600080fd5b8063ac9650d8116100de578063ac9650d81461043b578063b920deed1461045b578063bda65f4514610461578063be9babdc1461047457600080fd5b80637c8e28da146103c157806387352d65146103d457806389ccfe89146104105780638c3710c91461041857600080fd5b806329392e3211610187578063651514bf11610156578063651514bf146102ef57806372581cc01461030257806378d8016d146103295780637a7ebd7b1461035057600080fd5b806329392e321461028357806344dae6f8146102a357806354a2edf5146102ca5780635d2f07c5146102dd57600080fd5b80631a7ffe7a116101c35780631a7ffe7a1461022457806323cb3ac01461023757806324086cc21461024a578063249cb3fa1461027057600080fd5b806223de29146101e95780630abec58f146101fe5780630cd88d7214610211575b600080fd5b6101fc6101f7366004613a87565b61055b565b005b6101fc61020c366004613b54565b610817565b6101fc61021f366004613bc7565b6109af565b6101fc610232366004613c07565b610a80565b6101fc610245366004613c07565b610b50565b61025d610258366004613c2b565b610c1d565b6040519081526020015b60405180910390f35b61025d61027e366004613c48565b610d8a565b61028b600181565b6040516001600160601b039091168152602001610267565b61025d7f000000000000000000000000000000000000000000000000000000000000002881565b6101fc6102d8366004613c78565b610de4565b61028b6a084595161401484a00000081565b6101fc6102fd366004613c78565b610eb9565b61025d7fb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b81565b61025d7f000000000000000000000000000000000000000000000000000000000000004081565b6103b061035e366004613ca6565b6006602052600090815260409020546001600160601b03811690600160601b810465ffffffffffff1690600160901b810463ffffffff1690600160b01b810462ffffff1690600160c81b900460ff1685565b604051610267959493929190613cd5565b6101fc6103cf366004613c07565b610f89565b6103fb7f000000000000000000000000000000000000000000000000000000000000012c81565b60405163ffffffff9091168152602001610267565b6101fc611056565b61042b610426366004613d38565b61116f565b6040519015158152602001610267565b61044e610449366004613d5f565b6111f1565b6040516102679190613e24565b426103fb565b6101fc61046f366004613c78565b6112e6565b61025d610482366004613c78565b6113b6565b61025d60035481565b6101fc6113fb565b6104bc604051806040016040528060058152602001640312e302e360dc1b81525081565b6040516102679190613e86565b61025d60055481565b6104f97f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c181565b6040516001600160a01b039091168152602001610267565b6101fc61051f366004613e99565b611509565b6101fc610532366004613ece565b61169c565b6104bc604051806040016040528060058152602001640322e302e360dc1b81525081565b336001600160a01b037f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c116146105a457604051635079ff7560e11b815260040160405180910390fd5b6001600160a01b03861630146105cd57604051631738922160e31b815260040160405180910390fd5b821561080d577f0000000000000000000000000000000000000000000000000000000000000028830361072e576001600160601b038511156106225760405163293ceef960e21b815260040160405180910390fd5b600480546040516302265e3160e61b81528635606090811c9382018490526014880135901c916000916001600160a01b03909116906389978c4090602401602060405180830381865afa15801561067d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a19190613efc565b9050826001600160a01b03168a6001600160a01b0316036106e9576001600160a01b038116156106e45760405163acd5a82360e01b815260040160405180910390fd5b61071b565b896001600160a01b0316816001600160a01b03161461071b5760405163acd5a82360e01b815260040160405180910390fd5b61072683838a61176a565b50505061080d565b7f000000000000000000000000000000000000000000000000000000000000004083036107f4578335606090811c90601486013560a090811c916020880135901c906034880135901c88158061079957506107956001600160601b03808316908516613f2f565b8914155b156107b75760405163c52e3eff60e01b815260040160405180910390fd5b6001600160601b038316156107d1576107d184838561176a565b6001600160601b038116156107eb576107eb82858361176a565b5050505061080d565b604051630d3dcde560e31b815260040160405180910390fd5b5050505050505050565b6004548390600160a01b900460ff16610843576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b69190613efc565b6001600160a01b0316146108dd5760405163acd5a82360e01b815260040160405180910390fd5b6108e884848461176a565b6040516323b872dd60e01b81523360048201523060248201526001600160601b03831660448201527f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c16001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610963573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109879190613f42565b15156001146109a95760405163022e258160e11b815260040160405180910390fd5b50505050565b6004548390600160a01b900460ff166109db576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e9190613efc565b6001600160a01b031614610a755760405163acd5a82360e01b815260040160405180910390fd5b6109a9848484611b16565b600454600160a01b900460ff16610aaa576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015610af8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1c9190613efc565b6001600160a01b031614610b435760405163acd5a82360e01b815260040160405180910390fd5b610b4d3382612213565b50565b600454600160a01b900460ff16610b7a576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015610bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bec9190613efc565b6001600160a01b031614610c135760405163acd5a82360e01b815260040160405180910390fd5b610b4d338261238f565b600080610c2e836101000135612513565b90506000610c4260c0850160a08601613f64565b66ffffffffffffff166038610c5d60a0870160808801613f8d565b62ffffff16901b6050610c766080880160608901613fb2565b63ffffffff16901b6070610c906060890160408a01613fd8565b65ffffffffffff16901b60a0610cac60408a0160208b01614000565b6001600160601b0316901b171717179050600063fcb7796f60e01b85600001600001358385604051602001610d0193929190928352602083019190915260601b6001600160601b031916604082015260540190565b60408051808303601f1901815282825280516020918201206001600160e01b0319949094168184015282820193909352805180830382018152606083018252805190840120600554601960f81b6080850152600160f81b6081850152608284015260a2808401919091528151808403909101815260c29092019052805191012095945050505050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610db9576000610ddb565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b90505b92915050565b6004548290600160a01b900460ff16610e10576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610e5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e839190613efc565b6001600160a01b031614610eaa5760405163acd5a82360e01b815260040160405180910390fd5b610eb48383612213565b505050565b6004548290600160a01b900460ff16610ee5576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015610f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190613efc565b6001600160a01b031614610f7f5760405163acd5a82360e01b815260040160405180910390fd5b610eb4838361238f565b600454600160a01b900460ff16610fb3576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015611001573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110259190613efc565b6001600160a01b03161461104c5760405163acd5a82360e01b815260040160405180910390fd5b610b4d33826125d0565b604080518082018252600c81526b486f70724368616e6e656c7360a01b6020918201528151808301835260058152640322e302e360dc1b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f918101919091527f84e6908f343601d9ce9fb60d8250394eb8a51c56f1876bc1e017c97acd6567f2918101919091527fb4bcb154e38601c389396fa918314da42d4626f13ef6d0ceb07e5f5d26b2fbc360608201524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090506005548114610b4d57600581905560405181907f771f5240ae5fd8a7640d3fb82fa70aab2fb1dbf35f2ef464f8509946717664c590600090a250565b604080516020808201869052833582840152838101356060830152610100850135608083015260c0808601803560a08086019190915260e0808901358487015286518087039094018452909401909452805191012060009260c89190911c916111da91908601613f64565b66ffffffffffffff90811691161115949350505050565b60608167ffffffffffffffff81111561120c5761120c61401b565b60405190808252806020026020018201604052801561123f57816020015b606081526020019060019003908161122a5790505b50905060005b828110156112df576112af3085858481811061126357611263614031565b90506020028101906112759190614047565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061272092505050565b8282815181106112c1576112c1614031565b602002602001018190525080806112d79061408e565b915050611245565b5092915050565b6004548290600160a01b900460ff16611312576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b81526001600160a01b03848116938201939093523392909116906389978c4090602401602060405180830381865afa158015611361573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113859190613efc565b6001600160a01b0316146113ac5760405163acd5a82360e01b815260040160405180910390fd5b610eb483836125d0565b6040516001600160601b0319606084811b8216602084015283901b16603482015260009060480160405160208183030381529060405280519060200120905092915050565b604080518082018252600a8152692437b8392632b233b2b960b11b6020918201528151808301835260058152640312e302e360dc1b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f6cd681790c78c220517b099a737f8e85f69e797abe4e2910fb189b61db4bf2cd818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c090910190925281519101206003548114610b4d57600381905560405181907fa43fad83920fd09445855e854e73c9c532e17402c9ceb09993a2392843a5bdb990600090a250565b600454600160a01b900460ff16611533576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015611581573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a59190613efc565b6001600160a01b0316146115cc5760405163acd5a82360e01b815260040160405180910390fd5b6115d733838361176a565b6040516323b872dd60e01b81523360048201523060248201526001600160601b03821660448201527f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c16001600160a01b0316906323b872dd906064016020604051808303816000875af1158015611652573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116769190613f42565b15156001146116985760405163022e258160e11b815260040160405180910390fd5b5050565b600454600160a01b900460ff166116c6576040516308a9441960e31b815260040160405180910390fd5b600480546040516302265e3160e61b815233928101929092526000916001600160a01b03909116906389978c4090602401602060405180830381865afa158015611714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117389190613efc565b6001600160a01b03161461175f5760405163acd5a82360e01b815260040160405180910390fd5b611698338383611b16565b8060016001600160601b03821610156117965760405163c52e3eff60e01b815260040160405180910390fd5b6a084595161401484a0000006001600160601b03821611156117cb5760405163293ceef960e21b815260040160405180910390fd5b8383806001600160a01b0316826001600160a01b0316036117ff57604051634bd1d76960e11b815260040160405180910390fd5b6001600160a01b03821661185b5760405163eac0d38960e01b815260206004820152601860248201527f736f75726365206d757374206e6f7420626520656d707479000000000000000060448201526064015b60405180910390fd5b6001600160a01b0381166118b25760405163eac0d38960e01b815260206004820152601d60248201527f64657374696e6174696f6e206d757374206e6f7420626520656d7074790000006044820152606401611852565b60006118be87876113b6565b600081815260066020526040902090915060028154600160c81b900460ff1660028111156118ee576118ee613cbf565b0361194f5760405163499463c160e01b815260206004820152602a60248201527f63616e6e6f742066756e642061206368616e6e656c20746861742077696c6c2060448201526931b637b9b29039b7b7b760b11b6064820152608401611852565b80546119659087906001600160601b03166140a7565b81546001600160601b0319166001600160601b039190911617815560008154600160c81b900460ff16600281111561199f5761199f613cbf565b03611aaa5780546119bd90600160b01b900462ffffff1660016140c7565b815462ffffff91909116600160b01b026dff00000000000000ffffffffffff60601b19166dffffffff00000000ffffffffffff60601b1990911617600160c81b178155604080517fdd90f938230335e59dc925c57ecb0e27a28c2d87356e31f00cd5554abd6c1b2d602082015260608a811b6001600160601b03199081169383019390935289901b9091166054820152611a69906068015b604051602081830303815290604052612745565b866001600160a01b0316886001600160a01b03167fdd90f938230335e59dc925c57ecb0e27a28c2d87356e31f00cd5554abd6c1b2d60405160405180910390a35b8054604051611add91611a55916000805160206142c98339815191529186916001600160601b03909116906020016140e3565b80546040516001600160601b03909116815282906000805160206142c98339815191529060200160405180910390a25050505050505050565b611b266040830160208401614000565b60016001600160601b0382161015611b515760405163c52e3eff60e01b815260040160405180910390fd5b6a084595161401484a0000006001600160601b0382161115611b865760405163293ceef960e21b815260040160405180910390fd5b826101000135611b958161282b565b611bb257604051633ae4ed6b60e01b815260040160405180910390fd5b8335600090815260066020526040902060018154600160c81b900460ff166002811115611be157611be1613cbf565b14158015611c0c575060028154600160c81b900460ff166002811115611c0957611c09613cbf565b14155b15611c745760405163499463c160e01b815260206004820152603160248201527f7370656e64696e67206368616e6e656c206d757374206265204f50454e206f726044820152702050454e44494e475f544f5f434c4f534560781b6064820152608401611852565b611c8460a0860160808701613f8d565b8154600160b01b900462ffffff908116911614611ce45760405163499463c160e01b815260206004820152601860248201527f6368616e6e656c2065706f6368206d757374206d6174636800000000000000006044820152606401611852565b6000611cf66060870160408801613fd8565b90506000611d0a6080880160608901613fb2565b8354909150600160601b900465ffffffffffff16600163ffffffff83161080611d4257508065ffffffffffff168365ffffffffffff16105b15611d605760405163686e1e0f60e11b815260040160405180910390fd5b611d706040890160208a01614000565b84546001600160601b0391821691161015611d9e57604051632c51d8db60e21b815260040160405180910390fd5b6000611da989610c1d565b9050611db6818a8a61116f565b611dd35760405163ee835c8960e01b815260040160405180910390fd5b600060405180606001604052808381526020018c6001600160a01b03168152602001600554604051602001611e0a91815260200190565b60408051601f1981840301815291905290529050611e36611e30368b90038b018b614106565b8261284d565b611e53576040516312bfb7b760e31b815260040160405180910390fd5b6000611e688360c08d013560e08e0135612ad6565b90508a35611e76828e6113b6565b14611e94576040516366eea9ab60e11b815260040160405180910390fd5b611ea463ffffffff8616876141a4565b875465ffffffffffff91909116600160601b0265ffffffffffff60601b19909116178755611ed860408c0160208d01614000565b8754611eed91906001600160601b03166141c3565b87546001600160601b0319166001600160601b03919091169081178855604051611f4291611a55917f22e2a422a8860656a3a33cfa1daf771e76798ce5649747957235025de12e0b24918f35916020016140e3565b86546040516001600160601b0390911681528b35907f22e2a422a8860656a3a33cfa1daf771e76798ce5649747957235025de12e0b249060200160405180910390a26000611f908d836113b6565b9050600060066000838152602001908152602001600020905061201c7f7165e2ebc7ce35cc98cb7666f9945b3617f3f36326b76d18937ba5fecf18739a8e600001600001358b600001600c9054906101000a900465ffffffffffff16604051602001611a5593929190928352602083019190915260d01b6001600160d01b031916604082015260460190565b8854604051600160601b90910465ffffffffffff1681528d35907f7165e2ebc7ce35cc98cb7666f9945b3617f3f36326b76d18937ba5fecf18739a9060200160405180910390a260008154600160c81b900460ff16600281111561208257612082613cbf565b0361216c577f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c16001600160a01b031663a9059cbb338f60000160200160208101906120cd9190614000565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526001600160601b031660248201526044016020604051808303816000875af1158015612121573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121459190613f42565b15156001146121675760405163022e258160e11b815260040160405180910390fd5b612203565b61217c60408e0160208f01614000565b815461219191906001600160601b03166140a7565b81546001600160601b0319166001600160601b039190911690811782556040516121d391611a55916000805160206142c98339815191529186916020016140e3565b80546040516001600160601b03909116815282906000805160206142c98339815191529060200160405180910390a25b5050505050505050505050505050565b600061221f82846113b6565b60008181526006602052604081209192508154600160c81b900460ff16600281111561224d5761224d613cbf565b0361226b5760405163499463c160e01b8152600401611852906141e3565b8054600163ff00000160b01b031981168255604080516000805160206142e983398151915260208201529081018490526001600160601b03909116906122b390606001611a55565b60405183906000805160206142e983398151915290600090a280156123885760405163a9059cbb60e01b81526001600160a01b038581166004830152602482018390527f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c1169063a9059cbb906044015b6020604051808303816000875af1158015612342573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123669190613f42565b15156001146123885760405163022e258160e11b815260040160405180910390fd5b5050505050565b600061239b83836113b6565b600081815260066020526040902090915060028154600160c81b900460ff1660028111156123cb576123cb613cbf565b146124285760405163499463c160e01b815260206004820152602660248201527f6368616e6e656c207374617465206d7573742062652050454e44494e475f544f6044820152655f434c4f534560d01b6064820152608401611852565b805463ffffffff428116600160901b9092041610612459576040516338b2019560e11b815260040160405180910390fd5b8054600163ff00000160b01b031981168255604080516000805160206142e983398151915260208201529081018490526001600160601b03909116906124a190606001611a55565b60405183906000805160206142e983398151915290600090a280156123885760405163a9059cbb60e01b8152336004820152602481018290527f000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c16001600160a01b03169063a9059cbb90604401612323565b6000600181601b7f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179870014551231950b75fc4402da1732fc9bebe197f79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179887096040805160008152602081018083529590955260ff909316928401929092526060830152608082015260a0016020604051602081039080840390855afa1580156125bf573d6000803e3d6000fd5b5050604051601f1901519392505050565b60006125dc83836113b6565b60008181526006602052604081209192508154600160c81b900460ff16600281111561260a5761260a613cbf565b036126285760405163499463c160e01b8152600401611852906141e3565b6126527f000000000000000000000000000000000000000000000000000000000000012c42614233565b8154600160c91b67ff000000ffffffff60901b1990911660ff60c81b19600160901b63ffffffff949094168402161717808355604080517f07b5c950597fc3bed92e2ad37fa84f701655acb372982e486f5fad3607f04a5c602082015290810185905291900460e01b6001600160e01b03191660608201526126d690606401611a55565b8054604051600160901b90910463ffffffff16815282907f07b5c950597fc3bed92e2ad37fa84f701655acb372982e486f5fad3607f04a5c9060200160405180910390a250505050565b6060610ddb83836040518060600160405280602781526020016142a260279139612afc565b600154600090612783907f0000000000000000000000000000000000000000000000000000000005265c0090600160e01b900463ffffffff16613f2f565b42111561278e575060015b600354600154835160208086019190912060408051808401959095524360e01b6001600160e01b0319169085015291901b63ffffffff19166044830152606082015260800160408051601f19818403018152919052805160209182012063ffffffff4216600160e01b02911c1760015580156116985750506001546001600160e01b038116600160e01b9182900463ffffffff1690910217600255565b6000811580610dde57505070014551231950b75fc4402da1732fc9bebe191190565b60006401000003d019836060015110158061287257506401000003d019836040015110155b1561289057604051633ae4ed6b60e01b815260040160405180910390fd5b6128a283600001518460200151612b74565b6128bf57604051633922a54160e11b815260040160405180910390fd5b600080612911846020015185600001516040516020016128f892919060609290921b6001600160601b0319168252601482015260340190565b6040516020818303038152906040528560400151612b9f565b91509150600061292686604001518484612c25565b905061296186608001518760a00151604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6001600160a01b0316816001600160a01b03161461299257604051631dbfb9b360e31b815260040160405180910390fd5b60006129ab876060015188600001518960200151612c25565b90506129e68760c001518860e00151604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6001600160a01b0316816001600160a01b031614612a1757604051631dbfb9b360e31b815260040160405180910390fd5b600080612a4989608001518a60a001518b60c001518c60e001516401000003d019612a429190614250565b6000612cc4565b6020808b01518c518d8301518d51604051969850949650600095612ac195612aa8958a928a92910160609690961b6001600160601b03191686526014860194909452603485019290925260548401526074830152609482015260b40190565b6040516020818303038152906040528a60400151612e4b565b60608b01511497505050505050505092915050565b6000806000612ae6868686612ebc565b91509150612af381612ef5565b50949350505050565b6060600080856001600160a01b031685604051612b199190614263565b600060405180830381855af49150503d8060008114612b54576040519150601f19603f3d011682016040523d82523d6000602084013e612b59565b606091505b5091509150612b6a8683838761303f565b9695505050505050565b60006401000003d01980846401000003d019868709096007086401000003d019838409149392505050565b600080600080612baf86866130c0565b91509150600080612bbf8461317c565b91509150600080612bcf8561317c565b91509150600080612c03868686867f3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533612cc4565b91509150612c11828261343e565b9950995050505050505050505b9250929050565b600080612c3360028461427f565b600003612c425750601b612c46565b50601c5b60016000828670014551231950b75fc4402da1732fc9bebe19888a096040805160008152602081018083529590955260ff909316928401929092526060830152608082015260a0016020604051602081039080840390855afa158015612cb0573d6000803e3d6000fd5b5050604051601f1901519695505050505050565b600080838614198588141615612cd957600080fd5b600080858814878a141660018114612cf6578015612d7357612dee565b6401000003d019866401000003d0198b60020908915060405160208152602080820152602060408201528260608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa612d5357600080fd5b6401000003d01981516401000003d019808e8f0960030909935050612dee565b6401000003d0198a6401000003d019038908915060405160208152602080820152602060408201528260608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa612dce57600080fd5b6401000003d01981516401000003d0198c6401000003d019038b08099350505b50506401000003d01980896401000003d01903886401000003d01903086401000003d0198384090892506401000003d019876401000003d019036401000003d01980866401000003d019038c088409089150509550959350505050565b6000806000612e5a858561372b565b9150915060405160308152602080820152602060408201528260608201528160808201526001609082015270014551231950b75fc4402da1732fc9bebe1960b082015260208160d0836005600019fa612eb257600080fd5b5195945050505050565b6000806001600160ff1b03831681612ed960ff86901c601b613f2f565b9050612ee78782888561382b565b935093505050935093915050565b6000816004811115612f0957612f09613cbf565b03612f115750565b6001816004811115612f2557612f25613cbf565b03612f725760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611852565b6002816004811115612f8657612f86613cbf565b03612fd35760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611852565b6003816004811115612fe757612fe7613cbf565b03610b4d5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401611852565b606083156130ae5782516000036130a7576001600160a01b0385163b6130a75760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611852565b50816130b8565b6130b883836138ef565b949350505050565b60008060008060006130d28787613919565b9250925092506040516030815260208082015260206040820152836060820152826080820152600160908201526401000003d01960b082015260208160d0836005600019fa61312057600080fd5b80519550506040516030815260208082015282605082015260206040820152816070820152600160908201526401000003d01960b082015260208160d0836005600019fa61316d57600080fd5b80519450505050509250929050565b6000806401000003d0198384096401000003d019816401000003db190990506401000003d0198182096401000003d01982820890506401000003d019600182086401000003d0196106eb8209905060008215600181146131e15780156131ef576131fb565b6401000003db1991506131fb565b836401000003d0190391505b506401000003d019817f3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a4445330990506401000003d01982830992506401000003d0198182096401000003d019817f3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533096401000003d01981860894506401000003d01984860994506401000003d01983830991506401000003d019826106eb0990506401000003d0198186089450506401000003d01983860996506000806401000003d0198384096401000003d0198488096401000003d0198183099150604051602081526020808201526020604082015282606082015263400000f5600160fe1b0360808201526401000003d01960a082015260208160c0836005600019fa61332157600080fd5b6401000003d01982825109925050506401000003d0197f31fdf302724013e57ad13fb38f842afeec184f00a74789dd286729c8303c4a5982096401000003d0198283096401000003d0198682099050888114600181146133865780156133925761339a565b6001945083955061339a565b600094508295505b505050506401000003d0198a880997506401000003d019828909975080156133c3578498508197505b5050506002850660028806146133df57846401000003d0190394505b604051935060208452602080850152602060408501528060608501525050506401000003d21960808201526401000003d01960a082015260208160c0836005600019fa61342b57600080fd5b6401000003d01981518409925050915091565b6000806401000003d0198485096401000003d0198186096401000003d019807f8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c76401000003d019897f07d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c658109086401000003d01980857f534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262096401000003d019857f8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c0908086401000003d0197fd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b6401000003d019808a7fedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d140986080860405160208152602080820152602060408201528160608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa61359d57600080fd5b805191506401000003d01982840996506401000003d019807f4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c6401000003d0198c7fc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a309086401000003d01980887f29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931096401000003d019887f2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d8409080892506401000003d019806401000006c4196401000003d0198c7f7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c257309086401000003d01980887f6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f098708089450604051905060208152602080820152602060408201528460608201526401000003d21960808201526401000003d01960a082015260208160c0836005600019fa61370d57600080fd5b5193506401000003d019905083818389090993505050509250929050565b60008060ff8351111561373d57600080fd5b60006040516088602060005b885181101561376a5788820151848401526020928301929182019101613749565b505060898751019050603081830153600201602060005b87518110156137a25787820151848401526020928301929182019101613781565b5050608b8651885101019050855181830153508551855101608c018120915050604051818152600160208201536021602060005b87518110156137f757878201518484015260209283019291820191016137d6565b5050508451855160210182015384516022018120935083821881526002602082015384516022018120925050509250929050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561386257506000905060036138e6565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156138b6573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166138df576000600192509250506138e6565b9150600090505b94509492505050565b8151156138ff5781518083602001fd5b8060405162461bcd60e51b81526004016118529190613e86565b600080600060ff8451111561392d57600080fd5b60006040516088602060005b895181101561395a5789820151848401526020928301929182019101613939565b505060898851019050606081830153600201602060005b88518110156139925788820151848401526020928301929182019101613971565b5050608b8751895101019050865181830153508651865101608c018120915050604051818152600160208201536021602060005b88518110156139e757888201518484015260209283019291820191016139c6565b5050508551865160210182015385516022018120945084821881526002602082015385516022018120935083821881526003602082015385516022018120925050509250925092565b6001600160a01b0381168114610b4d57600080fd5b60008083601f840112613a5757600080fd5b50813567ffffffffffffffff811115613a6f57600080fd5b602083019150836020828501011115612c1e57600080fd5b60008060008060008060008060c0898b031215613aa357600080fd5b8835613aae81613a30565b97506020890135613abe81613a30565b96506040890135613ace81613a30565b955060608901359450608089013567ffffffffffffffff80821115613af257600080fd5b613afe8c838d01613a45565b909650945060a08b0135915080821115613b1757600080fd5b50613b248b828c01613a45565b999c989b5096995094979396929594505050565b80356001600160601b0381168114613b4f57600080fd5b919050565b600080600060608486031215613b6957600080fd5b8335613b7481613a30565b92506020840135613b8481613a30565b9150613b9260408501613b38565b90509250925092565b60006101208284031215613bae57600080fd5b50919050565b60006101008284031215613bae57600080fd5b60008060006102408486031215613bdd57600080fd5b8335613be881613a30565b9250613bf78560208601613b9b565b9150613b92856101408601613bb4565b600060208284031215613c1957600080fd5b8135613c2481613a30565b9392505050565b60006101208284031215613c3e57600080fd5b610ddb8383613b9b565b60008060408385031215613c5b57600080fd5b823591506020830135613c6d81613a30565b809150509250929050565b60008060408385031215613c8b57600080fd5b8235613c9681613a30565b91506020830135613c6d81613a30565b600060208284031215613cb857600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b6001600160601b038616815265ffffffffffff8516602082015263ffffffff8416604082015262ffffff8316606082015260a0810160038310613d2857634e487b7160e01b600052602160045260246000fd5b8260808301529695505050505050565b60008060006102408486031215613d4e57600080fd5b83359250613bf78560208601613b9b565b60008060208385031215613d7257600080fd5b823567ffffffffffffffff80821115613d8a57600080fd5b818501915085601f830112613d9e57600080fd5b813581811115613dad57600080fd5b8660208260051b8501011115613dc257600080fd5b60209290920196919550909350505050565b60005b83811015613def578181015183820152602001613dd7565b50506000910152565b60008151808452613e10816020860160208601613dd4565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613e7957603f19888603018452613e67858351613df8565b94509285019290850190600101613e4b565b5092979650505050505050565b602081526000610ddb6020830184613df8565b60008060408385031215613eac57600080fd5b8235613eb781613a30565b9150613ec560208401613b38565b90509250929050565b6000806102208385031215613ee257600080fd5b613eec8484613b9b565b9150613ec5846101208501613bb4565b600060208284031215613f0e57600080fd5b8151613c2481613a30565b634e487b7160e01b600052601160045260246000fd5b80820180821115610dde57610dde613f19565b600060208284031215613f5457600080fd5b81518015158114613c2457600080fd5b600060208284031215613f7657600080fd5b813566ffffffffffffff81168114613c2457600080fd5b600060208284031215613f9f57600080fd5b813562ffffff81168114613c2457600080fd5b600060208284031215613fc457600080fd5b813563ffffffff81168114613c2457600080fd5b600060208284031215613fea57600080fd5b813565ffffffffffff81168114613c2457600080fd5b60006020828403121561401257600080fd5b610ddb82613b38565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261405e57600080fd5b83018035915067ffffffffffffffff82111561407957600080fd5b602001915036819003821315612c1e57600080fd5b6000600182016140a0576140a0613f19565b5060010190565b6001600160601b038181168382160190808211156112df576112df613f19565b62ffffff8181168382160190808211156112df576112df613f19565b928352602083019190915260a01b6001600160a01b0319166040820152604c0190565b600061010080838503121561411a57600080fd5b6040519081019067ffffffffffffffff8211818310171561414b57634e487b7160e01b600052604160045260246000fd5b81604052833581526020840135602082015260408401356040820152606084013560608201526080840135608082015260a084013560a082015260c084013560c082015260e084013560e0820152809250505092915050565b65ffffffffffff8181168382160190808211156112df576112df613f19565b6001600160601b038281168282160390808211156112df576112df613f19565b60208082526030908201527f6368616e6e656c206d7573742068617665207374617465204f50454e206f722060408201526f50454e44494e475f544f5f434c4f534560801b606082015260800190565b63ffffffff8181168382160190808211156112df576112df613f19565b81810381811115610dde57610dde613f19565b60008251614275818460208701613dd4565b9190910192915050565b60008261429c57634e487b7160e01b600052601260045260246000fd5b50069056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65645fa17246d3a5d68d42baa94cde33042180b783a399c02bf63ac2076e0f708738ceeab2eef998c17fe96f30f83fbf3c55fc5047f6e40c55a0cf72d236e9d2ba72a26469706673582212209ded07965f6328884d9e87e9794963325b6e7136a1a2366eeac2078a7a5d0fde64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c1000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000e15c24a0910311c83ac78b5930d771089e93077b
-----Decoded View---------------
Arg [0] : _token (address): 0xD4fdec44DB9D44B8f2b6d529620f9C0C7066A2c1
Arg [1] : _noticePeriodChannelClosure (uint32): 300
Arg [2] : _safeRegistry (address): 0xe15C24a0910311c83aC78B5930d771089E93077b
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000d4fdec44db9d44b8f2b6d529620f9c0c7066a2c1
Arg [1] : 000000000000000000000000000000000000000000000000000000000000012c
Arg [2] : 000000000000000000000000e15c24a0910311c83ac78b5930d771089e93077b
Deployed Bytecode Sourcemap
111939:33095:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;134761:3040;;;;;;:::i;:::-;;:::i;:::-;;138053:429;;;;;;:::i;:::-;;:::i;121198:280::-;;;;;;:::i;:::-;;:::i;130363:148::-;;;;;;:::i;:::-;;:::i;132226:178::-;;;;;;:::i;:::-;;:::i;141862:1407::-;;;;;;:::i;:::-;;:::i;:::-;;;3967:25:1;;;3955:2;3940:18;141862:1407:0;;;;;;;;16780:263;;;;;;:::i;:::-;;:::i;113427:58::-;;113483:1;113427:58;;;;;-1:-1:-1;;;;;4514:39:1;;;4496:58;;4484:2;4469:18;113427:58:0;4323:237:1;113790:104:0;;;;;130130:163;;;;;;:::i;:::-;;:::i;113306:65::-;;113362:8;113306:65;;131905:241;;;;;;:::i;:::-;;:::i;112325:92::-;;112383:34;112325:92;;113579:153;;;;;118167:43;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;118167:43:0;;;-1:-1:-1;;;118167:43:0;;;;;-1:-1:-1;;;118167:43:0;;;;;-1:-1:-1;;;118167:43:0;;;;;-1:-1:-1;;;118167:43:0;;;;;;;;;;;;;;;;;:::i;128524:178::-;;;;;;:::i;:::-;;:::i;118425:53::-;;;;;;;;6504:10:1;6492:23;;;6474:42;;6462:2;6447:18;118425:53:0;6299:223:1;120452:650:0;;;:::i;143690:1341::-;;;;;;:::i;:::-;;:::i;:::-;;;7161:14:1;;7154:22;7136:41;;7124:2;7109:18;143690:1341:0;6996:187:1;10014:314:0;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;141110:169::-;141254:15;141110:169;;128203:241;;;;;;:::i;:::-;;:::i;140852:164::-;;;;;;:::i;:::-;;:::i;89889:36::-;;;;;;90657:701;;;:::i;89506:47::-;;;;;;;;;;;;;;;-1:-1:-1;;;89506:47:0;;;;;;;;;;;;:::i;113952:30::-;;;;;;118291:29;;;;;;;;-1:-1:-1;;;;;9556:32:1;;;9538:51;;9526:2;9511:18;118291:29:0;9378:217:1;138650:416:0;;;;;;:::i;:::-;;:::i;121540:256::-;;;;;;:::i;:::-;;:::i;113903:40::-;;;;;;;;;;;;;;;-1:-1:-1;;;113903:40:0;;;;;134761:3040;135090:10;-1:-1:-1;;;;;135112:5:0;135090:28;;135086:80;;135142:12;;-1:-1:-1;;;135142:12:0;;;;;;;;;;;135086:80;-1:-1:-1;;;;;135182:19:0;;135196:4;135182:19;135178:82;;135225:23;;-1:-1:-1;;;135225:23:0;;;;;;;;;;;135178:82;135272:211;;135465:7;135272:211;135556:29;135537:48;;135533:2261;;-1:-1:-1;;;;;135606:25:0;;135602:114;;;135659:41;;-1:-1:-1;;;135659:41:0;;;;;;;;;;;135602:114;135986:8;;;:24;;-1:-1:-1;;;135986:24:0;;135830:29;;135826:2;135822:38;;;135986:24;;;9538:51:1;;;135928:2:0;135907:24;;135894:38;135886:47;;;135732:11;;-1:-1:-1;;;;;135986:8:0;;;;:19;;9511:18:1;;135986:24:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;135964:46;;136117:3;-1:-1:-1;;;;;136109:11:0;:4;-1:-1:-1;;;;;136109:11:0;;136105:351;;-1:-1:-1;;;;;136201:25:0;;;136197:105;;136258:24;;-1:-1:-1;;;136258:24:0;;;;;;;;;;;136197:105;136105:351;;;136361:4;-1:-1:-1;;;;;136346:19:0;:11;-1:-1:-1;;;;;136346:19:0;;136342:99;;136397:24;;-1:-1:-1;;;136397:24:0;;;;;;;;;;;136342:99;136472:61;136493:3;136498:4;136524:6;136472:20;:61::i;:::-;135587:1023;;;135533:2261;;;136639:35;136620:54;;136616:1178;;136863:29;;136859:2;136855:38;;;;136948:4;136944:26;;136931:40;136926:3;136922:50;;;;137027:4;137023:26;;137010:40;137002:49;;;137106:4;137102:26;;137089:40;137080:50;;137165:11;;;:92;;-1:-1:-1;137190:67:0;-1:-1:-1;;;;;137225:32:0;;;;137190;;:67;:::i;:::-;137180:6;:77;;137165:92;137161:156;;;137285:16;;-1:-1:-1;;;137285:16:0;;;;;;;;;;;137161:156;-1:-1:-1;;;;;137404:27:0;;;137400:117;;137452:49;137473:8;137483;137493:7;137452:20;:49::i;:::-;-1:-1:-1;;;;;137602:27:0;;;137598:117;;137650:49;137671:8;137681;137691:7;137650:20;:49::i;:::-;136676:1050;;;;136616:1178;;;137754:28;;-1:-1:-1;;;137754:28:0;;;;;;;;;;;136616:1178;134761:3040;;;;;;;;:::o;138053:429::-;108852:11;;138156:4;;-1:-1:-1;;;108852:11:0;;;;108847:75;;108887:23;;-1:-1:-1;;;108887:23:0;;;;;;;;;;;108847:75;108938:8;;;:25;;-1:-1:-1;;;108938:25:0;;-1:-1:-1;;;;;9556:32:1;;;108938:25:0;;;9538:51:1;;;;108967:10:0;;108938:8;;;;:19;;9511:18:1;;108938:25:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;108938:39:0;;108934:103;;109001:24;;-1:-1:-1;;;109001:24:0;;;;;;;;;;;108934:103;138173:43:::1;138194:4;138200:7;138209:6;138173:20;:43::i;:::-;138285:69;::::0;-1:-1:-1;;;138285:69:0;;138304:10:::1;138285:69;::::0;::::1;11341:34:1::0;138324:4:0::1;11391:18:1::0;;;11384:43;-1:-1:-1;;;;;11463:39:1;;11443:18;;;11436:67;138285:5:0::1;-1:-1:-1::0;;;;;138285:18:0::1;::::0;::::1;::::0;11276::1;;138285:69:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:77;;138358:4;138285:77;138281:194;;138442:21;;-1:-1:-1::0;;;138442:21:0::1;;;;;;;;;;;138281:194;138053:429:::0;;;;:::o;121198:280::-;108852:11;;121401:4;;-1:-1:-1;;;108852:11:0;;;;108847:75;;108887:23;;-1:-1:-1;;;108887:23:0;;;;;;;;;;;108847:75;108938:8;;;:25;;-1:-1:-1;;;108938:25:0;;-1:-1:-1;;;;;9556:32:1;;;108938:25:0;;;9538:51:1;;;;108967:10:0;;108938:8;;;;:19;;9511:18:1;;108938:25:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;108938:39:0;;108934:103;;109001:24;;-1:-1:-1;;;109001:24:0;;;;;;;;;;;108934:103;121423:47:::1;121445:4;121451:10;121463:6;121423:21;:47::i;130363:148::-:0;109217:11;;-1:-1:-1;;;109217:11:0;;;;109212:75;;109252:23;;-1:-1:-1;;;109252:23:0;;;;;;;;;;;109212:75;109303:8;;;:31;;-1:-1:-1;;;109303:31:0;;109323:10;109303:31;;;9538:51:1;;;;109346:1:0;;-1:-1:-1;;;;;109303:8:0;;;;:19;;9511:18:1;;109303:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;109303:45:0;;109299:109;;109372:24;;-1:-1:-1;;;109372:24:0;;;;;;;;;;;109299:109;130454:49:::1;130484:10;130496:6;130454:29;:49::i;:::-;130363:148:::0;:::o;132226:178::-;109217:11;;-1:-1:-1;;;109217:11:0;;;;109212:75;;109252:23;;-1:-1:-1;;;109252:23:0;;;;;;;;;;;109212:75;109303:8;;;:31;;-1:-1:-1;;;109303:31:0;;109323:10;109303:31;;;9538:51:1;;;;109346:1:0;;-1:-1:-1;;;;;109303:8:0;;;;:19;;9511:18:1;;109303:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;109303:45:0;;109299:109;;109372:24;;-1:-1:-1;;;109372:24:0;;;;;;;;;;;109299:109;132332:64:::1;132372:10;132384:11;132332:39;:64::i;141862:1407::-:0;141945:7;141965:17;141985:53;142017:10;:20;;;141985:31;:53::i;:::-;141965:73;-1:-1:-1;142452:18:0;142797:23;;;;;;;;:::i;:::-;142774:48;;142768:2;142741:21;;;;;;;;:::i;:::-;142713:51;;:57;;142693:2;142660:27;;;;;;;;:::i;:::-;142627:62;;:68;;142606:3;142573:27;;;;;;;;:::i;:::-;142546:56;;:63;;142525:3;142497:22;;;;;;;;:::i;:::-;-1:-1:-1;;;;;142474:47:0;:54;;142473:137;:223;:298;:349;142452:370;;142935:18;143009:26;;;143081:10;:15;;:25;;;143108:10;143120:9;143064:66;;;;;;;;;13488:19:1;;;13532:2;13523:12;;13516:28;;;;13582:2;13578:15;-1:-1:-1;;;;;;13574:53:1;13569:2;13560:12;;13553:75;13653:2;13644:12;;13303:359;143064:66:0;;;;;;;-1:-1:-1;;143064:66:0;;;;;;143054:77;;143064:66;143054:77;;;;-1:-1:-1;;;;;;13857:33:1;;;;142980:166:0;;;13839:52:1;13907:18;;;13900:34;;;;142980:166:0;;;;;;;;;13812:18:1;;;142980:166:0;;142956:201;;;;;;143232:15;;-1:-1:-1;;;143187:73:0;;;14186:28:1;-1:-1:-1;;;14230:11:1;;;14223:36;14275:11;;;14268:27;14311:12;;;;14304:28;;;;143187:73:0;;;;;;;;;;14348:12:1;;;;143187:73:0;;143177:84;;;;;;141862:1407;-1:-1:-1;;;;;141862:1407:0:o;16780:263::-;16924:7;16951:35;;;;;;;;;;;-1:-1:-1;;;;;16951:44:0;;;;;;;;;;;;:84;;17030:4;16951:84;;;16566:33;16951:84;16944:91;;16780:263;;;;;:::o;130130:163::-;108852:11;;130225:4;;-1:-1:-1;;;108852:11:0;;;;108847:75;;108887:23;;-1:-1:-1;;;108887:23:0;;;;;;;;;;;108847:75;108938:8;;;:25;;-1:-1:-1;;;108938:25:0;;-1:-1:-1;;;;;9556:32:1;;;108938:25:0;;;9538:51:1;;;;108967:10:0;;108938:8;;;;:19;;9511:18:1;;108938:25:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;108938:39:0;;108934:103;;109001:24;;-1:-1:-1;;;109001:24:0;;;;;;;;;;;108934:103;130242:43:::1;130272:4;130278:6;130242:29;:43::i;:::-;130130:163:::0;;;:::o;131905:241::-;108852:11;;132058:4;;-1:-1:-1;;;108852:11:0;;;;108847:75;;108887:23;;-1:-1:-1;;;108887:23:0;;;;;;;;;;;108847:75;108938:8;;;:25;;-1:-1:-1;;;108938:25:0;;-1:-1:-1;;;;;9556:32:1;;;108938:25:0;;;9538:51:1;;;;108967:10:0;;108938:8;;;;:19;;9511:18:1;;108938:25:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;108938:39:0;;108934:103;;109001:24;;-1:-1:-1;;;109001:24:0;;;;;;;;;;;108934:103;132080:58:::1;132120:4;132126:11;132080:39;:58::i;128524:178::-:0;109217:11;;-1:-1:-1;;;109217:11:0;;;;109212:75;;109252:23;;-1:-1:-1;;;109252:23:0;;;;;;;;;;;109212:75;109303:8;;;:31;;-1:-1:-1;;;109303:31:0;;109323:10;109303:31;;;9538:51:1;;;;109346:1:0;;-1:-1:-1;;;;;109303:8:0;;;;:19;;9511:18:1;;109303:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;109303:45:0;;109299:109;;109372:24;;-1:-1:-1;;;109372:24:0;;;;;;;;;;;109299:109;128630:64:::1;128670:10;128682:11;128630:39;:64::i;120452:650::-:0;120761:21;;;;;;;;;;;-1:-1:-1;;;120761:21:0;;;;;120818:7;;;;;;;;;;-1:-1:-1;;;120818:7:0;;;;120608:298;;120637:95;120608:298;;;14630:25:1;;;;120751:32:0;14671:18:1;;;14664:34;;;;120802:25:0;14714:18:1;;;14707:34;120846:13:0;14757:18:1;;;14750:34;120886:4:0;14800:19:1;;;14793:61;-1:-1:-1;;14602:19:1;;120608:298:0;;;;;;;;;;;;120584:333;;;;;;120555:362;;120956:15;;120934:18;:37;120930:165;;120988:15;:36;;;121044:39;;121006:18;;121044:39;;;;;120492:610;120452:650::o;143690:1341::-;144195:687;;;144492:9;144195:687;;;15134:19:1;;;144452:9:0;;15169:12:1;;;15162:28;144492:9:0;;;;15206:12:1;;;15199:28;144620:20:0;;;;15243:12:1;;;15236:28;144779:20:0;;;;:22;;15280:13:1;;;;15273:29;;;;144832:23:0;;;;;15318:13:1;;;15311:29;144195:687:0;;;;;;;;;;15356:13:1;;;144195:687:0;;;144159:746;;;;;-1:-1:-1;;144105:834:0;;;;;;144999:23;;144779:20;144999:23;;;:::i;:::-;144970:53;;;;;;;;;143690:1341;-1:-1:-1;;;;143690:1341:0:o;10014:314::-;10082:22;10139:4;10127:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10117:34;;10167:9;10162:134;10182:15;;;10162:134;;;10232:52;10269:4;10276;;10281:1;10276:7;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;10232:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10232:28:0;;-1:-1:-1;;;10232:52:0:i;:::-;10219:7;10227:1;10219:10;;;;;;;;:::i;:::-;;;;;;:65;;;;10199:3;;;;;:::i;:::-;;;;10162:134;;;;10014:314;;;;:::o;128203:241::-;108852:11;;128356:4;;-1:-1:-1;;;108852:11:0;;;;108847:75;;108887:23;;-1:-1:-1;;;108887:23:0;;;;;;;;;;;108847:75;108938:8;;;:25;;-1:-1:-1;;;108938:25:0;;-1:-1:-1;;;;;9556:32:1;;;108938:25:0;;;9538:51:1;;;;108967:10:0;;108938:8;;;;:19;;9511:18:1;;108938:25:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;108938:39:0;;108934:103;;109001:24;;-1:-1:-1;;;109001:24:0;;;;;;;;;;;108934:103;128378:58:::1;128418:4;128424:11;128378:39;:58::i;140852:164::-:0;140970:37;;-1:-1:-1;;;;;;16537:2:1;16533:15;;;16529:24;;140970:37:0;;;16517::1;16588:15;;;16584:24;16570:12;;;16563:46;140933:7:0;;16625:12:1;;140970:37:0;;;;;;;;;;;;140960:48;;;;;;140953:55;;140852:164;;;;:::o;90657:701::-;90978:19;;;;;;;;;;;-1:-1:-1;;;90978:19:0;;;;;91033:14;;;;;;;;;;-1:-1:-1;;;91033:14:0;;;;90825:303;;90854:95;90825:303;;;14630:25:1;90968:30:0;14671:18:1;;;14664:34;91017:32:0;14714:18:1;;;14707:34;91068:13:0;14757:18:1;;;14750:34;91108:4:0;14800:19:1;;;;14793:61;;;;90825:303:0;;;;;;;;;;14602:19:1;;;;90825:303:0;;;90801:338;;;;;91182:21;;91154:49;;91150:201;;91220:21;:48;;;91288:51;;91244:24;;91288:51;;;;;90703:655;90657:701::o;138650:416::-;109217:11;;-1:-1:-1;;;109217:11:0;;;;109212:75;;109252:23;;-1:-1:-1;;;109252:23:0;;;;;;;;;;;109212:75;109303:8;;;:31;;-1:-1:-1;;;109303:31:0;;109323:10;109303:31;;;9538:51:1;;;;109346:1:0;;-1:-1:-1;;;;;109303:8:0;;;;:19;;9511:18:1;;109303:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;109303:45:0;;109299:109;;109372:24;;-1:-1:-1;;;109372:24:0;;;;;;;;;;;109299:109;138749:49:::1;138770:10;138782:7;138791:6;138749:20;:49::i;:::-;138869:69;::::0;-1:-1:-1;;;138869:69:0;;138888:10:::1;138869:69;::::0;::::1;11341:34:1::0;138908:4:0::1;11391:18:1::0;;;11384:43;-1:-1:-1;;;;;11463:39:1;;11443:18;;;11436:67;138869:5:0::1;-1:-1:-1::0;;;;;138869:18:0::1;::::0;::::1;::::0;11276::1;;138869:69:0::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:77;;138942:4;138869:77;138865:194;;139026:21;;-1:-1:-1::0;;;139026:21:0::1;;;;;;;;;;;138865:194;138650:416:::0;;:::o;121540:256::-;109217:11;;-1:-1:-1;;;109217:11:0;;;;109212:75;;109252:23;;-1:-1:-1;;;109252:23:0;;;;;;;;;;;109212:75;109303:8;;;:31;;-1:-1:-1;;;109303:31:0;;109323:10;109303:31;;;9538:51:1;;;;109346:1:0;;-1:-1:-1;;;;;109303:8:0;;;;:19;;9511:18:1;;109303:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;109303:45:0;;109299:109;;109372:24;;-1:-1:-1;;;109372:24:0;;;;;;;;;;;109299:109;121735:53:::1;121757:10;121769;121781:6;121735:21;:53::i;139363:1231::-:0;139517:6;113483:1;-1:-1:-1;;;;;119957:58:0;;;119953:114;;;120039:16;;-1:-1:-1;;;120039:16:0;;;;;;;;;;;119953:114;113362:8;-1:-1:-1;;;;;120081:58:0;;;120077:139;;;120163:41;;-1:-1:-1;;;120163:41:0;;;;;;;;;;;120077:139;139557:4:::1;139563:7;119549:11;-1:-1:-1::0;;;;;119539:21:0::1;:6;-1:-1:-1::0;;;;;119539:21:0::1;::::0;119535:86:::1;;119584:25;;-1:-1:-1::0;;;119584:25:0::1;;;;;;;;;;;119535:86;-1:-1:-1::0;;;;;119635:20:0;::::1;119631:111;;119679:51;::::0;-1:-1:-1;;;119679:51:0;;16850:2:1;119679:51:0::1;::::0;::::1;16832:21:1::0;16889:2;16869:18;;;16862:30;16928:26;16908:18;;;16901:54;16972:18;;119679:51:0::1;;;;;;;;119631:111;-1:-1:-1::0;;;;;119756:25:0;::::1;119752:121;;119805:56;::::0;-1:-1:-1;;;119805:56:0;;17203:2:1;119805:56:0::1;::::0;::::1;17185:21:1::0;17242:2;17222:18;;;17215:30;17281:31;17261:18;;;17254:59;17330:18;;119805:56:0::1;17001:353:1::0;119752:121:0::1;139588:17:::2;139608:28;139622:4;139628:7;139608:13;:28::i;:::-;139647:23;139673:19:::0;;;:8:::2;:19;::::0;;;;139588:48;;-1:-1:-1;139727:30:0::2;139709:14:::0;;-1:-1:-1;;;139709:14:0;::::2;;;:48;::::0;::::2;;;;;;:::i;:::-;::::0;139705:163:::2;;139781:75;::::0;-1:-1:-1;;;139781:75:0;;17561:2:1;139781:75:0::2;::::0;::::2;17543:21:1::0;17600:2;17580:18;;;17573:30;17639:34;17619:18;;;17612:62;-1:-1:-1;;;17690:18:1;;;17683:40;17740:19;;139781:75:0::2;17359:406:1::0;139705:163:0::2;139926:15:::0;;139911:56:::2;::::0;139960:6;;-1:-1:-1;;;;;139926:15:0::2;139911:56;:::i;:::-;139880:88:::0;;-1:-1:-1;;;;;;139880:88:0::2;-1:-1:-1::0;;;;;139880:88:0;;;::::2;;::::0;;-1:-1:-1;139985:14:0;;-1:-1:-1;;;139985:14:0;::::2;;;:38;::::0;::::2;;;;;;:::i;:::-;::::0;139981:436:::2;;140146:13:::0;;140126:38:::2;::::0;-1:-1:-1;;;140146:13:0;::::2;;;140163:1;140126:38;:::i;:::-;140092:73:::0;;::::2;::::0;;;::::2;-1:-1:-1::0;;;140092:73:0::2;-1:-1:-1::0;;;;140238:35:0;-1:-1:-1;;;;140238:35:0;;;;-1:-1:-1;;;140238:35:0::2;::::0;;140301:55:::2;::::0;;140318:22:::2;140301:55;::::0;::::2;18323:19:1::0;18430:2;18426:15;;;-1:-1:-1;;;;;;18422:24:1;;;18408:12;;;18401:46;;;;18481:15;;;18477:24;;;18463:12;;;18456:46;140290:67:0::2;::::0;18518:12:1;;140301:55:0::2;;;;;;;;;;;;;140290:10;:67::i;:::-;140397:7;-1:-1:-1::0;;;;;140377:28:0::2;140391:4;-1:-1:-1::0;;;;;140377:28:0::2;;;;;;;;;;;139981:436;140502:15:::0;;140440:78:::2;::::0;140429:90:::2;::::0;140440:78:::2;::::0;-1:-1:-1;;;;;;;;;;;140457:32:0;140491:9;;-1:-1:-1;;;;;140502:15:0;;::::2;::::0;140440:78:::2;;;:::i;140429:90::-;140570:15:::0;;140535:51:::2;::::0;-1:-1:-1;;;;;140570:15:0;;::::2;4496:58:1::0;;140559:9:0;;-1:-1:-1;;;;;;;;;;;140535:51:0;4484:2:1;4469:18;140535:51:0::2;;;;;;;139577:1017;;120226:1:::1;;139363:1231:::0;;;;:::o;124037:4052::-;124239:22;;;;;;;;:::i;:::-;113483:1;-1:-1:-1;;;;;119957:58:0;;;119953:114;;;120039:16;;-1:-1:-1;;;120039:16:0;;;;;;;;;;;119953:114;113362:8;-1:-1:-1;;;;;120081:58:0;;;120077:139;;;120163:41;;-1:-1:-1;;;120163:41:0;;;;;;;;;;;120077:139;124298:10:::1;:20;;;53449:26;53472:2;53449:22;:26::i;:::-;53444:88;;53499:21;;-1:-1:-1::0;;;53499:21:0::1;;;;;;;;;;;53444:88;124379:25:::0;::::2;124336:31;124370:35:::0;;;:8:::2;:35;::::0;;;;124448:18:::2;124422:22:::0;;-1:-1:-1;;;124422:22:0;::::2;;;:44;::::0;::::2;;;;;;:::i;:::-;;;:104;;;;-1:-1:-1::0;124496:30:0::2;124470:22:::0;;-1:-1:-1;;;124470:22:0;::::2;;;:56;::::0;::::2;;;;;;:::i;:::-;;;124422:104;124418:226;;;124550:82;::::0;-1:-1:-1;;;124550:82:0;;19140:2:1;124550:82:0::2;::::0;::::2;19122:21:1::0;19179:2;19159:18;;;19152:30;19218:34;19198:18;;;19191:62;-1:-1:-1;;;19269:18:1;;;19262:47;19326:19;;124550:82:0::2;18938:413:1::0;124418:226:0::2;124726:21;::::0;;;::::2;::::0;::::2;;:::i;:::-;124680::::0;;-1:-1:-1;;;124680:21:0;::::2;124660:88;124680:21:::0;;::::2;124660:88:::0;::::2;;124656:185;;124772:57;::::0;-1:-1:-1;;;124772:57:0;;19558:2:1;124772:57:0::2;::::0;::::2;19540:21:1::0;19597:2;19577:18;;;19570:30;19636:26;19616:18;;;19609:54;19680:18;;124772:57:0::2;19356:348:1::0;124656:185:0::2;125087:16;125125:27;::::0;;;::::2;::::0;::::2;;:::i;:::-;125087:66:::0;-1:-1:-1;125164:22:0::2;125214:27;::::0;;;::::2;::::0;::::2;;:::i;:::-;125294::::0;;125164:78;;-1:-1:-1;;;;125294:27:0;::::2;;;125355:1;125337:19;::::0;::::2;;::::0;:47:::2;;;125372:12;125360:24;;:9;:24;;;125337:47;125333:120;;;125408:33;;-1:-1:-1::0;;;125408:33:0::2;;;;;;;;;;;125333:120;125526:22;::::0;;;::::2;::::0;::::2;;:::i;:::-;125484:23:::0;;-1:-1:-1;;;;;125469:80:0;;::::2;125484:23:::0;::::2;125469:80;125465:148;;;125573:28;;-1:-1:-1::0;;;125573:28:0::2;;;;;;;;;;;125465:148;125725:18;125746:26;125761:10;125746:14;:26::i;:::-;125725:47;;125790:48;125807:10;125819;125831:6;125790:16;:48::i;:::-;125785:106;;125862:17;;-1:-1:-1::0;;;125862:17:0::2;;;;;;;;;;;125785:106;125903:36;125955:74;;;;;;;;125977:10;125955:74;;;;125989:4;-1:-1:-1::0;;;;;125955:74:0::2;;;;;126012:15;;125995:33;;;;;;19838:19:1::0;;19882:2;19873:12;;19709:182;125995:33:0::2;;::::0;;-1:-1:-1;;125995:33:0;;::::2;::::0;;;;;;125955:74;;125903:126;-1:-1:-1;126047:26:0::2;;;::::0;;::::2;::::0;::::2;126057:6:::0;126047:26:::2;:::i;:::-;126065:7;126047:9;:26::i;:::-;126042:84;;126097:17;;-1:-1:-1::0;;;126097:17:0::2;;;;;;;;;;;126042:84;126138:14;126155:74;126169:10:::0;126181:20:::2;::::0;::::2;:22;126205:23:::0;;;::::2;126155:13;:74::i;:::-;126138:91:::0;-1:-1:-1;126275:25:0;::::2;126244:27;126138:91:::0;126266:4;126244:13:::2;:27::i;:::-;:56;126240:120;;126324:24;;-1:-1:-1::0;;;126324:24:0::2;;;;;;;;;;;126240:120;126419:27;;::::0;::::2;:9:::0;:27:::2;:::i;:::-;126372:75:::0;;::::2;::::0;;;::::2;-1:-1:-1::0;;;126372:75:0::2;-1:-1:-1::0;;;;126372:75:0;;::::2;;::::0;;126567:22:::2;::::0;;;::::2;::::0;::::2;;:::i;:::-;126525:23:::0;;126510:80:::2;::::0;;-1:-1:-1;;;;;126525:23:0::2;126510:80;:::i;:::-;126458:133:::0;;-1:-1:-1;;;;;;126458:133:0::2;-1:-1:-1::0;;;;;126458:133:0;;;::::2;::::0;;::::2;::::0;;126627:102:::2;::::0;126602:138:::2;::::0;126627:102:::2;::::0;126644:32:::2;::::0;126678:25;::::2;::::0;126627:102:::2;;;:::i;126602:138::-;126807:23:::0;;126756:75:::2;::::0;-1:-1:-1;;;;;126807:23:0;;::::2;4496:58:1::0;;126780:25:0;::::2;::::0;126756:75:::2;::::0;4484:2:1;4469:18;126756:75:0::2;;;;;;;126844:25;126872:27;126886:4;126892:6;126872:13;:27::i;:::-;126844:55;;126910:30;126943:8;:27;126952:17;126943:27;;;;;;;;;;;126910:60;;127025:109;127053:23;127078:10;:15;;:25;;;127105:15;:27;;;;;;;;;;;;127036:97;;;;;;;;;21527:19:1::0;;;21571:2;21562:12;;21555:28;;;;21639:3;21617:16;-1:-1:-1;;;;;;21613:47:1;21608:2;21599:12;;21592:69;21686:2;21677:12;;21311:384;127025:109:0::2;127192:27:::0;;127150:70:::2;::::0;-1:-1:-1;;;127192:27:0;;::::2;;;21877:46:1::0;;127165:25:0;::::2;::::0;127150:70:::2;::::0;21865:2:1;21850:18;127150:70:0::2;;;;;;;127262:20;127237:21:::0;;-1:-1:-1;;;127237:21:0;::::2;;;:45;::::0;::::2;;;;;;:::i;:::-;::::0;127233:849:::2;;127391:5;-1:-1:-1::0;;;;;127391:14:0::2;;127406:10;127433;:15;;:22;;;;;;;;;;:::i;:::-;127391:66;::::0;-1:-1:-1;;;;;;127391:66:0::2;::::0;;;;;;-1:-1:-1;;;;;22125:32:1;;;127391:66:0::2;::::0;::::2;22107:51:1::0;-1:-1:-1;;;;;22194:39:1;22174:18;;;22167:67;22080:18;;127391:66:0::2;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:74;;127461:4;127391:74;127387:143;;127493:21;;-1:-1:-1::0;;;127493:21:0::2;;;;;;;;;;;127387:143;127233:849;;;127840:22;::::0;;;::::2;::::0;::::2;;:::i;:::-;127799::::0;;127784:79:::2;::::0;;-1:-1:-1;;;;;127799:22:0::2;127784:79;:::i;:::-;127729:135:::0;;-1:-1:-1;;;;;;127729:135:0::2;-1:-1:-1::0;;;;;127729:135:0;;;::::2;::::0;;::::2;::::0;;127890:93:::2;::::0;127879:105:::2;::::0;127890:93:::2;::::0;-1:-1:-1;;;;;;;;;;;127907:32:0;127941:17;;127890:93:::2;;;:::i;127879:105::-;128047:22:::0;;128004:66:::2;::::0;-1:-1:-1;;;;;128047:22:0;;::::2;4496:58:1::0;;128028:17:0;;-1:-1:-1;;;;;;;;;;;128004:66:0;4484:2:1;4469:18;128004:66:0::2;;;;;;;127233:849;124325:3764;;;;;;;;;120226:1:::1;124037:4052:::0;;;;:::o;130754:1037::-;130900:17;130920:27;130934:6;130942:4;130920:13;:27::i;:::-;130960:23;130986:19;;;:8;:19;;;;;130900:47;;-1:-1:-1;131022:14:0;;-1:-1:-1;;;131022:14:0;;;;:38;;;;;;;;:::i;:::-;;131018:159;;131084:81;;-1:-1:-1;;;131084:81:0;;;;;;;:::i;131018:159::-;131222:15;;-1:-1:-1;;;;;;131430:33:0;;;;131528:51;;;-1:-1:-1;;;;;;;;;;;131528:51:0;;;22819:19:1;22854:12;;;22847:28;;;-1:-1:-1;;;;;131222:15:0;;;;131517:63;;22891:12:1;;131528:51:0;22662:247:1;131517:63:0;131596:24;;131610:9;;-1:-1:-1;;;;;;;;;;;131596:24:0;;;;131637:11;;131633:151;;131669:31;;-1:-1:-1;;;131669:31:0;;-1:-1:-1;;;;;23106:32:1;;;131669:31:0;;;23088:51:1;23155:18;;;23148:34;;;131669:5:0;:14;;;;23061:18:1;;131669:31:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:39;;131704:4;131669:39;131665:108;;131736:21;;-1:-1:-1;;;131736:21:0;;;;;;;;;;;131665:108;130832:959;;;130754:1037;;:::o;132596:1219::-;132762:17;132782:32;132796:4;132802:11;132782:13;:32::i;:::-;132825:23;132851:19;;;:8;:19;;;;;132762:52;;-1:-1:-1;132905:30:0;132887:14;;-1:-1:-1;;;132887:14:0;;;;:48;;;;;;;;:::i;:::-;;132883:159;;132959:71;;-1:-1:-1;;;132959:71:0;;23395:2:1;132959:71:0;;;23377:21:1;23434:2;23414:18;;;23407:30;23473:34;23453:18;;;23446:62;-1:-1:-1;;;23524:18:1;;;23517:36;23570:19;;132959:71:0;23193:402:1;132883:159:0;133075:19;;133058:83;141254:15;133058:83;;-1:-1:-1;;;133075:19:0;;;;133058:83;133054:143;;133165:20;;-1:-1:-1;;;133165:20:0;;;;;;;;;;;133054:143;133242:15;;-1:-1:-1;;;;;;133450:33:0;;;;133548:51;;;-1:-1:-1;;;;;;;;;;;133548:51:0;;;22819:19:1;22854:12;;;22847:28;;;-1:-1:-1;;;;;133242:15:0;;;;133537:63;;22891:12:1;;133548:51:0;22662:247:1;133537:63:0;133616:24;;133630:9;;-1:-1:-1;;;;;;;;;;;133616:24:0;;;;133657:11;;133653:155;;133689:35;;-1:-1:-1;;;133689:35:0;;133704:10;133689:35;;;23088:51:1;23155:18;;;23148:34;;;133689:5:0;-1:-1:-1;;;;;133689:14:0;;;;23061:18:1;;133689:35:0;22914:274:1;54123:343:0;54192:7;54219:239;54192:7;49598:2;49390:66;-1:-1:-1;;49390:66:0;54382:6;54375:71;54219:239;;;54367:80;54219:239;;;;;;;;23967:25:1;;;;24040:4;24028:17;;;24008:18;;;24001:45;;;;24062:18;;;24055:34;24105:18;;;24098:34;23939:19;;54219:239:0;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;54219:239:0;;-1:-1:-1;;54219:239:0;;;54123:343;-1:-1:-1;;;54123:343:0:o;129012:1014::-;129178:17;129198:32;129212:4;129218:11;129198:13;:32::i;:::-;129241:23;129267:19;;;:8;:19;;;;;129178:52;;-1:-1:-1;129394:14:0;;-1:-1:-1;;;129394:14:0;;;;:38;;;;;;;;:::i;:::-;;129390:159;;129456:81;;-1:-1:-1;;;129456:81:0;;;;;;;:::i;129390:159::-;129611:89;129673:26;141254:15;129611:89;:::i;:::-;129561:140;;-1:-1:-1;;;;;;;129712:47:0;;;-1:-1:-1;;;;;;;129561:140:0;;;;;;;129712:47;;;;;;129848:90;;;129865:40;129848:90;;;24534:19:1;24569:12;;;24562:28;;;129918:19:0;;;24646:3:1;24624:16;-1:-1:-1;;;;;;24624:16:1;24606:12;;;24599:65;129837:102:0;;24680:12:1;;129848:90:0;24320:378:1;129837:102:0;129998:19;;129955:63;;-1:-1:-1;;;129998:19:0;;;;;6474:42:1;;129987:9:0;;129955:63;;6462:2:1;6447:18;129955:63:0;;;;;;;129105:921;;129012:1014;;:::o;6920:200::-;7003:12;7035:77;7056:6;7064:4;7035:77;;;;;;;;;;;;;;;;;:20;:77::i;91366:1041::-;91488:10;:20;91428:19;;91488:39;;91511:16;;-1:-1:-1;;;91488:20:0;;;;:39;:::i;:::-;91470:15;:57;91466:111;;;-1:-1:-1;91561:4:0;91466:111;91828:21;;92079:10;:19;92189:18;;92079:19;92189:18;;;;;;;91738:488;;;;;;24914:19:1;;;;91990:12:0;24989:3:1;24967:16;-1:-1:-1;;;;;;24963:43:1;24949:12;;;24942:65;92079:19:0;;;-1:-1:-1;;25037:28:1;25023:12;;;25016:50;25082:12;;;25075:28;25119:12;;91738:488:0;;;-1:-1:-1;;91738:488:0;;;;;;;;;91665:576;;91738:488;91665:576;;;;92263:46;92293:15;92263:46;-1:-1:-1;;;92263:46:0;91621:631;;92263:46;91621:10;92263:46;92322:78;;;;-1:-1:-1;;92378:10:0;92357:31;-1:-1:-1;;;;;92357:31:0;;-1:-1:-1;;;92357:31:0;;;;;;;;;;:18;:31;91366:1041::o;52330:136::-;52397:4;52421:7;;;:37;;-1:-1:-1;;;;;52432:26:0;52330:136::o;86837:1602::-;86935:4;-1:-1:-1;;86956:6:0;:8;;;:38;;:80;;;;-1:-1:-1;;86998:6:0;:8;;;:38;;86956:80;86952:141;;;87060:21;;-1:-1:-1;;;87060:21:0;;;;;;;;;;;86952:141;87110:42;87131:6;:9;;;87142:6;:9;;;87110:20;:42::i;:::-;87105:102;;87176:19;;-1:-1:-1;;;87176:19:0;;;;;;;;;;;87105:102;87267:10;87279;87293:75;87322:7;:14;;;87338:7;:15;;;87305:49;;;;;;;;25319:2:1;25315:15;;;;-1:-1:-1;;;;;;25311:53:1;25299:66;;25390:2;25381:12;;25374:28;25427:2;25418:12;;25142:294;87305:49:0;;;;;;;;;;;;;87356:7;:11;;;87293;:75::i;:::-;87266:102;;;;87512:16;87531:43;87557:6;:8;;;87567:2;87571;87531:25;:43::i;:::-;87512:62;;87603:38;87618:6;:10;;;87630:6;:10;;;55864:24;;;;;;;22819:19:1;;;;22854:12;;;22847:28;;;;55864:24:0;;;;;;;;;22891:12:1;;;;55864:24:0;;55854:35;;;;;;55732:168;87603:38;-1:-1:-1;;;;;87591:50:0;:8;-1:-1:-1;;;;;87591:50:0;;87587:111;;87665:21;;-1:-1:-1;;;87665:21:0;;;;;;;;;;;87587:111;87710:15;87728:57;87754:6;:8;;;87764:6;:9;;;87775:6;:9;;;87728:25;:57::i;:::-;87710:75;;87813:38;87828:6;:10;;;87840:6;:10;;;55864:24;;;;;;;22819:19:1;;;;22854:12;;;22847:28;;;;55864:24:0;;;;;;;;;22891:12:1;;;;55864:24:0;;55854:35;;;;;;55732:168;87813:38;-1:-1:-1;;;;;87802:49:0;:7;-1:-1:-1;;;;;87802:49:0;;87798:110;;87875:21;;-1:-1:-1;;;87875:21:0;;;;;;;;;;;87798:110;88133:10;88145;88159:85;88165:6;:10;;;88177:6;:10;;;88189:6;:10;;;88230:6;:10;;;-1:-1:-1;;88201:39:0;;;;:::i;:::-;88242:1;88159:5;:85::i;:::-;88317:14;;;;;88333:9;;88344;;;;88363:15;;88300:79;;88132:112;;-1:-1:-1;88132:112:0;;-1:-1:-1;88257:14:0;;88287:106;;88300:79;;88132:112;;;;88363:15;88300:79;25863:2:1;25859:15;;;;-1:-1:-1;;;;;;25855:53:1;25843:66;;25934:2;25925:12;;25918:28;;;;25971:2;25962:12;;25955:28;;;;26008:2;25999:12;;25992:28;26045:3;26036:13;;26029:29;26083:3;26074:13;;26067:29;26121:3;26112:13;;25574:557;88300:79:0;;;;;;;;;;;;;88381:7;:11;;;88287:12;:106::i;:::-;88423:8;;;;88413:18;;-1:-1:-1;;;;;;;;86837:1602:0;;;;:::o;43138:226::-;43215:7;43236:17;43255:18;43277:23;43288:4;43294:1;43297:2;43277:10;:23::i;:::-;43235:65;;;;43311:18;43323:5;43311:11;:18::i;:::-;-1:-1:-1;43347:9:0;43138:226;-1:-1:-1;;;;43138:226:0:o;7314:332::-;7459:12;7485;7499:23;7526:6;-1:-1:-1;;;;;7526:19:0;7546:4;7526:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7484:67;;;;7569:69;7596:6;7604:7;7613:10;7625:12;7569:26;:69::i;:::-;7562:76;7314:332;-1:-1:-1;;;;;;7314:332:0:o;52662:555::-;52739:6;-1:-1:-1;;53077:26:0;53073:2;-1:-1:-1;;53040:2:0;53036;53029:42;53022:82;52984:11;52951:229;-1:-1:-1;;52897:2:0;52893;52886:42;52861:338;;52662:555;-1:-1:-1;;;52662:555:0:o;62034:518::-;62118:10;62130;62154;62166;62180:27;62194:7;62203:3;62180:13;:27::i;:::-;62153:54;;;;62221:11;62234;62249:32;62277:2;62249:19;:32::i;:::-;62220:61;;;;62315:11;62328;62343:32;62371:2;62343:19;:32::i;:::-;62314:61;;;;62448:10;62460;62474:34;62480:3;62485;62490;62495;49737:66;62474:5;:34::i;:::-;62447:61;;;;62528:16;62537:2;62541;62528:8;:16::i;:::-;62521:23;;;;;;;;;;;;62034:518;;;;;;:::o;55118:344::-;55216:7;;55261:6;55266:1;55261:2;:6;:::i;:::-;55271:1;55261:11;55257:95;;-1:-1:-1;55296:2:0;55257:95;;;-1:-1:-1;55338:2:0;55257:95;55371:83;55381:1;55384:4;55398:2;-1:-1:-1;;55426:2:0;55418:6;55411:41;55371:83;;;55403:50;55371:83;;;;;;;;23967:25:1;;;;24040:4;24028:17;;;24008:18;;;24001:45;;;;24062:18;;;24055:34;24105:18;;;24098:34;23939:19;;55371:83:0;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;55371:83:0;;-1:-1:-1;;55371:83:0;;;55118:344;-1:-1:-1;;;;;;55118:344:0:o;56867:4599::-;57043:10;57055;57194:2;57190;57187:10;57183:15;57178:2;57174;57171:10;57167:32;57164:182;;;57329:1;57326;57319:12;57164:182;57360:10;57384:12;57440:2;57436;57433:10;57428:2;57424;57421:10;57417:27;57488:4;57483:1302;;;;58799:1557;;;;57410:2946;;57483:1302;-1:-1:-1;;57607:1:0;-1:-1:-1;;57574:2:0;57571:1;57564:41;57557:80;57545:92;;57756:4;57750:11;57795:4;57786:7;57779:21;57863:4;57856;57847:7;57843:18;57836:32;57935:4;57928;57919:7;57915:18;57908:32;58006:8;57999:4;57990:7;57986:18;57979:36;-1:-1:-1;;58061:4:0;58052:7;58048:18;58041:94;-1:-1:-1;;58182:4:0;58173:7;58169:18;58162:54;58304:4;58295:7;58289:4;58280:7;58274:4;58270:1;58266:6;58255:54;58245:170;;58394:1;58391;58384:12;58245:170;-1:-1:-1;;58686:7:0;58680:14;-1:-1:-1;;58597:26:0;58593:2;58589;58582:42;58579:1;58531:122;58464:306;58433:337;;;57483:1302;;58799:1557;-1:-1:-1;;59011:2:0;-1:-1:-1;;58979:35:0;58943:2;58897:202;58864:235;;59209:4;59203:11;59248:4;59239:7;59232:21;59316:4;59309;59300:7;59296:18;59289:32;59388:4;59381;59372:7;59368:18;59361:32;59459:8;59452:4;59443:7;59439:18;59432:36;-1:-1:-1;;59514:4:0;59505:7;59501:18;59494:94;-1:-1:-1;;59635:4:0;59626:7;59622:18;59615:54;59757:4;59748:7;59742:4;59733:7;59727:4;59723:1;59719:6;59708:54;59698:170;;59847:1;59844;59837:12;59698:170;-1:-1:-1;;60237:7:0;60231:14;-1:-1:-1;;60108:2:0;-1:-1:-1;;60076:35:0;60036:2;59986:218;59919:422;59888:453;;;57410:2946;;;-1:-1:-1;;60723:26:0;60684:2;-1:-1:-1;;60652:35:0;60613:2;-1:-1:-1;;60581:35:0;60533:239;-1:-1:-1;;60463:6:0;60455;60448:50;60395:445;60372:468;;-1:-1:-1;;61377:2:0;-1:-1:-1;;61345:35:0;-1:-1:-1;;61193:26:0;61150:2;-1:-1:-1;;61118:35:0;61078:2;61028:218;60995:6;60938:384;60879:569;60856:592;;;56867:4599;;;;;;;;:::o;75953:884::-;76038:9;76061:10;76073;76087:49;76123:7;76132:3;76087:35;:49::i;:::-;76060:76;;;;76305:4;76299:11;76359:4;76356:1;76349:15;76417:4;76410;76407:1;76403:12;76396:26;76479:4;76472;76469:1;76465:12;76458:26;76540:2;76533:4;76530:1;76526:12;76519:24;76586:2;76579:4;76576:1;76572:12;76565:24;76624:1;76617:4;76614:1;76610:12;76603:23;-1:-1:-1;;76666:4:0;76663:1;76659:12;76652:43;76767:4;76764:1;76758:4;76755:1;76749:4;76745:1;76741:6;76730:42;76720:70;;76786:1;76783;76776:12;76720:70;76811:8;;75953:884;-1:-1:-1;;;;;75953:884:0:o;42656:310::-;42736:7;;-1:-1:-1;;;;;42782:80:0;;42736:7;42889:25;42905:3;42890:18;;;42912:2;42889:25;:::i;:::-;42873:42;;42933:25;42944:4;42950:1;42953;42956;42933:10;:25::i;:::-;42926:32;;;;;;42656:310;;;;;;:::o;39006:521::-;39084:20;39075:5;:29;;;;;;;;:::i;:::-;;39071:449;;39006:521;:::o;39071:449::-;39182:29;39173:5;:38;;;;;;;;:::i;:::-;;39169:351;;39228:34;;-1:-1:-1;;;39228:34:0;;27096:2:1;39228:34:0;;;27078:21:1;27135:2;27115:18;;;27108:30;27174:26;27154:18;;;27147:54;27218:18;;39228:34:0;26894:348:1;39169:351:0;39293:35;39284:5;:44;;;;;;;;:::i;:::-;;39280:240;;39345:41;;-1:-1:-1;;;39345:41:0;;27449:2:1;39345:41:0;;;27431:21:1;27488:2;27468:18;;;27461:30;27527:33;27507:18;;;27500:61;27578:18;;39345:41:0;27247:355:1;39280:240:0;39417:30;39408:5;:39;;;;;;;;:::i;:::-;;39404:116;;39464:44;;-1:-1:-1;;;39464:44:0;;27809:2:1;39464:44:0;;;27791:21:1;27848:2;27828:18;;;27821:30;27887:34;27867:18;;;27860:62;-1:-1:-1;;;27938:18:1;;;27931:32;27980:19;;39464:44:0;27607:398:1;7942:644:0;8127:12;8156:7;8152:427;;;8184:10;:17;8205:1;8184:22;8180:290;;-1:-1:-1;;;;;1823:19:0;;;8394:60;;;;-1:-1:-1;;;8394:60:0;;28212:2:1;8394:60:0;;;28194:21:1;28251:2;28231:18;;;28224:30;28290:31;28270:18;;;28263:59;28339:18;;8394:60:0;28010:353:1;8394:60:0;-1:-1:-1;8491:10:0;8484:17;;8152:427;8534:33;8542:10;8554:12;8534:7;:33::i;:::-;7942:644;;;;;;:::o;73891:1578::-;73977:10;73989;74013;74025;74037;74051:42;74080:7;74089:3;74051:28;:42::i;:::-;74012:81;;;;;;74259:4;74253:11;74313:4;74310:1;74303:15;74371:4;74364;74361:1;74357:12;74350:26;74433:4;74426;74423:1;74419:12;74412:26;74494:2;74487:4;74484:1;74480:12;74473:24;74540:2;74533:4;74530:1;74526:12;74519:24;74578:1;74571:4;74568:1;74564:12;74557:23;-1:-1:-1;;74620:4:0;74617:1;74613:12;74606:48;74726:4;74723:1;74717:4;74714:1;74708:4;74704:1;74700:6;74689:42;74679:70;;74745:1;74742;74735:12;74679:70;74777:1;74771:8;74765:14;;;74956:4;74950:11;74985:4;74982:1;74975:15;75043:4;75036;75033:1;75029:12;75022:26;75105:2;75098:4;75095:1;75091:12;75084:24;75143:4;75136;75133:1;75129:12;75122:26;75204:2;75197:4;75194:1;75190:12;75183:24;75250:1;75243:4;75240:1;75236:12;75229:23;-1:-1:-1;;75292:4:0;75289:1;75285:12;75278:48;75398:4;75395:1;75389:4;75386:1;75380:4;75376:1;75372:6;75361:42;75351:70;;75417:1;75414;75407:12;75351:70;75449:1;75443:8;75437:14;;;74926:536;;;73891:1578;;;;;:::o;67980:5411::-;68043:10;68055;-1:-1:-1;;68180:1:0;68177;68170:40;-1:-1:-1;;68258:3:0;-1:-1:-1;;68248:42:0;68241:49;;-1:-1:-1;;68348:3:0;68343;68336:44;-1:-1:-1;;68432:3:0;68427;68420:44;68413:51;;-1:-1:-1;;68524:1:0;68519:3;68512:42;-1:-1:-1;;68608:7:0;68603:3;68596:48;68589:55;-1:-1:-1;68681:7:0;68709:10;;68788:4;68783:22;;;;68819:58;;;;68702:175;;68783:22;-1:-1:-1;;68795:8:0;;68783:22;;68819:58;68871:3;-1:-1:-1;;68839:36:0;68832:43;;68702:175;;-1:-1:-1;;68916:3:0;68907:7;68900:48;68893:55;;-1:-1:-1;;69002:3:0;68997;68990:44;68983:51;;-1:-1:-1;;69090:3:0;69085;69078:44;-1:-1:-1;;69182:3:0;69173:7;69166:48;-1:-1:-1;;69268:3:0;69263;69256:44;69249:51;;-1:-1:-1;;69356:3:0;69351;69344:44;69337:51;;-1:-1:-1;;69444:3:0;69439;69432:44;69425:51;;-1:-1:-1;;69536:3:0;69527:7;69520:48;69513:55;;-1:-1:-1;;69622:3:0;69617;69610:44;69603:51;;;-1:-1:-1;;69709:3:0;69704;69697:44;69691:50;;69843:6;69863:12;-1:-1:-1;;70307:3:0;70302;70295:44;-1:-1:-1;;70396:3:0;70391;70384:44;-1:-1:-1;;70485:3:0;70480;70473:44;70466:51;;70635:4;70629:11;70668:4;70665:1;70658:15;70730:4;70723;70720:1;70716:12;70709:26;70796:4;70789;70786:1;70782:12;70775:26;70861:3;70854:4;70851:1;70847:12;70840:25;-1:-1:-1;;;;;70905:4:0;70902:1;70898:12;70891:25;-1:-1:-1;;70960:4:0;70957:1;70953:12;70946:48;71070:4;71067:1;71061:4;71058:1;71052:4;71048:1;71044:6;71033:42;71023:158;;71160:1;71157;71150:12;71023:158;-1:-1:-1;;71233:3:0;71229:1;71223:8;71216:49;71201:64;;;;-1:-1:-1;;71334:3:0;71325:7;71318:48;-1:-1:-1;;71430:7:0;71421;71414:52;-1:-1:-1;;71520:3:0;71515;71508:44;71501:51;;71607:3;71602;71599:12;71673:4;71668:154;;;;71840:105;;;;71592:353;;71668:154;71764:4;71752:16;;71796:7;71790:13;;71668:154;;71840:105;71886:5;71874:17;;71919:7;71913:13;;71592:353;;;;;-1:-1:-1;;72050:1:0;72045:3;72038:42;72032:48;;-1:-1:-1;;72132:2:0;72128;72121:42;72115:48;;72202:8;72199:162;;;72236:3;72230:9;;72304:2;72298:8;;72199:162;;;;72454:1;72450:2;72446:10;72442:1;72439;72435:9;72432:25;72422:178;;72553:2;-1:-1:-1;;72521:35:0;72515:41;;72422:178;72670:4;72664:11;72649:26;;72705:4;72696:7;72689:21;72769:4;72762;72753:7;72749:18;72742:32;72837:4;72830;72821:7;72817:18;72810:32;72904:3;72897:4;72888:7;72884:18;72877:31;;;;-1:-1:-1;;72950:4:0;72941:7;72937:18;72930:94;-1:-1:-1;;73065:4:0;73056:7;73052:18;73045:54;73177:4;73168:7;73162:4;73153:7;73147:4;73143:1;73139:6;73128:54;73118:158;;73259:1;73256;73249:12;73118:158;-1:-1:-1;;73315:7:0;73309:14;73305:2;73298:54;73292:60;;;67980:5411;;;:::o;63384:3971::-;63449:10;63461;-1:-1:-1;;63592:2:0;63588;63581:42;-1:-1:-1;;63676:8:0;63672:2;63665:48;-1:-1:-1;;64202:26:0;64196:4;-1:-1:-1;;64163:2:0;64157:4;64150:44;64143:86;-1:-1:-1;;64017:26:0;64007:8;64001:4;63994:50;-1:-1:-1;;63931:7:0;63925:4;63918:49;63885:235;63856:441;-1:-1:-1;;64537:4:0;-1:-1:-1;;64458:26:0;64454:2;64448:4;64441:44;64431:8;64424:90;64395:214;64705:4;64699:11;64740:4;64731:7;64724:21;64804:4;64797;64788:7;64784:18;64777:32;64872:4;64865;64856:7;64852:18;64845:32;64939:4;64932;64923:7;64919:18;64912:32;-1:-1:-1;;64986:4:0;64977:7;64973:18;64966:94;-1:-1:-1;;65103:4:0;65094:7;65090:18;65083:54;65221:4;65212:7;65206:4;65197:7;65191:4;65187:1;65183:6;65172:54;65162:158;;65303:1;65300;65293:12;65162:158;65350:7;65344:14;65336:22;;-1:-1:-1;;65425:4:0;65419;65412:46;65406:52;;-1:-1:-1;;65931:26:0;65925:4;-1:-1:-1;;65892:2:0;65886:4;65879:44;65872:86;-1:-1:-1;;65746:26:0;65736:8;65730:4;65723:50;-1:-1:-1;;65660:7:0;65654:4;65647:49;65614:235;65585:441;65555:471;;-1:-1:-1;;66349:26:0;-1:-1:-1;;;;66310:2:0;66304:4;66297:44;66290:86;-1:-1:-1;;66211:26:0;66201:8;66195:4;66188:50;66179:7;66172:95;66143:301;66113:331;;66539:4;66533:11;66522:22;;66574:4;66565:7;66558:21;66638:4;66631;66622:7;66618:18;66611:32;66706:4;66699;66690:7;66686:18;66679:32;66773:5;66766:4;66757:7;66753:18;66746:33;-1:-1:-1;;66821:4:0;66812:7;66808:18;66801:94;-1:-1:-1;;66938:4:0;66929:7;66925:18;66918:54;67050:4;67041:7;67035:4;67026:7;67020:4;67016:1;67012:6;67001:54;66991:158;;67132:1;67129;67122:12;66991:158;67174:14;;-1:-1:-1;;;67310:26:0;-1:-1:-1;67174:14:0;67310:26;67267:5;67263:2;67256:45;67249:88;67243:94;;;;;63384:3971;;;;;:::o;81882:3280::-;82042:10;82054;82181:3;82175;82169:10;82166:19;82163:39;;;82198:1;82195;82188:12;82163:39;82218:6;82327:4;82321:11;82428:19;82505:4;82678:1;82663:271;82694:7;82688:14;82685:1;82682:21;82663:271;;;82790:19;;;82784:26;82756;;;82749:62;82863:4;82847:21;;;;82899:16;;;;82711:12;82663:271;;;82667:14;;83095:3;83085:7;83079:14;83075:24;83061:38;;83153:4;83140:10;83129:9;83125:26;83117:41;83285:1;83269:18;83233:4;83472:1;83457:260;83488:3;83482:10;83479:1;83476:17;83457:260;;;83580:14;;;83574:21;83546:26;;;83539:57;83648:4;83632:21;;;;83683:15;;;;83501:12;83457:260;;;83461:14;;83940:3;83933;83927:10;83917:7;83911:14;83907:31;83903:41;83889:55;;84004:3;83998:10;83985;83974:9;83970:26;83962:47;;84087:7;84081:14;84075:3;84069:10;84065:31;84060:3;84056:41;84045:9;84035:63;84029:69;;;84205:4;84199:11;84242:2;84231:9;84224:21;84325:1;84318:4;84307:9;84303:20;84295:32;84359:4;84389;84486:1;84471:238;84502:3;84496:10;84493:1;84490:17;84471:238;;;84588:14;;;84582:21;84556:24;;;84549:55;84648:4;84634:19;;;;84679:15;;;;84515:12;84471:238;;;84475:14;;;84842:3;84836:10;84828:3;84822:10;84816:4;84812:21;84801:9;84797:37;84789:58;84904:3;84898:10;84894:2;84890:19;84879:9;84869:41;84863:47;;84995:2;84991;84987:11;84976:9;84969:30;85079:1;85072:4;85061:9;85057:20;85049:32;85138:3;85132:10;85128:2;85124:19;85113:9;85103:41;85097:47;;;;81882:3280;;;;;:::o;43546:1477::-;43634:7;;44568:66;44555:79;;44551:163;;;-1:-1:-1;44667:1:0;;-1:-1:-1;44671:30:0;44651:51;;44551:163;44828:24;;;44811:14;44828:24;;;;;;;;;23967:25:1;;;24040:4;24028:17;;24008:18;;;24001:45;;;;24062:18;;;24055:34;;;24105:18;;;24098:34;;;44828:24:0;;23939:19:1;;44828:24:0;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;44828:24:0;;-1:-1:-1;;44828:24:0;;;-1:-1:-1;;;;;;;44867:20:0;;44863:103;;44920:1;44924:29;44904:50;;;;;;;44863:103;44986:6;-1:-1:-1;44994:20:0;;-1:-1:-1;43546:1477:0;;;;;;;;:::o;9128:552::-;9289:17;;:21;9285:388;;9521:10;9515:17;9578:15;9565:10;9561:2;9557:19;9550:44;9285:388;9648:12;9641:20;;-1:-1:-1;;;9641:20:0;;;;;;;;:::i;77603:3519::-;77756:10;77768;77780;77907:3;77901;77895:10;77892:19;77889:39;;;77924:1;77921;77914:12;77889:39;77944:6;78053:4;78047:11;78154:19;78231:4;78404:1;78389:271;78420:7;78414:14;78411:1;78408:21;78389:271;;;78516:19;;;78510:26;78482;;;78475:62;78589:4;78573:21;;;;78625:16;;;;78437:12;78389:271;;;78393:14;;78821:3;78811:7;78805:14;78801:24;78787:38;;78879:4;78866:10;78855:9;78851:26;78843:41;79011:1;78995:18;78959:4;79198:1;79183:260;79214:3;79208:10;79205:1;79202:17;79183:260;;;79306:14;;;79300:21;79272:26;;;79265:57;79374:4;79358:21;;;;79409:15;;;;79227:12;79183:260;;;79187:14;;79666:3;79659;79653:10;79643:7;79637:14;79633:31;79629:41;79615:55;;79730:3;79724:10;79711;79700:9;79696:26;79688:47;;79813:7;79807:14;79801:3;79795:10;79791:31;79786:3;79782:41;79771:9;79761:63;79755:69;;;79931:4;79925:11;79968:2;79957:9;79950:21;80051:1;80044:4;80033:9;80029:20;80021:32;80085:4;80115;80212:1;80197:238;80228:3;80222:10;80219:1;80216:17;80197:238;;;80314:14;;;80308:21;80282:24;;;80275:55;80374:4;80360:19;;;;80405:15;;;;80241:12;80197:238;;;80201:14;;;80568:3;80562:10;80554:3;80548:10;80542:4;80538:21;80527:9;80523:37;80515:58;80630:3;80624:10;80620:2;80616:19;80605:9;80595:41;80589:47;;80721:2;80717;80713:11;80702:9;80695:30;80805:1;80798:4;80787:9;80783:20;80775:32;80864:3;80858:10;80854:2;80850:19;80839:9;80829:41;80823:47;;80955:2;80951;80947:11;80936:9;80929:30;81039:1;81032:4;81021:9;81017:20;81009:32;81098:3;81092:10;81088:2;81084:19;81073:9;81063:41;81057:47;;;;77603:3519;;;;;:::o;14:131:1:-;-1:-1:-1;;;;;89:31:1;;79:42;;69:70;;135:1;132;125:12;150:347;201:8;211:6;265:3;258:4;250:6;246:17;242:27;232:55;;283:1;280;273:12;232:55;-1:-1:-1;306:20:1;;349:18;338:30;;335:50;;;381:1;378;371:12;335:50;418:4;410:6;406:17;394:29;;470:3;463:4;454:6;446;442:19;438:30;435:39;432:59;;;487:1;484;477:12;502:1205;628:6;636;644;652;660;668;676;684;737:3;725:9;716:7;712:23;708:33;705:53;;;754:1;751;744:12;705:53;793:9;780:23;812:31;837:5;812:31;:::i;:::-;862:5;-1:-1:-1;919:2:1;904:18;;891:32;932:33;891:32;932:33;:::i;:::-;984:7;-1:-1:-1;1043:2:1;1028:18;;1015:32;1056:33;1015:32;1056:33;:::i;:::-;1108:7;-1:-1:-1;1162:2:1;1147:18;;1134:32;;-1:-1:-1;1217:3:1;1202:19;;1189:33;1241:18;1271:14;;;1268:34;;;1298:1;1295;1288:12;1268:34;1337:58;1387:7;1378:6;1367:9;1363:22;1337:58;:::i;:::-;1414:8;;-1:-1:-1;1311:84:1;-1:-1:-1;1502:3:1;1487:19;;1474:33;;-1:-1:-1;1519:16:1;;;1516:36;;;1548:1;1545;1538:12;1516:36;;1587:60;1639:7;1628:8;1617:9;1613:24;1587:60;:::i;:::-;502:1205;;;;-1:-1:-1;502:1205:1;;-1:-1:-1;502:1205:1;;;;;;1666:8;-1:-1:-1;;;502:1205:1:o;1712:201::-;1801:20;;-1:-1:-1;;;;;1850:38:1;;1840:49;;1830:77;;1903:1;1900;1893:12;1830:77;1712:201;;;:::o;1918:511::-;2023:6;2031;2039;2092:2;2080:9;2071:7;2067:23;2063:32;2060:52;;;2108:1;2105;2098:12;2060:52;2147:9;2134:23;2166:31;2191:5;2166:31;:::i;:::-;2216:5;-1:-1:-1;2273:2:1;2258:18;;2245:32;2286:33;2245:32;2286:33;:::i;:::-;2338:7;-1:-1:-1;2364:59:1;2419:2;2404:18;;2364:59;:::i;:::-;2354:69;;1918:511;;;;;:::o;2434:165::-;2503:5;2548:3;2539:6;2534:3;2530:16;2526:26;2523:46;;;2565:1;2562;2555:12;2523:46;-1:-1:-1;2587:6:1;2434:165;-1:-1:-1;2434:165:1:o;2604:162::-;2670:5;2715:3;2706:6;2701:3;2697:16;2693:26;2690:46;;;2732:1;2729;2722:12;2771:531;2917:6;2925;2933;2986:3;2974:9;2965:7;2961:23;2957:33;2954:53;;;3003:1;3000;2993:12;2954:53;3042:9;3029:23;3061:31;3086:5;3061:31;:::i;:::-;3111:5;-1:-1:-1;3135:72:1;3199:7;3194:2;3179:18;;3135:72;:::i;:::-;3125:82;;3226:70;3288:7;3282:3;3271:9;3267:19;3226:70;:::i;3307:247::-;3366:6;3419:2;3407:9;3398:7;3394:23;3390:32;3387:52;;;3435:1;3432;3425:12;3387:52;3474:9;3461:23;3493:31;3518:5;3493:31;:::i;:::-;3543:5;3307:247;-1:-1:-1;;;3307:247:1:o;3559:257::-;3654:6;3707:3;3695:9;3686:7;3682:23;3678:33;3675:53;;;3724:1;3721;3714:12;3675:53;3747:63;3802:7;3791:9;3747:63;:::i;4003:315::-;4071:6;4079;4132:2;4120:9;4111:7;4107:23;4103:32;4100:52;;;4148:1;4145;4138:12;4100:52;4184:9;4171:23;4161:33;;4244:2;4233:9;4229:18;4216:32;4257:31;4282:5;4257:31;:::i;:::-;4307:5;4297:15;;;4003:315;;;;;:::o;4747:388::-;4815:6;4823;4876:2;4864:9;4855:7;4851:23;4847:32;4844:52;;;4892:1;4889;4882:12;4844:52;4931:9;4918:23;4950:31;4975:5;4950:31;:::i;:::-;5000:5;-1:-1:-1;5057:2:1;5042:18;;5029:32;5070:33;5029:32;5070:33;:::i;5140:180::-;5199:6;5252:2;5240:9;5231:7;5227:23;5223:32;5220:52;;;5268:1;5265;5258:12;5220:52;-1:-1:-1;5291:23:1;;5140:180;-1:-1:-1;5140:180:1:o;5325:127::-;5386:10;5381:3;5377:20;5374:1;5367:31;5417:4;5414:1;5407:15;5441:4;5438:1;5431:15;5457:837;-1:-1:-1;;;;;5869:39:1;;5851:58;;5957:14;5945:27;;5940:2;5925:18;;5918:55;6021:10;6009:23;;6004:2;5989:18;;5982:51;6081:8;6069:21;;6064:2;6049:18;;6042:49;5838:3;5823:19;;6121:1;6110:13;;6100:144;;6166:10;6161:3;6157:20;6154:1;6147:31;6201:4;6198:1;6191:15;6229:4;6226:1;6219:15;6100:144;6281:6;6275:3;6264:9;6260:19;6253:35;5457:837;;;;;;;;:::o;6527:464::-;6673:6;6681;6689;6742:3;6730:9;6721:7;6717:23;6713:33;6710:53;;;6759:1;6756;6749:12;6710:53;6795:9;6782:23;6772:33;;6824:72;6888:7;6883:2;6872:9;6868:18;6824:72;:::i;7188:626::-;7285:6;7293;7346:2;7334:9;7325:7;7321:23;7317:32;7314:52;;;7362:1;7359;7352:12;7314:52;7402:9;7389:23;7431:18;7472:2;7464:6;7461:14;7458:34;;;7488:1;7485;7478:12;7458:34;7526:6;7515:9;7511:22;7501:32;;7571:7;7564:4;7560:2;7556:13;7552:27;7542:55;;7593:1;7590;7583:12;7542:55;7633:2;7620:16;7659:2;7651:6;7648:14;7645:34;;;7675:1;7672;7665:12;7645:34;7728:7;7723:2;7713:6;7710:1;7706:14;7702:2;7698:23;7694:32;7691:45;7688:65;;;7749:1;7746;7739:12;7688:65;7780:2;7772:11;;;;;7802:6;;-1:-1:-1;7188:626:1;;-1:-1:-1;;;;7188:626:1:o;7819:250::-;7904:1;7914:113;7928:6;7925:1;7922:13;7914:113;;;8004:11;;;7998:18;7985:11;;;7978:39;7950:2;7943:10;7914:113;;;-1:-1:-1;;8061:1:1;8043:16;;8036:27;7819:250::o;8074:270::-;8115:3;8153:5;8147:12;8180:6;8175:3;8168:19;8196:76;8265:6;8258:4;8253:3;8249:14;8242:4;8235:5;8231:16;8196:76;:::i;:::-;8326:2;8305:15;-1:-1:-1;;8301:29:1;8292:39;;;;8333:4;8288:50;;8074:270;-1:-1:-1;;8074:270:1:o;8349:800::-;8509:4;8538:2;8578;8567:9;8563:18;8608:2;8597:9;8590:21;8631:6;8666;8660:13;8697:6;8689;8682:22;8735:2;8724:9;8720:18;8713:25;;8797:2;8787:6;8784:1;8780:14;8769:9;8765:30;8761:39;8747:53;;8835:2;8827:6;8823:15;8856:1;8866:254;8880:6;8877:1;8874:13;8866:254;;;8973:2;8969:7;8957:9;8949:6;8945:22;8941:36;8936:3;8929:49;9001:39;9033:6;9024;9018:13;9001:39;:::i;:::-;8991:49;-1:-1:-1;9098:12:1;;;;9063:15;;;;8902:1;8895:9;8866:254;;;-1:-1:-1;9137:6:1;;8349:800;-1:-1:-1;;;;;;;8349:800:1:o;9154:219::-;9303:2;9292:9;9285:21;9266:4;9323:44;9363:2;9352:9;9348:18;9340:6;9323:44;:::i;9600:370::-;9696:6;9704;9757:2;9745:9;9736:7;9732:23;9728:32;9725:52;;;9773:1;9770;9763:12;9725:52;9812:9;9799:23;9831:31;9856:5;9831:31;:::i;:::-;9881:5;-1:-1:-1;9905:59:1;9960:2;9945:18;;9905:59;:::i;:::-;9895:69;;9600:370;;;;;:::o;9975:396::-;10112:6;10120;10173:3;10161:9;10152:7;10148:23;10144:33;10141:53;;;10190:1;10187;10180:12;10141:53;10213:63;10268:7;10257:9;10213:63;:::i;:::-;10203:73;;10295:70;10357:7;10351:3;10340:9;10336:19;10295:70;:::i;10584:251::-;10654:6;10707:2;10695:9;10686:7;10682:23;10678:32;10675:52;;;10723:1;10720;10713:12;10675:52;10755:9;10749:16;10774:31;10799:5;10774:31;:::i;10840:127::-;10901:10;10896:3;10892:20;10889:1;10882:31;10932:4;10929:1;10922:15;10956:4;10953:1;10946:15;10972:125;11037:9;;;11058:10;;;11055:36;;;11071:18;;:::i;11514:277::-;11581:6;11634:2;11622:9;11613:7;11609:23;11605:32;11602:52;;;11650:1;11647;11640:12;11602:52;11682:9;11676:16;11735:5;11728:13;11721:21;11714:5;11711:32;11701:60;;11757:1;11754;11747:12;11796:311;11883:6;11936:2;11924:9;11915:7;11911:23;11907:32;11904:52;;;11952:1;11949;11942:12;11904:52;11991:9;11978:23;12041:16;12034:5;12030:28;12023:5;12020:39;12010:67;;12073:1;12070;12063:12;12112:308;12204:6;12257:2;12245:9;12236:7;12232:23;12228:32;12225:52;;;12273:1;12270;12263:12;12225:52;12312:9;12299:23;12362:8;12355:5;12351:20;12344:5;12341:31;12331:59;;12386:1;12383;12376:12;12425:315;12522:6;12575:2;12563:9;12554:7;12550:23;12546:32;12543:52;;;12591:1;12588;12581:12;12543:52;12630:9;12617:23;12680:10;12673:5;12669:22;12662:5;12659:33;12649:61;;12706:1;12703;12696:12;12745:313;12836:6;12889:2;12877:9;12868:7;12864:23;12860:32;12857:52;;;12905:1;12902;12895:12;12857:52;12944:9;12931:23;12994:14;12987:5;12983:26;12976:5;12973:37;12963:65;;13024:1;13021;13014:12;13063:235;13150:6;13203:2;13191:9;13182:7;13178:23;13174:32;13171:52;;;13219:1;13216;13209:12;13171:52;13242:50;13282:9;13242:50;:::i;15380:127::-;15441:10;15436:3;15432:20;15429:1;15422:31;15472:4;15469:1;15462:15;15496:4;15493:1;15486:15;15512:127;15573:10;15568:3;15564:20;15561:1;15554:31;15604:4;15601:1;15594:15;15628:4;15625:1;15618:15;15644:521;15721:4;15727:6;15787:11;15774:25;15881:2;15877:7;15866:8;15850:14;15846:29;15842:43;15822:18;15818:68;15808:96;;15900:1;15897;15890:12;15808:96;15927:33;;15979:20;;;-1:-1:-1;16022:18:1;16011:30;;16008:50;;;16054:1;16051;16044:12;16008:50;16087:4;16075:17;;-1:-1:-1;16118:14:1;16114:27;;;16104:38;;16101:58;;;16155:1;16152;16145:12;16170:135;16209:3;16230:17;;;16227:43;;16250:18;;:::i;:::-;-1:-1:-1;16297:1:1;16286:13;;16170:135::o;17770:188::-;-1:-1:-1;;;;;17883:10:1;;;17895;;;17879:27;;17918:11;;;17915:37;;;17932:18;;:::i;17963:170::-;18030:8;18058:10;;;18070;;;18054:27;;18093:11;;;18090:37;;;18107:18;;:::i;18541:392::-;18753:19;;;18797:2;18788:12;;18781:28;;;;18865:3;18843:16;-1:-1:-1;;;;;;18839:59:1;18834:2;18825:12;;18818:81;18924:2;18915:12;;18541:392::o;19896:1033::-;19986:6;20017:3;20061:2;20049:9;20040:7;20036:23;20032:32;20029:52;;;20077:1;20074;20067:12;20029:52;20110:2;20104:9;20140:15;;;;20185:18;20170:34;;20206:22;;;20167:62;20164:185;;;20271:10;20266:3;20262:20;20259:1;20252:31;20306:4;20303:1;20296:15;20334:4;20331:1;20324:15;20164:185;20369:10;20365:2;20358:22;20417:9;20404:23;20396:6;20389:39;20489:2;20478:9;20474:18;20461:32;20456:2;20448:6;20444:15;20437:57;20555:2;20544:9;20540:18;20527:32;20522:2;20514:6;20510:15;20503:57;20621:2;20610:9;20606:18;20593:32;20588:2;20580:6;20576:15;20569:57;20688:3;20677:9;20673:19;20660:33;20654:3;20646:6;20642:16;20635:59;20756:3;20745:9;20741:19;20728:33;20722:3;20714:6;20710:16;20703:59;20824:3;20813:9;20809:19;20796:33;20790:3;20782:6;20778:16;20771:59;20892:3;20881:9;20877:19;20864:33;20858:3;20850:6;20846:16;20839:59;20917:6;20907:16;;;;19896:1033;;;;:::o;20934:176::-;21001:14;21035:10;;;21047;;;21031:27;;21070:11;;;21067:37;;;21084:18;;:::i;21115:191::-;-1:-1:-1;;;;;21242:10:1;;;21230;;;21226:27;;21265:12;;;21262:38;;;21280:18;;:::i;22245:412::-;22447:2;22429:21;;;22486:2;22466:18;;;22459:30;22525:34;22520:2;22505:18;;22498:62;-1:-1:-1;;;22591:2:1;22576:18;;22569:46;22647:3;22632:19;;22245:412::o;24143:172::-;24210:10;24240;;;24252;;;24236:27;;24275:11;;;24272:37;;;24289:18;;:::i;25441:128::-;25508:9;;;25529:11;;;25526:37;;;25543:18;;:::i;26136:287::-;26265:3;26303:6;26297:13;26319:66;26378:6;26373:3;26366:4;26358:6;26354:17;26319:66;:::i;:::-;26401:16;;;;;26136:287;-1:-1:-1;;26136:287:1:o;26428:209::-;26460:1;26486;26476:132;;26530:10;26525:3;26521:20;26518:1;26511:31;26565:4;26562:1;26555:15;26593:4;26590:1;26583:15;26476:132;-1:-1:-1;26622:9:1;;26428:209::o
Swarm Source
ipfs://9ded07965f6328884d9e87e9794963325b6e7136a1a2366eeac2078a7a5d0fde
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in XDAI
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.