Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- LayerswapV8ERC20
- Optimization enabled
- true
- Compiler version
- v0.8.23+commit.f704f362
- Optimization runs
- 200
- EVM Version
- paris
- Verified at
- 2024-10-02T13:01:45.552757Z
contracts/HashedTimeLockERC20.sol
/* _ __ _____ | | __ _ _ _ ___ _ __ _____ ____ _ _ __ \ \ / ( _ ) | | / _` | | | |/ _ \ '__/ __\ \ /\ / / _` | '_ \ \ \ / // _ \ | |__| (_| | |_| | __/ | \__ \\ V V / (_| | |_) | \ V /| (_) | |_____\__,_|\__, |\___|_| |___/ \_/\_/ \__,_| .__/ \_/ \___/ |___/ |_| */ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; /** * @title Hashed Timelock contracts (HTLCs) on Ethereum ERC20 tokens. * * This contract provides a way to lock and keep HTLCs for ERC20 tokens. * * Protocol: * * 1) lock(srcReceiver, hashlock, timelock, tokenContract, amount) - a * sender calls this to lock a new HTLC on a given token (tokenContract) * for a given amount. A 32 byte contract id is returned * 2) redeem(contractId, secret) - once the srcReceiver knows the secret of * the hashlock hash they can claim the tokens with this function * 3) refund() - after timelock has expired and if the srcReceiver did not * redeem the tokens the sender / creator of the HTLC can get their tokens * back with this function. */ struct EIP712Domain { string name; string version; uint256 chainId; address verifyingContract; bytes32 salt; } interface IMessenger { function notify( bytes32 Id, bytes32 hashlock, string memory dstChain, string memory dstAsset, string memory dstAddress, string memory srcAsset, address payable sender, address payable srcReceiver, uint256 amount, uint256 timelock, address tokenContract ) external; } contract LayerswapV8ERC20 { using ECDSA for bytes32; using Address for address; bytes32 private DOMAIN_SEPARATOR; bytes32 private constant SALT = keccak256(abi.encodePacked('Layerswap V8')); constructor() { DOMAIN_SEPARATOR = hashDomain( EIP712Domain({ name: 'LayerswapV8ERC20', version: '1', chainId: block.chainid, verifyingContract: address(this), salt: SALT }) ); } error FundsNotSent(); error NotFutureTimelock(); error NotPassedTimelock(); error LockAlreadyExists(); error HTLCAlreadyExists(); error HTLCNotExists(); error HashlockNotMatch(); error AlreadyRedeemed(); error AlreadyRefunded(); error NoMessenger(); error IncorrectData(); error InsufficientBalance(); error NoAllowance(); error AlreadyLocked(); error InvalidSigniture(); error HashlockAlreadySet(); struct HTLC { string dstAddress; string dstChain; string dstAsset; string srcAsset; address payable sender; address payable srcReceiver; bytes32 hashlock; uint256 timelock; uint256 amount; uint256 secret; address tokenContract; address messenger; bool redeemed; bool refunded; } struct addLockMsg { bytes32 Id; bytes32 hashlock; uint256 timelock; } using SafeERC20 for IERC20; mapping(bytes32 => HTLC) contracts; bytes32[] contractIds; uint256 blockHashAsUint = uint256(blockhash(block.number - 1)); uint256 contractNonce = 0; event TokenCommitted( bytes32 indexed Id, string[] hopChains, string[] hopAssets, string[] hopAddresses, string dstChain, string dstAddress, string dstAsset, address indexed sender, address indexed srcReceiver, string srcAsset, uint amount, uint timelock, address messenger, address tokenContract ); event TokenLocked( bytes32 indexed Id, bytes32 hashlock, string dstChain, string dstAddress, string dstAsset, address indexed sender, address indexed srcReceiver, string srcAsset, uint amount, uint timelock, address messenger, address tokenContract ); event TokenLockAdded(bytes32 Id, address messenger, bytes32 hashlock, uint256 timelock); event TokenRedeemed(bytes32 indexed Id, address redeemAddress); event TokenRefunded(bytes32 indexed Id); event LowLevelErrorOccurred(bytes lowLevelData); modifier _exists(bytes32 Id) { if (!hasHTLC(Id)) revert HTLCNotExists(); _; } function commit( string[] memory hopChains, string[] memory hopAssets, string[] memory hopAddresses, string memory dstChain, string memory dstAsset, string memory dstAddress, string memory srcAsset, address srcReceiver, uint timelock, address messenger, uint amount, address tokenContract ) external returns (bytes32 Id) { if (amount == 0) { revert FundsNotSent(); } if (timelock <= block.timestamp) { revert NotFutureTimelock(); } IERC20 token = IERC20(tokenContract); if (token.balanceOf(msg.sender) < amount) { revert InsufficientBalance(); } if (token.allowance(msg.sender, address(this)) < amount) { revert NoAllowance(); } token.safeTransferFrom(msg.sender, address(this), amount); contractNonce += 1; Id = bytes32(blockHashAsUint ^ contractNonce); //Remove this check; the ID is guaranteed to be unique. if (hasHTLC(Id)) { revert HTLCAlreadyExists(); } contractIds.push(Id); contracts[Id] = HTLC( dstAddress, dstChain, dstAsset, srcAsset, payable(msg.sender), payable(srcReceiver), bytes32(0), timelock, amount, uint256(0), tokenContract, messenger, false, false ); emit TokenCommitted( Id, hopChains, hopAssets, hopAddresses, dstChain, dstAddress, dstAsset, msg.sender, srcReceiver, srcAsset, amount, timelock, messenger, tokenContract ); } function addLock(bytes32 Id, bytes32 hashlock, uint256 timelock) external _exists(Id) returns (bytes32) { HTLC storage htlc = contracts[Id]; if (htlc.refunded == true) { revert AlreadyRefunded(); } if (timelock <= block.timestamp) { revert NotFutureTimelock(); } if (msg.sender == htlc.sender || msg.sender == htlc.messenger || msg.sender == address(this)) { if (htlc.hashlock == 0) { htlc.hashlock = hashlock; htlc.timelock = timelock; } else { revert HashlockAlreadySet(); } // DISCLAIMER: `tx.origin` is used in this event, but it can be misleading as it tracks the // original transaction sender, which could be different from the expected sender in complex // flows (e.g., a -> ... -> b -> c where the actual sender is b in event will be emitted a) emit TokenLockAdded(Id, tx.origin, hashlock, timelock); return Id; } else { revert NoAllowance(); } } function addLockSig(addLockMsg memory message, uint8 v, bytes32 r, bytes32 s) external returns (bytes32) { if (verifyMessage(message, v, r, s)) { return this.addLock(message.Id, message.hashlock, message.timelock); } else { revert InvalidSigniture(); } } /** * @dev Sender / Payer sets up a new hash time lock contract depositing the * funds and providing the reciever and terms. * @param srcReceiver srcReceiver of the funds. * @param hashlock A sha-256 hash hashlock. * @param timelock UNIX epoch seconds time that the lock expires at. * unlocks can be made after this time. * @return Id Id of the new HTLC. This is needed for subsequent * calls. */ function lock( bytes32 Id, bytes32 hashlock, uint256 timelock, address srcReceiver, string memory srcAsset, string memory dstChain, string memory dstAddress, string memory dstAsset, address messenger, uint256 amount, address tokenContract ) external returns (bytes32) { if (timelock <= block.timestamp) { revert NotFutureTimelock(); } if (amount == 0) { revert FundsNotSent(); } if (hasHTLC(Id)) { revert HTLCAlreadyExists(); } IERC20 token = IERC20(tokenContract); if (token.balanceOf(msg.sender) < amount) { revert InsufficientBalance(); } if (token.allowance(msg.sender, address(this)) < amount) { revert NoAllowance(); } token.safeTransferFrom(msg.sender, address(this), amount); contracts[Id] = HTLC( dstAddress, dstChain, dstAsset, srcAsset, payable(msg.sender), payable(srcReceiver), hashlock, timelock, amount, 0x0, tokenContract, messenger, false, false ); contractIds.push(Id); emit TokenLocked( Id, hashlock, dstChain, dstAddress, dstAsset, msg.sender, srcReceiver, srcAsset, amount, timelock, messenger, tokenContract ); if (messenger != address(0)) { uint256 codeSize; assembly { codeSize := extcodesize(messenger) } if (codeSize > 0) { try IMessenger(messenger).notify( Id, hashlock, dstChain, dstAsset, dstAddress, srcAsset, payable(msg.sender), payable(srcReceiver), amount, timelock, tokenContract ) { // Notify successful } catch Error(string memory reason) { revert(reason); } catch (bytes memory lowLevelData) { emit LowLevelErrorOccurred(lowLevelData); revert('IMessenger notify failed'); } } else { revert NoMessenger(); } } return Id; } /** * @dev Called by the srcReceiver once they know the secret of the hashlock. * This will transfer the locked funds to their address. * * @param Id Id of the HTLC. * @param secret sha256(secret) should equal the contract hashlock. * @return bool true on success */ function redeem(bytes32 Id, uint256 secret) external _exists(Id) returns (bool) { HTLC storage htlc = contracts[Id]; if (htlc.hashlock != sha256(abi.encodePacked(secret))) revert HashlockNotMatch(); if (htlc.redeemed) revert AlreadyRedeemed(); if (htlc.refunded) revert AlreadyRefunded(); htlc.secret = secret; htlc.redeemed = true; IERC20(htlc.tokenContract).safeTransfer(htlc.srcReceiver, htlc.amount); emit TokenRedeemed(Id, msg.sender); return true; } /** * @dev Called by the sender if there was no redeem AND the time lock has * expired. This will refund the contract amount. * @param Id Id of HTLC to refund from. * @return bool true on success */ function refund(bytes32 Id) external _exists(Id) returns (bool) { HTLC storage htlc = contracts[Id]; if (htlc.refunded) revert AlreadyRefunded(); if (htlc.redeemed) revert AlreadyRedeemed(); if (htlc.timelock > block.timestamp) revert NotPassedTimelock(); htlc.refunded = true; IERC20(htlc.tokenContract).safeTransfer(htlc.sender, htlc.amount); emit TokenRefunded(Id); return true; } /** * @dev Get contract details. * @param Id HTLC contract id */ function getDetails(bytes32 Id) external view returns (HTLC memory) { return contracts[Id]; } /** * @dev Check if there is a contract with a given id. * @param Id Id into contracts mapping. */ function hasHTLC(bytes32 Id) internal view returns (bool exists) { exists = (contracts[Id].sender != address(0)); } function getContracts(address senderAddr) public view returns (bytes32[] memory) { uint count = 0; for (uint i = 0; i < contractIds.length; i++) { HTLC memory htlc = contracts[contractIds[i]]; if (htlc.sender == senderAddr) { count++; } } bytes32[] memory result = new bytes32[](count); uint j = 0; for (uint i = 0; i < contractIds.length; i++) { if (contracts[contractIds[i]].sender == senderAddr) { result[j] = contractIds[i]; j++; } } return result; } function hashDomain(EIP712Domain memory domain) private pure returns (bytes32) { return keccak256( abi.encode( keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)'), keccak256(bytes(domain.name)), keccak256(bytes(domain.version)), domain.chainId, domain.verifyingContract, domain.salt ) ); } // Hashes an EIP712 message struct function hashMessage(addLockMsg memory message) private pure returns (bytes32) { return keccak256( abi.encode( keccak256('addLockMsg(bytes32 Id,bytes32 hashlock,uint256 timelock)'), message.Id, message.hashlock, message.timelock ) ); } // Verifies an EIP712 message signature function verifyMessage(addLockMsg memory message, uint8 v, bytes32 r, bytes32 s) private view returns (bool) { bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, hashMessage(message))); address recoveredAddress = ecrecover(digest, v, r, s); return (recoveredAddress == contracts[message.Id].sender); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
@openzeppelin/contracts/utils/cryptography/ECDSA.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS, s); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
Compiler Settings
{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"error","name":"AddressEmptyCode","inputs":[{"type":"address","name":"target","internalType":"address"}]},{"type":"error","name":"AddressInsufficientBalance","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"error","name":"AlreadyLocked","inputs":[]},{"type":"error","name":"AlreadyRedeemed","inputs":[]},{"type":"error","name":"AlreadyRefunded","inputs":[]},{"type":"error","name":"FailedInnerCall","inputs":[]},{"type":"error","name":"FundsNotSent","inputs":[]},{"type":"error","name":"HTLCAlreadyExists","inputs":[]},{"type":"error","name":"HTLCNotExists","inputs":[]},{"type":"error","name":"HashlockAlreadySet","inputs":[]},{"type":"error","name":"HashlockNotMatch","inputs":[]},{"type":"error","name":"IncorrectData","inputs":[]},{"type":"error","name":"InsufficientBalance","inputs":[]},{"type":"error","name":"InvalidSigniture","inputs":[]},{"type":"error","name":"LockAlreadyExists","inputs":[]},{"type":"error","name":"NoAllowance","inputs":[]},{"type":"error","name":"NoMessenger","inputs":[]},{"type":"error","name":"NotFutureTimelock","inputs":[]},{"type":"error","name":"NotPassedTimelock","inputs":[]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"event","name":"LowLevelErrorOccurred","inputs":[{"type":"bytes","name":"lowLevelData","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"TokenCommitted","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32","indexed":true},{"type":"string[]","name":"hopChains","internalType":"string[]","indexed":false},{"type":"string[]","name":"hopAssets","internalType":"string[]","indexed":false},{"type":"string[]","name":"hopAddresses","internalType":"string[]","indexed":false},{"type":"string","name":"dstChain","internalType":"string","indexed":false},{"type":"string","name":"dstAddress","internalType":"string","indexed":false},{"type":"string","name":"dstAsset","internalType":"string","indexed":false},{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"address","name":"srcReceiver","internalType":"address","indexed":true},{"type":"string","name":"srcAsset","internalType":"string","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"timelock","internalType":"uint256","indexed":false},{"type":"address","name":"messenger","internalType":"address","indexed":false},{"type":"address","name":"tokenContract","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"TokenLockAdded","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32","indexed":false},{"type":"address","name":"messenger","internalType":"address","indexed":false},{"type":"bytes32","name":"hashlock","internalType":"bytes32","indexed":false},{"type":"uint256","name":"timelock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TokenLocked","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"hashlock","internalType":"bytes32","indexed":false},{"type":"string","name":"dstChain","internalType":"string","indexed":false},{"type":"string","name":"dstAddress","internalType":"string","indexed":false},{"type":"string","name":"dstAsset","internalType":"string","indexed":false},{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"address","name":"srcReceiver","internalType":"address","indexed":true},{"type":"string","name":"srcAsset","internalType":"string","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"timelock","internalType":"uint256","indexed":false},{"type":"address","name":"messenger","internalType":"address","indexed":false},{"type":"address","name":"tokenContract","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"TokenRedeemed","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32","indexed":true},{"type":"address","name":"redeemAddress","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"TokenRefunded","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"addLock","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32"},{"type":"bytes32","name":"hashlock","internalType":"bytes32"},{"type":"uint256","name":"timelock","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"addLockSig","inputs":[{"type":"tuple","name":"message","internalType":"struct LayerswapV8ERC20.addLockMsg","components":[{"type":"bytes32","name":"Id","internalType":"bytes32"},{"type":"bytes32","name":"hashlock","internalType":"bytes32"},{"type":"uint256","name":"timelock","internalType":"uint256"}]},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"Id","internalType":"bytes32"}],"name":"commit","inputs":[{"type":"string[]","name":"hopChains","internalType":"string[]"},{"type":"string[]","name":"hopAssets","internalType":"string[]"},{"type":"string[]","name":"hopAddresses","internalType":"string[]"},{"type":"string","name":"dstChain","internalType":"string"},{"type":"string","name":"dstAsset","internalType":"string"},{"type":"string","name":"dstAddress","internalType":"string"},{"type":"string","name":"srcAsset","internalType":"string"},{"type":"address","name":"srcReceiver","internalType":"address"},{"type":"uint256","name":"timelock","internalType":"uint256"},{"type":"address","name":"messenger","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"tokenContract","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32[]","name":"","internalType":"bytes32[]"}],"name":"getContracts","inputs":[{"type":"address","name":"senderAddr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct LayerswapV8ERC20.HTLC","components":[{"type":"string","name":"dstAddress","internalType":"string"},{"type":"string","name":"dstChain","internalType":"string"},{"type":"string","name":"dstAsset","internalType":"string"},{"type":"string","name":"srcAsset","internalType":"string"},{"type":"address","name":"sender","internalType":"address payable"},{"type":"address","name":"srcReceiver","internalType":"address payable"},{"type":"bytes32","name":"hashlock","internalType":"bytes32"},{"type":"uint256","name":"timelock","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"secret","internalType":"uint256"},{"type":"address","name":"tokenContract","internalType":"address"},{"type":"address","name":"messenger","internalType":"address"},{"type":"bool","name":"redeemed","internalType":"bool"},{"type":"bool","name":"refunded","internalType":"bool"}]}],"name":"getDetails","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"lock","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32"},{"type":"bytes32","name":"hashlock","internalType":"bytes32"},{"type":"uint256","name":"timelock","internalType":"uint256"},{"type":"address","name":"srcReceiver","internalType":"address"},{"type":"string","name":"srcAsset","internalType":"string"},{"type":"string","name":"dstChain","internalType":"string"},{"type":"string","name":"dstAddress","internalType":"string"},{"type":"string","name":"dstAsset","internalType":"string"},{"type":"address","name":"messenger","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"tokenContract","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"redeem","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32"},{"type":"uint256","name":"secret","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"refund","inputs":[{"type":"bytes32","name":"Id","internalType":"bytes32"}]}]
Contract Creation Code
0x608060409080825234620001885760001943014381116200017257406003556000908160045560208101906b098c2f2cae4e6eec2e040ac760a31b8252600c81526200004b816200018d565b51902082516001600160401b039390919060a08301858111848210176200015e578082526200007a816200018d565b601081526f04c6179657273776170563845524332360841b60c08501528352805192620000a7846200018d565b600184526020840190603160f81b825284602082015246838201523060608201528360808201525160208151910120935190209181519260208401947fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647286528385015260608401524660808401523060a084015260c083015260c0825260e0820194828610908611176200014a5784905251902090556126bd9081620001c08239f35b634e487b7160e01b84526041600452602484fd5b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b600052601160045260246000fd5b600080fd5b604081019081106001600160401b03821117620001a957604052565b634e487b7160e01b600052604160045260246000fdfe610140604052600436101561001357600080fd5b600061012052610120513560e01c8063213fe2b714611c21578063513388c314611105578063673da15414610fbc5780637249fbb614610ea657806375ad188114610e4c578063984bc70014610bc5578063beeaa61514610b3b5763dcc19d591461007d57600080fd5b34610b3457610180366003190112610b34576004356001600160401b038111610b34576100ae903690600401611fa3565b60e0526024356001600160401b038111610b34576100d0903690600401611fa3565b6044356001600160401b038111610b34576100ef903690600401611fa3565b906064356001600160401b038111610b345761010f903690600401611efd565b6084356001600160401b038111610b345761012e903690600401611efd565b60a4356001600160401b038111610b345761014d903690600401611efd565b9060c4356001600160401b038111610b345761016d903690600401611efd565b60e435939092906001600160a01b0385168503610ab1576101243560c08190526001600160a01b0381169003610ab15761016435966001600160a01b0388168803610ab1576101443515610b225742610104351115610b10576040516370a0823160e01b81523360048201526020816024816001600160a01b038d165afa908115610abe576101205191610ade575b506101443511610acc57604051636eb1769f60e11b81523360048201523060248201526020816044816001600160a01b038d165afa908115610abe576101205191610a87575b506101443511610a75576102646101443530336001600160a01b038c1661251e565b600454600181018111610a5b578060018092016004550160035418610100526102a861010051600052600160205260018060a01b0360046040600020015416151590565b610a49576102b861010051612173565b6040516102c481611e6f565b83815282602082015284604082015285606082015233608082015260018060a01b03871660a08201526101205160c08201526101043560e0820152610144356101008201526101205161012082015260018060a01b03891661014082015260018060a01b0360c0511661016082015261012051610180820152610120516101a08201526101005161012051526001602052604061012051209080518051906001600160401b0382116107aa57819061037c855461206f565b601f81116109f5575b506020906001601f841114610983576101205192610978575b50508160011b916000199060031b1c19161782555b60208101518051906001600160401b0382116107aa5781906103d8600186015461206f565b601f8111610921575b506020906001601f8411146108a957610120519261089e575b50508160011b916000199060031b1c19161760018301555b60408101518051906001600160401b0382116107aa578190610437600286015461206f565b601f8111610847575b506020906001601f8411146107cf5761012051926107c4575b50508160011b916000199060031b1c19161760028301555b606081015160a05260a051516080526001600160401b03608051116107aa5761049d600383015461206f565b601f811161075b575b5061012051506020601f608051116001146106be5793610609610625946105d061063398956101a0610617966106419d9c9a61012051906080516106b1575b5060805160011b9060001960805160031b1c19161760038401555b60808101516004840180546001600160a01b03199081166001600160a01b039384161790915560a083810151600587018054841691851691909117905560c0840151600687015560e0840151600787015561010084015160088701556101208401516009870155610140840151600a8701805490931690841617909155610160830151600b9590950180546101808501516001600160a81b03199091169690931695909517911515901b60ff60a01b161783555b0151815460ff60a81b191690151560a81b60ff60a81b16179055565b6105fb604051809d61016082526105ed610160830160e0516124c1565b9160208184039101526124c1565b8c810360408e0152906124c1565b908a820360608c0152611f67565b9088820360808a0152611f67565b9086820360a0880152611f67565b9084820360c0860152611f67565b6101443560e0840152610104356101008085019190915260c0516001600160a01b0390811661012086015294851661014085015251919093169233927f39aa705c04023f1efa19bdc0767577e0092740a3754926bb71c98869b20000239181900390a46020604051610100518152f35b905060a0510151386104e5565b6003830161012051526020610120512090610120515b608051601f191681106107415750610625946105d061063398956101a0610617966106419d9c9a9661060996608051601f196080511610610724575b50506001608051811b016003840155610500565b60a051015160001960f860805160031b161c191690553880610710565b90916020600181928560a0510151815501930191016106d4565b60038301610120515260206101205120601f6080510160051c81016020608051106107a3575b601f830160051c820181106107975750506104a6565b60008155600101610781565b5080610781565b634e487b7160e01b61012051526041600452602461012051fd5b015190503880610459565b9250600285016101205152602061012051209061012051935b601f198416851061082c576001945083601f19811610610813575b505050811b016002830155610471565b015160001960f88460031b161c19169055388080610803565b818101518355602094850194600190930192909101906107e8565b90915060028501610120515260206101205120601f840160051c810160208510610897575b90849392915b601f830160051c82018110610888575050610440565b60008155859450600101610872565b508061086c565b0151905038806103fa565b9250600185016101205152602061012051209061012051935b601f1984168510610906576001945083601f198116106108ed575b505050811b016001830155610412565b015160001960f88460031b161c191690553880806108dd565b818101518355602094850194600190930192909101906108c2565b90915060018501610120515260206101205120601f840160051c810160208510610971575b90849392915b601f830160051c820181106109625750506103e1565b6000815585945060010161094c565b5080610946565b01519050388061039e565b9250846101205152602061012051209061012051935b601f19841685106109da576001945083601f198116106109c1575b505050811b0182556103b3565b015160001960f88460031b161c191690553880806109b4565b81810151835560209485019460019093019290910190610999565b90915084610120515260206101205120601f840160051c810160208510610a42575b90849392915b601f830160051c82018110610a33575050610385565b60008155859450600101610a1d565b5080610a17565b604051630ed8e66b60e21b8152600490fd5b634e487b7160e01b61012051526011600452602461012051fd5b604051631d7eb35960e31b8152600490fd5b90506020813d602011610ab6575b81610aa260209383611ec1565b81010312610ab1575138610242565b600080fd5b3d9150610a95565b6040513d61012051823e3d90fd5b604051631e9acf1760e31b8152600490fd5b90506020813d602011610b08575b81610af960209383611ec1565b81010312610ab15751386101fc565b3d9150610aec565b604051633dd8280960e21b8152600490fd5b604051636f8ab38d60e11b8152600490fd5b6101205180fd5b34610b3457366003190160c08112610b3457606013610b3457604051606081018181106001600160401b03821117610baf576040526004358152602435602082015260443560408201526064359060ff82168203610b3457602091610ba79160a4359160843591612350565b604051908152f35b634e487b7160e01b600052604160045260246000fd5b34610b34576020366003190112610b3457604051610be281611e6f565b6060815260606020820152606060408201526060808201526101205160808201526101205160a08201526101205160c08201526101205160e082015261012051610100820152610120516101208201526101205161014082015261012051610160820152610120516101808201526101a061012051910152600435610120515260016020526040610120512060ff600b60405192610c7f84611e6f565b610c88816120a9565b8452610c96600182016120a9565b6020850152610ca7600282016120a9565b6040850152610cb8600382016120a9565b606085015260018060a01b03600482015416608085015260018060a01b0360058201541660a0850152600681015460c0850152600781015460e08501526008810154610100850152600981015461012085015260018060a01b03600a82015416610140850152015460018060a01b038116610160840152818160a01c16151561018084015260a81c1615156101a08201526040518091602082526101a0610db8610d7083516101c060208701526101e0860190611f67565b610da3610d8f602086015192601f1993848983030160408a0152611f67565b604086015183888303016060890152611f67565b90606085015190868303016080870152611f67565b9160018060a01b0360808201511660a085015260018060a01b0360a08201511660c085015260c081015160e085015260e081015161010085015261010081015161012085015261012081015161014085015260018060a01b036101408201511661016085015260018060a01b0361016082015116610180850152610180810151151582850152015115156101c08301520390f35b34610b34576060366003190112610b3457600480356000818152600160205260409020909101546001600160a01b031615610e9457610ba76020916044359060243590612250565b6040516368cadcd560e01b8152600490fd5b34610b34576020366003190112610b3457600480356000818152600160205260409020909101546001600160a01b031615610e945780610120515260016020526040610120512090600b82019182549060ff8260a81c16610faa5760ff8260a01c16610f985760078101544210610f865760ff60a81b19909116600160a81b17909255600a8201546004830154600890930154602093610f52926001600160a01b039182169116612565565b604051907f92b8d387b3b4732fb701784c5e553091c36997ec127e0865a7c990bc62cc73826101205161012051a260018152f35b60405163b3f711f760e01b8152600490fd5b6040516306d3830f60e21b8152600490fd5b60405163542f378d60e11b8152600490fd5b34610b34576040366003190112610b34576004803560008181526001602052604090209091015460243591906001600160a01b031615610e94578061012051526020916001835260406101205120600681015460405185810184815286825260408201908282106001600160401b03831117610baf5787926110478392836040528251948591611f44565b61012051928101039060025afa15610abe576101205151036110f357600b8101805460ff8160a01c16610f985760ff8160a81c16610faa57600983019390935560ff60a01b19909216600160a01b17909155600a81015460058201546008909201546110c19290916001600160a01b039182169116612565565b7f0f7c50d316885e5d3719752f760bbbbf7d56363501e721eea4d913e255b632e482604051338152a260405160018152f35b60405163e73bcb3560e01b8152600490fd5b34610b3457610160366003190112610b34576064356001600160a01b0381168103610b34576084356001600160401b038111610b3457611149903690600401611efd565b9060a4356001600160401b038111610b3457611169903690600401611efd565b9160c4356001600160401b038111610b3457611189903690600401611efd565b6001600160401b0360e43511610b34576111a83660e435600401611efd565b91610104356001600160a01b0381169003610b345761014435926001600160a01b0384168403610b3457426044351115610b10576101243515610b225760048035600090815260016020526040902001546001600160a01b0316610a49576040516370a0823160e01b81523360048201526020816024816001600160a01b0389165afa908115610abe576101205191611bef575b506101243511610acc57604051636eb1769f60e11b81523360048201523060248201526020816044816001600160a01b0389165afa908115610abe576101205191611bbd575b506101243511610a75576112a46101243530336001600160a01b03881661251e565b6040516112b081611e6f565b83815260208082018890526040808301849052606083018590523360808401526001600160a01b0388811660a085015260243560c085015260443560e08501526101243561010085015261012080518186015288821661014086015261010435909116610160850152805161018085015280516101a085015280516004359052600190925290512081518051919291906001600160401b0382116107aa57819061135a855461206f565b601f8111611b67575b506020906001601f841114611af5576101205192611aea575b50508160011b916000199060031b1c19161782555b60208101518051906001600160401b0382116107aa5781906113b6600186015461206f565b601f8111611a91575b506020906001601f841114611a19576101205192611a0e575b50508160011b916000199060031b1c19161760018301555b60408101518051906001600160401b0382116107aa578190611415600286015461206f565b601f81116119b5575b506020906001601f84111461193d576101205192611932575b50508160011b916000199060031b1c19161760028301555b60608101518051906001600160401b0382116107aa57611472600385015461206f565b601f81116118e5575b506020906001601f84111461186757918061156f9594926101a094610120519261185c575b50508160011b916000199060031b1c191617600384015560808101516004840180546001600160a01b03199081166001600160a01b039384161790915560a083810151600587018054841691851691909117905560c0840151600687015560e0840151600787015561010084015160088701556101208401516009870155610140840151600a8701805490931690841617909155610160830151600b9590950180546101808501516001600160a81b03199091169690931695909517911515901b60ff60a01b161783556105b4565b61157a600435612173565b604051602435815261012060208201526115cb6115bd6115af6115a161012085018b611f67565b848103604086015287611f67565b838103606085015284611f67565b828103608084015284611f67565b6101243560a083015260443560c08301526001600160a01b0361010435811660e08401528681166101008401528716913391600435917fbfd96a3bd675298365bdbc061608afe07e329a620d017bce9992f7c331de91c99181900390a4610104356001600160a01b0316611646575b60206040516004358152f35b610104353b1561184a57610104356001600160a01b03163b15610b34576116d8926116b46116c6926116a260405199635e863a5b60e01b8b5260043560048c015260243560248c015261016060448c01526101648b0190611f67565b8981036003190160648b015290611f67565b87810360031901608489015290611f67565b8581036003190160a487015290611f67565b913360c485015260018060a01b031660e48401526101243561010484015260443561012484015260018060a01b03166101448301528180610120519203816101205160018060a01b0361010435165af19081611833575b50611828576101205160033d1161180f575b6308c379a0146117d4575b7f7b5bdb2f87e8e20f082bba9be5f7e870728f9bba18540559ccdcdcbb1fc5a08761178c611778612220565b604051918291602083526020830190611f67565b0390a160405162461bcd60e51b815260206004820152601860248201527f494d657373656e676572206e6f74696679206661696c656400000000000000006044820152606490fd5b6117dc6121af565b806117e7575061174c565b60405162461bcd60e51b81526020600482015290819061180b906024830190611f67565b0390fd5b50600461012051610120513e610120515160e01c611741565b80808080808061163a565b6001600160401b0381116107aa576040528161172f565b604051633127e9d760e11b8152600490fd5b015190508c806114a0565b906003850161012051526020610120512091610120515b601f19851681106118cd57509261156f9594926001926101a09583601f198116106118b4575b505050811b016003840155610500565b015160001960f88460031b161c191690558c80806118a4565b9192602060018192868501518155019401920161187e565b60038501610120515260206101205120601f840160051c81016020851061192b575b601f830160051c8201811061191d57505061147b565b610120518155600101611907565b5080611907565b015190508a80611437565b9250600285016101205152602061012051209061012051935b601f198416851061199a576001945083601f19811610611981575b505050811b01600283015561144f565b015160001960f88460031b161c191690558a8080611971565b81810151835560209485019460019093019290910190611956565b90915060028501610120515260206101205120601f840160051c810160208510611a07575b90849392915b601f830160051c820181106119f657505061141e565b6101205181558594506001016119e0565b50806119da565b015190508a806113d8565b9250600185016101205152602061012051209061012051935b601f1984168510611a76576001945083601f19811610611a5d575b505050811b0160018301556113f0565b015160001960f88460031b161c191690558a8080611a4d565b81810151835560209485019460019093019290910190611a32565b90915060018501610120515260206101205120601f840160051c810160208510611ae3575b90849392915b601f830160051c82018110611ad25750506113bf565b610120518155859450600101611abc565b5080611ab6565b015190508a8061137c565b9250846101205152602061012051209061012051935b601f1984168510611b4c576001945083601f19811610611b33575b505050811b018255611391565b015160001960f88460031b161c191690558a8080611b26565b81810151835560209485019460019093019290910190611b0b565b90915084610120515260206101205120601f840160051c810160208510611bb6575b90849392915b601f830160051c82018110611ba5575050611363565b610120518155859450600101611b8f565b5080611b89565b90506020813d602011611be7575b81611bd860209383611ec1565b81010312610b34575187611282565b3d9150611bcb565b90506020813d602011611c19575b81611c0a60209383611ec1565b81010312610b3457518761123c565b3d9150611bfd565b34610b3457602080600319360112610b34576001600160a01b0360043581811690819003610b34576101205160028054919290835b838110611d66575050611c81611c6b84611f8c565b93611c796040519586611ec1565b808552611f8c565b8386019490601f19013686376101205191825b848110611ce0578786886040519283928184019082855251809152604084019291610120515b828110611cc957505050500390f35b835185528695509381019392810192600101611cba565b611ce981612022565b9054600391821b1c610120515260018952828460046040610120512001541614611d17575b50600101611c94565b611d22829592612022565b9054911b1c8651821015611d4c5781611d45918a60019460051b8a01015261214e565b9390611d0e565b634e487b7160e01b61012051526032600452602461012051fd5b82611d7082612022565b9054600391821b1c61012051526001808a52611dca6040610120512092611daf60405193611d9d85611e6f565b611da6866120a9565b855285016120a9565b8c840152611dbe8785016120a9565b604084015283016120a9565b60608201526101a08960048401541692836080840152600b8b6005830154169160a09283860152600681015460c0860152600781015460e0860152600881015461010086015260098101546101208601528c600a82015416610140860152015480918c821661016086015260ff9283911c16151561018085015260a81c16151591015214611e5b575b600101611c56565b93611e6760019161214e565b949050611e53565b6101c081019081106001600160401b03821117610baf57604052565b60a081019081106001600160401b03821117610baf57604052565b608081019081106001600160401b03821117610baf57604052565b90601f801991011681019081106001600160401b03821117610baf57604052565b6001600160401b038111610baf57601f01601f191660200190565b81601f82011215610ab157803590611f1482611ee2565b92611f226040519485611ec1565b82845260208383010111610ab157816000926020809301838601378301015290565b60005b838110611f575750506000910152565b8181015183820152602001611f47565b90602091611f8081518092818552858086019101611f44565b601f01601f1916010190565b6001600160401b038111610baf5760051b60200190565b81601f82011215610ab157803591602091611fbd84611f8c565b93611fcb6040519586611ec1565b808552838086019160051b83010192808411610ab157848301915b848310611ff65750505050505090565b82356001600160401b038111610ab157869161201784848094890101611efd565b815201920191611fe6565b6002548110156120595760026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0190600090565b634e487b7160e01b600052603260045260246000fd5b90600182811c9216801561209f575b602083101461208957565b634e487b7160e01b600052602260045260246000fd5b91607f169161207e565b906040519182600082546120bc8161206f565b9081845260209460019160018116908160001461212c57506001146120ed575b5050506120eb92500383611ec1565b565b600090815285812095935091905b8183106121145750506120eb93508201013880806120dc565b855488840185015294850194879450918301916120fb565b925050506120eb94925060ff191682840152151560051b8201013880806120dc565b600019811461215d5760010190565b634e487b7160e01b600052601160045260246000fd5b60025468010000000000000000811015610baf578060016121979201600255612022565b819291549060031b91821b91600019901b1916179055565b600060443d1061220c57604051600319913d83016004833e81516001600160401b03918282113d60248401111761220f57818401948551938411612217573d8501016020848701011161220f575061220c92910160200190611ec1565b90565b949350505050565b50949350505050565b3d1561224b573d9061223182611ee2565b9161223f6040519384611ec1565b82523d6000602084013e565b606090565b9182600052600160205260409182600020600b810154600160ff8260a81c1615151461233f574284111561232e576004820154336001600160a01b0391821614918215612321575b50508015612318575b15612307576006810180546122f65782905560070182905582518481523260208201529283015260608201527fc444cd90bb87c0733c799513fba068d7635632ccbd9dffb878caa3d2b278397190608090a190565b8451636e6870d560e01b8152600490fd5b8351631d7eb35960e31b8152600490fd5b503033146122a1565b9091501633143880612298565b8451633dd8280960e21b8152600490fd5b845163542f378d60e11b8152600490fd5b9060009182549080519584608060209586938486019960ff8b51986040809e818b019b8c518351918c8301937f45906d7e53feffed68a8db947e0f8839b1ee252166e8eead8c6ab3951a09c7b68552858401526060830152898201528881526123b881611e8b565b5190209051908982019261190160f01b845260228301526042820152604281526123e181611ea6565b519020938d5194855216868401528b830152606082015282805260015afa156124b75783518151855260018452868520600401546001600160a01b039182169116036124a6575193519051908551946375ad188160e01b8652600486015260248501526044840152808360648185305af193841561249b57508193612467575b50505090565b9091809350813d8311612494575b61247f8183611ec1565b81010312612491575051388080612461565b80fd5b503d612475565b51913d9150823e3d90fd5b855163553470cf60e01b8152600490fd5b85513d85823e3d90fd5b90808251908181526020809101926020808460051b8301019501936000915b8483106124f05750505050505090565b909192939495848061250e600193601f198682030187528a51611f67565b98019301930191949392906124e0565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526120eb9161256082611e8b565b61259e565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526120eb9161256082611ea6565b60018060a01b0316906125c8600080836020829551910182875af16125c1612220565b9084612624565b9081519182151592836125f8575b5050506125e05750565b60249060405190635274afe760e01b82526004820152fd5b81929350906020918101031261262057602001519081159182150361249157503880806125d6565b5080fd5b9061264b575080511561263957805190602001fd5b604051630a12f52160e11b8152600490fd5b8151158061267e575b61265c575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561265456fea26469706673582212200dee20c76df399efb991db946b42c9cd9441740d349e196ac4b05a0ce9a718e864736f6c63430008170033
Deployed ByteCode
0x610140604052600436101561001357600080fd5b600061012052610120513560e01c8063213fe2b714611c21578063513388c314611105578063673da15414610fbc5780637249fbb614610ea657806375ad188114610e4c578063984bc70014610bc5578063beeaa61514610b3b5763dcc19d591461007d57600080fd5b34610b3457610180366003190112610b34576004356001600160401b038111610b34576100ae903690600401611fa3565b60e0526024356001600160401b038111610b34576100d0903690600401611fa3565b6044356001600160401b038111610b34576100ef903690600401611fa3565b906064356001600160401b038111610b345761010f903690600401611efd565b6084356001600160401b038111610b345761012e903690600401611efd565b60a4356001600160401b038111610b345761014d903690600401611efd565b9060c4356001600160401b038111610b345761016d903690600401611efd565b60e435939092906001600160a01b0385168503610ab1576101243560c08190526001600160a01b0381169003610ab15761016435966001600160a01b0388168803610ab1576101443515610b225742610104351115610b10576040516370a0823160e01b81523360048201526020816024816001600160a01b038d165afa908115610abe576101205191610ade575b506101443511610acc57604051636eb1769f60e11b81523360048201523060248201526020816044816001600160a01b038d165afa908115610abe576101205191610a87575b506101443511610a75576102646101443530336001600160a01b038c1661251e565b600454600181018111610a5b578060018092016004550160035418610100526102a861010051600052600160205260018060a01b0360046040600020015416151590565b610a49576102b861010051612173565b6040516102c481611e6f565b83815282602082015284604082015285606082015233608082015260018060a01b03871660a08201526101205160c08201526101043560e0820152610144356101008201526101205161012082015260018060a01b03891661014082015260018060a01b0360c0511661016082015261012051610180820152610120516101a08201526101005161012051526001602052604061012051209080518051906001600160401b0382116107aa57819061037c855461206f565b601f81116109f5575b506020906001601f841114610983576101205192610978575b50508160011b916000199060031b1c19161782555b60208101518051906001600160401b0382116107aa5781906103d8600186015461206f565b601f8111610921575b506020906001601f8411146108a957610120519261089e575b50508160011b916000199060031b1c19161760018301555b60408101518051906001600160401b0382116107aa578190610437600286015461206f565b601f8111610847575b506020906001601f8411146107cf5761012051926107c4575b50508160011b916000199060031b1c19161760028301555b606081015160a05260a051516080526001600160401b03608051116107aa5761049d600383015461206f565b601f811161075b575b5061012051506020601f608051116001146106be5793610609610625946105d061063398956101a0610617966106419d9c9a61012051906080516106b1575b5060805160011b9060001960805160031b1c19161760038401555b60808101516004840180546001600160a01b03199081166001600160a01b039384161790915560a083810151600587018054841691851691909117905560c0840151600687015560e0840151600787015561010084015160088701556101208401516009870155610140840151600a8701805490931690841617909155610160830151600b9590950180546101808501516001600160a81b03199091169690931695909517911515901b60ff60a01b161783555b0151815460ff60a81b191690151560a81b60ff60a81b16179055565b6105fb604051809d61016082526105ed610160830160e0516124c1565b9160208184039101526124c1565b8c810360408e0152906124c1565b908a820360608c0152611f67565b9088820360808a0152611f67565b9086820360a0880152611f67565b9084820360c0860152611f67565b6101443560e0840152610104356101008085019190915260c0516001600160a01b0390811661012086015294851661014085015251919093169233927f39aa705c04023f1efa19bdc0767577e0092740a3754926bb71c98869b20000239181900390a46020604051610100518152f35b905060a0510151386104e5565b6003830161012051526020610120512090610120515b608051601f191681106107415750610625946105d061063398956101a0610617966106419d9c9a9661060996608051601f196080511610610724575b50506001608051811b016003840155610500565b60a051015160001960f860805160031b161c191690553880610710565b90916020600181928560a0510151815501930191016106d4565b60038301610120515260206101205120601f6080510160051c81016020608051106107a3575b601f830160051c820181106107975750506104a6565b60008155600101610781565b5080610781565b634e487b7160e01b61012051526041600452602461012051fd5b015190503880610459565b9250600285016101205152602061012051209061012051935b601f198416851061082c576001945083601f19811610610813575b505050811b016002830155610471565b015160001960f88460031b161c19169055388080610803565b818101518355602094850194600190930192909101906107e8565b90915060028501610120515260206101205120601f840160051c810160208510610897575b90849392915b601f830160051c82018110610888575050610440565b60008155859450600101610872565b508061086c565b0151905038806103fa565b9250600185016101205152602061012051209061012051935b601f1984168510610906576001945083601f198116106108ed575b505050811b016001830155610412565b015160001960f88460031b161c191690553880806108dd565b818101518355602094850194600190930192909101906108c2565b90915060018501610120515260206101205120601f840160051c810160208510610971575b90849392915b601f830160051c820181106109625750506103e1565b6000815585945060010161094c565b5080610946565b01519050388061039e565b9250846101205152602061012051209061012051935b601f19841685106109da576001945083601f198116106109c1575b505050811b0182556103b3565b015160001960f88460031b161c191690553880806109b4565b81810151835560209485019460019093019290910190610999565b90915084610120515260206101205120601f840160051c810160208510610a42575b90849392915b601f830160051c82018110610a33575050610385565b60008155859450600101610a1d565b5080610a17565b604051630ed8e66b60e21b8152600490fd5b634e487b7160e01b61012051526011600452602461012051fd5b604051631d7eb35960e31b8152600490fd5b90506020813d602011610ab6575b81610aa260209383611ec1565b81010312610ab1575138610242565b600080fd5b3d9150610a95565b6040513d61012051823e3d90fd5b604051631e9acf1760e31b8152600490fd5b90506020813d602011610b08575b81610af960209383611ec1565b81010312610ab15751386101fc565b3d9150610aec565b604051633dd8280960e21b8152600490fd5b604051636f8ab38d60e11b8152600490fd5b6101205180fd5b34610b3457366003190160c08112610b3457606013610b3457604051606081018181106001600160401b03821117610baf576040526004358152602435602082015260443560408201526064359060ff82168203610b3457602091610ba79160a4359160843591612350565b604051908152f35b634e487b7160e01b600052604160045260246000fd5b34610b34576020366003190112610b3457604051610be281611e6f565b6060815260606020820152606060408201526060808201526101205160808201526101205160a08201526101205160c08201526101205160e082015261012051610100820152610120516101208201526101205161014082015261012051610160820152610120516101808201526101a061012051910152600435610120515260016020526040610120512060ff600b60405192610c7f84611e6f565b610c88816120a9565b8452610c96600182016120a9565b6020850152610ca7600282016120a9565b6040850152610cb8600382016120a9565b606085015260018060a01b03600482015416608085015260018060a01b0360058201541660a0850152600681015460c0850152600781015460e08501526008810154610100850152600981015461012085015260018060a01b03600a82015416610140850152015460018060a01b038116610160840152818160a01c16151561018084015260a81c1615156101a08201526040518091602082526101a0610db8610d7083516101c060208701526101e0860190611f67565b610da3610d8f602086015192601f1993848983030160408a0152611f67565b604086015183888303016060890152611f67565b90606085015190868303016080870152611f67565b9160018060a01b0360808201511660a085015260018060a01b0360a08201511660c085015260c081015160e085015260e081015161010085015261010081015161012085015261012081015161014085015260018060a01b036101408201511661016085015260018060a01b0361016082015116610180850152610180810151151582850152015115156101c08301520390f35b34610b34576060366003190112610b3457600480356000818152600160205260409020909101546001600160a01b031615610e9457610ba76020916044359060243590612250565b6040516368cadcd560e01b8152600490fd5b34610b34576020366003190112610b3457600480356000818152600160205260409020909101546001600160a01b031615610e945780610120515260016020526040610120512090600b82019182549060ff8260a81c16610faa5760ff8260a01c16610f985760078101544210610f865760ff60a81b19909116600160a81b17909255600a8201546004830154600890930154602093610f52926001600160a01b039182169116612565565b604051907f92b8d387b3b4732fb701784c5e553091c36997ec127e0865a7c990bc62cc73826101205161012051a260018152f35b60405163b3f711f760e01b8152600490fd5b6040516306d3830f60e21b8152600490fd5b60405163542f378d60e11b8152600490fd5b34610b34576040366003190112610b34576004803560008181526001602052604090209091015460243591906001600160a01b031615610e94578061012051526020916001835260406101205120600681015460405185810184815286825260408201908282106001600160401b03831117610baf5787926110478392836040528251948591611f44565b61012051928101039060025afa15610abe576101205151036110f357600b8101805460ff8160a01c16610f985760ff8160a81c16610faa57600983019390935560ff60a01b19909216600160a01b17909155600a81015460058201546008909201546110c19290916001600160a01b039182169116612565565b7f0f7c50d316885e5d3719752f760bbbbf7d56363501e721eea4d913e255b632e482604051338152a260405160018152f35b60405163e73bcb3560e01b8152600490fd5b34610b3457610160366003190112610b34576064356001600160a01b0381168103610b34576084356001600160401b038111610b3457611149903690600401611efd565b9060a4356001600160401b038111610b3457611169903690600401611efd565b9160c4356001600160401b038111610b3457611189903690600401611efd565b6001600160401b0360e43511610b34576111a83660e435600401611efd565b91610104356001600160a01b0381169003610b345761014435926001600160a01b0384168403610b3457426044351115610b10576101243515610b225760048035600090815260016020526040902001546001600160a01b0316610a49576040516370a0823160e01b81523360048201526020816024816001600160a01b0389165afa908115610abe576101205191611bef575b506101243511610acc57604051636eb1769f60e11b81523360048201523060248201526020816044816001600160a01b0389165afa908115610abe576101205191611bbd575b506101243511610a75576112a46101243530336001600160a01b03881661251e565b6040516112b081611e6f565b83815260208082018890526040808301849052606083018590523360808401526001600160a01b0388811660a085015260243560c085015260443560e08501526101243561010085015261012080518186015288821661014086015261010435909116610160850152805161018085015280516101a085015280516004359052600190925290512081518051919291906001600160401b0382116107aa57819061135a855461206f565b601f8111611b67575b506020906001601f841114611af5576101205192611aea575b50508160011b916000199060031b1c19161782555b60208101518051906001600160401b0382116107aa5781906113b6600186015461206f565b601f8111611a91575b506020906001601f841114611a19576101205192611a0e575b50508160011b916000199060031b1c19161760018301555b60408101518051906001600160401b0382116107aa578190611415600286015461206f565b601f81116119b5575b506020906001601f84111461193d576101205192611932575b50508160011b916000199060031b1c19161760028301555b60608101518051906001600160401b0382116107aa57611472600385015461206f565b601f81116118e5575b506020906001601f84111461186757918061156f9594926101a094610120519261185c575b50508160011b916000199060031b1c191617600384015560808101516004840180546001600160a01b03199081166001600160a01b039384161790915560a083810151600587018054841691851691909117905560c0840151600687015560e0840151600787015561010084015160088701556101208401516009870155610140840151600a8701805490931690841617909155610160830151600b9590950180546101808501516001600160a81b03199091169690931695909517911515901b60ff60a01b161783556105b4565b61157a600435612173565b604051602435815261012060208201526115cb6115bd6115af6115a161012085018b611f67565b848103604086015287611f67565b838103606085015284611f67565b828103608084015284611f67565b6101243560a083015260443560c08301526001600160a01b0361010435811660e08401528681166101008401528716913391600435917fbfd96a3bd675298365bdbc061608afe07e329a620d017bce9992f7c331de91c99181900390a4610104356001600160a01b0316611646575b60206040516004358152f35b610104353b1561184a57610104356001600160a01b03163b15610b34576116d8926116b46116c6926116a260405199635e863a5b60e01b8b5260043560048c015260243560248c015261016060448c01526101648b0190611f67565b8981036003190160648b015290611f67565b87810360031901608489015290611f67565b8581036003190160a487015290611f67565b913360c485015260018060a01b031660e48401526101243561010484015260443561012484015260018060a01b03166101448301528180610120519203816101205160018060a01b0361010435165af19081611833575b50611828576101205160033d1161180f575b6308c379a0146117d4575b7f7b5bdb2f87e8e20f082bba9be5f7e870728f9bba18540559ccdcdcbb1fc5a08761178c611778612220565b604051918291602083526020830190611f67565b0390a160405162461bcd60e51b815260206004820152601860248201527f494d657373656e676572206e6f74696679206661696c656400000000000000006044820152606490fd5b6117dc6121af565b806117e7575061174c565b60405162461bcd60e51b81526020600482015290819061180b906024830190611f67565b0390fd5b50600461012051610120513e610120515160e01c611741565b80808080808061163a565b6001600160401b0381116107aa576040528161172f565b604051633127e9d760e11b8152600490fd5b015190508c806114a0565b906003850161012051526020610120512091610120515b601f19851681106118cd57509261156f9594926001926101a09583601f198116106118b4575b505050811b016003840155610500565b015160001960f88460031b161c191690558c80806118a4565b9192602060018192868501518155019401920161187e565b60038501610120515260206101205120601f840160051c81016020851061192b575b601f830160051c8201811061191d57505061147b565b610120518155600101611907565b5080611907565b015190508a80611437565b9250600285016101205152602061012051209061012051935b601f198416851061199a576001945083601f19811610611981575b505050811b01600283015561144f565b015160001960f88460031b161c191690558a8080611971565b81810151835560209485019460019093019290910190611956565b90915060028501610120515260206101205120601f840160051c810160208510611a07575b90849392915b601f830160051c820181106119f657505061141e565b6101205181558594506001016119e0565b50806119da565b015190508a806113d8565b9250600185016101205152602061012051209061012051935b601f1984168510611a76576001945083601f19811610611a5d575b505050811b0160018301556113f0565b015160001960f88460031b161c191690558a8080611a4d565b81810151835560209485019460019093019290910190611a32565b90915060018501610120515260206101205120601f840160051c810160208510611ae3575b90849392915b601f830160051c82018110611ad25750506113bf565b610120518155859450600101611abc565b5080611ab6565b015190508a8061137c565b9250846101205152602061012051209061012051935b601f1984168510611b4c576001945083601f19811610611b33575b505050811b018255611391565b015160001960f88460031b161c191690558a8080611b26565b81810151835560209485019460019093019290910190611b0b565b90915084610120515260206101205120601f840160051c810160208510611bb6575b90849392915b601f830160051c82018110611ba5575050611363565b610120518155859450600101611b8f565b5080611b89565b90506020813d602011611be7575b81611bd860209383611ec1565b81010312610b34575187611282565b3d9150611bcb565b90506020813d602011611c19575b81611c0a60209383611ec1565b81010312610b3457518761123c565b3d9150611bfd565b34610b3457602080600319360112610b34576001600160a01b0360043581811690819003610b34576101205160028054919290835b838110611d66575050611c81611c6b84611f8c565b93611c796040519586611ec1565b808552611f8c565b8386019490601f19013686376101205191825b848110611ce0578786886040519283928184019082855251809152604084019291610120515b828110611cc957505050500390f35b835185528695509381019392810192600101611cba565b611ce981612022565b9054600391821b1c610120515260018952828460046040610120512001541614611d17575b50600101611c94565b611d22829592612022565b9054911b1c8651821015611d4c5781611d45918a60019460051b8a01015261214e565b9390611d0e565b634e487b7160e01b61012051526032600452602461012051fd5b82611d7082612022565b9054600391821b1c61012051526001808a52611dca6040610120512092611daf60405193611d9d85611e6f565b611da6866120a9565b855285016120a9565b8c840152611dbe8785016120a9565b604084015283016120a9565b60608201526101a08960048401541692836080840152600b8b6005830154169160a09283860152600681015460c0860152600781015460e0860152600881015461010086015260098101546101208601528c600a82015416610140860152015480918c821661016086015260ff9283911c16151561018085015260a81c16151591015214611e5b575b600101611c56565b93611e6760019161214e565b949050611e53565b6101c081019081106001600160401b03821117610baf57604052565b60a081019081106001600160401b03821117610baf57604052565b608081019081106001600160401b03821117610baf57604052565b90601f801991011681019081106001600160401b03821117610baf57604052565b6001600160401b038111610baf57601f01601f191660200190565b81601f82011215610ab157803590611f1482611ee2565b92611f226040519485611ec1565b82845260208383010111610ab157816000926020809301838601378301015290565b60005b838110611f575750506000910152565b8181015183820152602001611f47565b90602091611f8081518092818552858086019101611f44565b601f01601f1916010190565b6001600160401b038111610baf5760051b60200190565b81601f82011215610ab157803591602091611fbd84611f8c565b93611fcb6040519586611ec1565b808552838086019160051b83010192808411610ab157848301915b848310611ff65750505050505090565b82356001600160401b038111610ab157869161201784848094890101611efd565b815201920191611fe6565b6002548110156120595760026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0190600090565b634e487b7160e01b600052603260045260246000fd5b90600182811c9216801561209f575b602083101461208957565b634e487b7160e01b600052602260045260246000fd5b91607f169161207e565b906040519182600082546120bc8161206f565b9081845260209460019160018116908160001461212c57506001146120ed575b5050506120eb92500383611ec1565b565b600090815285812095935091905b8183106121145750506120eb93508201013880806120dc565b855488840185015294850194879450918301916120fb565b925050506120eb94925060ff191682840152151560051b8201013880806120dc565b600019811461215d5760010190565b634e487b7160e01b600052601160045260246000fd5b60025468010000000000000000811015610baf578060016121979201600255612022565b819291549060031b91821b91600019901b1916179055565b600060443d1061220c57604051600319913d83016004833e81516001600160401b03918282113d60248401111761220f57818401948551938411612217573d8501016020848701011161220f575061220c92910160200190611ec1565b90565b949350505050565b50949350505050565b3d1561224b573d9061223182611ee2565b9161223f6040519384611ec1565b82523d6000602084013e565b606090565b9182600052600160205260409182600020600b810154600160ff8260a81c1615151461233f574284111561232e576004820154336001600160a01b0391821614918215612321575b50508015612318575b15612307576006810180546122f65782905560070182905582518481523260208201529283015260608201527fc444cd90bb87c0733c799513fba068d7635632ccbd9dffb878caa3d2b278397190608090a190565b8451636e6870d560e01b8152600490fd5b8351631d7eb35960e31b8152600490fd5b503033146122a1565b9091501633143880612298565b8451633dd8280960e21b8152600490fd5b845163542f378d60e11b8152600490fd5b9060009182549080519584608060209586938486019960ff8b51986040809e818b019b8c518351918c8301937f45906d7e53feffed68a8db947e0f8839b1ee252166e8eead8c6ab3951a09c7b68552858401526060830152898201528881526123b881611e8b565b5190209051908982019261190160f01b845260228301526042820152604281526123e181611ea6565b519020938d5194855216868401528b830152606082015282805260015afa156124b75783518151855260018452868520600401546001600160a01b039182169116036124a6575193519051908551946375ad188160e01b8652600486015260248501526044840152808360648185305af193841561249b57508193612467575b50505090565b9091809350813d8311612494575b61247f8183611ec1565b81010312612491575051388080612461565b80fd5b503d612475565b51913d9150823e3d90fd5b855163553470cf60e01b8152600490fd5b85513d85823e3d90fd5b90808251908181526020809101926020808460051b8301019501936000915b8483106124f05750505050505090565b909192939495848061250e600193601f198682030187528a51611f67565b98019301930191949392906124e0565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526120eb9161256082611e8b565b61259e565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526120eb9161256082611ea6565b60018060a01b0316906125c8600080836020829551910182875af16125c1612220565b9084612624565b9081519182151592836125f8575b5050506125e05750565b60249060405190635274afe760e01b82526004820152fd5b81929350906020918101031261262057602001519081159182150361249157503880806125d6565b5080fd5b9061264b575080511561263957805190602001fd5b604051630a12f52160e11b8152600490fd5b8151158061267e575b61265c575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561265456fea26469706673582212200dee20c76df399efb991db946b42c9cd9441740d349e196ac4b05a0ce9a718e864736f6c63430008170033