Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- FastSwitchboard
- Optimization enabled
- true
- Compiler version
- v0.8.19+commit.7dd6d404
- Optimization runs
- 999999
- EVM Version
- default
- Verified at
- 2024-10-23T11:04:27.616815Z
Constructor Arguments
0x00000000000000000000000044a44837894b5edc2bde64567fc62599b3b88f4c0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f0000000000000000000000000000000000000000000000000000000000028c610000000000000000000000000000000000000000000000000000000000001c20000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f
Arg [0] (address) : 0x44a44837894b5edc2bde64567fc62599b3b88f4c
Arg [1] (address) : 0x5c4186d343eef952c9ed886e45f8243edf0a503f
Arg [2] (uint32) : 167009
Arg [3] (uint256) : 7200
Arg [4] (address) : 0xef1ae2f8b4e18afdddf1ca92037772891100a39f
contracts/switchboard/default-switchboards/FastSwitchboard.sol
// SPDX-License-Identifier: GPL-3.0-only // Sources flattened with hardhat v2.12.2 https://hardhat.org // File contracts/interfaces/ISwitchboard.sol pragma solidity 0.8.19; /** * @title ISwitchboard * @dev The interface for a switchboard contract that is responsible for verification of packets between * different blockchain networks. */ interface ISwitchboard { /** * @notice Registers itself in Socket for given `siblingChainSlug_`. * @dev This function is expected to be only called by admin as it handles the capacitor config for given chain * @param siblingChainSlug_ The slug of the sibling chain to register switchboard with. * @param maxPacketLength_ The maximum length of a packet allowed by the switchboard. * @param capacitorType_ The type of capacitor that the switchboard uses. * @param initialPacketCount_ The packet count at the time of registering switchboard. Packets with packet count below this won't be allowed * @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_` */ function registerSiblingSlug( uint32 siblingChainSlug_, uint256 maxPacketLength_, uint256 capacitorType_, uint256 initialPacketCount_, address siblingSwitchboard_ ) external; /** * @notice Updates the sibling switchboard for given `siblingChainSlug_`. * @dev This function is expected to be only called by admin * @param siblingChainSlug_ The slug of the sibling chain to register switchboard with. * @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_` */ function updateSibling( uint32 siblingChainSlug_, address siblingSwitchboard_ ) external; /** * @notice Checks if a packet can be allowed to go through the switchboard. * @param root the packet root. * @param packetId The unique identifier for the packet. * @param proposalCount The unique identifier for a proposal for the packet. * @param srcChainSlug The unique identifier for the source chain of the packet. * @param proposeTime The time when the packet was proposed. * @return A boolean indicating whether the packet is allowed to go through the switchboard or not. */ function allowPacket( bytes32 root, bytes32 packetId, uint256 proposalCount, uint32 srcChainSlug, uint256 proposeTime ) external view returns (bool); /** * @notice Retrieves the minimum fees required for the destination chain to process the packet. * @param dstChainSlug the unique identifier for the destination chain of the packet. * @return switchboardFee the switchboard fee required for the destination chain to process the packet. * @return verificationOverheadFees the verification fee required for the destination chain to process the packet. */ function getMinFees( uint32 dstChainSlug ) external view returns (uint128 switchboardFee, uint128 verificationOverheadFees); /** * @notice Receives the fees for processing of packet. * @param siblingChainSlug_ the chain slug of the sibling chain. */ function receiveFees(uint32 siblingChainSlug_) external payable; /** * @notice Sets the minimum fees required for the destination chain to process the packet. * @param nonce_ the nonce of fee Updater to avoid replay. * @param dstChainSlug_ the unique identifier for the destination chain. * @param switchboardFees_ the switchboard fee required for the destination chain to process the packet. * @param verificationOverheadFees_ the verification fee required for the destination chain to process the packet. * @param signature_ the signature of the request. * @dev not important to override in all switchboards */ function setFees( uint256 nonce_, uint32 dstChainSlug_, uint128 switchboardFees_, uint128 verificationOverheadFees_, bytes calldata signature_ ) external; } // File contracts/interfaces/IExecutionManager.sol pragma solidity 0.8.19; /** * @title Execution Manager Interface * @dev This interface defines the functions for managing and executing transactions on external chains * @dev It is also responsible for collecting all the socket fees, which can then be pulled by others */ interface IExecutionManager { struct ExecutionFeesParam { // for calculating perGasCost * gasLimit uint80 perGasCost; // for calculating cost for executing payload (needed for rollups) uint80 perByteCost; // additional cost (differs based on chain) uint80 overhead; } /** * @notice Returns the executor of the packed message and whether the executor is authorized * @param packedMessage The message packed with payload, fees and config * @param sig The signature of the message * @return The address of the executor and a boolean indicating if the executor is authorized */ function isExecutor( bytes32 packedMessage, bytes memory sig ) external view returns (address, bool); /** * @notice Pays the fees for executing a transaction on the external chain * @dev This function is payable and assumes the socket is going to send correct amount of fees. * @param minMsgGasLimit_ The minimum gas limit for the transaction * @param payloadSize_ The payload size in bytes * @param executionParams_ Extra params for execution * @param transmissionParams_ Extra params for transmission * @param siblingChainSlug_ Sibling chain identifier * @param switchboardFees_ fee charged by switchboard for processing transaction * @param verificationOverheadFees_ fee charged for verifying transaction * @param transmitManager_ The transmitManager address * @param switchboard_ The switchboard address * @param maxPacketLength_ The maxPacketLength for the capacitor */ function payAndCheckFees( uint256 minMsgGasLimit_, uint256 payloadSize_, bytes32 executionParams_, bytes32 transmissionParams_, uint32 siblingChainSlug_, uint128 switchboardFees_, uint128 verificationOverheadFees_, address transmitManager_, address switchboard_, uint256 maxPacketLength_ ) external payable returns (uint128, uint128); /** * @notice Returns the minimum fees required for executing a transaction on the external chain * @param minMsgGasLimit_ minMsgGasLimit_ * @param siblingChainSlug_ The destination slug * @return The minimum fees required for executing the transaction */ function getMinFees( uint256 minMsgGasLimit_, uint256 payloadSize_, bytes32 executionParams_, uint32 siblingChainSlug_ ) external view returns (uint128); /** * @notice function for getting the minimum fees required for executing and transmitting a cross-chain transaction * @dev this function is called at source to calculate the execution cost. * @param payloadSize_ byte length of payload. Currently only used to check max length, later on will be used for fees calculation. * @param executionParams_ Can be used for providing extra information. Currently used for msgValue * @param siblingChainSlug_ Sibling chain identifier * @return minExecutionFee : Minimum fees required for executing the transaction */ function getExecutionTransmissionMinFees( uint256 minMsgGasLimit_, uint256 payloadSize_, bytes32 executionParams_, bytes32 transmissionParams_, uint32 siblingChainSlug_, address transmitManager_ ) external view returns (uint128, uint128); /** * @notice Updates the execution fees for an executor and message ID * @param executor The executor address * @param executionFees The execution fees to update * @param msgId The ID of the message */ function updateExecutionFees( address executor, uint128 executionFees, bytes32 msgId ) external; /** * @notice updates the transmission fee * @param remoteChainSlug_ sibling chain identifier * @param transmitMinFees_ transmission fees collected */ function setTransmissionMinFees( uint32 remoteChainSlug_, uint128 transmitMinFees_ ) external; /** * @notice sets the minimum execution fees required for executing at `siblingChainSlug_` * @dev this function currently sets the price for a constant msg gas limit and payload size * @param nonce_ incremental id to prevent signature replay * @param siblingChainSlug_ sibling chain identifier * @param executionFees_ total fees where price in destination native token is converted to source native tokens * @param signature_ signature of fee updater */ function setExecutionFees( uint256 nonce_, uint32 siblingChainSlug_, ExecutionFeesParam calldata executionFees_, bytes calldata signature_ ) external; /** * @notice sets the min limit for msg value for `siblingChainSlug_` * @param nonce_ incremental id to prevent signature replay * @param siblingChainSlug_ sibling chain identifier * @param msgValueMinThreshold_ min msg value * @param signature_ signature of fee updater */ function setMsgValueMinThreshold( uint256 nonce_, uint32 siblingChainSlug_, uint256 msgValueMinThreshold_, bytes calldata signature_ ) external; /** * @notice sets the max limit for msg value for `siblingChainSlug_` * @param nonce_ incremental id to prevent signature replay * @param siblingChainSlug_ sibling chain identifier * @param msgValueMaxThreshold_ max msg value * @param signature_ signature of fee updater */ function setMsgValueMaxThreshold( uint256 nonce_, uint32 siblingChainSlug_, uint256 msgValueMaxThreshold_, bytes calldata signature_ ) external; /** * @notice sets the relative token price for `siblingChainSlug_` * @dev this function is expected to be called frequently to match the original prices * @param nonce_ incremental id to prevent signature replay * @param siblingChainSlug_ sibling chain identifier * @param relativeNativeTokenPrice_ relative price * @param signature_ signature of fee updater */ function setRelativeNativeTokenPrice( uint256 nonce_, uint32 siblingChainSlug_, uint256 relativeNativeTokenPrice_, bytes calldata signature_ ) external; /** * @notice called by socket while executing message to validate if the msg value provided is enough * @param executionParams_ a bytes32 string where first byte gives param type (if value is 0 or not) * and remaining bytes give the msg value needed * @param msgValue_ msg.value to be sent with inbound */ function verifyParams( bytes32 executionParams_, uint256 msgValue_ ) external view; /** * @notice withdraws switchboard fees from contract * @param siblingChainSlug_ withdraw fees corresponding to this slug * @param amount_ withdraw amount */ function withdrawSwitchboardFees( uint32 siblingChainSlug_, address switchboard_, uint128 amount_ ) external; /** * @dev this function gets the transmitManager address from the socket contract. If it is ever upgraded in socket, * @dev remove the fees from executionManager first, and then upgrade address at socket. * @notice withdraws transmission fees from contract * @param siblingChainSlug_ withdraw fees corresponding to this slug * @param amount_ withdraw amount */ function withdrawTransmissionFees( uint32 siblingChainSlug_, uint128 amount_ ) external; } // File contracts/interfaces/ITransmitManager.sol pragma solidity 0.8.19; /** * @title ITransmitManager * @dev The interface for a transmit manager contract */ interface ITransmitManager { /** * @notice Checks if a given transmitter is authorized to send transactions to the destination chain. * @param siblingSlug The unique identifier for the sibling chain. * @param digest The digest of the message being signed. * @param signature The signature of the message being signed. * @return The address of the transmitter and a boolean indicating whether the transmitter is authorized or not. */ function checkTransmitter( uint32 siblingSlug, bytes32 digest, bytes calldata signature ) external view returns (address, bool); /** * @notice sets the transmission fee needed to transmit message to given `siblingSlug_` * @dev recovered address should add have feeUpdater role for `siblingSlug_` * @param nonce_ The incremental nonce to prevent signature replay * @param siblingSlug_ sibling id for which fee updater is registered * @param transmissionFees_ digest which is signed by transmitter * @param signature_ signature */ function setTransmissionFees( uint256 nonce_, uint32 siblingSlug_, uint128 transmissionFees_, bytes calldata signature_ ) external; /** * @notice receives fees from Execution manager * @dev this function can be used to keep track of fees received for each slug * @param siblingSlug_ sibling id for which fee updater is registered */ function receiveFees(uint32 siblingSlug_) external payable; } // File contracts/interfaces/ISocket.sol pragma solidity 0.8.19; /** * @title ISocket * @notice An interface for a cross-chain communication contract * @dev This interface provides methods for transmitting and executing messages between chains, * connecting a plug to a remote chain and setting up switchboards for the message transmission * This interface also emits events for important operations such as message transmission, execution status, * and plug connection */ interface ISocket { /** * @notice A struct containing fees required for message transmission and execution * @param transmissionFees fees needed for transmission * @param switchboardFees fees needed by switchboard * @param executionFee fees needed for execution */ struct Fees { uint128 transmissionFees; uint128 executionFee; uint128 switchboardFees; } /** * @title MessageDetails * @dev This struct defines the details of a message to be executed in a Decapacitor contract. */ struct MessageDetails { // A unique identifier for the message. bytes32 msgId; // The fee to be paid for executing the message. uint256 executionFee; // The min amount of gas that can be used to execute the message. uint256 minMsgGasLimit; // The extra params which might provide msg value and additional info needed for message exec bytes32 executionParams; // The payload data to be executed in the message. bytes payload; } /** * @title ExecutionDetails * @dev This struct defines the execution details */ struct ExecutionDetails { // packet id bytes32 packetId; // proposal count uint256 proposalCount; // gas limit needed to execute inbound uint256 executionGasLimit; // proof data required by the Decapacitor contract to verify the message's authenticity bytes decapacitorProof; // signature of executor bytes signature; } /** * @notice emits the status of message after inbound call * @param msgId msg id which is executed */ event ExecutionSuccess(bytes32 msgId); /** * @notice emits the config set by a plug for a remoteChainSlug * @param plug address of plug on current chain * @param siblingChainSlug sibling chain slug * @param siblingPlug address of plug on sibling chain * @param inboundSwitchboard inbound switchboard (select from registered options) * @param outboundSwitchboard outbound switchboard (select from registered options) * @param capacitor capacitor selected based on outbound switchboard * @param decapacitor decapacitor selected based on inbound switchboard */ event PlugConnected( address plug, uint32 siblingChainSlug, address siblingPlug, address inboundSwitchboard, address outboundSwitchboard, address capacitor, address decapacitor ); /** * @notice registers a message * @dev Packs the message and includes it in a packet with capacitor * @param remoteChainSlug_ the remote chain slug * @param minMsgGasLimit_ the gas limit needed to execute the payload on remote * @param payload_ the data which is needed by plug at inbound call on remote */ function outbound( uint32 remoteChainSlug_, uint256 minMsgGasLimit_, bytes32 executionParams_, bytes32 transmissionParams_, bytes calldata payload_ ) external payable returns (bytes32 msgId); /** * @notice executes a message * @param executionDetails_ the packet details, proof and signature needed for message execution * @param messageDetails_ the message details */ function execute( ISocket.ExecutionDetails calldata executionDetails_, ISocket.MessageDetails calldata messageDetails_ ) external payable; /** * @notice seals data in capacitor for specific batchSize * @param batchSize_ size of batch to be sealed * @param capacitorAddress_ address of capacitor * @param signature_ signed Data needed for verification */ function seal( uint256 batchSize_, address capacitorAddress_, bytes calldata signature_ ) external payable; /** * @notice proposes a packet * @param packetId_ packet id * @param root_ root data * @param switchboard_ The address of switchboard for which this packet is proposed * @param signature_ signed Data needed for verification */ function proposeForSwitchboard( bytes32 packetId_, bytes32 root_, address switchboard_, bytes calldata signature_ ) external payable; /** * @notice sets the config specific to the plug * @param siblingChainSlug_ the sibling chain slug * @param siblingPlug_ address of plug present at sibling chain to call inbound * @param inboundSwitchboard_ the address of switchboard to use for receiving messages * @param outboundSwitchboard_ the address of switchboard to use for sending messages */ function connect( uint32 siblingChainSlug_, address siblingPlug_, address inboundSwitchboard_, address outboundSwitchboard_ ) external; /** * @notice deploy capacitor and decapacitor for a switchboard with a specified max packet length, sibling chain slug, and capacitor type. * @param siblingChainSlug_ The slug of the sibling chain that the switchboard is registered with. * @param maxPacketLength_ The maximum length of a packet allowed by the switchboard. * @param capacitorType_ The type of capacitor that the switchboard uses. * @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_` */ function registerSwitchboardForSibling( uint32 siblingChainSlug_, uint256 maxPacketLength_, uint256 capacitorType_, address siblingSwitchboard_ ) external returns (address capacitor, address decapacitor); /** * @notice Emits the sibling switchboard for given `siblingChainSlug_`. * @dev This function is expected to be only called by switchboard. * @dev the event emitted is tracked by transmitters to decide which switchboard a packet should be proposed on * @param siblingChainSlug_ The slug of the sibling chain * @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_` */ function useSiblingSwitchboard( uint32 siblingChainSlug_, address siblingSwitchboard_ ) external; /** * @notice Retrieves the packet id roots for a specified packet id. * @param packetId_ The packet id for which to retrieve the root. * @param proposalCount_ The proposal id for packetId_ for which to retrieve the root. * @param switchboard_ The address of switchboard for which this packet is proposed * @return The packet id roots for the specified packet id. */ function packetIdRoots( bytes32 packetId_, uint256 proposalCount_, address switchboard_ ) external view returns (bytes32); /** * @notice Retrieves the latest proposalCount for a packet id. * @return The proposal count for the specified packet id. */ function proposalCount(bytes32 packetId_) external view returns (uint256); /** * @notice Retrieves the minimum fees required for a message with a specified gas limit and destination chain. * @param minMsgGasLimit_ The gas limit of the message. * @param remoteChainSlug_ The slug of the destination chain for the message. * @param plug_ The address of the plug through which the message is sent. * @return totalFees The minimum fees required for the specified message. */ function getMinFees( uint256 minMsgGasLimit_, uint256 payloadSize_, bytes32 executionParams_, bytes32 transmissionParams_, uint32 remoteChainSlug_, address plug_ ) external view returns (uint256 totalFees); /// return instance of transmit manager function transmitManager__() external view returns (ITransmitManager); /// return instance of execution manager function executionManager__() external view returns (IExecutionManager); } // File contracts/utils/Ownable.sol pragma solidity 0.8.19; /** * @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.19; /** * @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/AccessControlExtended.sol pragma solidity 0.8.19; /** * @title AccessControlExtended * @dev This contract extends the functionality of the AccessControl contract by adding * the ability to grant and revoke roles based on a combination of role name and a chain slug. * It also provides batch operations for granting and revoking roles. */ contract AccessControlExtended is AccessControl { /** * @dev Constructor that sets the owner of the contract. */ constructor(address owner_) AccessControl(owner_) {} /** * @dev thrown when array lengths are not equal */ error UnequalArrayLengths(); /** * @dev Checks if an address has the role. * @param roleName_ The name of the role. * @param chainSlug_ The chain slug associated with the role. * @param address_ The address to be granted the role. */ function _checkRoleWithSlug( bytes32 roleName_, uint256 chainSlug_, address address_ ) internal virtual { bytes32 roleHash = keccak256(abi.encode(roleName_, chainSlug_)); if (!_hasRole(roleHash, address_)) revert NoPermit(roleHash); } /** * @dev Grants a role to an address based on the role name and chain slug. * @param roleName_ The name of the role. * @param chainSlug_ The chain slug associated with the role. * @param grantee_ The address to be granted the role. */ function grantRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address grantee_ ) external virtual onlyOwner { _grantRoleWithSlug(roleName_, chainSlug_, grantee_); } /** * @dev Grants multiple roles to multiple addresses in batch. * @param roleNames_ The names of the roles to grant. * @param slugs_ The slugs for chain specific roles. For roles which are not chain-specific, we can use slug = 0 * @param grantees_ The addresses to be granted the roles. */ function grantBatchRole( bytes32[] calldata roleNames_, uint32[] calldata slugs_, address[] calldata grantees_ ) external virtual onlyOwner { if ( roleNames_.length != grantees_.length || roleNames_.length != slugs_.length ) revert UnequalArrayLengths(); uint256 totalRoles = roleNames_.length; for (uint256 index = 0; index < totalRoles; ) { if (slugs_[index] > 0) _grantRoleWithSlug( roleNames_[index], slugs_[index], grantees_[index] ); else _grantRole(roleNames_[index], grantees_[index]); // inputs are controlled by owner unchecked { ++index; } } } /** * @dev Revokes multiple roles from multiple addresses in batch. * @param roleNames_ The names of the roles to revoke. * @param slugs_ The slugs for chain specific roles. For roles which are not chain-specific, we can use slug = 0 * @param grantees_ The addresses to be revoked the roles. */ function revokeBatchRole( bytes32[] calldata roleNames_, uint32[] calldata slugs_, address[] calldata grantees_ ) external virtual onlyOwner { if ( roleNames_.length != grantees_.length || roleNames_.length != slugs_.length ) revert UnequalArrayLengths(); uint256 totalRoles = roleNames_.length; for (uint256 index = 0; index < totalRoles; ) { if (slugs_[index] > 0) _revokeRoleWithSlug( roleNames_[index], slugs_[index], grantees_[index] ); else _revokeRole(roleNames_[index], grantees_[index]); // inputs are controlled by owner unchecked { ++index; } } } function _grantRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address grantee_ ) internal { _grantRole(keccak256(abi.encode(roleName_, chainSlug_)), grantee_); } /** * @dev Checks if an address has a role based on the role name and chain slug. * @param roleName_ The name of the role. * @param chainSlug_ The chain slug associated with the role. * @param address_ The address to check for the role. * @return A boolean indicating whether the address has the specified role. */ function hasRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address address_ ) external view returns (bool) { return _hasRoleWithSlug(roleName_, chainSlug_, address_); } function _hasRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address address_ ) internal view returns (bool) { return _hasRole(keccak256(abi.encode(roleName_, chainSlug_)), address_); } /** * @dev Revokes roles from an address * @param roleName_ The names of the roles to revoke. * @param chainSlug_ The chain slug associated with the role. * @param grantee_ The addresses to be revoked the roles. */ function revokeRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address grantee_ ) external virtual onlyOwner { _revokeRoleWithSlug(roleName_, chainSlug_, grantee_); } function _revokeRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address revokee_ ) internal { _revokeRole(keccak256(abi.encode(roleName_, chainSlug_)), revokee_); } } // 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.19; error ZeroAddress(); /** * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ library RescueFundsLib { /** * @dev The address used to identify ETH. */ address public constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); /** * @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/interfaces/ISignatureVerifier.sol pragma solidity 0.8.19; /** * @title Signature Verifier * @notice Verifies the signatures and returns the address of signer recovered from the input signature or digest. */ interface ISignatureVerifier { /** * @notice returns the address of signer recovered from input signature and digest */ function recoverSigner( bytes32 digest_, bytes memory signature_ ) external pure returns (address signer); } // File contracts/utils/AccessRoles.sol pragma solidity 0.8.19; // contains role hashes used in socket dl for various different operations // used to rescue funds bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); // used to withdraw fees bytes32 constant WITHDRAW_ROLE = keccak256("WITHDRAW_ROLE"); // used to trip switchboards bytes32 constant TRIP_ROLE = keccak256("TRIP_ROLE"); // used to un trip switchboards bytes32 constant UN_TRIP_ROLE = keccak256("UN_TRIP_ROLE"); // used by governance bytes32 constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE"); //used by executors which executes message at destination bytes32 constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); // used by transmitters who seal and propose packets in socket bytes32 constant TRANSMITTER_ROLE = keccak256("TRANSMITTER_ROLE"); // used by switchboard watchers who work against transmitters bytes32 constant WATCHER_ROLE = keccak256("WATCHER_ROLE"); // used by fee updaters responsible for updating fees at switchboards, transmit manager and execution manager bytes32 constant FEES_UPDATER_ROLE = keccak256("FEES_UPDATER_ROLE"); // File contracts/utils/SigIdentifiers.sol pragma solidity 0.8.19; // contains unique identifiers which are hashes of strings, they help in making signature digest unique // hence preventing signature replay attacks // default switchboards bytes32 constant TRIP_PATH_SIG_IDENTIFIER = keccak256("TRIP_PATH"); bytes32 constant TRIP_PROPOSAL_SIG_IDENTIFIER = keccak256("TRIP_PROPOSAL"); bytes32 constant TRIP_GLOBAL_SIG_IDENTIFIER = keccak256("TRIP_GLOBAL"); bytes32 constant UN_TRIP_PATH_SIG_IDENTIFIER = keccak256("UN_TRIP_PATH"); bytes32 constant UN_TRIP_GLOBAL_SIG_IDENTIFIER = keccak256("UN_TRIP_GLOBAL"); // native switchboards bytes32 constant TRIP_NATIVE_SIG_IDENTIFIER = keccak256("TRIP_NATIVE"); bytes32 constant UN_TRIP_NATIVE_SIG_IDENTIFIER = keccak256("UN_TRIP_NATIVE"); // value threshold, price and fee updaters bytes32 constant FEES_UPDATE_SIG_IDENTIFIER = keccak256("FEES_UPDATE"); bytes32 constant RELATIVE_NATIVE_TOKEN_PRICE_UPDATE_SIG_IDENTIFIER = keccak256( "RELATIVE_NATIVE_TOKEN_PRICE_UPDATE" ); bytes32 constant MSG_VALUE_MIN_THRESHOLD_SIG_IDENTIFIER = keccak256( "MSG_VALUE_MIN_THRESHOLD_UPDATE" ); bytes32 constant MSG_VALUE_MAX_THRESHOLD_SIG_IDENTIFIER = keccak256( "MSG_VALUE_MAX_THRESHOLD_UPDATE" ); // File contracts/switchboard/default-switchboards/SwitchboardBase.sol pragma solidity 0.8.19; abstract contract SwitchboardBase is ISwitchboard, AccessControlExtended { // signature verifier contract ISignatureVerifier public immutable signatureVerifier__; // socket contract ISocket public immutable socket__; // chain slug of deployed chain uint32 public immutable chainSlug; // timeout after which packets become valid // optimistic switchboard: this is the wait time to validate packet // fast switchboard: this makes packets valid even if all watchers have not attested // used to make the system work when watchers are inactive due to infra etc problems // this is only applicable if none of the trips are triggered uint256 public immutable timeoutInSeconds; // variable to pause the switchboard completely, to be used only in case of smart contract bug // trip can be done by TRIP_ROLE holders // untrip can be done by UN_TRIP_ROLE holders bool public isGlobalTipped; // pause all proposals coming from given chain. // to be used if a transmitter has gone rogue and needs to be kicked to resume normal functioning // trip can be done by WATCHER_ROLE holders // untrip can be done by UN_TRIP_ROLE holders // sourceChain => isPaused mapping(uint32 => bool) public isPathTripped; // block execution of single proposal // to be used if transmitter proposes wrong packet root single time // trip can be done by WATCHER_ROLE holders // untrip not possible, but same root can be proposed again at next proposalCount // isProposalTripped(packetId => proposalCount => isTripped) mapping(bytes32 => mapping(uint256 => bool)) public isProposalTripped; // incrementing nonce for each signer // watcher => nextNonce mapping(address => uint256) public nextNonce; struct Fees { uint128 switchboardFees; // Fees paid to Switchboard per packet uint128 verificationOverheadFees; // Fees paid to executor per message } // destinationChainSlug => fees-struct with verificationOverheadFees and switchboardFees mapping(uint32 => Fees) public fees; // destinationChainSlug => initialPacketCount - packets with packetCount after this will be accepted at the switchboard. // This is to prevent attacks with sending messages for chain slugs before the switchboard is registered for them. mapping(uint32 => uint256) public initialPacketCount; /** * @dev Emitted when global trip status changes * @param isGlobalTipped New trip status of the contract */ event GlobalTripChanged(bool isGlobalTipped); /** * @dev Emitted when path trip status changes * @param srcChainSlug Chain slug of the source chain * @param isPathTripped New trip status of the path */ event PathTripChanged(uint32 srcChainSlug, bool isPathTripped); /** * @dev Emitted when a proposal for a packetId is tripped * @param packetId packetId of packet * @param proposalCount proposalCount being tripped */ event ProposalTripped(bytes32 packetId, uint256 proposalCount); /** * @dev Emitted when a fees is set for switchboard * @param siblingChainSlug Chain slug of the sibling chain * @param fees Fees struct with verificationOverheadFees and switchboardFees */ event SwitchboardFeesSet(uint32 siblingChainSlug, Fees fees); // Error hit when a signature with unexpected nonce is received error InvalidNonce(); // Error hit when tx from invalid ExecutionManager is received error OnlyExecutionManager(); /** * @dev Constructor of SwitchboardBase * @param socket_ Address of the socket contract * @param chainSlug_ Chain slug of deployment chain * @param timeoutInSeconds_ Time after which proposals become valid if not tripped * @param signatureVerifier_ signatureVerifier_ contract */ constructor( address socket_, uint32 chainSlug_, uint256 timeoutInSeconds_, ISignatureVerifier signatureVerifier_ ) { socket__ = ISocket(socket_); chainSlug = chainSlug_; timeoutInSeconds = timeoutInSeconds_; signatureVerifier__ = signatureVerifier_; } /** * @inheritdoc ISwitchboard */ function getMinFees( uint32 dstChainSlug_ ) external view override returns (uint128, uint128) { Fees memory minFees = fees[dstChainSlug_]; return (minFees.switchboardFees, minFees.verificationOverheadFees); } /** * @inheritdoc ISwitchboard */ function registerSiblingSlug( uint32 siblingChainSlug_, uint256 maxPacketLength_, uint256 capacitorType_, uint256 initialPacketCount_, address siblingSwitchboard_ ) external override onlyRole(GOVERNANCE_ROLE) { initialPacketCount[siblingChainSlug_] = initialPacketCount_; socket__.registerSwitchboardForSibling( siblingChainSlug_, maxPacketLength_, capacitorType_, siblingSwitchboard_ ); } /** * @notice Signals sibling switchboard for given `siblingChainSlug_`. * @dev This function is expected to be only called by governance * @param siblingChainSlug_ The slug of the sibling chain whos switchboard is being connected. * @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_` */ function updateSibling( uint32 siblingChainSlug_, address siblingSwitchboard_ ) external onlyRole(GOVERNANCE_ROLE) { socket__.useSiblingSwitchboard(siblingChainSlug_, siblingSwitchboard_); } /** * @notice Pauses this switchboard completely. To be used in case of contract bug. * @param nonce_ The nonce used for signature. * @param signature_ The signature provided to validate the trip. */ function tripGlobal(uint256 nonce_, bytes memory signature_) external { address tripper = signatureVerifier__.recoverSigner( // it includes trip status at the end keccak256( abi.encode( TRIP_GLOBAL_SIG_IDENTIFIER, address(this), chainSlug, nonce_, true ) ), signature_ ); _checkRole(TRIP_ROLE, tripper); // Nonce is used by gated roles and we don't expect nonce to reach the max value of uint256 unchecked { if (nonce_ != nextNonce[tripper]++) revert InvalidNonce(); } isGlobalTipped = true; emit GlobalTripChanged(true); } /** * @notice Pauses a path. To be used when a transmitter goes rogue and needs to be kicked. * @param nonce_ The nonce used for signature. * @param srcChainSlug_ The source chain slug of the path to be paused. * @param signature_ The signature provided to validate the trip. */ function tripPath( uint256 nonce_, uint32 srcChainSlug_, bytes memory signature_ ) external { address watcher = signatureVerifier__.recoverSigner( // it includes trip status at the end keccak256( abi.encode( TRIP_PATH_SIG_IDENTIFIER, address(this), srcChainSlug_, chainSlug, nonce_, true ) ), signature_ ); _checkRoleWithSlug(WATCHER_ROLE, srcChainSlug_, watcher); // Nonce is used by gated roles and we don't expect nonce to reach the max value of uint256 unchecked { if (nonce_ != nextNonce[watcher]++) revert InvalidNonce(); } //source chain based tripping isPathTripped[srcChainSlug_] = true; emit PathTripChanged(srcChainSlug_, true); } /** * @notice Pauses a particular proposal of a packet. To be used if transmitter proposes wrong root. * @param nonce_ The nonce used for signature. * @param packetId_ The ID of the packet. * @param proposalCount_ The count of the proposal to be paused. * @param signature_ The signature provided to validate the trip. */ function tripProposal( uint256 nonce_, bytes32 packetId_, uint256 proposalCount_, bytes memory signature_ ) external { uint32 srcChainSlug = uint32(uint256(packetId_) >> 224); address watcher = signatureVerifier__.recoverSigner( keccak256( abi.encode( TRIP_PROPOSAL_SIG_IDENTIFIER, address(this), srcChainSlug, chainSlug, nonce_, packetId_, proposalCount_ ) ), signature_ ); _checkRoleWithSlug(WATCHER_ROLE, srcChainSlug, watcher); // Nonce is used by gated roles and we don't expect nonce to reach the max value of uint256 unchecked { if (nonce_ != nextNonce[watcher]++) revert InvalidNonce(); } isProposalTripped[packetId_][proposalCount_] = true; emit ProposalTripped(packetId_, proposalCount_); } /** * @notice Unpauses a path. To be used after bad transmitter has been kicked from system. * @param nonce_ The nonce used for the signature. * @param srcChainSlug_ The source chain slug of the path to be unpaused. * @param signature_ The signature provided to validate the un trip. */ function unTripPath( uint256 nonce_, uint32 srcChainSlug_, bytes memory signature_ ) external { address unTripper = signatureVerifier__.recoverSigner( // it includes trip status at the end keccak256( abi.encode( UN_TRIP_PATH_SIG_IDENTIFIER, address(this), srcChainSlug_, chainSlug, nonce_, false ) ), signature_ ); _checkRole(UN_TRIP_ROLE, unTripper); // Nonce is used by gated roles and we don't expect nonce to reach the max value of uint256 unchecked { if (nonce_ != nextNonce[unTripper]++) revert InvalidNonce(); } isPathTripped[srcChainSlug_] = false; emit PathTripChanged(srcChainSlug_, false); } /** * @notice Unpauses global execution. To be used if contract bug is addressed. * @param nonce_ The nonce used for the signature. * @param signature_ The signature provided to validate the un trip. */ function unTrip(uint256 nonce_, bytes memory signature_) external { address unTripper = signatureVerifier__.recoverSigner( // it includes trip status at the end keccak256( abi.encode( UN_TRIP_GLOBAL_SIG_IDENTIFIER, address(this), chainSlug, nonce_, false ) ), signature_ ); _checkRole(UN_TRIP_ROLE, unTripper); // Nonce is used by gated roles and we don't expect nonce to reach the max value of uint256 unchecked { if (nonce_ != nextNonce[unTripper]++) revert InvalidNonce(); } isGlobalTipped = false; emit GlobalTripChanged(false); } /** * @notice Withdraw fees from the contract to an account. * @param withdrawTo_ The address where we should send the fees. */ function withdrawFees( address withdrawTo_ ) external onlyRole(WITHDRAW_ROLE) { if (withdrawTo_ == address(0)) revert ZeroAddress(); SafeTransferLib.safeTransferETH(withdrawTo_, address(this).balance); } /** * @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_); } /** * @inheritdoc ISwitchboard * @dev Receiving only allowed from execution manager * @dev Need to receive fees before change in case execution manager * is being updated on socket. */ function receiveFees(uint32) external payable override { if (msg.sender != address(socket__.executionManager__())) revert OnlyExecutionManager(); } } // File contracts/switchboard/default-switchboards/FastSwitchboard.sol pragma solidity 0.8.19; /** * @title FastSwitchboard contract * @dev This contract implements a fast version of the SwitchboardBase contract * that enables packet attestations and watchers registration. */ contract FastSwitchboard is SwitchboardBase { // dstChainSlug => totalWatchers registered mapping(uint32 => uint256) public totalWatchers; // used to track which watcher have attested a root // watcher => root => isAttested mapping(address => mapping(bytes32 => bool)) public isAttested; // used to detect when enough attestations are reached // root => attestationCount mapping(bytes32 => uint256) public attestations; // mapping to store if root is valid // marked when all watchers have attested for a root // root => isValid mapping(bytes32 => bool) public isRootValid; // Event emitted when a new socket is set event SocketSet(address newSocket); // Event emitted when a proposal is attested event ProposalAttested( bytes32 packetId, uint256 proposalCount, bytes32 root, address watcher, uint256 attestationsCount ); // Error emitted when a watcher already has role while granting error WatcherFound(); // Error emitted when a watcher is not found while attesting or while revoking role error WatcherNotFound(); // Error emitted when a root is already attested by a specific watcher. // This is hit even if they are attesting a new proposalCount with same root. error AlreadyAttested(); // Error emitted if grant/revoke is tried for watcher role using generic grant/revoke functions. // Watcher role is handled seperately bacause totalWatchers and fees need to be updated along with role change. error InvalidRole(); // Error emitted while attesting if root is zero or it doesnt match the root on socket for given proposal // helps in cases where attest tx has been sent but root changes on socket due to reorgs. error InvalidRoot(); /** * @dev Constructor function for the FastSwitchboard contract * @param owner_ Address of the owner of the contract * @param socket_ Address of the socket contract * @param chainSlug_ Chain slug of the chain where the contract is deployed * @param timeoutInSeconds_ Timeout in seconds after which proposals become valid if not tripped * @param signatureVerifier_ The address of the signature verifier contract */ constructor( address owner_, address socket_, uint32 chainSlug_, uint256 timeoutInSeconds_, ISignatureVerifier signatureVerifier_ ) AccessControlExtended(owner_) SwitchboardBase( socket_, chainSlug_, timeoutInSeconds_, signatureVerifier_ ) {} /** * @dev Function to attest a packet * @param packetId_ Packet ID * @param proposalCount_ Proposal count * @param root_ Root of the packet * @param signature_ Signature of the watcher * @notice we are attesting a root uniquely identified with packetId and proposalCount. However, * there can be multiple proposals for same root. To avoid need to re-attest for different proposals * with same root, we are storing attestations against root instead of packetId and proposalCount. */ function attest( bytes32 packetId_, uint256 proposalCount_, bytes32 root_, bytes calldata signature_ ) external { uint32 srcChainSlug = uint32(uint256(packetId_) >> 224); bytes32 root = socket__.packetIdRoots( packetId_, proposalCount_, address(this) ); if (root == bytes32(0)) revert InvalidRoot(); if (root != root_) revert InvalidRoot(); address watcher = signatureVerifier__.recoverSigner( keccak256( abi.encode( address(this), chainSlug, packetId_, proposalCount_, root_ ) ), signature_ ); if (isAttested[watcher][root]) revert AlreadyAttested(); if (!_hasRoleWithSlug(WATCHER_ROLE, srcChainSlug, watcher)) revert WatcherNotFound(); isAttested[watcher][root] = true; ++attestations[root]; if (attestations[root] >= totalWatchers[srcChainSlug]) isRootValid[root] = true; emit ProposalAttested( packetId_, proposalCount_, root, watcher, attestations[root] ); } /** * @inheritdoc ISwitchboard */ function setFees( uint256 nonce_, uint32 dstChainSlug_, uint128 switchboardFees_, uint128 verificationOverheadFees_, bytes calldata signature_ ) external override { address feesUpdater = signatureVerifier__.recoverSigner( keccak256( abi.encode( FEES_UPDATE_SIG_IDENTIFIER, address(this), chainSlug, dstChainSlug_, nonce_, switchboardFees_, verificationOverheadFees_ ) ), signature_ ); _checkRoleWithSlug(FEES_UPDATER_ROLE, dstChainSlug_, feesUpdater); // Nonce is used by gated roles and we don't expect nonce to reach the max value of uint256 unchecked { if (nonce_ != nextNonce[feesUpdater]++) revert InvalidNonce(); } // switchboardFees_ input is amount needed per watcher, multipled and stored on chain to avoid watcher set tracking offchain. // switchboardFees_ are paid to switchboard per packet // verificationOverheadFees_ are paid to executor per message Fees memory feesObject = Fees({ switchboardFees: switchboardFees_ * uint128(totalWatchers[dstChainSlug_]), verificationOverheadFees: verificationOverheadFees_ }); fees[dstChainSlug_] = feesObject; emit SwitchboardFeesSet(dstChainSlug_, feesObject); } /** * @inheritdoc ISwitchboard */ function allowPacket( bytes32 root_, bytes32 packetId_, uint256 proposalCount_, uint32 srcChainSlug_, uint256 proposeTime_ ) external view override returns (bool) { uint64 packetCount = uint64(uint256(packetId_)); // any relevant trips triggered or invalid packet count. if ( isGlobalTipped || isPathTripped[srcChainSlug_] || isProposalTripped[packetId_][proposalCount_] || packetCount < initialPacketCount[srcChainSlug_] ) return false; // root has enough attestations if (isRootValid[root_]) return true; // this makes packets valid even if all watchers have not attested // used to make the system work when watchers are inactive due to infra etc problems if (block.timestamp - proposeTime_ > timeoutInSeconds) return true; // not enough attestations and timeout not hit return false; } /** * @notice adds a watcher for `srcChainSlug_` chain * @param srcChainSlug_ chain slug of the chain where the watcher is being added * @param watcher_ watcher address */ function grantWatcherRole( uint32 srcChainSlug_, address watcher_ ) external onlyRole(GOVERNANCE_ROLE) { if (_hasRoleWithSlug(WATCHER_ROLE, srcChainSlug_, watcher_)) revert WatcherFound(); _grantRoleWithSlug(WATCHER_ROLE, srcChainSlug_, watcher_); Fees storage fees = fees[srcChainSlug_]; uint128 watchersBefore = uint128(totalWatchers[srcChainSlug_]); // edge case handled by calling setFees function after boorstrapping is done. if (watchersBefore != 0 && fees.switchboardFees != 0) fees.switchboardFees = (fees.switchboardFees * (watchersBefore + 1)) / watchersBefore; ++totalWatchers[srcChainSlug_]; } /** * @notice removes a watcher from `srcChainSlug_` chain list * @param srcChainSlug_ chain slug of the chain where the watcher is being removed * @param watcher_ watcher address */ function revokeWatcherRole( uint32 srcChainSlug_, address watcher_ ) external onlyRole(GOVERNANCE_ROLE) { if (!_hasRoleWithSlug(WATCHER_ROLE, srcChainSlug_, watcher_)) revert WatcherNotFound(); _revokeRoleWithSlug(WATCHER_ROLE, srcChainSlug_, watcher_); Fees storage fees = fees[srcChainSlug_]; uint128 watchersBefore = uint128(totalWatchers[srcChainSlug_]); // revoking all watchers is an extreme case not expected to be hit after setup is done. if (watchersBefore > 1 && fees.switchboardFees != 0) fees.switchboardFees = (fees.switchboardFees * (watchersBefore - 1)) / watchersBefore; totalWatchers[srcChainSlug_]--; } /** * @notice returns true if non watcher role. Used to avoid granting watcher role directly * @dev If adding any new role to FastSwitchboard, have to add it here as well to make sure it can be set */ function isNonWatcherRole(bytes32 role_) public pure returns (bool) { if ( role_ == TRIP_ROLE || role_ == UN_TRIP_ROLE || role_ == WITHDRAW_ROLE || role_ == RESCUE_ROLE || role_ == GOVERNANCE_ROLE || role_ == FEES_UPDATER_ROLE ) return true; return false; } /** * @dev Overriding this function from AccessControl to make sure owner can't grant Watcher Role directly, and should * only use grantWatcherRole function instead. This is to make sure watcher count remains correct */ function grantRole( bytes32 role_, address grantee_ ) external override onlyOwner { if (isNonWatcherRole(role_)) { _grantRole(role_, grantee_); } else { revert InvalidRole(); } } /** * @dev Overriding this function from AccessControlExtended to make sure owner can't grant Watcher Role directly, and should * only use grantWatcherRole function instead. This is to make sure watcher count remains correct */ function grantRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address grantee_ ) external override onlyOwner { if (roleName_ != FEES_UPDATER_ROLE) revert InvalidRole(); _grantRoleWithSlug(roleName_, chainSlug_, grantee_); } /** * @dev Overriding this function from AccessControl to make sure owner can't revoke Watcher Role directly, and should * only use revokeWatcherRole function instead. This is to make sure watcher count remains correct */ function revokeRole( bytes32 role_, address grantee_ ) external override onlyOwner { if (isNonWatcherRole(role_)) { _revokeRole(role_, grantee_); } else { revert InvalidRole(); } } /** * @dev Overriding this function from AccessControlExtended to make sure owner can't revoke Watcher Role directly, and should * only use revokeWatcherRole function instead. This is to make sure watcher count remains correct */ function revokeRoleWithSlug( bytes32 roleName_, uint32 chainSlug_, address grantee_ ) external override onlyOwner { if (roleName_ != FEES_UPDATER_ROLE) revert InvalidRole(); _revokeRoleWithSlug(roleName_, chainSlug_, grantee_); } /** * @dev Overriding this function from AccessControlExtended to make sure owner can't grant Watcher Role directly, and should * only use grantWatcherRole function instead. This is to make sure watcher count remains correct */ function grantBatchRole( bytes32[] calldata roleNames_, uint32[] calldata slugs_, address[] calldata grantees_ ) external override onlyOwner { if ( roleNames_.length != grantees_.length || roleNames_.length != slugs_.length ) revert UnequalArrayLengths(); uint256 totalRoles = roleNames_.length; for (uint256 index = 0; index < totalRoles; ) { if (isNonWatcherRole(roleNames_[index])) { if (slugs_[index] > 0) _grantRoleWithSlug( roleNames_[index], slugs_[index], grantees_[index] ); else _grantRole(roleNames_[index], grantees_[index]); } else { revert InvalidRole(); } // we will reach block gas limit before this overflows unchecked { ++index; } } } /** * @dev Overriding this function from AccessControlExtended to make sure owner can't revoke Watcher Role directly, and should * only use revokeWatcherRole function instead. This is to make sure watcher count remains correct */ function revokeBatchRole( bytes32[] calldata roleNames_, uint32[] calldata slugs_, address[] calldata grantees_ ) external override onlyOwner { if ( roleNames_.length != grantees_.length || roleNames_.length != slugs_.length ) revert UnequalArrayLengths(); uint256 totalRoles = roleNames_.length; for (uint256 index = 0; index < totalRoles; ) { if (isNonWatcherRole(roleNames_[index])) { if (slugs_[index] > 0) _revokeRoleWithSlug( roleNames_[index], slugs_[index], grantees_[index] ); else _revokeRole(roleNames_[index], grantees_[index]); } else { revert InvalidRole(); } // we will reach block gas limit before this overflows unchecked { ++index; } } } }
Compiler Settings
{"outputSelection":{"*":{"*":["*"]}},"optimizer":{"runs":999999,"enabled":true},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"owner_","internalType":"address"},{"type":"address","name":"socket_","internalType":"address"},{"type":"uint32","name":"chainSlug_","internalType":"uint32"},{"type":"uint256","name":"timeoutInSeconds_","internalType":"uint256"},{"type":"address","name":"signatureVerifier_","internalType":"contract ISignatureVerifier"}]},{"type":"error","name":"AlreadyAttested","inputs":[]},{"type":"error","name":"InvalidNonce","inputs":[]},{"type":"error","name":"InvalidRole","inputs":[]},{"type":"error","name":"InvalidRoot","inputs":[]},{"type":"error","name":"InvalidTokenAddress","inputs":[]},{"type":"error","name":"NoPermit","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"error","name":"OnlyExecutionManager","inputs":[]},{"type":"error","name":"OnlyNominee","inputs":[]},{"type":"error","name":"OnlyOwner","inputs":[]},{"type":"error","name":"UnequalArrayLengths","inputs":[]},{"type":"error","name":"WatcherFound","inputs":[]},{"type":"error","name":"WatcherNotFound","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"event","name":"GlobalTripChanged","inputs":[{"type":"bool","name":"isGlobalTipped","internalType":"bool","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":"PathTripChanged","inputs":[{"type":"uint32","name":"srcChainSlug","internalType":"uint32","indexed":false},{"type":"bool","name":"isPathTripped","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalAttested","inputs":[{"type":"bytes32","name":"packetId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"proposalCount","internalType":"uint256","indexed":false},{"type":"bytes32","name":"root","internalType":"bytes32","indexed":false},{"type":"address","name":"watcher","internalType":"address","indexed":false},{"type":"uint256","name":"attestationsCount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalTripped","inputs":[{"type":"bytes32","name":"packetId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"proposalCount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"grantee","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"revokee","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SocketSet","inputs":[{"type":"address","name":"newSocket","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SwitchboardFeesSet","inputs":[{"type":"uint32","name":"siblingChainSlug","internalType":"uint32","indexed":false},{"type":"tuple","name":"fees","internalType":"struct SwitchboardBase.Fees","indexed":false,"components":[{"type":"uint128","name":"switchboardFees","internalType":"uint128"},{"type":"uint128","name":"verificationOverheadFees","internalType":"uint128"}]}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"allowPacket","inputs":[{"type":"bytes32","name":"root_","internalType":"bytes32"},{"type":"bytes32","name":"packetId_","internalType":"bytes32"},{"type":"uint256","name":"proposalCount_","internalType":"uint256"},{"type":"uint32","name":"srcChainSlug_","internalType":"uint32"},{"type":"uint256","name":"proposeTime_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"attest","inputs":[{"type":"bytes32","name":"packetId_","internalType":"bytes32"},{"type":"uint256","name":"proposalCount_","internalType":"uint256"},{"type":"bytes32","name":"root_","internalType":"bytes32"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"attestations","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"chainSlug","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"switchboardFees","internalType":"uint128"},{"type":"uint128","name":"verificationOverheadFees","internalType":"uint128"}],"name":"fees","inputs":[{"type":"uint32","name":"","internalType":"uint32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"","internalType":"uint128"},{"type":"uint128","name":"","internalType":"uint128"}],"name":"getMinFees","inputs":[{"type":"uint32","name":"dstChainSlug_","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantBatchRole","inputs":[{"type":"bytes32[]","name":"roleNames_","internalType":"bytes32[]"},{"type":"uint32[]","name":"slugs_","internalType":"uint32[]"},{"type":"address[]","name":"grantees_","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role_","internalType":"bytes32"},{"type":"address","name":"grantee_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRoleWithSlug","inputs":[{"type":"bytes32","name":"roleName_","internalType":"bytes32"},{"type":"uint32","name":"chainSlug_","internalType":"uint32"},{"type":"address","name":"grantee_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantWatcherRole","inputs":[{"type":"uint32","name":"srcChainSlug_","internalType":"uint32"},{"type":"address","name":"watcher_","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":"bool","name":"","internalType":"bool"}],"name":"hasRoleWithSlug","inputs":[{"type":"bytes32","name":"roleName_","internalType":"bytes32"},{"type":"uint32","name":"chainSlug_","internalType":"uint32"},{"type":"address","name":"address_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"initialPacketCount","inputs":[{"type":"uint32","name":"","internalType":"uint32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isAttested","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isGlobalTipped","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isNonWatcherRole","inputs":[{"type":"bytes32","name":"role_","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isPathTripped","inputs":[{"type":"uint32","name":"","internalType":"uint32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isProposalTripped","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isRootValid","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nextNonce","inputs":[{"type":"address","name":"","internalType":"address"}]},{"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":"receiveFees","inputs":[{"type":"uint32","name":"","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registerSiblingSlug","inputs":[{"type":"uint32","name":"siblingChainSlug_","internalType":"uint32"},{"type":"uint256","name":"maxPacketLength_","internalType":"uint256"},{"type":"uint256","name":"capacitorType_","internalType":"uint256"},{"type":"uint256","name":"initialPacketCount_","internalType":"uint256"},{"type":"address","name":"siblingSwitchboard_","internalType":"address"}]},{"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":"revokeBatchRole","inputs":[{"type":"bytes32[]","name":"roleNames_","internalType":"bytes32[]"},{"type":"uint32[]","name":"slugs_","internalType":"uint32[]"},{"type":"address[]","name":"grantees_","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role_","internalType":"bytes32"},{"type":"address","name":"grantee_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRoleWithSlug","inputs":[{"type":"bytes32","name":"roleName_","internalType":"bytes32"},{"type":"uint32","name":"chainSlug_","internalType":"uint32"},{"type":"address","name":"grantee_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeWatcherRole","inputs":[{"type":"uint32","name":"srcChainSlug_","internalType":"uint32"},{"type":"address","name":"watcher_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFees","inputs":[{"type":"uint256","name":"nonce_","internalType":"uint256"},{"type":"uint32","name":"dstChainSlug_","internalType":"uint32"},{"type":"uint128","name":"switchboardFees_","internalType":"uint128"},{"type":"uint128","name":"verificationOverheadFees_","internalType":"uint128"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ISignatureVerifier"}],"name":"signatureVerifier__","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ISocket"}],"name":"socket__","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"timeoutInSeconds","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalWatchers","inputs":[{"type":"uint32","name":"","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tripGlobal","inputs":[{"type":"uint256","name":"nonce_","internalType":"uint256"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tripPath","inputs":[{"type":"uint256","name":"nonce_","internalType":"uint256"},{"type":"uint32","name":"srcChainSlug_","internalType":"uint32"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tripProposal","inputs":[{"type":"uint256","name":"nonce_","internalType":"uint256"},{"type":"bytes32","name":"packetId_","internalType":"bytes32"},{"type":"uint256","name":"proposalCount_","internalType":"uint256"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unTrip","inputs":[{"type":"uint256","name":"nonce_","internalType":"uint256"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unTripPath","inputs":[{"type":"uint256","name":"nonce_","internalType":"uint256"},{"type":"uint32","name":"srcChainSlug_","internalType":"uint32"},{"type":"bytes","name":"signature_","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateSibling","inputs":[{"type":"uint32","name":"siblingChainSlug_","internalType":"uint32"},{"type":"address","name":"siblingSwitchboard_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawFees","inputs":[{"type":"address","name":"withdrawTo_","internalType":"address"}]}]
Contract Creation Code
0x6101006040523480156200001257600080fd5b5060405162004034380380620040348339810160408190526200003591620000e2565b83838383888080620000478162000076565b5050506001600160a01b0393841660a05263ffffffff90921660c05260e05216608052506200015e9350505050565b600080546001600160a01b0383166001600160a01b0319918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b6001600160a01b0381168114620000df57600080fd5b50565b600080600080600060a08688031215620000fb57600080fd5b85516200010881620000c9565b60208701519095506200011b81620000c9565b604087015190945063ffffffff811681146200013657600080fd5b6060870151608088015191945092506200015081620000c9565b809150509295509295909350565b60805160a05160c05160e051613e176200021d600039600081816109b201526117dc0152600081816107c00152818161122201528181611acf01528181611d0e0152818161233a0152818161267c015281816129e40152612c7701526000818161085901528181612059015281816121a301528181612206015261257a0152600081816109070152818161126f01528181611b1c01528181611d4d01528181612387015281816126fb01528181612a310152612ce50152613e176000f3fe6080604052600436106102dc5760003560e01c806389ee875511610184578063c6a261d2116100d6578063d5b8da731161008a578063efec6cd411610064578063efec6cd4146109a0578063f22b460e146109d4578063f37f3e37146109f457600080fd5b8063d5b8da73146108f5578063ecb9748414610929578063ef7b76901461094957600080fd5b8063d2883fbe116100bb578063d2883fbe14610895578063d39a38c1146108b5578063d547741f146108d557600080fd5b8063c6a261d214610847578063c87793081461087b57600080fd5b8063a085186311610138578063b349ba6511610112578063b349ba65146107ae578063b947aac6146107f7578063be94a4761461082757600080fd5b8063a085186314610740578063a9a541b214610760578063aac965571461077357600080fd5b806391d148541161016957806391d14854146106d3578063940992a3146106f35780639aefe4e01461072057600080fd5b806389ee8755146106885780638da5cb5b146106a857600080fd5b80632f2ff15d1161023d5780634c408180116101f15780637a83df80116101cb5780637a83df801461061b5780637c1388141461063b57806382987d301461066857600080fd5b80634c408180146105bb5780635b94db27146105db5780636ccae054146105fb57600080fd5b806331d1e0631161022257806331d1e0631461054b5780633bd1adec1461058657806343fa97ca1461059b57600080fd5b80632f2ff15d146104eb57806330ef41b41461050b57600080fd5b80631e8673111161029457806320f99c0a1161027957806320f99c0a1461045f57806325750e37146104ab5780632e7eb258146104cb57600080fd5b80631e8673111461041f5780632085254a1461043f57600080fd5b80630de8ede4116102c55780630de8ede4146103bd578063164e68de146103df5780631ba8e484146103ff57600080fd5b806306da42e0146102e15780630cd55abf14610382575b600080fd5b3480156102ed57600080fd5b506103546102fc3660046134cb565b63ffffffff166000908152600760209081526040918290208251808401909352546fffffffffffffffffffffffffffffffff808216808552700100000000000000000000000000000000909204169290910182905291565b604080516fffffffffffffffffffffffffffffffff9384168152929091166020830152015b60405180910390f35b34801561038e57600080fd5b506103af61039d366004613508565b60066020526000908152604090205481565b604051908152602001610379565b3480156103c957600080fd5b506103dd6103d836600461352c565b610a21565b005b3480156103eb57600080fd5b506103dd6103fa366004613508565b610c37565b34801561040b57600080fd5b506103dd61041a366004613563565b610d1f565b34801561042b57600080fd5b506103dd61043a3660046135ef565b610dd9565b34801561044b57600080fd5b506103dd61045a36600461352c565b610fd7565b34801561046b57600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610379565b3480156104b757600080fd5b506103dd6104c6366004613763565b6111e9565b3480156104d757600080fd5b506103dd6104e6366004613563565b611402565b3480156104f757600080fd5b506103dd6105063660046137aa565b6114b7565b34801561051757600080fd5b5061053b6105263660046137cf565b600c6020526000908152604090205460ff1681565b6040519015158152602001610379565b34801561055757600080fd5b5061053b6105663660046137e8565b600a60209081526000928352604080842090915290825290205460ff1681565b34801561059257600080fd5b506103dd611520565b3480156105a757600080fd5b506103dd6105b63660046135ef565b61157c565b3480156105c757600080fd5b5061053b6105d6366004613814565b611732565b3480156105e757600080fd5b506103dd6105f6366004613508565b611824565b34801561060757600080fd5b506103dd61061636600461385b565b6118e4565b34801561062757600080fd5b5061053b6106363660046137cf565b611982565b34801561064757600080fd5b506103af6106563660046134cb565b60086020526000908152604090205481565b34801561067457600080fd5b506103dd61068336600461389c565b611a8e565b34801561069457600080fd5b506103dd6106a33660046138f3565b611cc9565b3480156106b457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610486565b3480156106df57600080fd5b5061053b6106ee3660046137aa565b611f1c565b3480156106ff57600080fd5b506103af61070e3660046137cf565b600b6020526000908152604090205481565b34801561072c57600080fd5b506103dd61073b36600461394d565b611f59565b34801561074c57600080fd5b506103dd61075b36600461352c565b6120c5565b6103dd61076e3660046134cb565b612204565b34801561077f57600080fd5b5061053b61078e3660046139a1565b600560209081526000928352604080842090915290825290205460ff1681565b3480156107ba57600080fd5b506107e27f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610379565b34801561080357600080fd5b5061053b6108123660046134cb565b60046020526000908152604090205460ff1681565b34801561083357600080fd5b506103dd61084236600461389c565b6122f9565b34801561085357600080fd5b506104867f000000000000000000000000000000000000000000000000000000000000000081565b34801561088757600080fd5b5060035461053b9060ff1681565b3480156108a157600080fd5b506103dd6108b0366004613a05565b612535565b3480156108c157600080fd5b506103dd6108d0366004613763565b6129ab565b3480156108e157600080fd5b506103dd6108f03660046137aa565b612bc0565b34801561090157600080fd5b506104867f000000000000000000000000000000000000000000000000000000000000000081565b34801561093557600080fd5b5061053b610944366004613563565b612c29565b34801561095557600080fd5b506103546109643660046134cb565b6007602052600090815260409020546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b3480156109ac57600080fd5b506103af7f000000000000000000000000000000000000000000000000000000000000000081565b3480156109e057600080fd5b506103dd6109ef366004613a86565b612c3e565b348015610a0057600080fd5b506103af610a0f3660046134cb565b60096020526000908152604090205481565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16610ab3576040517f962f6333000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b610ade7f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898484612eee565b610b14576040517fa278e4ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b3f7f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898484612f7c565b63ffffffff8316600090815260076020908152604080832060099092529091205460016fffffffffffffffffffffffffffffffff8216118015610b94575081546fffffffffffffffffffffffffffffffff1615155b15610c0a5780610ba5600182613b22565b8354610bc391906fffffffffffffffffffffffffffffffff16613b52565b610bcd9190613b86565b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff919091161782555b63ffffffff85166000908152600960205260408120805491610c2b83613bdc565b91905055505050505050565b3360009081527f9b7ec560033b826bc253a5f1a8b5e6a61c8d2a6e8731a9872b68296f6024c5ac60205260409020547f5d8e12c39142ff96d79d04d15d1ba1269e4fe57bb9d26f43523628b34ba108ec9060ff16610cc4576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b73ffffffffffffffffffffffffffffffffffffffff8216610d11576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1b8247612fbd565b5050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610d70576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d38314610dc9576040517fd954416a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd4838383612f7c565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e2a576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8481141580610e395750848314155b15610e70576040517f11e86f7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b81811015610fcd57610e9d888883818110610e9157610e91613c11565b90506020020135611982565b15610f93576000868683818110610eb657610eb6613c11565b9050602002016020810190610ecb91906134cb565b63ffffffff161115610f4b57610f46888883818110610eec57610eec613c11565b90506020020135878784818110610f0557610f05613c11565b9050602002016020810190610f1a91906134cb565b868685818110610f2c57610f2c613c11565b9050602002016020810190610f419190613508565b612f7c565b610fc5565b610f46888883818110610f6057610f60613c11565b90506020020135858584818110610f7957610f79613c11565b9050602002016020810190610f8e9190613508565b613032565b6040517fd954416a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101610e74565b5050505050505050565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16611064576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b61108f7f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898484612eee565b156110c6576040517fa080b55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f17f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c8984846130b5565b63ffffffff831660009081526007602090815260408083206009909252909120546fffffffffffffffffffffffffffffffff811615801590611145575081546fffffffffffffffffffffffffffffffff1615155b156111bb5780611156816001613c40565b835461117491906fffffffffffffffffffffffffffffffff16613b52565b61117e9190613b86565b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff919091161782555b63ffffffff8516600090815260096020526040812080549091906111de90613c69565b909155505050505050565b604080517fa7d456f2444d46f78a51def7ecdf108d72f72da2e10ce355caa5e99a17bed2de6020820152309181019190915263ffffffff7f000000000000000000000000000000000000000000000000000000000000000016606082015260808101839052600060a082018190529073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906397aba7f99060c00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b81526004016112cd929190613ca1565b602060405180830381865afa1580156112ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130e9190613d15565b905061133a7fb0a81253fa41a5cd7c5885c220c30d519a0d552984e0d69edda03449f69fa6db826130f6565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260066020526040902080546001810190915583146113a0576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055604051600081527f42a60e0dc12250ea896a8f5156f1f3aa4567cd2bd87a72bc097210dc02ec2e05906020015b60405180910390a1505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611453576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d383146114ac576040517fd954416a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd48383836130b5565b60005473ffffffffffffffffffffffffffffffffffffffff163314611508576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61151182611982565b15610f9357610d1b8282613162565b60015473ffffffffffffffffffffffffffffffffffffffff163314611571576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61157a336131e8565b565b60005473ffffffffffffffffffffffffffffffffffffffff1633146115cd576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84811415806115dc5750848314155b15611613576040517f11e86f7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b81811015610fcd57611634888883818110610e9157610e91613c11565b15610f9357600086868381811061164d5761164d613c11565b905060200201602081019061166291906134cb565b63ffffffff1611156116e2576116dd88888381811061168357611683613c11565b9050602002013587878481811061169c5761169c613c11565b90506020020160208101906116b191906134cb565b8686858181106116c3576116c3613c11565b90506020020160208101906116d89190613508565b6130b5565b61172a565b6116dd8888838181106116f7576116f7613c11565b9050602002013585858481811061171057611710613c11565b90506020020160208101906117259190613508565b613162565b600101611617565b600354600090859060ff168061175d575063ffffffff841660009081526004602052604090205460ff165b806117815750600086815260056020908152604080832088845290915290205460ff165b806117aa575063ffffffff841660009081526008602052604090205467ffffffffffffffff8216105b156117b957600091505061181b565b6000878152600c602052604090205460ff16156117da57600191505061181b565b7f00000000000000000000000000000000000000000000000000000000000000006118058442613d32565b111561181557600191505061181b565b60009150505b95945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611875576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b3360009081527f4933f7bec34ee32db93e9f5cd7e0519781b395282211f4f6857489046ea38f7660205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16611971576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b61197c848484613260565b50505050565b60007fd66a896d5b3e8cd31bfd557e6ebdb5bba4561c7ce6573d5c6716876cbc83dbdd8214806119d157507fb0a81253fa41a5cd7c5885c220c30d519a0d552984e0d69edda03449f69fa6db82145b806119fb57507f5d8e12c39142ff96d79d04d15d1ba1269e4fe57bb9d26f43523628b34ba108ec82145b80611a2557507fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f2925382145b80611a4f57507f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb182145b80611a7957507f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d382145b15611a8657506001919050565b506000919050565b604080517fc9c6c9004cdd87e14f6d7be800064bea944fdafc4e66c833d9da91c10d4ced356020820152309181019190915263ffffffff80841660608301527f000000000000000000000000000000000000000000000000000000000000000016608082015260a08101849052600060c082018190529073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906397aba7f99060e00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b8152600401611b7a929190613ca1565b602060405180830381865afa158015611b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbb9190613d15565b9050611be77fb0a81253fa41a5cd7c5885c220c30d519a0d552984e0d69edda03449f69fa6db826130f6565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558414611c4d576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8316600081815260046020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690558051938452908301919091527f43b1a9ba48bea5f72f92aaaf96b18ec134518690384df35d04b1316ca68147e191015b60405180910390a150505050565b604080517f119101cda5c91f869cec4313411657e772bec4c5a184dd6ab7a60859e3d347836020820152309181019190915260e084811c6060830181905263ffffffff7f000000000000000000000000000000000000000000000000000000000000000016608084015260a0830187905260c08301869052908201849052906000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906397aba7f9906101000160405160208183030381529060405280519060200120856040518363ffffffff1660e01b8152600401611dc1929190613ca1565b602060405180830381865afa158015611dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e029190613d15565b9050611e357f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898363ffffffff1683613350565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558614611e9b576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260056020908152604080832087845282529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905581518781529081018690527f876c775784dd569d1ecd6e1a52c87169c22c4e906a138c91a59f451fa0f431b5910160405180910390a1505050505050565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff165b90505b92915050565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16611fe6576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b63ffffffff861660008181526008602052604090819020859055517f0b04bb410000000000000000000000000000000000000000000000000000000081526004810191909152602481018690526044810185905273ffffffffffffffffffffffffffffffffffffffff83811660648301527f00000000000000000000000000000000000000000000000000000000000000001690630b04bb419060840160408051808303816000875af11580156120a1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcd9190613d45565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16612152576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b6040517f81403a0200000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301527f000000000000000000000000000000000000000000000000000000000000000016906381403a0290604401600060405180830381600087803b1580156121e757600080fd5b505af11580156121fb573d6000803e3d6000fd5b50505050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16626186f76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561226e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122929190613d15565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122f6576040517f03c377c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b604080517f4623a5d5b82ed238c8ba6e51cffa4ddaf241150d9af32813faab00132f55a4566020820152309181019190915263ffffffff80841660608301527f000000000000000000000000000000000000000000000000000000000000000016608082015260a08101849052600160c082015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906397aba7f99060e00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b81526004016123e5929190613ca1565b602060405180830381865afa158015612402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124269190613d15565b90506124597f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898463ffffffff1683613350565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260066020526040902080546001810190915584146124bf576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff831660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558251938452908301527f43b1a9ba48bea5f72f92aaaf96b18ec134518690384df35d04b1316ca68147e19101611cbb565b6040517f4ad701bc000000000000000000000000000000000000000000000000000000008152600481018690526024810185905230604482015260e086901c906000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634ad701bc90606401602060405180830381865afa1580156125d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125fa9190613d74565b905080612633576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84811461266c576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051306020808301919091527f000000000000000000000000000000000000000000000000000000000000000063ffffffff1682840152606082018a90526080820189905260a08083018990528351808403909101815260c083019384905280519101207f97aba7f9000000000000000000000000000000000000000000000000000000009092526000917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16916397aba7f991612747918990899060c401613d8d565b602060405180830381865afa158015612764573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127889190613d15565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a6020908152604080832086845290915290205490915060ff16156127f6576040517f35d9080500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128217f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898483612eee565b612857576040517fa278e4ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a60209081526040808320858452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055600b909152812080549091906128c590613c69565b9091555063ffffffff8316600090815260096020908152604080832054858452600b909252909120541061292b576000828152600c6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b6000828152600b60209081526040918290205482518b81529182018a905291810184905273ffffffffffffffffffffffffffffffffffffffff8316606082015260808101919091527f8cfaedaacfb148cee9ec5ed036100285c6f3ede20c1637a618733cbd631e92079060a0015b60405180910390a15050505050505050565b604080517fbdfa1b22b053b80e8209cb4421ce6b25b45bb164e02718501cb19a46c4df987c6020820152309181019190915263ffffffff7f000000000000000000000000000000000000000000000000000000000000000016606082015260808101839052600160a082015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906397aba7f99060c00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b8152600401612a8f929190613ca1565b602060405180830381865afa158015612aac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ad09190613d15565b9050612afc7fd66a896d5b3e8cd31bfd557e6ebdb5bba4561c7ce6573d5c6716876cbc83dbdd826130f6565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558314612b62576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091556040519081527f42a60e0dc12250ea896a8f5156f1f3aa4567cd2bd87a72bc097210dc02ec2e05906020016113f5565b60005473ffffffffffffffffffffffffffffffffffffffff163314612c11576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c1a82611982565b15610f9357610d1b8282613032565b6000612c36848484612eee565b949350505050565b604080517ff2deac1744cfd52ddfce21c5ff26abf413f6418dc4a35916e26daefd87e04ab66020820152309181019190915263ffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660608301528616608082015260a081018790526fffffffffffffffffffffffffffffffff80861660c0830152841660e082015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906397aba7f990610100016040516020818303038152906040528051906020012085856040518463ffffffff1660e01b8152600401612d4693929190613d8d565b602060405180830381865afa158015612d63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d879190613d15565b9050612dba7f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d38763ffffffff1683613350565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558714612e20576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825263ffffffff881660009081526009602052918220548190612e4a9089613b52565b6fffffffffffffffffffffffffffffffff908116825287811660209283015263ffffffff8a166000818152600784526040908190208551868601805186167001000000000000000000000000000000000291861691909117909155815192835285518416948301949094529251909116918101919091529091507faebc3dd035ce00dd026bd60872bea44979224507aa49434f67ec5f9ae924aaa390606001612999565b6000612c368484604051602001612f1592919091825263ffffffff16602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009081526002835281812073ffffffffffffffffffffffffffffffffffffffff8716825290925290205460ff1690565b610dd48383604051602001612fa192919091825263ffffffff16602082015260400190565b6040516020818303038152906040528051906020012082613032565b600080600080600085875af1905080610dd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610aaa565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b610dd483836040516020016130da92919091825263ffffffff16602082015260400190565b6040516020818303038152906040528051906020012082613162565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610d1b576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101839052602401610aaa565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6000805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b73ffffffffffffffffffffffffffffffffffffffff82166132ad576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8416016132f457610dd48282612fbd565b8273ffffffffffffffffffffffffffffffffffffffff163b600003613345576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd48383836133e3565b6040805160208082018690528183018590528251808303840181526060909201835281519181019190912060008181526002835283812073ffffffffffffffffffffffffffffffffffffffff861682529092529190205460ff1661197c576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061197c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610aaa565b803563ffffffff811681146134c657600080fd5b919050565b6000602082840312156134dd57600080fd5b611f50826134b2565b73ffffffffffffffffffffffffffffffffffffffff811681146122f657600080fd5b60006020828403121561351a57600080fd5b8135613525816134e6565b9392505050565b6000806040838503121561353f57600080fd5b613548836134b2565b91506020830135613558816134e6565b809150509250929050565b60008060006060848603121561357857600080fd5b83359250613588602085016134b2565b91506040840135613598816134e6565b809150509250925092565b60008083601f8401126135b557600080fd5b50813567ffffffffffffffff8111156135cd57600080fd5b6020830191508360208260051b85010111156135e857600080fd5b9250929050565b6000806000806000806060878903121561360857600080fd5b863567ffffffffffffffff8082111561362057600080fd5b61362c8a838b016135a3565b9098509650602089013591508082111561364557600080fd5b6136518a838b016135a3565b9096509450604089013591508082111561366a57600080fd5b5061367789828a016135a3565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126136c957600080fd5b813567ffffffffffffffff808211156136e4576136e4613689565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561372a5761372a613689565b8160405283815286602085880101111561374357600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561377657600080fd5b82359150602083013567ffffffffffffffff81111561379457600080fd5b6137a0858286016136b8565b9150509250929050565b600080604083850312156137bd57600080fd5b823591506020830135613558816134e6565b6000602082840312156137e157600080fd5b5035919050565b600080604083850312156137fb57600080fd5b8235613806816134e6565b946020939093013593505050565b600080600080600060a0868803121561382c57600080fd5b85359450602086013593506040860135925061384a606087016134b2565b949793965091946080013592915050565b60008060006060848603121561387057600080fd5b833561387b816134e6565b9250602084013561388b816134e6565b929592945050506040919091013590565b6000806000606084860312156138b157600080fd5b833592506138c1602085016134b2565b9150604084013567ffffffffffffffff8111156138dd57600080fd5b6138e9868287016136b8565b9150509250925092565b6000806000806080858703121561390957600080fd5b843593506020850135925060408501359150606085013567ffffffffffffffff81111561393557600080fd5b613941878288016136b8565b91505092959194509250565b600080600080600060a0868803121561396557600080fd5b61396e866134b2565b94506020860135935060408601359250606086013591506080860135613993816134e6565b809150509295509295909350565b600080604083850312156139b457600080fd5b50508035926020909101359150565b60008083601f8401126139d557600080fd5b50813567ffffffffffffffff8111156139ed57600080fd5b6020830191508360208285010111156135e857600080fd5b600080600080600060808688031215613a1d57600080fd5b853594506020860135935060408601359250606086013567ffffffffffffffff811115613a4957600080fd5b613a55888289016139c3565b969995985093965092949392505050565b80356fffffffffffffffffffffffffffffffff811681146134c657600080fd5b60008060008060008060a08789031215613a9f57600080fd5b86359550613aaf602088016134b2565b9450613abd60408801613a66565b9350613acb60608801613a66565b9250608087013567ffffffffffffffff811115613ae757600080fd5b61367789828a016139c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6fffffffffffffffffffffffffffffffff828116828216039080821115613b4b57613b4b613af3565b5092915050565b6fffffffffffffffffffffffffffffffff818116838216028082169190828114613b7e57613b7e613af3565b505092915050565b60006fffffffffffffffffffffffffffffffff80841680613bd0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600081613beb57613beb613af3565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6fffffffffffffffffffffffffffffffff818116838216019080821115613b4b57613b4b613af3565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613c9a57613c9a613af3565b5060010190565b82815260006020604081840152835180604085015260005b81811015613cd557858101830151858201606001528201613cb9565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b600060208284031215613d2757600080fd5b8151613525816134e6565b81810381811115611f5357611f53613af3565b60008060408385031215613d5857600080fd5b8251613d63816134e6565b6020840151909250613558816134e6565b600060208284031215613d8657600080fd5b5051919050565b83815260406020820152816040820152818360608301376000818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019291505056fea2646970667358221220b8cec95aaa2ff8833457ec547d323dec00d40f2da3558028ac79ac5d9335aa5364736f6c6343000813003300000000000000000000000044a44837894b5edc2bde64567fc62599b3b88f4c0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f0000000000000000000000000000000000000000000000000000000000028c610000000000000000000000000000000000000000000000000000000000001c20000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f
Deployed ByteCode
0x6080604052600436106102dc5760003560e01c806389ee875511610184578063c6a261d2116100d6578063d5b8da731161008a578063efec6cd411610064578063efec6cd4146109a0578063f22b460e146109d4578063f37f3e37146109f457600080fd5b8063d5b8da73146108f5578063ecb9748414610929578063ef7b76901461094957600080fd5b8063d2883fbe116100bb578063d2883fbe14610895578063d39a38c1146108b5578063d547741f146108d557600080fd5b8063c6a261d214610847578063c87793081461087b57600080fd5b8063a085186311610138578063b349ba6511610112578063b349ba65146107ae578063b947aac6146107f7578063be94a4761461082757600080fd5b8063a085186314610740578063a9a541b214610760578063aac965571461077357600080fd5b806391d148541161016957806391d14854146106d3578063940992a3146106f35780639aefe4e01461072057600080fd5b806389ee8755146106885780638da5cb5b146106a857600080fd5b80632f2ff15d1161023d5780634c408180116101f15780637a83df80116101cb5780637a83df801461061b5780637c1388141461063b57806382987d301461066857600080fd5b80634c408180146105bb5780635b94db27146105db5780636ccae054146105fb57600080fd5b806331d1e0631161022257806331d1e0631461054b5780633bd1adec1461058657806343fa97ca1461059b57600080fd5b80632f2ff15d146104eb57806330ef41b41461050b57600080fd5b80631e8673111161029457806320f99c0a1161027957806320f99c0a1461045f57806325750e37146104ab5780632e7eb258146104cb57600080fd5b80631e8673111461041f5780632085254a1461043f57600080fd5b80630de8ede4116102c55780630de8ede4146103bd578063164e68de146103df5780631ba8e484146103ff57600080fd5b806306da42e0146102e15780630cd55abf14610382575b600080fd5b3480156102ed57600080fd5b506103546102fc3660046134cb565b63ffffffff166000908152600760209081526040918290208251808401909352546fffffffffffffffffffffffffffffffff808216808552700100000000000000000000000000000000909204169290910182905291565b604080516fffffffffffffffffffffffffffffffff9384168152929091166020830152015b60405180910390f35b34801561038e57600080fd5b506103af61039d366004613508565b60066020526000908152604090205481565b604051908152602001610379565b3480156103c957600080fd5b506103dd6103d836600461352c565b610a21565b005b3480156103eb57600080fd5b506103dd6103fa366004613508565b610c37565b34801561040b57600080fd5b506103dd61041a366004613563565b610d1f565b34801561042b57600080fd5b506103dd61043a3660046135ef565b610dd9565b34801561044b57600080fd5b506103dd61045a36600461352c565b610fd7565b34801561046b57600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610379565b3480156104b757600080fd5b506103dd6104c6366004613763565b6111e9565b3480156104d757600080fd5b506103dd6104e6366004613563565b611402565b3480156104f757600080fd5b506103dd6105063660046137aa565b6114b7565b34801561051757600080fd5b5061053b6105263660046137cf565b600c6020526000908152604090205460ff1681565b6040519015158152602001610379565b34801561055757600080fd5b5061053b6105663660046137e8565b600a60209081526000928352604080842090915290825290205460ff1681565b34801561059257600080fd5b506103dd611520565b3480156105a757600080fd5b506103dd6105b63660046135ef565b61157c565b3480156105c757600080fd5b5061053b6105d6366004613814565b611732565b3480156105e757600080fd5b506103dd6105f6366004613508565b611824565b34801561060757600080fd5b506103dd61061636600461385b565b6118e4565b34801561062757600080fd5b5061053b6106363660046137cf565b611982565b34801561064757600080fd5b506103af6106563660046134cb565b60086020526000908152604090205481565b34801561067457600080fd5b506103dd61068336600461389c565b611a8e565b34801561069457600080fd5b506103dd6106a33660046138f3565b611cc9565b3480156106b457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610486565b3480156106df57600080fd5b5061053b6106ee3660046137aa565b611f1c565b3480156106ff57600080fd5b506103af61070e3660046137cf565b600b6020526000908152604090205481565b34801561072c57600080fd5b506103dd61073b36600461394d565b611f59565b34801561074c57600080fd5b506103dd61075b36600461352c565b6120c5565b6103dd61076e3660046134cb565b612204565b34801561077f57600080fd5b5061053b61078e3660046139a1565b600560209081526000928352604080842090915290825290205460ff1681565b3480156107ba57600080fd5b506107e27f0000000000000000000000000000000000000000000000000000000000028c6181565b60405163ffffffff9091168152602001610379565b34801561080357600080fd5b5061053b6108123660046134cb565b60046020526000908152604090205460ff1681565b34801561083357600080fd5b506103dd61084236600461389c565b6122f9565b34801561085357600080fd5b506104867f0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f81565b34801561088757600080fd5b5060035461053b9060ff1681565b3480156108a157600080fd5b506103dd6108b0366004613a05565b612535565b3480156108c157600080fd5b506103dd6108d0366004613763565b6129ab565b3480156108e157600080fd5b506103dd6108f03660046137aa565b612bc0565b34801561090157600080fd5b506104867f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f81565b34801561093557600080fd5b5061053b610944366004613563565b612c29565b34801561095557600080fd5b506103546109643660046134cb565b6007602052600090815260409020546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b3480156109ac57600080fd5b506103af7f0000000000000000000000000000000000000000000000000000000000001c2081565b3480156109e057600080fd5b506103dd6109ef366004613a86565b612c3e565b348015610a0057600080fd5b506103af610a0f3660046134cb565b60096020526000908152604090205481565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16610ab3576040517f962f6333000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b610ade7f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898484612eee565b610b14576040517fa278e4ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b3f7f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898484612f7c565b63ffffffff8316600090815260076020908152604080832060099092529091205460016fffffffffffffffffffffffffffffffff8216118015610b94575081546fffffffffffffffffffffffffffffffff1615155b15610c0a5780610ba5600182613b22565b8354610bc391906fffffffffffffffffffffffffffffffff16613b52565b610bcd9190613b86565b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff919091161782555b63ffffffff85166000908152600960205260408120805491610c2b83613bdc565b91905055505050505050565b3360009081527f9b7ec560033b826bc253a5f1a8b5e6a61c8d2a6e8731a9872b68296f6024c5ac60205260409020547f5d8e12c39142ff96d79d04d15d1ba1269e4fe57bb9d26f43523628b34ba108ec9060ff16610cc4576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b73ffffffffffffffffffffffffffffffffffffffff8216610d11576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1b8247612fbd565b5050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610d70576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d38314610dc9576040517fd954416a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd4838383612f7c565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e2a576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8481141580610e395750848314155b15610e70576040517f11e86f7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b81811015610fcd57610e9d888883818110610e9157610e91613c11565b90506020020135611982565b15610f93576000868683818110610eb657610eb6613c11565b9050602002016020810190610ecb91906134cb565b63ffffffff161115610f4b57610f46888883818110610eec57610eec613c11565b90506020020135878784818110610f0557610f05613c11565b9050602002016020810190610f1a91906134cb565b868685818110610f2c57610f2c613c11565b9050602002016020810190610f419190613508565b612f7c565b610fc5565b610f46888883818110610f6057610f60613c11565b90506020020135858584818110610f7957610f79613c11565b9050602002016020810190610f8e9190613508565b613032565b6040517fd954416a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101610e74565b5050505050505050565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16611064576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b61108f7f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898484612eee565b156110c6576040517fa080b55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f17f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c8984846130b5565b63ffffffff831660009081526007602090815260408083206009909252909120546fffffffffffffffffffffffffffffffff811615801590611145575081546fffffffffffffffffffffffffffffffff1615155b156111bb5780611156816001613c40565b835461117491906fffffffffffffffffffffffffffffffff16613b52565b61117e9190613b86565b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff919091161782555b63ffffffff8516600090815260096020526040812080549091906111de90613c69565b909155505050505050565b604080517fa7d456f2444d46f78a51def7ecdf108d72f72da2e10ce355caa5e99a17bed2de6020820152309181019190915263ffffffff7f0000000000000000000000000000000000000000000000000000000000028c6116606082015260808101839052600060a082018190529073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f16906397aba7f99060c00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b81526004016112cd929190613ca1565b602060405180830381865afa1580156112ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130e9190613d15565b905061133a7fb0a81253fa41a5cd7c5885c220c30d519a0d552984e0d69edda03449f69fa6db826130f6565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260066020526040902080546001810190915583146113a0576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055604051600081527f42a60e0dc12250ea896a8f5156f1f3aa4567cd2bd87a72bc097210dc02ec2e05906020015b60405180910390a1505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611453576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d383146114ac576040517fd954416a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd48383836130b5565b60005473ffffffffffffffffffffffffffffffffffffffff163314611508576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61151182611982565b15610f9357610d1b8282613162565b60015473ffffffffffffffffffffffffffffffffffffffff163314611571576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61157a336131e8565b565b60005473ffffffffffffffffffffffffffffffffffffffff1633146115cd576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84811415806115dc5750848314155b15611613576040517f11e86f7300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b81811015610fcd57611634888883818110610e9157610e91613c11565b15610f9357600086868381811061164d5761164d613c11565b905060200201602081019061166291906134cb565b63ffffffff1611156116e2576116dd88888381811061168357611683613c11565b9050602002013587878481811061169c5761169c613c11565b90506020020160208101906116b191906134cb565b8686858181106116c3576116c3613c11565b90506020020160208101906116d89190613508565b6130b5565b61172a565b6116dd8888838181106116f7576116f7613c11565b9050602002013585858481811061171057611710613c11565b90506020020160208101906117259190613508565b613162565b600101611617565b600354600090859060ff168061175d575063ffffffff841660009081526004602052604090205460ff165b806117815750600086815260056020908152604080832088845290915290205460ff165b806117aa575063ffffffff841660009081526008602052604090205467ffffffffffffffff8216105b156117b957600091505061181b565b6000878152600c602052604090205460ff16156117da57600191505061181b565b7f0000000000000000000000000000000000000000000000000000000000001c206118058442613d32565b111561181557600191505061181b565b60009150505b95945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611875576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b3360009081527f4933f7bec34ee32db93e9f5cd7e0519781b395282211f4f6857489046ea38f7660205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16611971576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b61197c848484613260565b50505050565b60007fd66a896d5b3e8cd31bfd557e6ebdb5bba4561c7ce6573d5c6716876cbc83dbdd8214806119d157507fb0a81253fa41a5cd7c5885c220c30d519a0d552984e0d69edda03449f69fa6db82145b806119fb57507f5d8e12c39142ff96d79d04d15d1ba1269e4fe57bb9d26f43523628b34ba108ec82145b80611a2557507fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f2925382145b80611a4f57507f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb182145b80611a7957507f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d382145b15611a8657506001919050565b506000919050565b604080517fc9c6c9004cdd87e14f6d7be800064bea944fdafc4e66c833d9da91c10d4ced356020820152309181019190915263ffffffff80841660608301527f0000000000000000000000000000000000000000000000000000000000028c6116608082015260a08101849052600060c082018190529073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f16906397aba7f99060e00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b8152600401611b7a929190613ca1565b602060405180830381865afa158015611b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbb9190613d15565b9050611be77fb0a81253fa41a5cd7c5885c220c30d519a0d552984e0d69edda03449f69fa6db826130f6565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558414611c4d576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8316600081815260046020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690558051938452908301919091527f43b1a9ba48bea5f72f92aaaf96b18ec134518690384df35d04b1316ca68147e191015b60405180910390a150505050565b604080517f119101cda5c91f869cec4313411657e772bec4c5a184dd6ab7a60859e3d347836020820152309181019190915260e084811c6060830181905263ffffffff7f0000000000000000000000000000000000000000000000000000000000028c6116608084015260a0830187905260c08301869052908201849052906000907f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f73ffffffffffffffffffffffffffffffffffffffff16906397aba7f9906101000160405160208183030381529060405280519060200120856040518363ffffffff1660e01b8152600401611dc1929190613ca1565b602060405180830381865afa158015611dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e029190613d15565b9050611e357f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898363ffffffff1683613350565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558614611e9b576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260056020908152604080832087845282529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905581518781529081018690527f876c775784dd569d1ecd6e1a52c87169c22c4e906a138c91a59f451fa0f431b5910160405180910390a1505050505050565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff165b90505b92915050565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16611fe6576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b63ffffffff861660008181526008602052604090819020859055517f0b04bb410000000000000000000000000000000000000000000000000000000081526004810191909152602481018690526044810185905273ffffffffffffffffffffffffffffffffffffffff83811660648301527f0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f1690630b04bb419060840160408051808303816000875af11580156120a1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcd9190613d45565b3360009081527f0f4a237b7f9e071a612ddea08a06576f25cc7dffe13a46307ee64fe2318b3c3e60205260409020547f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19060ff16612152576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b6040517f81403a0200000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301527f0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f16906381403a0290604401600060405180830381600087803b1580156121e757600080fd5b505af11580156121fb573d6000803e3d6000fd5b50505050505050565b7f0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f73ffffffffffffffffffffffffffffffffffffffff16626186f76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561226e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122929190613d15565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122f6576040517f03c377c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b604080517f4623a5d5b82ed238c8ba6e51cffa4ddaf241150d9af32813faab00132f55a4566020820152309181019190915263ffffffff80841660608301527f0000000000000000000000000000000000000000000000000000000000028c6116608082015260a08101849052600160c082015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f16906397aba7f99060e00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b81526004016123e5929190613ca1565b602060405180830381865afa158015612402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124269190613d15565b90506124597f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898463ffffffff1683613350565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260066020526040902080546001810190915584146124bf576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff831660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558251938452908301527f43b1a9ba48bea5f72f92aaaf96b18ec134518690384df35d04b1316ca68147e19101611cbb565b6040517f4ad701bc000000000000000000000000000000000000000000000000000000008152600481018690526024810185905230604482015260e086901c906000907f0000000000000000000000005c4186d343eef952c9ed886e45f8243edf0a503f73ffffffffffffffffffffffffffffffffffffffff1690634ad701bc90606401602060405180830381865afa1580156125d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125fa9190613d74565b905080612633576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84811461266c576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051306020808301919091527f0000000000000000000000000000000000000000000000000000000000028c6163ffffffff1682840152606082018a90526080820189905260a08083018990528351808403909101815260c083019384905280519101207f97aba7f9000000000000000000000000000000000000000000000000000000009092526000917f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f73ffffffffffffffffffffffffffffffffffffffff16916397aba7f991612747918990899060c401613d8d565b602060405180830381865afa158015612764573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127889190613d15565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a6020908152604080832086845290915290205490915060ff16156127f6576040517f35d9080500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128217f2125d1e225cadc5c8296e2cc1f96ee607770bf4a4a16131e62f6819937437c898483612eee565b612857576040517fa278e4ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a60209081526040808320858452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055600b909152812080549091906128c590613c69565b9091555063ffffffff8316600090815260096020908152604080832054858452600b909252909120541061292b576000828152600c6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b6000828152600b60209081526040918290205482518b81529182018a905291810184905273ffffffffffffffffffffffffffffffffffffffff8316606082015260808101919091527f8cfaedaacfb148cee9ec5ed036100285c6f3ede20c1637a618733cbd631e92079060a0015b60405180910390a15050505050505050565b604080517fbdfa1b22b053b80e8209cb4421ce6b25b45bb164e02718501cb19a46c4df987c6020820152309181019190915263ffffffff7f0000000000000000000000000000000000000000000000000000000000028c6116606082015260808101839052600160a082015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f16906397aba7f99060c00160405160208183030381529060405280519060200120846040518363ffffffff1660e01b8152600401612a8f929190613ca1565b602060405180830381865afa158015612aac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ad09190613d15565b9050612afc7fd66a896d5b3e8cd31bfd557e6ebdb5bba4561c7ce6573d5c6716876cbc83dbdd826130f6565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558314612b62576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091556040519081527f42a60e0dc12250ea896a8f5156f1f3aa4567cd2bd87a72bc097210dc02ec2e05906020016113f5565b60005473ffffffffffffffffffffffffffffffffffffffff163314612c11576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c1a82611982565b15610f9357610d1b8282613032565b6000612c36848484612eee565b949350505050565b604080517ff2deac1744cfd52ddfce21c5ff26abf413f6418dc4a35916e26daefd87e04ab66020820152309181019190915263ffffffff7f0000000000000000000000000000000000000000000000000000000000028c61811660608301528616608082015260a081018790526fffffffffffffffffffffffffffffffff80861660c0830152841660e082015260009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ef1ae2f8b4e18afdddf1ca92037772891100a39f16906397aba7f990610100016040516020818303038152906040528051906020012085856040518463ffffffff1660e01b8152600401612d4693929190613d8d565b602060405180830381865afa158015612d63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d879190613d15565b9050612dba7f429672025b2dcb8754ca4b57943f34ac66900598787cb1f1e857291dedbf34d38763ffffffff1683613350565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090208054600181019091558714612e20576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825263ffffffff881660009081526009602052918220548190612e4a9089613b52565b6fffffffffffffffffffffffffffffffff908116825287811660209283015263ffffffff8a166000818152600784526040908190208551868601805186167001000000000000000000000000000000000291861691909117909155815192835285518416948301949094529251909116918101919091529091507faebc3dd035ce00dd026bd60872bea44979224507aa49434f67ec5f9ae924aaa390606001612999565b6000612c368484604051602001612f1592919091825263ffffffff16602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009081526002835281812073ffffffffffffffffffffffffffffffffffffffff8716825290925290205460ff1690565b610dd48383604051602001612fa192919091825263ffffffff16602082015260400190565b6040516020818303038152906040528051906020012082613032565b600080600080600085875af1905080610dd4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610aaa565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b610dd483836040516020016130da92919091825263ffffffff16602082015260400190565b6040516020818303038152906040528051906020012082613162565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610d1b576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101839052602401610aaa565b600082815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6000805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b73ffffffffffffffffffffffffffffffffffffffff82166132ad576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8416016132f457610dd48282612fbd565b8273ffffffffffffffffffffffffffffffffffffffff163b600003613345576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd48383836133e3565b6040805160208082018690528183018590528251808303840181526060909201835281519181019190912060008181526002835283812073ffffffffffffffffffffffffffffffffffffffff861682529092529190205460ff1661197c576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610aaa565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061197c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610aaa565b803563ffffffff811681146134c657600080fd5b919050565b6000602082840312156134dd57600080fd5b611f50826134b2565b73ffffffffffffffffffffffffffffffffffffffff811681146122f657600080fd5b60006020828403121561351a57600080fd5b8135613525816134e6565b9392505050565b6000806040838503121561353f57600080fd5b613548836134b2565b91506020830135613558816134e6565b809150509250929050565b60008060006060848603121561357857600080fd5b83359250613588602085016134b2565b91506040840135613598816134e6565b809150509250925092565b60008083601f8401126135b557600080fd5b50813567ffffffffffffffff8111156135cd57600080fd5b6020830191508360208260051b85010111156135e857600080fd5b9250929050565b6000806000806000806060878903121561360857600080fd5b863567ffffffffffffffff8082111561362057600080fd5b61362c8a838b016135a3565b9098509650602089013591508082111561364557600080fd5b6136518a838b016135a3565b9096509450604089013591508082111561366a57600080fd5b5061367789828a016135a3565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126136c957600080fd5b813567ffffffffffffffff808211156136e4576136e4613689565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561372a5761372a613689565b8160405283815286602085880101111561374357600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561377657600080fd5b82359150602083013567ffffffffffffffff81111561379457600080fd5b6137a0858286016136b8565b9150509250929050565b600080604083850312156137bd57600080fd5b823591506020830135613558816134e6565b6000602082840312156137e157600080fd5b5035919050565b600080604083850312156137fb57600080fd5b8235613806816134e6565b946020939093013593505050565b600080600080600060a0868803121561382c57600080fd5b85359450602086013593506040860135925061384a606087016134b2565b949793965091946080013592915050565b60008060006060848603121561387057600080fd5b833561387b816134e6565b9250602084013561388b816134e6565b929592945050506040919091013590565b6000806000606084860312156138b157600080fd5b833592506138c1602085016134b2565b9150604084013567ffffffffffffffff8111156138dd57600080fd5b6138e9868287016136b8565b9150509250925092565b6000806000806080858703121561390957600080fd5b843593506020850135925060408501359150606085013567ffffffffffffffff81111561393557600080fd5b613941878288016136b8565b91505092959194509250565b600080600080600060a0868803121561396557600080fd5b61396e866134b2565b94506020860135935060408601359250606086013591506080860135613993816134e6565b809150509295509295909350565b600080604083850312156139b457600080fd5b50508035926020909101359150565b60008083601f8401126139d557600080fd5b50813567ffffffffffffffff8111156139ed57600080fd5b6020830191508360208285010111156135e857600080fd5b600080600080600060808688031215613a1d57600080fd5b853594506020860135935060408601359250606086013567ffffffffffffffff811115613a4957600080fd5b613a55888289016139c3565b969995985093965092949392505050565b80356fffffffffffffffffffffffffffffffff811681146134c657600080fd5b60008060008060008060a08789031215613a9f57600080fd5b86359550613aaf602088016134b2565b9450613abd60408801613a66565b9350613acb60608801613a66565b9250608087013567ffffffffffffffff811115613ae757600080fd5b61367789828a016139c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6fffffffffffffffffffffffffffffffff828116828216039080821115613b4b57613b4b613af3565b5092915050565b6fffffffffffffffffffffffffffffffff818116838216028082169190828114613b7e57613b7e613af3565b505092915050565b60006fffffffffffffffffffffffffffffffff80841680613bd0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600081613beb57613beb613af3565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6fffffffffffffffffffffffffffffffff818116838216019080821115613b4b57613b4b613af3565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613c9a57613c9a613af3565b5060010190565b82815260006020604081840152835180604085015260005b81811015613cd557858101830151858201606001528201613cb9565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b600060208284031215613d2757600080fd5b8151613525816134e6565b81810381811115611f5357611f53613af3565b60008060408385031215613d5857600080fd5b8251613d63816134e6565b6020840151909250613558816134e6565b600060208284031215613d8657600080fd5b5051919050565b83815260406020820152816040820152818360608301376000818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019291505056fea2646970667358221220b8cec95aaa2ff8833457ec547d323dec00d40f2da3558028ac79ac5d9335aa5364736f6c63430008130033