Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- LimitHook
- Optimization enabled
- true
- Compiler version
- v0.8.13+commit.abaa5c0e
- Optimization runs
- 999999
- EVM Version
- default
- Verified at
- 2024-10-23T11:16:52.528364Z
Constructor Arguments
0x00000000000000000000000044a44837894b5edc2bde64567fc62599b3b88f4c000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d0000000000000000000000000000000000000000000000000000000000000001
Arg [0] (address) : 0x44a44837894b5edc2bde64567fc62599b3b88f4c
Arg [1] (address) : 0xb5b1e1ba18d323a2a7313f970bf4310cf8d60b8d
Arg [2] (bool) : true
contracts/hooks/LimitHook.sol
// SPDX-License-Identifier: GPL-3.0-only // Sources flattened with hardhat v2.22.4 https://hardhat.org // File contracts/common/Constants.sol pragma solidity 0.8.13; address constant ETH_ADDRESS = address( 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ); bytes32 constant NORMAL_CONTROLLER = keccak256("NORMAL_CONTROLLER"); bytes32 constant FIAT_TOKEN_CONTROLLER = keccak256("FIAT_TOKEN_CONTROLLER"); bytes32 constant LIMIT_HOOK = keccak256("LIMIT_HOOK"); bytes32 constant LIMIT_EXECUTION_HOOK = keccak256("LIMIT_EXECUTION_HOOK"); bytes32 constant LIMIT_EXECUTION_YIELD_HOOK = keccak256( "LIMIT_EXECUTION_YIELD_HOOK" ); bytes32 constant LIMIT_EXECUTION_YIELD_TOKEN_HOOK = keccak256( "LIMIT_EXECUTION_YIELD_TOKEN_HOOK" ); bytes32 constant ERC20_VAULT = keccak256("ERC20_VAULT"); bytes32 constant NATIVE_VAULT = keccak256("NATIVE_VAULT"); // File contracts/common/Errors.sol pragma solidity 0.8.13; error SiblingNotSupported(); error NotAuthorized(); error NotBridge(); error NotSocket(); error ConnectorUnavailable(); error InvalidPoolId(); error CannotTransferOrExecuteOnBridgeContracts(); error NoPendingData(); error MessageIdMisMatched(); error NotMessageBridge(); error InvalidSiblingChainSlug(); error InvalidTokenContract(); error InvalidExchangeRateContract(); error InvalidConnector(); error InvalidConnectorPoolId(); error ZeroAddressReceiver(); error ZeroAddress(); error ZeroAmount(); error DebtRatioTooHigh(); error NotEnoughAssets(); error VaultShutdown(); error InsufficientFunds(); error PermitDeadlineExpired(); error InvalidSigner(); error InsufficientMsgValue(); error InvalidOptionsLength(); // File contracts/common/Structs.sol pragma solidity 0.8.13; struct UpdateLimitParams { bool isMint; address connector; uint256 maxLimit; uint256 ratePerSecond; } struct SrcPreHookCallParams { address connector; address msgSender; TransferInfo transferInfo; } struct SrcPostHookCallParams { address connector; bytes options; bytes postHookData; TransferInfo transferInfo; } struct DstPreHookCallParams { address connector; bytes connectorCache; TransferInfo transferInfo; } struct DstPostHookCallParams { address connector; bytes32 messageId; bytes connectorCache; bytes postHookData; TransferInfo transferInfo; } struct PreRetryHookCallParams { address connector; CacheData cacheData; } struct PostRetryHookCallParams { address connector; bytes32 messageId; bytes postHookData; CacheData cacheData; } struct TransferInfo { address receiver; uint256 amount; bytes extraData; } struct CacheData { bytes identifierCache; bytes connectorCache; } struct LimitParams { uint256 lastUpdateTimestamp; uint256 ratePerSecond; uint256 maxLimit; uint256 lastUpdateLimit; } // File contracts/interfaces/IHook.sol pragma solidity ^0.8.3; interface IHook { /** * @notice Executes pre-hook call for source underlyingAsset. * @dev This function is used to execute a pre-hook call for the source underlyingAsset before initiating a transfer. * @param params_ Parameters for the pre-hook call. * @return transferInfo Information about the transfer. * @return postHookData returned from the pre-hook call. */ function srcPreHookCall( SrcPreHookCallParams calldata params_ ) external returns (TransferInfo memory transferInfo, bytes memory postHookData); function srcPostHookCall( SrcPostHookCallParams calldata params_ ) external returns (TransferInfo memory transferInfo); /** * @notice Executes pre-hook call for destination underlyingAsset. * @dev This function is used to execute a pre-hook call for the destination underlyingAsset before initiating a transfer. * @param params_ Parameters for the pre-hook call. */ function dstPreHookCall( DstPreHookCallParams calldata params_ ) external returns (bytes memory postHookData, TransferInfo memory transferInfo); /** * @notice Executes post-hook call for destination underlyingAsset. * @dev This function is used to execute a post-hook call for the destination underlyingAsset after completing a transfer. * @param params_ Parameters for the post-hook call. * @return cacheData Cached data for the post-hook call. */ function dstPostHookCall( DstPostHookCallParams calldata params_ ) external returns (CacheData memory cacheData); /** * @notice Executes a pre-retry hook for a failed transaction. * @dev This function is used to execute a pre-retry hook for a failed transaction. * @param params_ Parameters for the pre-retry hook. * @return postHookData Data from the post-retry hook. * @return transferInfo Information about the transfer. */ function preRetryHook( PreRetryHookCallParams calldata params_ ) external returns (bytes memory postHookData, TransferInfo memory transferInfo); /** * @notice Executes a post-retry hook for a failed transaction. * @dev This function is used to execute a post-retry hook for a failed transaction. * @param params_ Parameters for the post-retry hook. * @return cacheData Cached data for the post-retry hook. */ function postRetryHook( PostRetryHookCallParams calldata params_ ) external returns (CacheData memory cacheData); } // File lib/solmate/src/tokens/ERC20.sol pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } // File lib/solmate/src/utils/SafeTransferLib.sol pragma solidity >=0.8.0; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } } // File contracts/libraries/RescueFundsLib.sol pragma solidity 0.8.13; /** * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ library RescueFundsLib { /** * @dev thrown when the given token address don't have any code */ error InvalidTokenAddress(); /** * @dev Rescues funds from a contract. * @param token_ The address of the token contract. * @param rescueTo_ The address of the user. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) internal { if (rescueTo_ == address(0)) revert ZeroAddress(); if (token_ == ETH_ADDRESS) { SafeTransferLib.safeTransferETH(rescueTo_, amount_); } else { if (token_.code.length == 0) revert InvalidTokenAddress(); SafeTransferLib.safeTransfer(ERC20(token_), rescueTo_, amount_); } } } // File contracts/utils/Ownable.sol pragma solidity 0.8.13; /** * @title Ownable * @dev The Ownable contract provides a simple way to manage ownership of a contract * and allows for ownership to be transferred to a nominated address. */ abstract contract Ownable { address private _owner; address private _nominee; event OwnerNominated(address indexed nominee); event OwnerClaimed(address indexed claimer); error OnlyOwner(); error OnlyNominee(); /** * @dev Sets the contract's owner to the address that is passed to the constructor. */ constructor(address owner_) { _claimOwner(owner_); } /** * @dev Modifier that restricts access to only the contract's owner. * Throws an error if the caller is not the owner. */ modifier onlyOwner() { if (msg.sender != _owner) revert OnlyOwner(); _; } /** * @dev Returns the current owner of the contract. */ function owner() external view returns (address) { return _owner; } /** * @dev Returns the current nominee for ownership of the contract. */ function nominee() external view returns (address) { return _nominee; } /** * @dev Allows the current owner to nominate a new owner for the contract. * Throws an error if the caller is not the owner. * Emits an `OwnerNominated` event with the address of the nominee. */ function nominateOwner(address nominee_) external { if (msg.sender != _owner) revert OnlyOwner(); _nominee = nominee_; emit OwnerNominated(_nominee); } /** * @dev Allows the nominated owner to claim ownership of the contract. * Throws an error if the caller is not the nominee. * Sets the nominated owner as the new owner of the contract. * Emits an `OwnerClaimed` event with the address of the new owner. */ function claimOwner() external { if (msg.sender != _nominee) revert OnlyNominee(); _claimOwner(msg.sender); } /** * @dev Internal function that sets the owner of the contract to the specified address * and sets the nominee to address(0). */ function _claimOwner(address claimer_) internal { _owner = claimer_; _nominee = address(0); emit OwnerClaimed(claimer_); } } // File contracts/utils/AccessControl.sol pragma solidity 0.8.13; /** * @title AccessControl * @dev This abstract contract implements access control mechanism based on roles. * Each role can have one or more addresses associated with it, which are granted * permission to execute functions with the onlyRole modifier. */ abstract contract AccessControl is Ownable { /** * @dev A mapping of roles to a mapping of addresses to boolean values indicating whether or not they have the role. */ mapping(bytes32 => mapping(address => bool)) private _permits; /** * @dev Emitted when a role is granted to an address. */ event RoleGranted(bytes32 indexed role, address indexed grantee); /** * @dev Emitted when a role is revoked from an address. */ event RoleRevoked(bytes32 indexed role, address indexed revokee); /** * @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier. */ error NoPermit(bytes32 role); /** * @dev Constructor that sets the owner of the contract. */ constructor(address owner_) Ownable(owner_) {} /** * @dev Modifier that restricts access to addresses having roles * Throws an error if the caller do not have permit */ modifier onlyRole(bytes32 role) { if (!_permits[role][msg.sender]) revert NoPermit(role); _; } /** * @dev Checks and reverts if an address do not have a specific role. * @param role_ The role to check. * @param address_ The address to check. */ function _checkRole(bytes32 role_, address address_) internal virtual { if (!_hasRole(role_, address_)) revert NoPermit(role_); } /** * @dev Grants a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. * Can only be called by the owner of the contract. */ function grantRole( bytes32 role_, address grantee_ ) external virtual onlyOwner { _grantRole(role_, grantee_); } /** * @dev Revokes a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. * Can only be called by the owner of the contract. */ function revokeRole( bytes32 role_, address revokee_ ) external virtual onlyOwner { _revokeRole(role_, revokee_); } /** * @dev Internal function to grant a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. */ function _grantRole(bytes32 role_, address grantee_) internal { _permits[role_][grantee_] = true; emit RoleGranted(role_, grantee_); } /** * @dev Internal function to revoke a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. */ function _revokeRole(bytes32 role_, address revokee_) internal { _permits[role_][revokee_] = false; emit RoleRevoked(role_, revokee_); } /** * @dev Checks whether an address has a specific role. * @param role_ The role to check. * @param address_ The address to check. * @return A boolean value indicating whether or not the address has the role. */ function hasRole( bytes32 role_, address address_ ) external view returns (bool) { return _hasRole(role_, address_); } function _hasRole( bytes32 role_, address address_ ) internal view returns (bool) { return _permits[role_][address_]; } } // File contracts/utils/RescueBase.sol pragma solidity 0.8.13; /** * @title Base contract for super token and vault * @notice It contains relevant execution payload storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ abstract contract RescueBase is AccessControl { bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyRole(RESCUE_ROLE) { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } } // File lib/solmate/src/utils/ReentrancyGuard.sol pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } } // File contracts/hooks/HookBase.sol pragma solidity 0.8.13; /** * @title Base contract for super token and vault * @notice It contains relevant execution payload storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ abstract contract HookBase is ReentrancyGuard, IHook, RescueBase { address public immutable vaultOrController; bytes32 public hookType; /** * @notice Constructor for creating a new SuperToken. */ constructor( address owner_, address vaultOrController_ ) AccessControl(owner_) { vaultOrController = vaultOrController_; _grantRole(RESCUE_ROLE, owner_); } modifier isVaultOrController() { if (msg.sender != vaultOrController) revert NotAuthorized(); _; } } // File contracts/hooks/plugins/ConnectorPoolPlugin.sol pragma solidity 0.8.13; abstract contract ConnectorPoolPlugin is HookBase { // connectorPoolId => totalLockedAmount mapping(uint256 => uint256) public poolLockedAmounts; // connector => connectorPoolId mapping(address => uint256) public connectorPoolIds; event ConnectorPoolIdUpdated(address connector, uint256 poolId); event PoolLockedAmountUpdated(uint256 poolId, uint256 amount); function updateConnectorPoolId( address[] calldata connectors, uint256[] calldata poolIds_ ) external onlyOwner { uint256 length = connectors.length; for (uint256 i; i < length; i++) { if (poolIds_[i] == 0) revert InvalidPoolId(); connectorPoolIds[connectors[i]] = poolIds_[i]; emit ConnectorPoolIdUpdated(connectors[i], poolIds_[i]); } } function updatePoolLockedAmounts( uint256[] calldata poolIds_, uint256[] calldata amounts_ ) external onlyOwner { uint256 length = poolIds_.length; for (uint256 i; i < length; i++) { if (poolIds_[i] == 0) revert InvalidPoolId(); poolLockedAmounts[poolIds_[i]] = amounts_[i]; emit PoolLockedAmountUpdated(poolIds_[i], amounts_[i]); } } function _poolSrcHook(address connector_, uint256 amount_) internal { uint256 connectorPoolId = connectorPoolIds[connector_]; if (connectorPoolId == 0) revert InvalidPoolId(); if (amount_ > poolLockedAmounts[connectorPoolId]) revert InsufficientFunds(); poolLockedAmounts[connectorPoolId] -= amount_; } function _poolDstHook( address connector_, uint256 amount_ ) internal returns (uint256 oldLockedAmount) { uint256 connectorPoolId = connectorPoolIds[connector_]; if (connectorPoolId == 0) revert InvalidPoolId(); oldLockedAmount = poolLockedAmounts[connectorPoolId]; poolLockedAmounts[connectorPoolId] += amount_; } } // File contracts/utils/Gauge.sol pragma solidity 0.8.13; abstract contract Gauge { error AmountOutsideLimit(); function _getCurrentLimit( LimitParams storage _params ) internal view returns (uint256 _limit) { uint256 timeElapsed = block.timestamp - _params.lastUpdateTimestamp; uint256 limitIncrease = timeElapsed * _params.ratePerSecond; if (limitIncrease + _params.lastUpdateLimit > _params.maxLimit) { _limit = _params.maxLimit; } else { _limit = limitIncrease + _params.lastUpdateLimit; } } function _consumePartLimit( uint256 amount_, LimitParams storage _params ) internal returns (uint256 consumedAmount, uint256 pendingAmount) { uint256 currentLimit = _getCurrentLimit(_params); _params.lastUpdateTimestamp = block.timestamp; if (currentLimit >= amount_) { _params.lastUpdateLimit = currentLimit - amount_; consumedAmount = amount_; pendingAmount = 0; } else { _params.lastUpdateLimit = 0; consumedAmount = currentLimit; pendingAmount = amount_ - currentLimit; } } function _consumeFullLimit( uint256 amount_, LimitParams storage _params ) internal { uint256 currentLimit = _getCurrentLimit(_params); if (currentLimit >= amount_) { _params.lastUpdateTimestamp = block.timestamp; _params.lastUpdateLimit = currentLimit - amount_; } else { revert AmountOutsideLimit(); } } } // File contracts/hooks/plugins/LimitPlugin.sol pragma solidity 0.8.13; abstract contract LimitPlugin is Gauge, HookBase { bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE"); // connector => receivingLimitParams mapping(address => LimitParams) _receivingLimitParams; // connector => sendingLimitParams mapping(address => LimitParams) _sendingLimitParams; //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // Emitted when limit parameters are updated event LimitParamsUpdated(UpdateLimitParams[] updates); // Emitted when pending tokens are minted to the receiver event PendingTokensBridged( address connector, address receiver, uint256 consumedAmount, uint256 pendingAmount, bytes32 messageId ); // Emitted when the transfer reaches the limit, and the token mint is added to the pending queue event TokensPending( address connector, address receiver, uint256 consumedAmount, uint256 pendingAmount, bytes32 messageId ); /** * @notice This function is used to set bridge limits. * @dev It can only be updated by the owner. * @param updates An array of structs containing update parameters. */ function updateLimitParams( UpdateLimitParams[] calldata updates ) external onlyRole(LIMIT_UPDATER_ROLE) { for (uint256 i = 0; i < updates.length; i++) { if (updates[i].isMint) { _consumePartLimit( 0, _receivingLimitParams[updates[i].connector] ); // To keep the current limit in sync _receivingLimitParams[updates[i].connector].maxLimit = updates[ i ].maxLimit; _receivingLimitParams[updates[i].connector] .ratePerSecond = updates[i].ratePerSecond; } else { _consumePartLimit(0, _sendingLimitParams[updates[i].connector]); // To keep the current limit in sync _sendingLimitParams[updates[i].connector].maxLimit = updates[i] .maxLimit; _sendingLimitParams[updates[i].connector] .ratePerSecond = updates[i].ratePerSecond; } } emit LimitParamsUpdated(updates); } function getCurrentReceivingLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_receivingLimitParams[connector_]); } function getCurrentSendingLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_sendingLimitParams[connector_]); } function getReceivingLimitParams( address connector_ ) external view returns (LimitParams memory) { return _receivingLimitParams[connector_]; } function getSendingLimitParams( address connector_ ) external view returns (LimitParams memory) { return _sendingLimitParams[connector_]; } function _limitSrcHook(address connector_, uint256 amount_) internal { if (_sendingLimitParams[connector_].maxLimit == 0) revert SiblingNotSupported(); _consumeFullLimit(amount_, _sendingLimitParams[connector_]); // Reverts on limit hit } function _limitDstHook( address connector_, uint256 amount_ ) internal returns (uint256 consumedAmount, uint256 pendingAmount) { if (_receivingLimitParams[connector_].maxLimit == 0) revert SiblingNotSupported(); (consumedAmount, pendingAmount) = _consumePartLimit( amount_, _receivingLimitParams[connector_] ); } function _getConnectorPendingAmount( bytes memory connectorCache_ ) internal pure returns (uint256) { if (connectorCache_.length > 0) { return abi.decode(connectorCache_, (uint256)); } else return 0; } } // File contracts/interfaces/IController.sol pragma solidity ^0.8.13; interface IController { function identifierCache( bytes32 messageId_ ) external payable returns (bytes memory cache); function connectorCache( address connector_ ) external payable returns (bytes memory cache); } // File contracts/hooks/LimitHook.sol pragma solidity 0.8.13; contract LimitHook is LimitPlugin, ConnectorPoolPlugin { bool public immutable useControllerPools; /** * @notice Constructor for creating a new SuperToken. * @param owner_ Owner of this contract. */ constructor( address owner_, address controller_, bool useControllerPools_ ) HookBase(owner_, controller_) { useControllerPools = useControllerPools_; hookType = LIMIT_HOOK; _grantRole(LIMIT_UPDATER_ROLE, owner_); } function srcPreHookCall( SrcPreHookCallParams memory params_ ) external isVaultOrController returns (TransferInfo memory transferInfo, bytes memory postHookData) { if (useControllerPools) _poolSrcHook(params_.connector, params_.transferInfo.amount); _limitSrcHook(params_.connector, params_.transferInfo.amount); transferInfo = params_.transferInfo; postHookData = hex""; } function srcPostHookCall( SrcPostHookCallParams memory params_ ) external view isVaultOrController returns (TransferInfo memory) { return params_.transferInfo; } function dstPreHookCall( DstPreHookCallParams memory params_ ) external isVaultOrController returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (useControllerPools) _poolDstHook(params_.connector, params_.transferInfo.amount); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, params_.transferInfo.amount ); postHookData = abi.encode(consumedAmount, pendingAmount); transferInfo = params_.transferInfo; transferInfo.amount = consumedAmount; } function dstPostHookCall( DstPostHookCallParams memory params_ ) external isVaultOrController returns (CacheData memory cacheData) { (uint256 consumedAmount, uint256 pendingAmount) = abi.decode( params_.postHookData, (uint256, uint256) ); uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.connectorCache ); if (pendingAmount > 0) { cacheData = CacheData( abi.encode( params_.transferInfo.receiver, pendingAmount, params_.connector ), abi.encode(connectorPendingAmount + pendingAmount) ); emit TokensPending( params_.connector, params_.transferInfo.receiver, consumedAmount, pendingAmount, params_.messageId ); } else { cacheData = CacheData( bytes(""), abi.encode(connectorPendingAmount + pendingAmount) ); } } function preRetryHook( PreRetryHookCallParams memory params_ ) external nonReentrant isVaultOrController returns (bytes memory postHookData, TransferInfo memory transferInfo) { (address receiver, uint256 pendingMint, address connector) = abi.decode( params_.cacheData.identifierCache, (address, uint256, address) ); if (connector != params_.connector) revert InvalidConnector(); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, pendingMint ); postHookData = abi.encode(receiver, consumedAmount, pendingAmount); transferInfo = TransferInfo(receiver, consumedAmount, bytes("")); } function postRetryHook( PostRetryHookCallParams calldata params_ ) external isVaultOrController nonReentrant returns (CacheData memory cacheData) { (address receiver, uint256 consumedAmount, uint256 pendingAmount) = abi .decode(params_.postHookData, (address, uint256, uint256)); // code reaches here after minting/unlocking the pending amount emit PendingTokensBridged( params_.connector, receiver, consumedAmount, pendingAmount, params_.messageId ); uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.cacheData.connectorCache ); cacheData.connectorCache = abi.encode( connectorPendingAmount - consumedAmount ); cacheData.identifierCache = abi.encode( receiver, pendingAmount, params_.connector ); if (pendingAmount == 0) { cacheData.identifierCache = new bytes(0); } } function getConnectorPendingAmount( address connector_ ) external returns (uint256) { bytes memory cache = IController(vaultOrController).connectorCache( connector_ ); return _getConnectorPendingAmount(cache); } function _getIdentifierPendingAmount( bytes memory identifierCache_ ) internal pure returns (uint256) { if (identifierCache_.length > 0) { (, uint256 pendingAmount, ) = abi.decode( identifierCache_, (address, uint256, address) ); return pendingAmount; } else return 0; } function getIdentifierPendingAmount( bytes32 messageId_ ) external returns (uint256) { bytes memory cache = IController(vaultOrController).identifierCache( messageId_ ); return _getIdentifierPendingAmount(cache); } }
Compiler Settings
{"outputSelection":{"*":{"*":["*"]}},"optimizer":{"runs":999999,"enabled":true},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"owner_","internalType":"address"},{"type":"address","name":"controller_","internalType":"address"},{"type":"bool","name":"useControllerPools_","internalType":"bool"}]},{"type":"error","name":"AmountOutsideLimit","inputs":[]},{"type":"error","name":"InsufficientFunds","inputs":[]},{"type":"error","name":"InvalidConnector","inputs":[]},{"type":"error","name":"InvalidPoolId","inputs":[]},{"type":"error","name":"InvalidTokenAddress","inputs":[]},{"type":"error","name":"NoPermit","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"error","name":"NotAuthorized","inputs":[]},{"type":"error","name":"OnlyNominee","inputs":[]},{"type":"error","name":"OnlyOwner","inputs":[]},{"type":"error","name":"SiblingNotSupported","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"event","name":"ConnectorPoolIdUpdated","inputs":[{"type":"address","name":"connector","internalType":"address","indexed":false},{"type":"uint256","name":"poolId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LimitParamsUpdated","inputs":[{"type":"tuple[]","name":"updates","internalType":"struct UpdateLimitParams[]","indexed":false,"components":[{"type":"bool","name":"isMint","internalType":"bool"},{"type":"address","name":"connector","internalType":"address"},{"type":"uint256","name":"maxLimit","internalType":"uint256"},{"type":"uint256","name":"ratePerSecond","internalType":"uint256"}]}],"anonymous":false},{"type":"event","name":"OwnerClaimed","inputs":[{"type":"address","name":"claimer","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnerNominated","inputs":[{"type":"address","name":"nominee","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PendingTokensBridged","inputs":[{"type":"address","name":"connector","internalType":"address","indexed":false},{"type":"address","name":"receiver","internalType":"address","indexed":false},{"type":"uint256","name":"consumedAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"pendingAmount","internalType":"uint256","indexed":false},{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"PoolLockedAmountUpdated","inputs":[{"type":"uint256","name":"poolId","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"grantee","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"revokee","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TokensPending","inputs":[{"type":"address","name":"connector","internalType":"address","indexed":false},{"type":"address","name":"receiver","internalType":"address","indexed":false},{"type":"uint256","name":"consumedAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"pendingAmount","internalType":"uint256","indexed":false},{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"connectorPoolIds","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"tuple","name":"cacheData","internalType":"struct CacheData","components":[{"type":"bytes","name":"identifierCache","internalType":"bytes"},{"type":"bytes","name":"connectorCache","internalType":"bytes"}]}],"name":"dstPostHookCall","inputs":[{"type":"tuple","name":"params_","internalType":"struct DstPostHookCallParams","components":[{"type":"address","name":"connector","internalType":"address"},{"type":"bytes32","name":"messageId","internalType":"bytes32"},{"type":"bytes","name":"connectorCache","internalType":"bytes"},{"type":"bytes","name":"postHookData","internalType":"bytes"},{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes","name":"postHookData","internalType":"bytes"},{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}],"name":"dstPreHookCall","inputs":[{"type":"tuple","name":"params_","internalType":"struct DstPreHookCallParams","components":[{"type":"address","name":"connector","internalType":"address"},{"type":"bytes","name":"connectorCache","internalType":"bytes"},{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getConnectorPendingAmount","inputs":[{"type":"address","name":"connector_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentReceivingLimit","inputs":[{"type":"address","name":"connector_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentSendingLimit","inputs":[{"type":"address","name":"connector_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getIdentifierPendingAmount","inputs":[{"type":"bytes32","name":"messageId_","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct LimitParams","components":[{"type":"uint256","name":"lastUpdateTimestamp","internalType":"uint256"},{"type":"uint256","name":"ratePerSecond","internalType":"uint256"},{"type":"uint256","name":"maxLimit","internalType":"uint256"},{"type":"uint256","name":"lastUpdateLimit","internalType":"uint256"}]}],"name":"getReceivingLimitParams","inputs":[{"type":"address","name":"connector_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct LimitParams","components":[{"type":"uint256","name":"lastUpdateTimestamp","internalType":"uint256"},{"type":"uint256","name":"ratePerSecond","internalType":"uint256"},{"type":"uint256","name":"maxLimit","internalType":"uint256"},{"type":"uint256","name":"lastUpdateLimit","internalType":"uint256"}]}],"name":"getSendingLimitParams","inputs":[{"type":"address","name":"connector_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role_","internalType":"bytes32"},{"type":"address","name":"grantee_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role_","internalType":"bytes32"},{"type":"address","name":"address_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"hookType","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"nominateOwner","inputs":[{"type":"address","name":"nominee_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"nominee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolLockedAmounts","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"tuple","name":"cacheData","internalType":"struct CacheData","components":[{"type":"bytes","name":"identifierCache","internalType":"bytes"},{"type":"bytes","name":"connectorCache","internalType":"bytes"}]}],"name":"postRetryHook","inputs":[{"type":"tuple","name":"params_","internalType":"struct PostRetryHookCallParams","components":[{"type":"address","name":"connector","internalType":"address"},{"type":"bytes32","name":"messageId","internalType":"bytes32"},{"type":"bytes","name":"postHookData","internalType":"bytes"},{"type":"tuple","name":"cacheData","internalType":"struct CacheData","components":[{"type":"bytes","name":"identifierCache","internalType":"bytes"},{"type":"bytes","name":"connectorCache","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes","name":"postHookData","internalType":"bytes"},{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}],"name":"preRetryHook","inputs":[{"type":"tuple","name":"params_","internalType":"struct PreRetryHookCallParams","components":[{"type":"address","name":"connector","internalType":"address"},{"type":"tuple","name":"cacheData","internalType":"struct CacheData","components":[{"type":"bytes","name":"identifierCache","internalType":"bytes"},{"type":"bytes","name":"connectorCache","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescueFunds","inputs":[{"type":"address","name":"token_","internalType":"address"},{"type":"address","name":"rescueTo_","internalType":"address"},{"type":"uint256","name":"amount_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role_","internalType":"bytes32"},{"type":"address","name":"revokee_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}],"name":"srcPostHookCall","inputs":[{"type":"tuple","name":"params_","internalType":"struct SrcPostHookCallParams","components":[{"type":"address","name":"connector","internalType":"address"},{"type":"bytes","name":"options","internalType":"bytes"},{"type":"bytes","name":"postHookData","internalType":"bytes"},{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]},{"type":"bytes","name":"postHookData","internalType":"bytes"}],"name":"srcPreHookCall","inputs":[{"type":"tuple","name":"params_","internalType":"struct SrcPreHookCallParams","components":[{"type":"address","name":"connector","internalType":"address"},{"type":"address","name":"msgSender","internalType":"address"},{"type":"tuple","name":"transferInfo","internalType":"struct TransferInfo","components":[{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"bytes","name":"extraData","internalType":"bytes"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateConnectorPoolId","inputs":[{"type":"address[]","name":"connectors","internalType":"address[]"},{"type":"uint256[]","name":"poolIds_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateLimitParams","inputs":[{"type":"tuple[]","name":"updates","internalType":"struct UpdateLimitParams[]","components":[{"type":"bool","name":"isMint","internalType":"bool"},{"type":"address","name":"connector","internalType":"address"},{"type":"uint256","name":"maxLimit","internalType":"uint256"},{"type":"uint256","name":"ratePerSecond","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updatePoolLockedAmounts","inputs":[{"type":"uint256[]","name":"poolIds_","internalType":"uint256[]"},{"type":"uint256[]","name":"amounts_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"useControllerPools","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"vaultOrController","inputs":[]}]
Contract Creation Code
0x60c060405260016000553480156200001657600080fd5b50604051620032a1380380620032a18339810160408190526200003991620001b0565b828281806200004881620000e4565b50506001600160a01b038116608052620000837fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292538362000138565b505080151560a0527f3b7129f10106e202769096fb024bf2ffbeba2cd8f6d0fc43430d78dad12acb26600455620000db7f129109734c476f17817fbe7bcf461de566dcce58e4c0cb61b62d1b9af26fe47f8462000138565b50505062000202565b600180546001600160a01b0383166001600160a01b031991821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60008281526003602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b80516001600160a01b0381168114620001ab57600080fd5b919050565b600080600060608486031215620001c657600080fd5b620001d18462000193565b9250620001e16020850162000193565b915060408401518015158114620001f757600080fd5b809150509250925092565b60805160a0516130336200026e6000396000818161048c015281816116850152611b1f01526000818161034201528181610519015281816105e601528181610fc0015281816113660152818161162c01528181611797015281816119f80152611ab001526130336000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c806374f86ade116100f9578063d4bbfea311610097578063e445e7dd11610071578063e445e7dd1461046b578063e73915c214610474578063f2f09c9614610487578063f59ad990146104ae57600080fd5b8063d4bbfea314610425578063d547741f14610445578063dd19fe381461045857600080fd5b806391d14854116100d357806391d14854146103c9578063af93f83b146103ec578063b5f1e82a146103ff578063cf36b9171461041257600080fd5b806374f86ade146103775780637afb99531461038a5780638da5cb5b146103ab57600080fd5b80633caef0f41161016657806362811bf21161014057806362811bf2146102d7578063629a0393146102f75780636651232a1461033d5780636ccae0541461036457600080fd5b80633caef0f41461029e57806352957178146102b15780635b94db27146102c457600080fd5b80632bc9c08a116101a25780632bc9c08a1461024e5780632f2ff15d1461026357806336cc21b8146102765780633bd1adec1461029657600080fd5b8063093c5eb1146101c95780631306ac3b146101ef57806320f99c0a1461020f575b600080fd5b6101dc6101d7366004612362565b6104cf565b6040519081526020015b60405180910390f35b6102026101fd36600461237f565b6105ba565b6040516101e69190612430565b60025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e6565b61026161025c366004612490565b61089c565b005b610261610271366004612505565b610c19565b6101dc610284366004612362565b60086020526000908152604090205481565b610261610c78565b6102616102ac366004612581565b610cd4565b6101dc6102bf366004612362565b610e98565b6102616102d2366004612362565b610ecc565b6102ea6102e53660046127fe565b610f8c565b6040516101e6919061290d565b61030a610305366004612362565b611023565b6040516101e691908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b6102297f000000000000000000000000000000000000000000000000000000000000000081565b610261610372366004612920565b6110a9565b610261610385366004612581565b611147565b61039d610398366004612961565b6112c2565b6040516101e6929190612a38565b60015473ffffffffffffffffffffffffffffffffffffffff16610229565b6103dc6103d7366004612505565b61150d565b60405190151581526020016101e6565b61030a6103fa366004612362565b611545565b6101dc61040d366004612362565b6115cb565b61039d610420366004612a5d565b6115f9565b6101dc610433366004612b01565b60076020526000908152604090205481565b610261610453366004612505565b611710565b610202610466366004612b1a565b61176b565b6101dc60045481565b6101dc610482366004612b01565b6119af565b6103dc7f000000000000000000000000000000000000000000000000000000000000000081565b6104c16104bc366004612bea565b611a92565b6040516101e6929190612c62565b6040517f4b0a885400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff828116600483015260009182917f00000000000000000000000000000000000000000000000000000000000000001690634b0a8854906024016000604051808303816000875af1158015610562573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526105a89190810190612c87565b90506105b381611b87565b9392505050565b60408051808201909152606080825260208201523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461063d576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000546001146106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002600090815580806106c46040860186612cf5565b8101906106d19190612d5a565b919450925090507f8b3544fa225539b17e942d41841bf832b900055aa60660631db9559bbdcd8b366107066020870187612362565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291861660208381019190915290820185905260608201849052870135608082015260a00160405180910390a160006107aa6107626060880188612d8f565b610770906020810190612cf5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b8792505050565b90506107b68382612dfc565b6040516020016107c891815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020868101919091528490839061080f90890189612362565b6040805173ffffffffffffffffffffffffffffffffffffffff9485166020820152908101929092529091166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528552600082900361088d5760408051600081526020810190915285525b50506001600055509092915050565b3360009081527fbd500bc6ab4a9bcd7eb9f453a5938fad0b08d3dbaae22ba7976ac385056bde2960205260409020547f129109734c476f17817fbe7bcf461de566dcce58e4c0cb61b62d1b9af26fe47f9060ff16610929576040517f962f6333000000000000000000000000000000000000000000000000000000008152600481018290526024016106a5565b60005b82811015610bda5783838281811061094657610946612e13565b61095c9260206080909202019081019150612e52565b15610ac2576109d360006005600087878681811061097c5761097c612e13565b90506080020160200160208101906109949190612362565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020611bad565b50508383828181106109e7576109e7612e13565b9050608002016040013560056000868685818110610a0757610a07612e13565b9050608002016020016020810190610a1f9190612362565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060020155838382818110610a5b57610a5b612e13565b9050608002016060013560056000868685818110610a7b57610a7b612e13565b9050608002016020016020810190610a939190612362565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060010155610bc8565b610add60006006600087878681811061097c5761097c612e13565b5050838382818110610af157610af1612e13565b9050608002016040013560066000868685818110610b1157610b11612e13565b9050608002016020016020810190610b299190612362565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060020155838382818110610b6557610b65612e13565b9050608002016060013560066000868685818110610b8557610b85612e13565b9050608002016020016020810190610b9d9190612362565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020600101555b80610bd281612e6d565b91505061092c565b507f19863caed14ed012a54f927e56250018b7ac3c04fd197e0edf23369f75a6ccd48383604051610c0c929190612ea5565b60405180910390a1505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610c6a576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c748282611c01565b5050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610cc9576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cd233611c87565b565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d25576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610e9057838382818110610d4357610d43612e13565b90506020020135600003610d83576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838382818110610d9557610d95612e13565b9050602002013560086000888885818110610db257610db2612e13565b9050602002016020810190610dc79190612362565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020557f0fdcc1015152882d3bf501506be25357c645aff7e0df94d2b21c45e102e81a0c868683818110610e2157610e21612e13565b9050602002016020810190610e369190612362565b858584818110610e4857610e48612e13565b6040805173ffffffffffffffffffffffffffffffffffffffff90951685526020918202939093013590840152500160405180910390a180610e8881612e6d565b915050610d29565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600660205260408120610ec690611d00565b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610f1d576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b604080516060808201835260008083526020830152918101919091523373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611017576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060608101515b919050565b61104e6040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260056020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b3360009081527f271b3e2292ab6fd3ff496cd98d6d375af02f11568a701741f48bba7789f13a7060205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16611136576040517f962f6333000000000000000000000000000000000000000000000000000000008152600481018290526024016106a5565b611141848484611d67565b50505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611198576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610e90578585828181106111b6576111b6612e13565b905060200201356000036111f6576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83838281811061120857611208612e13565b905060200201356007600088888581811061122557611225612e13565b905060200201358152602001908152602001600020819055507f6cc2f5d066d41fc5850125943b942623567700e44d15463154a87bb5d3332e9186868381811061127157611271612e13565b9050602002013585858481811061128a5761128a612e13565b905060200201356040516112a8929190918252602082015260400190565b60405180910390a1806112ba81612e6d565b91505061119c565b604080516060818101835260008083526020830152918101829052600054600114611349576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106a5565b60026000553373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146113bd576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008560200151600001518060200190518101906113de9190612f28565b925092509250856000015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461144d576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061145e886000015185611e5c565b6040805173ffffffffffffffffffffffffffffffffffffffff89166020820152908101839052606081018290529193509150608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815260608301825273ffffffffffffffffffffffffffffffffffffffff9097168252602082810194909452805193840181526000808552908201939093526001909255509296929550919350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff166105b3565b6115706040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260066020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260408120610ec690611d00565b6040805160608181018352600080835260208301529181018290523373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611683576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000156116c1576116bf8360000151846040015160200151611efa565b505b6000806116da8560000151866040015160200151611e5c565b60408051602080820185905281830193909352815180820383018152606090910182529701519081019190915294959350505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611761576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c748282611f84565b60408051808201909152606080825260208201523373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146117ee576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008083606001518060200190518101906118099190612f6b565b91509150600061181c8560400151611b87565b9050811561195d576040805180820190915260808681015151875173ffffffffffffffffffffffffffffffffffffffff91821660608501529183018590521660a08201528060c08101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905281526020016118a28484612f8f565b6040516020016118b491815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815291528651608080890151516020808b0151855173ffffffffffffffffffffffffffffffffffffffff95861681529490921690840152828401889052606083018790529082015290519195507fec590f5738bffdca5d4fb4f9e9d902924be6ad3aedb5c5d0b39b208ea41c1e1b919081900360a00190a16119a7565b604080516060810182526000918101918252908152602081016119808484612f8f565b60405160200161199291815260200190565b60405160208183030381529060405281525093505b505050919050565b6040517ff290aafa00000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063f290aafa906024016000604051808303816000875af1158015611a41573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611a879190810190612c87565b90506105b381612007565b604080516060808201835260008083526020830152918101829052907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314611b1d576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000015611b5957611b598360000151846040015160200151612030565b611b6f8360000151846040015160200151612100565b50506040908101518151602081019092526000825291565b805160009015611ba55781806020019051810190610ec69190612fa7565b506000919050565b6000806000611bbb84611d00565b4285559050848110611be257611bd18582612dfc565b600385015584925060009150611bf9565b60006003850155915081611bf68186612dfc565b91505b509250929050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b80546000908190611d119042612dfc565b90506000836001015482611d259190612fc0565b90508360020154846003015482611d3c9190612f8f565b1115611d4e5783600201549250611d60565b6003840154611d5d9082612f8f565b92505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff8216611db4576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff841601611e0057611dfb8282612191565b505050565b8273ffffffffffffffffffffffffffffffffffffffff163b600003611e51576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dfb838383612206565b73ffffffffffffffffffffffffffffffffffffffff821660009081526005602052604081206002015481908103611ebf576040517fec914c0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166000908152600560205260409020611eef908490611bad565b909590945092505050565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260086020526040812054808203611f59576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600760205260408120805493508491611f778386612f8f565b9091555091949350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b805160009015611ba5576000828060200190518101906120279190612f28565b50949350505050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526008602052604081205490819003612090576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600760205260409020548211156120d8576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260076020526040812080548492906120f6908490612dfc565b9091555050505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600660205260408120600201549003612161576040517fec914c0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600660205260409020610c749082906122d5565b600080600080600085875af1905080611dfb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c45440000000000000000000000000060448201526064016106a5565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080611141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016106a5565b60006122e082611d00565b9050828110612300574282556122f68382612dfc565b6003830155505050565b6040517f47ebad2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461235457600080fd5b50565b803561101e81612332565b60006020828403121561237457600080fd5b81356105b381612332565b60006020828403121561239157600080fd5b813567ffffffffffffffff8111156123a857600080fd5b8201608081850312156105b357600080fd5b60005b838110156123d55781810151838201526020016123bd565b838111156111415750506000910152565b600081518084526123fe8160208601602086016123ba565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600082516040602084015261244c60608401826123e6565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084830301604085015261248782826123e6565b95945050505050565b600080602083850312156124a357600080fd5b823567ffffffffffffffff808211156124bb57600080fd5b818501915085601f8301126124cf57600080fd5b8135818111156124de57600080fd5b8660208260071b85010111156124f357600080fd5b60209290920196919550909350505050565b6000806040838503121561251857600080fd5b82359150602083013561252a81612332565b809150509250929050565b60008083601f84011261254757600080fd5b50813567ffffffffffffffff81111561255f57600080fd5b6020830191508360208260051b850101111561257a57600080fd5b9250929050565b6000806000806040858703121561259757600080fd5b843567ffffffffffffffff808211156125af57600080fd5b6125bb88838901612535565b909650945060208701359150808211156125d457600080fd5b506125e187828801612535565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561263f5761263f6125ed565b60405290565b6040516080810167ffffffffffffffff8111828210171561263f5761263f6125ed565b6040805190810167ffffffffffffffff8111828210171561263f5761263f6125ed565b60405160a0810167ffffffffffffffff8111828210171561263f5761263f6125ed565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156126f5576126f56125ed565b604052919050565b600067ffffffffffffffff821115612717576127176125ed565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261275457600080fd5b8135612767612762826126fd565b6126ae565b81815284602083860101111561277c57600080fd5b816020850160208301376000918101602001919091529392505050565b6000606082840312156127ab57600080fd5b6127b361261c565b905081356127c081612332565b815260208281013590820152604082013567ffffffffffffffff8111156127e657600080fd5b6127f284828501612743565b60408301525092915050565b60006020828403121561281057600080fd5b813567ffffffffffffffff8082111561282857600080fd5b908301906080828603121561283c57600080fd5b612844612645565b823561284f81612332565b815260208301358281111561286357600080fd5b61286f87828601612743565b60208301525060408301358281111561288757600080fd5b61289387828601612743565b6040830152506060830135828111156128ab57600080fd5b6128b787828601612799565b60608301525095945050505050565b73ffffffffffffffffffffffffffffffffffffffff815116825260208101516020830152600060408201516060604085015261290560608501826123e6565b949350505050565b6020815260006105b360208301846128c6565b60008060006060848603121561293557600080fd5b833561294081612332565b9250602084013561295081612332565b929592945050506040919091013590565b60006020828403121561297357600080fd5b813567ffffffffffffffff8082111561298b57600080fd5b908301906040828603121561299f57600080fd5b6129a7612668565b82356129b281612332565b81526020830135828111156129c657600080fd5b9290920191604083870312156129db57600080fd5b6129e3612668565b8335838111156129f257600080fd5b6129fe88828701612743565b825250602084013583811115612a1357600080fd5b612a1f88828701612743565b6020830152508060208301525080935050505092915050565b604081526000612a4b60408301856123e6565b828103602084015261248781856128c6565b600060208284031215612a6f57600080fd5b813567ffffffffffffffff80821115612a8757600080fd5b9083019060608286031215612a9b57600080fd5b612aa361261c565b8235612aae81612332565b8152602083013582811115612ac257600080fd5b612ace87828601612743565b602083015250604083013582811115612ae657600080fd5b612af287828601612799565b60408301525095945050505050565b600060208284031215612b1357600080fd5b5035919050565b600060208284031215612b2c57600080fd5b813567ffffffffffffffff80821115612b4457600080fd5b9083019060a08286031215612b5857600080fd5b612b6061268b565b612b6983612357565b815260208301356020820152604083013582811115612b8757600080fd5b612b9387828601612743565b604083015250606083013582811115612bab57600080fd5b612bb787828601612743565b606083015250608083013582811115612bcf57600080fd5b612bdb87828601612799565b60808301525095945050505050565b600060208284031215612bfc57600080fd5b813567ffffffffffffffff80821115612c1457600080fd5b9083019060608286031215612c2857600080fd5b612c3061261c565b8235612c3b81612332565b81526020830135612c4b81612332565b6020820152604083013582811115612ae657600080fd5b604081526000612c7560408301856128c6565b828103602084015261248781856123e6565b600060208284031215612c9957600080fd5b815167ffffffffffffffff811115612cb057600080fd5b8201601f81018413612cc157600080fd5b8051612ccf612762826126fd565b818152856020838501011115612ce457600080fd5b6124878260208301602086016123ba565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612d2a57600080fd5b83018035915067ffffffffffffffff821115612d4557600080fd5b60200191503681900382131561257a57600080fd5b600080600060608486031215612d6f57600080fd5b8335612d7a81612332565b95602085013595506040909401359392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612dc357600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612e0e57612e0e612dcd565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8035801515811461101e57600080fd5b600060208284031215612e6457600080fd5b6105b382612e42565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612e9e57612e9e612dcd565b5060010190565b6020808252818101839052600090604080840186845b87811015612f1b57612ecc82612e42565b1515835284820135612edd81612332565b73ffffffffffffffffffffffffffffffffffffffff168386015281840135848401526060808301359084015260809283019290910190600101612ebb565b5090979650505050505050565b600080600060608486031215612f3d57600080fd5b8351612f4881612332565b602085015160408601519194509250612f6081612332565b809150509250925092565b60008060408385031215612f7e57600080fd5b505080516020909101519092909150565b60008219821115612fa257612fa2612dcd565b500190565b600060208284031215612fb957600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ff857612ff8612dcd565b50029056fea2646970667358221220a13ccb40499af75f6380cffdc976428f7edc0a7ca6844e5b2f2e9a75df2b0fa764736f6c634300080d003300000000000000000000000044a44837894b5edc2bde64567fc62599b3b88f4c000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d0000000000000000000000000000000000000000000000000000000000000001
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101c45760003560e01c806374f86ade116100f9578063d4bbfea311610097578063e445e7dd11610071578063e445e7dd1461046b578063e73915c214610474578063f2f09c9614610487578063f59ad990146104ae57600080fd5b8063d4bbfea314610425578063d547741f14610445578063dd19fe381461045857600080fd5b806391d14854116100d357806391d14854146103c9578063af93f83b146103ec578063b5f1e82a146103ff578063cf36b9171461041257600080fd5b806374f86ade146103775780637afb99531461038a5780638da5cb5b146103ab57600080fd5b80633caef0f41161016657806362811bf21161014057806362811bf2146102d7578063629a0393146102f75780636651232a1461033d5780636ccae0541461036457600080fd5b80633caef0f41461029e57806352957178146102b15780635b94db27146102c457600080fd5b80632bc9c08a116101a25780632bc9c08a1461024e5780632f2ff15d1461026357806336cc21b8146102765780633bd1adec1461029657600080fd5b8063093c5eb1146101c95780631306ac3b146101ef57806320f99c0a1461020f575b600080fd5b6101dc6101d7366004612362565b6104cf565b6040519081526020015b60405180910390f35b6102026101fd36600461237f565b6105ba565b6040516101e69190612430565b60025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e6565b61026161025c366004612490565b61089c565b005b610261610271366004612505565b610c19565b6101dc610284366004612362565b60086020526000908152604090205481565b610261610c78565b6102616102ac366004612581565b610cd4565b6101dc6102bf366004612362565b610e98565b6102616102d2366004612362565b610ecc565b6102ea6102e53660046127fe565b610f8c565b6040516101e6919061290d565b61030a610305366004612362565b611023565b6040516101e691908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b6102297f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d81565b610261610372366004612920565b6110a9565b610261610385366004612581565b611147565b61039d610398366004612961565b6112c2565b6040516101e6929190612a38565b60015473ffffffffffffffffffffffffffffffffffffffff16610229565b6103dc6103d7366004612505565b61150d565b60405190151581526020016101e6565b61030a6103fa366004612362565b611545565b6101dc61040d366004612362565b6115cb565b61039d610420366004612a5d565b6115f9565b6101dc610433366004612b01565b60076020526000908152604090205481565b610261610453366004612505565b611710565b610202610466366004612b1a565b61176b565b6101dc60045481565b6101dc610482366004612b01565b6119af565b6103dc7f000000000000000000000000000000000000000000000000000000000000000181565b6104c16104bc366004612bea565b611a92565b6040516101e6929190612c62565b6040517f4b0a885400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff828116600483015260009182917f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d1690634b0a8854906024016000604051808303816000875af1158015610562573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526105a89190810190612c87565b90506105b381611b87565b9392505050565b60408051808201909152606080825260208201523373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d161461063d576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000546001146106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002600090815580806106c46040860186612cf5565b8101906106d19190612d5a565b919450925090507f8b3544fa225539b17e942d41841bf832b900055aa60660631db9559bbdcd8b366107066020870187612362565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291861660208381019190915290820185905260608201849052870135608082015260a00160405180910390a160006107aa6107626060880188612d8f565b610770906020810190612cf5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b8792505050565b90506107b68382612dfc565b6040516020016107c891815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020868101919091528490839061080f90890189612362565b6040805173ffffffffffffffffffffffffffffffffffffffff9485166020820152908101929092529091166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528552600082900361088d5760408051600081526020810190915285525b50506001600055509092915050565b3360009081527fbd500bc6ab4a9bcd7eb9f453a5938fad0b08d3dbaae22ba7976ac385056bde2960205260409020547f129109734c476f17817fbe7bcf461de566dcce58e4c0cb61b62d1b9af26fe47f9060ff16610929576040517f962f6333000000000000000000000000000000000000000000000000000000008152600481018290526024016106a5565b60005b82811015610bda5783838281811061094657610946612e13565b61095c9260206080909202019081019150612e52565b15610ac2576109d360006005600087878681811061097c5761097c612e13565b90506080020160200160208101906109949190612362565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020611bad565b50508383828181106109e7576109e7612e13565b9050608002016040013560056000868685818110610a0757610a07612e13565b9050608002016020016020810190610a1f9190612362565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060020155838382818110610a5b57610a5b612e13565b9050608002016060013560056000868685818110610a7b57610a7b612e13565b9050608002016020016020810190610a939190612362565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060010155610bc8565b610add60006006600087878681811061097c5761097c612e13565b5050838382818110610af157610af1612e13565b9050608002016040013560066000868685818110610b1157610b11612e13565b9050608002016020016020810190610b299190612362565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060020155838382818110610b6557610b65612e13565b9050608002016060013560066000868685818110610b8557610b85612e13565b9050608002016020016020810190610b9d9190612362565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020600101555b80610bd281612e6d565b91505061092c565b507f19863caed14ed012a54f927e56250018b7ac3c04fd197e0edf23369f75a6ccd48383604051610c0c929190612ea5565b60405180910390a1505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610c6a576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c748282611c01565b5050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610cc9576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cd233611c87565b565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d25576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610e9057838382818110610d4357610d43612e13565b90506020020135600003610d83576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838382818110610d9557610d95612e13565b9050602002013560086000888885818110610db257610db2612e13565b9050602002016020810190610dc79190612362565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020557f0fdcc1015152882d3bf501506be25357c645aff7e0df94d2b21c45e102e81a0c868683818110610e2157610e21612e13565b9050602002016020810190610e369190612362565b858584818110610e4857610e48612e13565b6040805173ffffffffffffffffffffffffffffffffffffffff90951685526020918202939093013590840152500160405180910390a180610e8881612e6d565b915050610d29565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600660205260408120610ec690611d00565b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610f1d576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b604080516060808201835260008083526020830152918101919091523373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d1614611017576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060608101515b919050565b61104e6040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260056020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b3360009081527f271b3e2292ab6fd3ff496cd98d6d375af02f11568a701741f48bba7789f13a7060205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16611136576040517f962f6333000000000000000000000000000000000000000000000000000000008152600481018290526024016106a5565b611141848484611d67565b50505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611198576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610e90578585828181106111b6576111b6612e13565b905060200201356000036111f6576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83838281811061120857611208612e13565b905060200201356007600088888581811061122557611225612e13565b905060200201358152602001908152602001600020819055507f6cc2f5d066d41fc5850125943b942623567700e44d15463154a87bb5d3332e9186868381811061127157611271612e13565b9050602002013585858481811061128a5761128a612e13565b905060200201356040516112a8929190918252602082015260400190565b60405180910390a1806112ba81612e6d565b91505061119c565b604080516060818101835260008083526020830152918101829052600054600114611349576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064016106a5565b60026000553373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d16146113bd576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008560200151600001518060200190518101906113de9190612f28565b925092509250856000015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461144d576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061145e886000015185611e5c565b6040805173ffffffffffffffffffffffffffffffffffffffff89166020820152908101839052606081018290529193509150608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815260608301825273ffffffffffffffffffffffffffffffffffffffff9097168252602082810194909452805193840181526000808552908201939093526001909255509296929550919350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff166105b3565b6115706040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260066020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260408120610ec690611d00565b6040805160608181018352600080835260208301529181018290523373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d1614611683576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000001156116c1576116bf8360000151846040015160200151611efa565b505b6000806116da8560000151866040015160200151611e5c565b60408051602080820185905281830193909352815180820383018152606090910182529701519081019190915294959350505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611761576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c748282611f84565b60408051808201909152606080825260208201523373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d16146117ee576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008083606001518060200190518101906118099190612f6b565b91509150600061181c8560400151611b87565b9050811561195d576040805180820190915260808681015151875173ffffffffffffffffffffffffffffffffffffffff91821660608501529183018590521660a08201528060c08101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905281526020016118a28484612f8f565b6040516020016118b491815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815291528651608080890151516020808b0151855173ffffffffffffffffffffffffffffffffffffffff95861681529490921690840152828401889052606083018790529082015290519195507fec590f5738bffdca5d4fb4f9e9d902924be6ad3aedb5c5d0b39b208ea41c1e1b919081900360a00190a16119a7565b604080516060810182526000918101918252908152602081016119808484612f8f565b60405160200161199291815260200190565b60405160208183030381529060405281525093505b505050919050565b6040517ff290aafa00000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d169063f290aafa906024016000604051808303816000875af1158015611a41573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611a879190810190612c87565b90506105b381612007565b604080516060808201835260008083526020830152918101829052907f000000000000000000000000b5b1e1ba18d323a2a7313f970bf4310cf8d60b8d73ffffffffffffffffffffffffffffffffffffffff163314611b1d576040517fea8e4eb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000115611b5957611b598360000151846040015160200151612030565b611b6f8360000151846040015160200151612100565b50506040908101518151602081019092526000825291565b805160009015611ba55781806020019051810190610ec69190612fa7565b506000919050565b6000806000611bbb84611d00565b4285559050848110611be257611bd18582612dfc565b600385015584925060009150611bf9565b60006003850155915081611bf68186612dfc565b91505b509250929050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b80546000908190611d119042612dfc565b90506000836001015482611d259190612fc0565b90508360020154846003015482611d3c9190612f8f565b1115611d4e5783600201549250611d60565b6003840154611d5d9082612f8f565b92505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff8216611db4576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff841601611e0057611dfb8282612191565b505050565b8273ffffffffffffffffffffffffffffffffffffffff163b600003611e51576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dfb838383612206565b73ffffffffffffffffffffffffffffffffffffffff821660009081526005602052604081206002015481908103611ebf576040517fec914c0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166000908152600560205260409020611eef908490611bad565b909590945092505050565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260086020526040812054808203611f59576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600760205260408120805493508491611f778386612f8f565b9091555091949350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b805160009015611ba5576000828060200190518101906120279190612f28565b50949350505050565b73ffffffffffffffffffffffffffffffffffffffff821660009081526008602052604081205490819003612090576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600760205260409020548211156120d8576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260076020526040812080548492906120f6908490612dfc565b9091555050505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600660205260408120600201549003612161576040517fec914c0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600660205260409020610c749082906122d5565b600080600080600085875af1905080611dfb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c45440000000000000000000000000060448201526064016106a5565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080611141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064016106a5565b60006122e082611d00565b9050828110612300574282556122f68382612dfc565b6003830155505050565b6040517f47ebad2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461235457600080fd5b50565b803561101e81612332565b60006020828403121561237457600080fd5b81356105b381612332565b60006020828403121561239157600080fd5b813567ffffffffffffffff8111156123a857600080fd5b8201608081850312156105b357600080fd5b60005b838110156123d55781810151838201526020016123bd565b838111156111415750506000910152565b600081518084526123fe8160208601602086016123ba565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600082516040602084015261244c60608401826123e6565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084830301604085015261248782826123e6565b95945050505050565b600080602083850312156124a357600080fd5b823567ffffffffffffffff808211156124bb57600080fd5b818501915085601f8301126124cf57600080fd5b8135818111156124de57600080fd5b8660208260071b85010111156124f357600080fd5b60209290920196919550909350505050565b6000806040838503121561251857600080fd5b82359150602083013561252a81612332565b809150509250929050565b60008083601f84011261254757600080fd5b50813567ffffffffffffffff81111561255f57600080fd5b6020830191508360208260051b850101111561257a57600080fd5b9250929050565b6000806000806040858703121561259757600080fd5b843567ffffffffffffffff808211156125af57600080fd5b6125bb88838901612535565b909650945060208701359150808211156125d457600080fd5b506125e187828801612535565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561263f5761263f6125ed565b60405290565b6040516080810167ffffffffffffffff8111828210171561263f5761263f6125ed565b6040805190810167ffffffffffffffff8111828210171561263f5761263f6125ed565b60405160a0810167ffffffffffffffff8111828210171561263f5761263f6125ed565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156126f5576126f56125ed565b604052919050565b600067ffffffffffffffff821115612717576127176125ed565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261275457600080fd5b8135612767612762826126fd565b6126ae565b81815284602083860101111561277c57600080fd5b816020850160208301376000918101602001919091529392505050565b6000606082840312156127ab57600080fd5b6127b361261c565b905081356127c081612332565b815260208281013590820152604082013567ffffffffffffffff8111156127e657600080fd5b6127f284828501612743565b60408301525092915050565b60006020828403121561281057600080fd5b813567ffffffffffffffff8082111561282857600080fd5b908301906080828603121561283c57600080fd5b612844612645565b823561284f81612332565b815260208301358281111561286357600080fd5b61286f87828601612743565b60208301525060408301358281111561288757600080fd5b61289387828601612743565b6040830152506060830135828111156128ab57600080fd5b6128b787828601612799565b60608301525095945050505050565b73ffffffffffffffffffffffffffffffffffffffff815116825260208101516020830152600060408201516060604085015261290560608501826123e6565b949350505050565b6020815260006105b360208301846128c6565b60008060006060848603121561293557600080fd5b833561294081612332565b9250602084013561295081612332565b929592945050506040919091013590565b60006020828403121561297357600080fd5b813567ffffffffffffffff8082111561298b57600080fd5b908301906040828603121561299f57600080fd5b6129a7612668565b82356129b281612332565b81526020830135828111156129c657600080fd5b9290920191604083870312156129db57600080fd5b6129e3612668565b8335838111156129f257600080fd5b6129fe88828701612743565b825250602084013583811115612a1357600080fd5b612a1f88828701612743565b6020830152508060208301525080935050505092915050565b604081526000612a4b60408301856123e6565b828103602084015261248781856128c6565b600060208284031215612a6f57600080fd5b813567ffffffffffffffff80821115612a8757600080fd5b9083019060608286031215612a9b57600080fd5b612aa361261c565b8235612aae81612332565b8152602083013582811115612ac257600080fd5b612ace87828601612743565b602083015250604083013582811115612ae657600080fd5b612af287828601612799565b60408301525095945050505050565b600060208284031215612b1357600080fd5b5035919050565b600060208284031215612b2c57600080fd5b813567ffffffffffffffff80821115612b4457600080fd5b9083019060a08286031215612b5857600080fd5b612b6061268b565b612b6983612357565b815260208301356020820152604083013582811115612b8757600080fd5b612b9387828601612743565b604083015250606083013582811115612bab57600080fd5b612bb787828601612743565b606083015250608083013582811115612bcf57600080fd5b612bdb87828601612799565b60808301525095945050505050565b600060208284031215612bfc57600080fd5b813567ffffffffffffffff80821115612c1457600080fd5b9083019060608286031215612c2857600080fd5b612c3061261c565b8235612c3b81612332565b81526020830135612c4b81612332565b6020820152604083013582811115612ae657600080fd5b604081526000612c7560408301856128c6565b828103602084015261248781856123e6565b600060208284031215612c9957600080fd5b815167ffffffffffffffff811115612cb057600080fd5b8201601f81018413612cc157600080fd5b8051612ccf612762826126fd565b818152856020838501011115612ce457600080fd5b6124878260208301602086016123ba565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612d2a57600080fd5b83018035915067ffffffffffffffff821115612d4557600080fd5b60200191503681900382131561257a57600080fd5b600080600060608486031215612d6f57600080fd5b8335612d7a81612332565b95602085013595506040909401359392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612dc357600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612e0e57612e0e612dcd565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8035801515811461101e57600080fd5b600060208284031215612e6457600080fd5b6105b382612e42565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612e9e57612e9e612dcd565b5060010190565b6020808252818101839052600090604080840186845b87811015612f1b57612ecc82612e42565b1515835284820135612edd81612332565b73ffffffffffffffffffffffffffffffffffffffff168386015281840135848401526060808301359084015260809283019290910190600101612ebb565b5090979650505050505050565b600080600060608486031215612f3d57600080fd5b8351612f4881612332565b602085015160408601519194509250612f6081612332565b809150509250925092565b60008060408385031215612f7e57600080fd5b505080516020909101519092909150565b60008219821115612fa257612fa2612dcd565b500190565b600060208284031215612fb957600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ff857612ff8612dcd565b50029056fea2646970667358221220a13ccb40499af75f6380cffdc976428f7edc0a7ca6844e5b2f2e9a75df2b0fa764736f6c634300080d0033