Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Controller
- Optimization enabled
- true
- Compiler version
- v0.8.13+commit.abaa5c0e
- Optimization runs
- 999999
- EVM Version
- default
- Verified at
- 2024-10-23T11:38:15.910430Z
Constructor Arguments
0x000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da
Arg [0] (address) : 0xcb246b3719fa7f37ab2696ecdcbb7a9b5dd816da
contracts/bridge/Controller.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/interfaces/IBridge.sol pragma solidity ^0.8.3; interface IBridge { function bridge( address receiver_, uint256 amount_, uint256 msgGasLimit_, address connector_, bytes calldata extraData_, bytes calldata options_ ) external payable; function receiveInbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable; function retry(address connector_, bytes32 messageId_) external; } // File contracts/interfaces/IConnector.sol pragma solidity ^0.8.13; interface IConnector { function outbound( uint256 msgGasLimit_, bytes memory payload_, bytes memory options_ ) external payable returns (bytes32 messageId_); function siblingChainSlug() external view returns (uint32); function getMinFees( uint256 msgGasLimit_, uint256 payloadSize_ ) external view returns (uint256 totalFees); function getMessageId() external view returns (bytes32); } // 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 contracts/interfaces/IMintableERC20.sol pragma solidity 0.8.13; interface IMintableERC20 { function mint(address receiver_, uint256 amount_) external; function burn(address burner_, uint256 amount_) external; } // 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/bridge/Base.sol pragma solidity 0.8.13; abstract contract Base is ReentrancyGuard, IBridge, RescueBase { address public immutable token; bytes32 public bridgeType; IHook public hook__; // message identifier => cache mapping(bytes32 => bytes) public identifierCache; // connector => cache mapping(address => bytes) public connectorCache; mapping(address => bool) public validConnectors; event ConnectorStatusUpdated(address connector, bool status); event HookUpdated(address newHook); event BridgingTokens( address connector, address sender, address receiver, uint256 amount, bytes32 messageId ); event TokensBridged( address connecter, address receiver, uint256 amount, bytes32 messageId ); constructor(address token_) AccessControl(msg.sender) { if (token_ != ETH_ADDRESS && token_.code.length == 0) revert InvalidTokenContract(); token = token_; _grantRole(RESCUE_ROLE, msg.sender); } /** * @notice this function is used to update hook * @dev it can only be updated by owner * @dev should be carefully migrated as it can risk user funds * @param hook_ new hook address */ function updateHook( address hook_, bool approve_ ) external virtual onlyOwner { // remove the approval from the old hook if (token != ETH_ADDRESS) { if (ERC20(token).allowance(address(this), address(hook__)) > 0) { SafeTransferLib.safeApprove(ERC20(token), address(hook__), 0); } if (approve_) { SafeTransferLib.safeApprove( ERC20(token), hook_, type(uint256).max ); } } hook__ = IHook(hook_); emit HookUpdated(hook_); } function updateConnectorStatus( address[] calldata connectors, bool[] calldata statuses ) external onlyOwner { uint256 length = connectors.length; for (uint256 i; i < length; i++) { validConnectors[connectors[i]] = statuses[i]; emit ConnectorStatusUpdated(connectors[i], statuses[i]); } } /** * @notice Executes pre-bridge operations before initiating a token bridge transfer. * @dev This internal function is called before initiating a token bridge transfer. * It validates the receiver address and the connector, and if a pre-hook contract is defined, * it executes the source pre-hook call. * @param connector_ The address of the connector responsible for the transfer. * @param transferInfo_ Information about the transfer. * @return transferInfo Information about the transfer after pre-bridge operations. * @return postHookData Data returned from the pre-hook call. * @dev Reverts with `ZeroAddressReceiver` if the receiver address is zero. * Reverts with `InvalidConnector` if the connector address is not valid. */ function _beforeBridge( address connector_, TransferInfo memory transferInfo_ ) internal returns (TransferInfo memory transferInfo, bytes memory postHookData) { if (transferInfo_.receiver == address(0)) revert ZeroAddressReceiver(); if (!validConnectors[connector_]) revert InvalidConnector(); if (token == ETH_ADDRESS && msg.value < transferInfo_.amount) revert InsufficientMsgValue(); if (address(hook__) != address(0)) { (transferInfo, postHookData) = hook__.srcPreHookCall( SrcPreHookCallParams(connector_, msg.sender, transferInfo_) ); } else { transferInfo = transferInfo_; } } /** * @notice Executes post-bridge operations after completing a token bridge transfer. * @dev This internal function is called after completing a token bridge transfer. * It executes the source post-hook call if a hook contract is defined, calculates fees, * calls the outbound function of the connector, and emits an event for tokens withdrawn. * @param msgGasLimit_ The gas limit for the outbound call. * @param connector_ The address of the connector responsible for the transfer. * @param options_ Additional options for the outbound call. * @param postHookData_ Data returned from the source post-hook call. * @param transferInfo_ Information about the transfer. * @dev Reverts with `MessageIdMisMatched` if the returned message ID does not match the expected message ID. */ function _afterBridge( uint256 msgGasLimit_, address connector_, bytes memory options_, bytes memory postHookData_, TransferInfo memory transferInfo_ ) internal { TransferInfo memory transferInfo = transferInfo_; if (address(hook__) != address(0)) { transferInfo = hook__.srcPostHookCall( SrcPostHookCallParams( connector_, options_, postHookData_, transferInfo_ ) ); } uint256 fees = token == ETH_ADDRESS ? msg.value - transferInfo.amount : msg.value; bytes32 messageId = IConnector(connector_).getMessageId(); bytes32 returnedMessageId = IConnector(connector_).outbound{ value: fees }( msgGasLimit_, abi.encode( transferInfo.receiver, transferInfo.amount, messageId, transferInfo.extraData ), options_ ); if (returnedMessageId != messageId) revert MessageIdMisMatched(); emit BridgingTokens( connector_, msg.sender, transferInfo.receiver, transferInfo.amount, messageId ); } /** * @notice Executes pre-mint operations before minting tokens. * @dev This internal function is called before minting tokens. * It validates the caller as a valid connector, checks if the receiver is not this contract, the bridge contract, * or the token contract, and executes the destination pre-hook call if a hook contract is defined. * @param transferInfo_ Information about the transfer. * @return postHookData Data returned from the destination pre-hook call. * @return transferInfo Information about the transfer after pre-mint operations. * @dev Reverts with `InvalidConnector` if the caller is not a valid connector. * Reverts with `CannotTransferOrExecuteOnBridgeContracts` if the receiver is this contract, the bridge contract, * or the token contract. */ function _beforeMint( uint32, TransferInfo memory transferInfo_ ) internal returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (!validConnectors[msg.sender]) revert InvalidConnector(); // no need of source check here, as if invalid caller, will revert with InvalidPoolId if ( transferInfo_.receiver == address(this) || // transferInfo_.receiver == address(bridge__) || transferInfo_.receiver == token ) revert CannotTransferOrExecuteOnBridgeContracts(); if (address(hook__) != address(0)) { (postHookData, transferInfo) = hook__.dstPreHookCall( DstPreHookCallParams( msg.sender, connectorCache[msg.sender], transferInfo_ ) ); } else { transferInfo = transferInfo_; } } /** * @notice Executes post-mint operations after minting tokens. * @dev This internal function is called after minting tokens. * It executes the destination post-hook call if a hook contract is defined and updates cache data. * @param messageId_ The unique identifier for the mint transaction. * @param postHookData_ Data returned from the destination pre-hook call. * @param transferInfo_ Information about the mint transaction. */ function _afterMint( uint256, bytes32 messageId_, bytes memory postHookData_, TransferInfo memory transferInfo_ ) internal { if (address(hook__) != address(0)) { CacheData memory cacheData = hook__.dstPostHookCall( DstPostHookCallParams( msg.sender, messageId_, connectorCache[msg.sender], postHookData_, transferInfo_ ) ); identifierCache[messageId_] = cacheData.identifierCache; connectorCache[msg.sender] = cacheData.connectorCache; } emit TokensBridged( msg.sender, transferInfo_.receiver, transferInfo_.amount, messageId_ ); } /** * @notice Executes pre-retry operations before retrying a failed transaction. * @dev This internal function is called before retrying a failed transaction. * It validates the connector, retrieves cache data for the given message ID, * and executes the pre-retry hook if defined. * @param connector_ The address of the connector responsible for the failed transaction. * @param messageId_ The unique identifier for the failed transaction. * @return postHookData Data returned from the pre-retry hook call. * @return transferInfo Information about the transfer. * @dev Reverts with `InvalidConnector` if the connector is not valid. * Reverts with `NoPendingData` if there is no pending data for the given message ID. */ function _beforeRetry( address connector_, bytes32 messageId_ ) internal returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (!validConnectors[connector_]) revert InvalidConnector(); CacheData memory cacheData = CacheData( identifierCache[messageId_], connectorCache[connector_] ); if (cacheData.identifierCache.length == 0) revert NoPendingData(); (postHookData, transferInfo) = hook__.preRetryHook( PreRetryHookCallParams(connector_, cacheData) ); } /** * @notice Executes post-retry operations after retrying a failed transaction. * @dev This internal function is called after retrying a failed transaction. * It retrieves cache data for the given message ID, executes the post-retry hook if defined, * and updates cache data. * @param connector_ The address of the connector responsible for the failed transaction. * @param messageId_ The unique identifier for the failed transaction. * @param postHookData Data returned from the pre-retry hook call. */ function _afterRetry( address connector_, bytes32 messageId_, bytes memory postHookData ) internal { CacheData memory cacheData = CacheData( identifierCache[messageId_], connectorCache[connector_] ); (cacheData) = hook__.postRetryHook( PostRetryHookCallParams( connector_, messageId_, postHookData, cacheData ) ); identifierCache[messageId_] = cacheData.identifierCache; connectorCache[connector_] = cacheData.connectorCache; } /** * @notice Retrieves the minimum fees required for a transaction from a connector. * @dev This function returns the minimum fees required for a transaction from the specified connector, * based on the provided message gas limit and payload size. * @param connector_ The address of the connector. * @param msgGasLimit_ The gas limit for the transaction. * @param payloadSize_ The size of the payload for the transaction. * @return totalFees The total minimum fees required for the transaction. */ function getMinFees( address connector_, uint256 msgGasLimit_, uint256 payloadSize_ ) external view returns (uint256 totalFees) { return IConnector(connector_).getMinFees(msgGasLimit_, payloadSize_); } } // File contracts/bridge/Controller.sol pragma solidity 0.8.13; contract Controller is Base { uint256 public totalMinted; constructor(address token_) Base(token_) { bridgeType = NORMAL_CONTROLLER; } /** * @notice Bridges tokens between chains. * @dev This function allows bridging tokens between different chains. * @param receiver_ The address to receive the bridged tokens. * @param amount_ The amount of tokens to bridge. * @param msgGasLimit_ The gas limit for the execution of the bridging process. * @param connector_ The address of the connector contract responsible for the bridge. * @param extraData_ The extra data passed to hook functions. * @param options_ Additional options for the bridging process. */ function bridge( address receiver_, uint256 amount_, uint256 msgGasLimit_, address connector_, bytes calldata extraData_, bytes calldata options_ ) external payable nonReentrant { ( TransferInfo memory transferInfo, bytes memory postHookData ) = _beforeBridge( connector_, TransferInfo(receiver_, amount_, extraData_) ); // to maintain socket dl specific accounting for super token // re check this logic for mint and mint use cases and if other minter involved totalMinted -= transferInfo.amount; _burn(msg.sender, transferInfo.amount); _afterBridge( msgGasLimit_, connector_, options_, postHookData, transferInfo ); } /** * @notice Receives inbound tokens from another chain. * @dev This function is used to receive tokens from another chain. * @param siblingChainSlug_ The identifier of the sibling chain. * @param payload_ The payload containing the inbound tokens. */ function receiveInbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { ( address receiver, uint256 lockAmount, bytes32 messageId, bytes memory extraData ) = abi.decode(payload_, (address, uint256, bytes32, bytes)); // convert to shares TransferInfo memory transferInfo = TransferInfo( receiver, lockAmount, extraData ); bytes memory postHookData; (postHookData, transferInfo) = _beforeMint( siblingChainSlug_, transferInfo ); _mint(transferInfo.receiver, transferInfo.amount); totalMinted += transferInfo.amount; _afterMint(lockAmount, messageId, postHookData, transferInfo); } /** * @notice Retry a failed transaction. * @dev This function allows retrying a failed transaction sent through a connector. * @param connector_ The address of the connector contract responsible for the failed transaction. * @param messageId_ The unique identifier of the failed transaction. */ function retry( address connector_, bytes32 messageId_ ) external nonReentrant { ( bytes memory postHookData, TransferInfo memory transferInfo ) = _beforeRetry(connector_, messageId_); _mint(transferInfo.receiver, transferInfo.amount); totalMinted += transferInfo.amount; _afterRetry(connector_, messageId_, postHookData); } function _burn(address user_, uint256 burnAmount_) internal virtual { IMintableERC20(token).burn(user_, burnAmount_); } function _mint(address user_, uint256 mintAmount_) internal virtual { if (mintAmount_ == 0) return; IMintableERC20(token).mint(user_, mintAmount_); } }
Compiler Settings
{"outputSelection":{"*":{"*":["*"]}},"optimizer":{"runs":999999,"enabled":true},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"token_","internalType":"address"}]},{"type":"error","name":"CannotTransferOrExecuteOnBridgeContracts","inputs":[]},{"type":"error","name":"InsufficientMsgValue","inputs":[]},{"type":"error","name":"InvalidConnector","inputs":[]},{"type":"error","name":"InvalidTokenAddress","inputs":[]},{"type":"error","name":"InvalidTokenContract","inputs":[]},{"type":"error","name":"MessageIdMisMatched","inputs":[]},{"type":"error","name":"NoPendingData","inputs":[]},{"type":"error","name":"NoPermit","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"error","name":"OnlyNominee","inputs":[]},{"type":"error","name":"OnlyOwner","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"error","name":"ZeroAddressReceiver","inputs":[]},{"type":"event","name":"BridgingTokens","inputs":[{"type":"address","name":"connector","internalType":"address","indexed":false},{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"address","name":"receiver","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"ConnectorStatusUpdated","inputs":[{"type":"address","name":"connector","internalType":"address","indexed":false},{"type":"bool","name":"status","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"HookUpdated","inputs":[{"type":"address","name":"newHook","internalType":"address","indexed":false}],"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":"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":"TokensBridged","inputs":[{"type":"address","name":"connecter","internalType":"address","indexed":false},{"type":"address","name":"receiver","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"payable","outputs":[],"name":"bridge","inputs":[{"type":"address","name":"receiver_","internalType":"address"},{"type":"uint256","name":"amount_","internalType":"uint256"},{"type":"uint256","name":"msgGasLimit_","internalType":"uint256"},{"type":"address","name":"connector_","internalType":"address"},{"type":"bytes","name":"extraData_","internalType":"bytes"},{"type":"bytes","name":"options_","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"bridgeType","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"connectorCache","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"totalFees","internalType":"uint256"}],"name":"getMinFees","inputs":[{"type":"address","name":"connector_","internalType":"address"},{"type":"uint256","name":"msgGasLimit_","internalType":"uint256"},{"type":"uint256","name":"payloadSize_","internalType":"uint256"}]},{"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":"address","name":"","internalType":"contract IHook"}],"name":"hook__","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"identifierCache","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"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":"payable","outputs":[],"name":"receiveInbound","inputs":[{"type":"uint32","name":"siblingChainSlug_","internalType":"uint32"},{"type":"bytes","name":"payload_","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":"retry","inputs":[{"type":"address","name":"connector_","internalType":"address"},{"type":"bytes32","name":"messageId_","internalType":"bytes32"}]},{"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":"address","name":"","internalType":"address"}],"name":"token","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalMinted","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateConnectorStatus","inputs":[{"type":"address[]","name":"connectors","internalType":"address[]"},{"type":"bool[]","name":"statuses","internalType":"bool[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateHook","inputs":[{"type":"address","name":"hook_","internalType":"address"},{"type":"bool","name":"approve_","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"validConnectors","inputs":[{"type":"address","name":"","internalType":"address"}]}]
Contract Creation Code
0x60a060405260016000553480156200001657600080fd5b506040516200359d3803806200359d8339810160408190526200003991620001b2565b803380620000478162000103565b50506001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015906200007f57506001600160a01b0381163b155b156200009e57604051630a6f7ecd60e21b815260040160405180910390fd5b6001600160a01b038116608052620000d77fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292533362000157565b50507ff429e6a62fa0281dc1be2d92b346977a47bde9521f323f08f996b64bdbabd5f3600455620001e4565b600180546001600160a01b0383166001600160a01b031991821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60008281526003602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b600060208284031215620001c557600080fd5b81516001600160a01b0381168114620001dd57600080fd5b9392505050565b60805161335e6200023f6000396000818161042e01528181610b0b01528181610bab01528181610c2501528181610c6d015281816111c9015281816113ae0152818161150b015281816118c70152611b23015261335e6000f3fe60806040526004361061016a5760003560e01c80638da5cb5b116100cb578063d547741f1161007f578063f290aafa11610059578063f290aafa146103fc578063fc0c546a1461041c578063fc3a7b981461045057600080fd5b8063d547741f1461038c578063e272ad3f146103ac578063e9ee1eaf146103cc57600080fd5b80639dc7b023116100b05780639dc7b02314610336578063a2309ff814610356578063aad48d801461036c57600080fd5b80638da5cb5b146102db57806391d148541461030657600080fd5b80634b0a8854116101225780636ccae054116101075780636ccae0541461027b57806370bab2c01461029b578063873ea755146102c857600080fd5b80634b0a88541461022e5780635b94db271461025b57600080fd5b80632f2ff15d116101535780632f2ff15d146101e45780633bd1adec14610206578063405e720a1461021b57600080fd5b806320f99c0a1461016f5780632421e155146101c0575b600080fd5b34801561017b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101cc57600080fd5b506101d660045481565b6040519081526020016101b7565b3480156101f057600080fd5b506102046101ff3660046126a5565b610470565b005b34801561021257600080fd5b506102046104cf565b610204610229366004612717565b61052b565b34801561023a57600080fd5b5061024e6102493660046127bf565b61069a565b6040516101b79190612852565b34801561026757600080fd5b506102046102763660046127bf565b610734565b34801561028757600080fd5b50610204610296366004612865565b6107f4565b3480156102a757600080fd5b506005546101969073ffffffffffffffffffffffffffffffffffffffff1681565b6102046102d636600461296a565b610892565b3480156102e757600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16610196565b34801561031257600080fd5b506103266103213660046126a5565b6109b7565b60405190151581526020016101b7565b34801561034257600080fd5b50610204610351366004612a06565b6109f2565b34801561036257600080fd5b506101d660095481565b34801561037857600080fd5b50610204610387366004612a47565b610ab8565b34801561039857600080fd5b506102046103a73660046126a5565b610d2d565b3480156103b857600080fd5b506102046103c7366004612ac1565b610d88565b3480156103d857600080fd5b506103266103e73660046127bf565b60086020526000908152604090205460ff1681565b34801561040857600080fd5b5061024e610417366004612b2d565b610f42565b34801561042857600080fd5b506101967f000000000000000000000000000000000000000000000000000000000000000081565b34801561045c57600080fd5b506101d661046b366004612b46565b610f5b565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104c1576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282610ffc565b5050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610520576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61052933611082565b565b60005460011461059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b60026000819055506000806106158760405180606001604052808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c815260200189898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506110fb565b9150915081602001516009600082825461062f9190612baa565b92505081905550610644338360200151611362565b610689888886868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508792508891506114079050565b505060016000555050505050505050565b600760205260009081526040902080546106b390612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546106df90612bc1565b801561072c5780601f106107015761010080835404028352916020019161072c565b820191906000526020600020905b81548152906001019060200180831161070f57829003601f168201915b505050505081565b60015473ffffffffffffffffffffffffffffffffffffffff163314610785576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b3360009081527f271b3e2292ab6fd3ff496cd98d6d375af02f11568a701741f48bba7789f13a7060205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16610881576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610593565b61088c84848461174c565b50505050565b6000546001146108fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600081905550600080600080848060200190518101906109209190612c59565b9350935093509350600060405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200185815260200183815250905060606109698883611841565b805160208201519194509192506109809190611acb565b8160200151600960008282546109969190612cbe565b909155506109a8905085858385611b52565b50506001600055505050505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff165b9392505050565b600054600114610a5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600090815580610a708484611d8e565b91509150610a8681600001518260200151611acb565b806020015160096000828254610a9c9190612cbe565b90915550610aad9050848484612096565b505060016000555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b09576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610cb3576005546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91821660248201526000917f0000000000000000000000000000000000000000000000000000000000000000169063dd62ed3e90604401602060405180830381865afa158015610bf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c169190612cd6565b1115610c6257600554610c62907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff166000612351565b8015610cb357610cb37f0000000000000000000000000000000000000000000000000000000000000000837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612351565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527fe816c20840d998c8612f9b624b91687a80510eeb293cb09f7637379f6d73342d9060200160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d7e576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282612420565b60015473ffffffffffffffffffffffffffffffffffffffff163314610dd9576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610f3a57838382818110610df757610df7612cef565b9050602002016020810190610e0c9190612d1e565b60086000888885818110610e2257610e22612cef565b9050602002016020810190610e3791906127bf565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790557f857309f1a328784e9fc9749624be5d32fd8e7afab58e7fe9a218dd613a37f15c868683818110610ebd57610ebd612cef565b9050602002016020810190610ed291906127bf565b858584818110610ee457610ee4612cef565b9050602002016020810190610ef99190612d1e565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835290151560208301520160405180910390a180610f3281612d39565b915050610ddd565b505050505050565b600660205260009081526040902080546106b390612bc1565b6040517f666758ca000000000000000000000000000000000000000000000000000000008152600481018390526024810182905260009073ffffffffffffffffffffffffffffffffffffffff85169063666758ca90604401602060405180830381865afa158015610fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff49190612cd6565b949350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60408051606080820183526000808352602083015291810191909152815160609073ffffffffffffffffffffffffffffffffffffffff16611168576040517f96bbcf1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff166111c7576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480156112235750826020015134105b1561125a576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611357576005546040805160608101825273ffffffffffffffffffffffffffffffffffffffff878116825233602083015281830187905291517ff59ad990000000000000000000000000000000000000000000000000000000008152919092169163f59ad990916112e89190600401612db0565b6000604051808303816000875af1158015611307573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d9190810190612e7e565b909250905061135b565b8291505b9250929050565b6040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f00000000000000000000000000000000000000000000000000000000000000001690639dc29fac906044015b600060405180830381600087803b1580156113f357600080fd5b505af1158015610f3a573d6000803e3d6000fd5b600554819073ffffffffffffffffffffffffffffffffffffffff1615611507576005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820186905291517f62811bf200000000000000000000000000000000000000000000000000000000815291909216916362811bf29161149f9190600401612ee2565b6000604051808303816000875af11580156114be573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526115049190810190612f81565b90505b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611560573461156f565b602082015161156f9034612baa565b905060008673ffffffffffffffffffffffffffffffffffffffff166374fa24a66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e29190612cd6565b905060008773ffffffffffffffffffffffffffffffffffffffff1663ac0710cb848b87600001518860200151878a604001516040516020016116279493929190612fb6565b6040516020818303038152906040528b6040518563ffffffff1660e01b815260040161165593929190612ffb565b60206040518083038185885af1158015611673573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116989190612cd6565b90508181146116d3576040517f7b7bbbe000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83516020808601516040805173ffffffffffffffffffffffffffffffffffffffff8d811682523394820194909452929093168284015260608201526080810184905290517fc74a00177d2c63e6eead5ea7936974ad9d0121f86140723b8909f8ec9662cc619181900360a00190a1505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216611799576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8416016117e5576117e082826124a3565b505050565b8273ffffffffffffffffffffffffffffffffffffffff163b600003611836576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117e0838383612518565b6040805160608181018352600080835260208301529181018290523360009081526008602052604090205460ff166118a5576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825173ffffffffffffffffffffffffffffffffffffffff1630148061191957507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff16145b15611950576040517f285c601600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611ac4576005546040805160608101825233808252600090815260076020908152929020805473ffffffffffffffffffffffffffffffffffffffff9094169363cf36b91793830191906119bc90612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546119e890612bc1565b8015611a355780601f10611a0a57610100808354040283529160200191611a35565b820191906000526020600020905b815481529060010190602001808311611a1857829003601f168201915b50505050508152602001868152506040518263ffffffff1660e01b8152600401611a5f9190613026565b6000604051808303816000875af1158015611a7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d919081019061309d565b5092909150565b80600003611ad7575050565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f000000000000000000000000000000000000000000000000000000000000000016906340c10f19906044016113d9565b60055473ffffffffffffffffffffffffffffffffffffffff1615611d24576005546040805160a0810182523380825260208083018890526000918252600790528281208054919473ffffffffffffffffffffffffffffffffffffffff169363dd19fe3893929083019190611bc590612bc1565b80601f0160208091040260200160405190810160405280929190818152602001828054611bf190612bc1565b8015611c3e5780601f10611c1357610100808354040283529160200191611c3e565b820191906000526020600020905b815481529060010190602001808311611c2157829003601f168201915b50505050508152602001868152602001858152506040518263ffffffff1660e01b8152600401611c6e91906130f7565b6000604051808303816000875af1158015611c8d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611cd39190810190613197565b805160008681526006602090815260409091208251939450611cfb93909291909101906125e7565b5060208082015133600090815260078352604090208151611d21939192909101906125e7565b50505b80516020808301516040805133815273ffffffffffffffffffffffffffffffffffffffff909416928401929092528282015260608201859052517f9afd47907e25028cdaca89d193518c302bbb128617d5a992c5abd458155265939181900360800190a150505050565b60408051606081810183526000808352602083015291810182905273ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff16611e08576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518082018252600085815260066020529182208054829190611e2c90612bc1565b80601f0160208091040260200160405190810160405280929190818152602001828054611e5890612bc1565b8015611ea55780601f10611e7a57610100808354040283529160200191611ea5565b820191906000526020600020905b815481529060010190602001808311611e8857829003601f168201915b50505050508152602001600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054611ef990612bc1565b80601f0160208091040260200160405190810160405280929190818152602001828054611f2590612bc1565b8015611f725780601f10611f4757610100808354040283529160200191611f72565b820191906000526020600020905b815481529060010190602001808311611f5557829003601f168201915b50505050508152509050806000015151600003611fbb576040517fd3d38f6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460408051808201825273ffffffffffffffffffffffffffffffffffffffff88811682526020820185905291517f7afb99530000000000000000000000000000000000000000000000000000000081529190921691637afb995391612025919060040161326c565b6000604051808303816000875af1158015612044573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261208a919081019061309d565b90969095509350505050565b6040805180820182526000848152600660205291822080548291906120ba90612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546120e690612bc1565b80156121335780601f1061210857610100808354040283529160200191612133565b820191906000526020600020905b81548152906001019060200180831161211657829003601f168201915b50505050508152602001600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805461218790612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546121b390612bc1565b80156122005780601f106121d557610100808354040283529160200191612200565b820191906000526020600020905b8154815290600101906020018083116121e357829003601f168201915b5050509190925250506005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820185905291517f1306ac3b000000000000000000000000000000000000000000000000000000008152939450911691631306ac3b91612281916004016132a7565b6000604051808303816000875af11580156122a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526122e69190810190613197565b80516000858152600660209081526040909120825193945061230e93909291909101906125e7565b5060208082015173ffffffffffffffffffffffffffffffffffffffff861660009081526007835260409020815161234a939192909101906125e7565b5050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610593565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b600080600080600085875af19050806117e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610593565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610593565b8280546125f390612bc1565b90600052602060002090601f016020900481019282612615576000855561265b565b82601f1061262e57805160ff191683800117855561265b565b8280016001018555821561265b579182015b8281111561265b578251825591602001919060010190612640565b5061266792915061266b565b5090565b5b80821115612667576000815560010161266c565b73ffffffffffffffffffffffffffffffffffffffff811681146126a257600080fd5b50565b600080604083850312156126b857600080fd5b8235915060208301356126ca81612680565b809150509250929050565b60008083601f8401126126e757600080fd5b50813567ffffffffffffffff8111156126ff57600080fd5b60208301915083602082850101111561135b57600080fd5b60008060008060008060008060c0898b03121561273357600080fd5b883561273e81612680565b97506020890135965060408901359550606089013561275c81612680565b9450608089013567ffffffffffffffff8082111561277957600080fd5b6127858c838d016126d5565b909650945060a08b013591508082111561279e57600080fd5b506127ab8b828c016126d5565b999c989b5096995094979396929594505050565b6000602082840312156127d157600080fd5b81356109eb81612680565b60005b838110156127f75781810151838201526020016127df565b8381111561088c5750506000910152565b600081518084526128208160208601602086016127dc565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006109eb6020830184612808565b60008060006060848603121561287a57600080fd5b833561288581612680565b9250602084013561289581612680565b929592945050506040919091013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561291c5761291c6128a6565b604052919050565b600067ffffffffffffffff82111561293e5761293e6128a6565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000806040838503121561297d57600080fd5b823563ffffffff8116811461299157600080fd5b9150602083013567ffffffffffffffff8111156129ad57600080fd5b8301601f810185136129be57600080fd5b80356129d16129cc82612924565b6128d5565b8181528660208385010111156129e657600080fd5b816020840160208301376000602083830101528093505050509250929050565b60008060408385031215612a1957600080fd5b8235612a2481612680565b946020939093013593505050565b80358015158114612a4257600080fd5b919050565b60008060408385031215612a5a57600080fd5b8235612a6581612680565b9150612a7360208401612a32565b90509250929050565b60008083601f840112612a8e57600080fd5b50813567ffffffffffffffff811115612aa657600080fd5b6020830191508360208260051b850101111561135b57600080fd5b60008060008060408587031215612ad757600080fd5b843567ffffffffffffffff80821115612aef57600080fd5b612afb88838901612a7c565b90965094506020870135915080821115612b1457600080fd5b50612b2187828801612a7c565b95989497509550505050565b600060208284031215612b3f57600080fd5b5035919050565b600080600060608486031215612b5b57600080fd5b8335612b6681612680565b95602085013595506040909401359392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612bbc57612bbc612b7b565b500390565b600181811c90821680612bd557607f821691505b602082108103612c0e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f830112612c2557600080fd5b8151612c336129cc82612924565b818152846020838601011115612c4857600080fd5b610ff48260208301602087016127dc565b60008060008060808587031215612c6f57600080fd5b8451612c7a81612680565b809450506020850151925060408501519150606085015167ffffffffffffffff811115612ca657600080fd5b612cb287828801612c14565b91505092959194509250565b60008219821115612cd157612cd1612b7b565b500190565b600060208284031215612ce857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215612d3057600080fd5b6109eb82612a32565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612d6a57612d6a612b7b565b5060010190565b73ffffffffffffffffffffffffffffffffffffffff8151168252602081015160208301526000604082015160606040850152610ff46060850182612808565b60208152600073ffffffffffffffffffffffffffffffffffffffff808451166020840152806020850151166040840152506040830151606080840152610ff46080840182612d71565b600060608284031215612e0b57600080fd5b6040516060810167ffffffffffffffff8282108183111715612e2f57612e2f6128a6565b8160405282935084519150612e4382612680565b818352602085015160208401526040850151915080821115612e6457600080fd5b50612e7185828601612c14565b6040830152505092915050565b60008060408385031215612e9157600080fd5b825167ffffffffffffffff80821115612ea957600080fd5b612eb586838701612df9565b93506020850151915080821115612ecb57600080fd5b50612ed885828601612c14565b9150509250929050565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526000602083015160806040840152612f1e60a0840182612808565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080858403016060860152612f5a8383612808565b9250606086015191508085840301608086015250612f788282612d71565b95945050505050565b600060208284031215612f9357600080fd5b815167ffffffffffffffff811115612faa57600080fd5b610ff484828501612df9565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152826040820152608060608201526000612ff16080830184612808565b9695505050505050565b8381526060602082015260006130146060830185612808565b8281036040840152612ff18185612808565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151606060408401526130626080840182612808565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016060850152612f788282612d71565b600080604083850312156130b057600080fd5b825167ffffffffffffffff808211156130c857600080fd5b6130d486838701612c14565b935060208501519150808211156130ea57600080fd5b50612ed885828601612df9565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152602082015160408201526000604083015160a0606084015261313d60c0840182612808565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0808584030160808601526131798383612808565b925060808601519150808584030160a086015250612f788282612d71565b6000602082840312156131a957600080fd5b815167ffffffffffffffff808211156131c157600080fd5b90830190604082860312156131d557600080fd5b6040516040810181811083821117156131f0576131f06128a6565b60405282518281111561320257600080fd5b61320e87828601612c14565b82525060208301518281111561322357600080fd5b61322f87828601612c14565b60208301525095945050505050565b60008151604084526132536040850182612808565b905060208301518482036020860152612f788282612808565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152610ff4606084018261323e565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526020820151604082015260006040830151608060608401526132ed60a0840182612808565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016080850152612f78828261323e56fea2646970667358221220a2bbddfad7ed3b856aafedec1c0f90261118eb56cc9bdd1cf6510d2fcbae8be264736f6c634300080d0033000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da
Deployed ByteCode
0x60806040526004361061016a5760003560e01c80638da5cb5b116100cb578063d547741f1161007f578063f290aafa11610059578063f290aafa146103fc578063fc0c546a1461041c578063fc3a7b981461045057600080fd5b8063d547741f1461038c578063e272ad3f146103ac578063e9ee1eaf146103cc57600080fd5b80639dc7b023116100b05780639dc7b02314610336578063a2309ff814610356578063aad48d801461036c57600080fd5b80638da5cb5b146102db57806391d148541461030657600080fd5b80634b0a8854116101225780636ccae054116101075780636ccae0541461027b57806370bab2c01461029b578063873ea755146102c857600080fd5b80634b0a88541461022e5780635b94db271461025b57600080fd5b80632f2ff15d116101535780632f2ff15d146101e45780633bd1adec14610206578063405e720a1461021b57600080fd5b806320f99c0a1461016f5780632421e155146101c0575b600080fd5b34801561017b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101cc57600080fd5b506101d660045481565b6040519081526020016101b7565b3480156101f057600080fd5b506102046101ff3660046126a5565b610470565b005b34801561021257600080fd5b506102046104cf565b610204610229366004612717565b61052b565b34801561023a57600080fd5b5061024e6102493660046127bf565b61069a565b6040516101b79190612852565b34801561026757600080fd5b506102046102763660046127bf565b610734565b34801561028757600080fd5b50610204610296366004612865565b6107f4565b3480156102a757600080fd5b506005546101969073ffffffffffffffffffffffffffffffffffffffff1681565b6102046102d636600461296a565b610892565b3480156102e757600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16610196565b34801561031257600080fd5b506103266103213660046126a5565b6109b7565b60405190151581526020016101b7565b34801561034257600080fd5b50610204610351366004612a06565b6109f2565b34801561036257600080fd5b506101d660095481565b34801561037857600080fd5b50610204610387366004612a47565b610ab8565b34801561039857600080fd5b506102046103a73660046126a5565b610d2d565b3480156103b857600080fd5b506102046103c7366004612ac1565b610d88565b3480156103d857600080fd5b506103266103e73660046127bf565b60086020526000908152604090205460ff1681565b34801561040857600080fd5b5061024e610417366004612b2d565b610f42565b34801561042857600080fd5b506101967f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da81565b34801561045c57600080fd5b506101d661046b366004612b46565b610f5b565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104c1576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282610ffc565b5050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610520576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61052933611082565b565b60005460011461059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b60026000819055506000806106158760405180606001604052808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c815260200189898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506110fb565b9150915081602001516009600082825461062f9190612baa565b92505081905550610644338360200151611362565b610689888886868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508792508891506114079050565b505060016000555050505050505050565b600760205260009081526040902080546106b390612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546106df90612bc1565b801561072c5780601f106107015761010080835404028352916020019161072c565b820191906000526020600020905b81548152906001019060200180831161070f57829003601f168201915b505050505081565b60015473ffffffffffffffffffffffffffffffffffffffff163314610785576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b3360009081527f271b3e2292ab6fd3ff496cd98d6d375af02f11568a701741f48bba7789f13a7060205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16610881576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610593565b61088c84848461174c565b50505050565b6000546001146108fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600081905550600080600080848060200190518101906109209190612c59565b9350935093509350600060405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200185815260200183815250905060606109698883611841565b805160208201519194509192506109809190611acb565b8160200151600960008282546109969190612cbe565b909155506109a8905085858385611b52565b50506001600055505050505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff165b9392505050565b600054600114610a5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600090815580610a708484611d8e565b91509150610a8681600001518260200151611acb565b806020015160096000828254610a9c9190612cbe565b90915550610aad9050848484612096565b505060016000555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b09576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da73ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610cb3576005546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91821660248201526000917f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da169063dd62ed3e90604401602060405180830381865afa158015610bf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c169190612cd6565b1115610c6257600554610c62907f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da9073ffffffffffffffffffffffffffffffffffffffff166000612351565b8015610cb357610cb37f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612351565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527fe816c20840d998c8612f9b624b91687a80510eeb293cb09f7637379f6d73342d9060200160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d7e576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282612420565b60015473ffffffffffffffffffffffffffffffffffffffff163314610dd9576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610f3a57838382818110610df757610df7612cef565b9050602002016020810190610e0c9190612d1e565b60086000888885818110610e2257610e22612cef565b9050602002016020810190610e3791906127bf565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790557f857309f1a328784e9fc9749624be5d32fd8e7afab58e7fe9a218dd613a37f15c868683818110610ebd57610ebd612cef565b9050602002016020810190610ed291906127bf565b858584818110610ee457610ee4612cef565b9050602002016020810190610ef99190612d1e565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835290151560208301520160405180910390a180610f3281612d39565b915050610ddd565b505050505050565b600660205260009081526040902080546106b390612bc1565b6040517f666758ca000000000000000000000000000000000000000000000000000000008152600481018390526024810182905260009073ffffffffffffffffffffffffffffffffffffffff85169063666758ca90604401602060405180830381865afa158015610fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff49190612cd6565b949350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60408051606080820183526000808352602083015291810191909152815160609073ffffffffffffffffffffffffffffffffffffffff16611168576040517f96bbcf1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff166111c7576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da73ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480156112235750826020015134105b1561125a576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611357576005546040805160608101825273ffffffffffffffffffffffffffffffffffffffff878116825233602083015281830187905291517ff59ad990000000000000000000000000000000000000000000000000000000008152919092169163f59ad990916112e89190600401612db0565b6000604051808303816000875af1158015611307573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d9190810190612e7e565b909250905061135b565b8291505b9250929050565b6040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da1690639dc29fac906044015b600060405180830381600087803b1580156113f357600080fd5b505af1158015610f3a573d6000803e3d6000fd5b600554819073ffffffffffffffffffffffffffffffffffffffff1615611507576005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820186905291517f62811bf200000000000000000000000000000000000000000000000000000000815291909216916362811bf29161149f9190600401612ee2565b6000604051808303816000875af11580156114be573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526115049190810190612f81565b90505b60007f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da73ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611560573461156f565b602082015161156f9034612baa565b905060008673ffffffffffffffffffffffffffffffffffffffff166374fa24a66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e29190612cd6565b905060008773ffffffffffffffffffffffffffffffffffffffff1663ac0710cb848b87600001518860200151878a604001516040516020016116279493929190612fb6565b6040516020818303038152906040528b6040518563ffffffff1660e01b815260040161165593929190612ffb565b60206040518083038185885af1158015611673573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116989190612cd6565b90508181146116d3576040517f7b7bbbe000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83516020808601516040805173ffffffffffffffffffffffffffffffffffffffff8d811682523394820194909452929093168284015260608201526080810184905290517fc74a00177d2c63e6eead5ea7936974ad9d0121f86140723b8909f8ec9662cc619181900360a00190a1505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216611799576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8416016117e5576117e082826124a3565b505050565b8273ffffffffffffffffffffffffffffffffffffffff163b600003611836576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117e0838383612518565b6040805160608181018352600080835260208301529181018290523360009081526008602052604090205460ff166118a5576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825173ffffffffffffffffffffffffffffffffffffffff1630148061191957507f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da73ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff16145b15611950576040517f285c601600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611ac4576005546040805160608101825233808252600090815260076020908152929020805473ffffffffffffffffffffffffffffffffffffffff9094169363cf36b91793830191906119bc90612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546119e890612bc1565b8015611a355780601f10611a0a57610100808354040283529160200191611a35565b820191906000526020600020905b815481529060010190602001808311611a1857829003601f168201915b50505050508152602001868152506040518263ffffffff1660e01b8152600401611a5f9190613026565b6000604051808303816000875af1158015611a7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d919081019061309d565b5092909150565b80600003611ad7575050565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f000000000000000000000000cb246b3719fa7f37ab2696ecdcbb7a9b5dd816da16906340c10f19906044016113d9565b60055473ffffffffffffffffffffffffffffffffffffffff1615611d24576005546040805160a0810182523380825260208083018890526000918252600790528281208054919473ffffffffffffffffffffffffffffffffffffffff169363dd19fe3893929083019190611bc590612bc1565b80601f0160208091040260200160405190810160405280929190818152602001828054611bf190612bc1565b8015611c3e5780601f10611c1357610100808354040283529160200191611c3e565b820191906000526020600020905b815481529060010190602001808311611c2157829003601f168201915b50505050508152602001868152602001858152506040518263ffffffff1660e01b8152600401611c6e91906130f7565b6000604051808303816000875af1158015611c8d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611cd39190810190613197565b805160008681526006602090815260409091208251939450611cfb93909291909101906125e7565b5060208082015133600090815260078352604090208151611d21939192909101906125e7565b50505b80516020808301516040805133815273ffffffffffffffffffffffffffffffffffffffff909416928401929092528282015260608201859052517f9afd47907e25028cdaca89d193518c302bbb128617d5a992c5abd458155265939181900360800190a150505050565b60408051606081810183526000808352602083015291810182905273ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff16611e08576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518082018252600085815260066020529182208054829190611e2c90612bc1565b80601f0160208091040260200160405190810160405280929190818152602001828054611e5890612bc1565b8015611ea55780601f10611e7a57610100808354040283529160200191611ea5565b820191906000526020600020905b815481529060010190602001808311611e8857829003601f168201915b50505050508152602001600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054611ef990612bc1565b80601f0160208091040260200160405190810160405280929190818152602001828054611f2590612bc1565b8015611f725780601f10611f4757610100808354040283529160200191611f72565b820191906000526020600020905b815481529060010190602001808311611f5557829003601f168201915b50505050508152509050806000015151600003611fbb576040517fd3d38f6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460408051808201825273ffffffffffffffffffffffffffffffffffffffff88811682526020820185905291517f7afb99530000000000000000000000000000000000000000000000000000000081529190921691637afb995391612025919060040161326c565b6000604051808303816000875af1158015612044573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261208a919081019061309d565b90969095509350505050565b6040805180820182526000848152600660205291822080548291906120ba90612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546120e690612bc1565b80156121335780601f1061210857610100808354040283529160200191612133565b820191906000526020600020905b81548152906001019060200180831161211657829003601f168201915b50505050508152602001600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805461218790612bc1565b80601f01602080910402602001604051908101604052809291908181526020018280546121b390612bc1565b80156122005780601f106121d557610100808354040283529160200191612200565b820191906000526020600020905b8154815290600101906020018083116121e357829003601f168201915b5050509190925250506005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820185905291517f1306ac3b000000000000000000000000000000000000000000000000000000008152939450911691631306ac3b91612281916004016132a7565b6000604051808303816000875af11580156122a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526122e69190810190613197565b80516000858152600660209081526040909120825193945061230e93909291909101906125e7565b5060208082015173ffffffffffffffffffffffffffffffffffffffff861660009081526007835260409020815161234a939192909101906125e7565b5050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610593565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b600080600080600085875af19050806117e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610593565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610593565b8280546125f390612bc1565b90600052602060002090601f016020900481019282612615576000855561265b565b82601f1061262e57805160ff191683800117855561265b565b8280016001018555821561265b579182015b8281111561265b578251825591602001919060010190612640565b5061266792915061266b565b5090565b5b80821115612667576000815560010161266c565b73ffffffffffffffffffffffffffffffffffffffff811681146126a257600080fd5b50565b600080604083850312156126b857600080fd5b8235915060208301356126ca81612680565b809150509250929050565b60008083601f8401126126e757600080fd5b50813567ffffffffffffffff8111156126ff57600080fd5b60208301915083602082850101111561135b57600080fd5b60008060008060008060008060c0898b03121561273357600080fd5b883561273e81612680565b97506020890135965060408901359550606089013561275c81612680565b9450608089013567ffffffffffffffff8082111561277957600080fd5b6127858c838d016126d5565b909650945060a08b013591508082111561279e57600080fd5b506127ab8b828c016126d5565b999c989b5096995094979396929594505050565b6000602082840312156127d157600080fd5b81356109eb81612680565b60005b838110156127f75781810151838201526020016127df565b8381111561088c5750506000910152565b600081518084526128208160208601602086016127dc565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006109eb6020830184612808565b60008060006060848603121561287a57600080fd5b833561288581612680565b9250602084013561289581612680565b929592945050506040919091013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561291c5761291c6128a6565b604052919050565b600067ffffffffffffffff82111561293e5761293e6128a6565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000806040838503121561297d57600080fd5b823563ffffffff8116811461299157600080fd5b9150602083013567ffffffffffffffff8111156129ad57600080fd5b8301601f810185136129be57600080fd5b80356129d16129cc82612924565b6128d5565b8181528660208385010111156129e657600080fd5b816020840160208301376000602083830101528093505050509250929050565b60008060408385031215612a1957600080fd5b8235612a2481612680565b946020939093013593505050565b80358015158114612a4257600080fd5b919050565b60008060408385031215612a5a57600080fd5b8235612a6581612680565b9150612a7360208401612a32565b90509250929050565b60008083601f840112612a8e57600080fd5b50813567ffffffffffffffff811115612aa657600080fd5b6020830191508360208260051b850101111561135b57600080fd5b60008060008060408587031215612ad757600080fd5b843567ffffffffffffffff80821115612aef57600080fd5b612afb88838901612a7c565b90965094506020870135915080821115612b1457600080fd5b50612b2187828801612a7c565b95989497509550505050565b600060208284031215612b3f57600080fd5b5035919050565b600080600060608486031215612b5b57600080fd5b8335612b6681612680565b95602085013595506040909401359392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612bbc57612bbc612b7b565b500390565b600181811c90821680612bd557607f821691505b602082108103612c0e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f830112612c2557600080fd5b8151612c336129cc82612924565b818152846020838601011115612c4857600080fd5b610ff48260208301602087016127dc565b60008060008060808587031215612c6f57600080fd5b8451612c7a81612680565b809450506020850151925060408501519150606085015167ffffffffffffffff811115612ca657600080fd5b612cb287828801612c14565b91505092959194509250565b60008219821115612cd157612cd1612b7b565b500190565b600060208284031215612ce857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215612d3057600080fd5b6109eb82612a32565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612d6a57612d6a612b7b565b5060010190565b73ffffffffffffffffffffffffffffffffffffffff8151168252602081015160208301526000604082015160606040850152610ff46060850182612808565b60208152600073ffffffffffffffffffffffffffffffffffffffff808451166020840152806020850151166040840152506040830151606080840152610ff46080840182612d71565b600060608284031215612e0b57600080fd5b6040516060810167ffffffffffffffff8282108183111715612e2f57612e2f6128a6565b8160405282935084519150612e4382612680565b818352602085015160208401526040850151915080821115612e6457600080fd5b50612e7185828601612c14565b6040830152505092915050565b60008060408385031215612e9157600080fd5b825167ffffffffffffffff80821115612ea957600080fd5b612eb586838701612df9565b93506020850151915080821115612ecb57600080fd5b50612ed885828601612c14565b9150509250929050565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526000602083015160806040840152612f1e60a0840182612808565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080858403016060860152612f5a8383612808565b9250606086015191508085840301608086015250612f788282612d71565b95945050505050565b600060208284031215612f9357600080fd5b815167ffffffffffffffff811115612faa57600080fd5b610ff484828501612df9565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152826040820152608060608201526000612ff16080830184612808565b9695505050505050565b8381526060602082015260006130146060830185612808565b8281036040840152612ff18185612808565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151606060408401526130626080840182612808565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016060850152612f788282612d71565b600080604083850312156130b057600080fd5b825167ffffffffffffffff808211156130c857600080fd5b6130d486838701612c14565b935060208501519150808211156130ea57600080fd5b50612ed885828601612df9565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152602082015160408201526000604083015160a0606084015261313d60c0840182612808565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0808584030160808601526131798383612808565b925060808601519150808584030160a086015250612f788282612d71565b6000602082840312156131a957600080fd5b815167ffffffffffffffff808211156131c157600080fd5b90830190604082860312156131d557600080fd5b6040516040810181811083821117156131f0576131f06128a6565b60405282518281111561320257600080fd5b61320e87828601612c14565b82525060208301518281111561322357600080fd5b61322f87828601612c14565b60208301525095945050505050565b60008151604084526132536040850182612808565b905060208301518482036020860152612f788282612808565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152610ff4606084018261323e565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526020820151604082015260006040830151608060608401526132ed60a0840182612808565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016080850152612f78828261323e56fea2646970667358221220a2bbddfad7ed3b856aafedec1c0f90261118eb56cc9bdd1cf6510d2fcbae8be264736f6c634300080d0033