Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00Multichain Info
N/A
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | ||||
---|---|---|---|---|---|---|---|
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12412519 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12411726 | 5 days ago | 0 ETH | |||||
12398523 | 5 days ago | 0 ETH | |||||
12398523 | 5 days ago | 0 ETH | |||||
12398523 | 5 days ago | 0 ETH | |||||
12398523 | 5 days ago | 0 ETH | |||||
12398523 | 5 days ago | 0 ETH | |||||
12398520 | 5 days ago | 0 ETH | |||||
12398520 | 5 days ago | 0 ETH | |||||
12398520 | 5 days ago | 0 ETH | |||||
12398520 | 5 days ago | 0 ETH |
Loading...
Loading
Contract Name:
Socket
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 1 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./SocketUtils.sol"; import {WRITE} from "../utils/common/Constants.sol"; import {createPayloadId} from "../utils/common/IdUtils.sol"; /** * @title Socket * @dev Socket is an abstract contract that inherits from SocketUtils and SocketConfig and * provides functionality for payload execution, verification, and management of payload execution status */ contract Socket is SocketUtils { using LibCall for address; // mapping of payload id to execution status mapping(bytes32 => ExecutionStatus) public payloadExecuted; // mapping of payload id to digest mapping(bytes32 => bytes32) public payloadIdToDigest; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// /// @notice Thrown when a payload has already been executed error PayloadAlreadyExecuted(ExecutionStatus status); /// @notice Thrown when verification fails error VerificationFailed(); /// @notice Thrown when less gas limit is provided for execution than expected error LowGasLimit(); /// @notice Thrown when the message value is insufficient error InsufficientMsgValue(); /** * @notice Constructor for the Socket contract * @param chainSlug_ The chain slug * @param owner_ The owner of the contract * @param version_ The version of the contract */ constructor( uint32 chainSlug_, address owner_, string memory version_ ) SocketUtils(chainSlug_, owner_, version_) { gasLimitBuffer = 105; } /** * @notice Executes a payload that has been delivered by transmitters and authenticated by switchboards * @param executeParams_ The execution parameters * @param transmissionParams_ The transmission parameters * @return success True if the payload was executed successfully * @return returnData The return data from the execution */ function execute( ExecuteParams calldata executeParams_, TransmissionParams calldata transmissionParams_ ) external payable returns (bool, bytes memory) { // check if the deadline has passed if (executeParams_.deadline < block.timestamp) revert DeadlinePassed(); // check if the call type is valid if (executeParams_.callType != WRITE) revert InvalidCallType(); // check if the plug is connected PlugConfigEvm storage plugConfig = _plugConfigs[executeParams_.target]; if (plugConfig.appGatewayId == bytes32(0)) revert PlugNotFound(); // check if the message value is sufficient if (msg.value < executeParams_.value + transmissionParams_.socketFees) revert InsufficientMsgValue(); bytes32 payloadId = createPayloadId( executeParams_.payloadPointer, plugConfig.switchboardId, chainSlug ); // validate the execution status _validateExecutionStatus(payloadId); // verify the digest _verify(payloadId, plugConfig, executeParams_, transmissionParams_.transmitterProof); // execute the payload return _execute(payloadId, executeParams_, transmissionParams_); } //////////////////////////////////////////////////////// ////////////////// INTERNAL FUNCS ////////////////////// //////////////////////////////////////////////////////// /** * @notice Verifies the digest of the payload * @param payloadId_ The id of the payload * @param plugConfig_ The plug configuration * @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit) * @param transmitterProof_ The transmitter proof */ function _verify( bytes32 payloadId_, PlugConfigEvm memory plugConfig_, ExecuteParams calldata executeParams_, bytes calldata transmitterProof_ ) internal { if (isValidSwitchboard[plugConfig_.switchboardId] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); // NOTE: the first un-trusted call in the system address transmitter = ISwitchboard(switchboardAddresses[plugConfig_.switchboardId]) .getTransmitter(msg.sender, payloadId_, transmitterProof_); // create the digest // transmitter, payloadId, appGateway, executeParams_ and there contents are validated using digest verification from switchboard bytes32 digest = _createDigest( transmitter, payloadId_, plugConfig_.appGatewayId, executeParams_ ); payloadIdToDigest[payloadId_] = digest; if ( !ISwitchboard(switchboardAddresses[plugConfig_.switchboardId]).allowPayload( digest, payloadId_ ) ) revert VerificationFailed(); } /** * @notice Executes the payload * @param payloadId_ The id of the payload * @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit) * @param transmissionParams_ The transmission parameters (socketFees, transmitterProof, refundAddress) */ function _execute( bytes32 payloadId_, ExecuteParams calldata executeParams_, TransmissionParams calldata transmissionParams_ ) internal returns (bool success, bytes memory returnData) { // check if the gas limit is sufficient // bump by 5% to account for gas used by current contract execution if (gasleft() < (executeParams_.gasLimit * gasLimitBuffer) / 100) revert LowGasLimit(); // NOTE: external un-trusted call bool exceededMaxCopy; (success, exceededMaxCopy, returnData) = executeParams_.target.tryCall( executeParams_.value, executeParams_.gasLimit, maxCopyBytes, executeParams_.payload ); if (success) { emit ExecutionSuccess(payloadId_, exceededMaxCopy, returnData); // pay and check fees if (address(socketFeeManager) != address(0)) { socketFeeManager.payAndCheckFees{value: transmissionParams_.socketFees}( executeParams_, transmissionParams_ ); } } else { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; // refund the fees address receiver = transmissionParams_.refundAddress; if (receiver == address(0)) receiver = msg.sender; SafeTransferLib.forceSafeTransferETH(receiver, msg.value); emit ExecutionFailed(payloadId_, exceededMaxCopy, returnData); } return (success, returnData); } /** * @notice Validates the execution status of a payload * @dev This function can be retried till execution status is executed * @param payloadId_ The id of the payload */ function _validateExecutionStatus(bytes32 payloadId_) internal { if (payloadExecuted[payloadId_] == ExecutionStatus.Executed) revert PayloadAlreadyExecuted(payloadExecuted[payloadId_]); payloadExecuted[payloadId_] = ExecutionStatus.Executed; } //////////////////////////////////////////////////////// ////////////////////// Trigger ////////////////////// //////////////////////////////////////////////////////// /** * @notice To trigger to a connected remote chain. Should only be called by a plug. * @param data_ The data to trigger the app gateway * @return triggerId The id of the trigger */ function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId) { triggerId = _triggerAppGateway(msg.sender, msg.value, data_); } /** * @notice To trigger to a connected remote chain. Should only be called by a plug. * @param plug_ The address of the plug * @param value_ The value to trigger the app gateway * @param data_ The data to trigger the app gateway * @return triggerId The id of the trigger */ function _triggerAppGateway( address plug_, uint256 value_, bytes calldata data_ ) internal returns (bytes32 triggerId) { PlugConfigEvm memory plugConfig = _plugConfigs[plug_]; // if no sibling plug is found for the given chain slug, revert if (plugConfig.appGatewayId == bytes32(0)) revert PlugNotFound(); if (isValidSwitchboard[plugConfig.switchboardId] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); bytes memory plugOverrides = IPlug(plug_).overrides(); triggerId = _encodeTriggerId(); // todo: need gas limit? ISwitchboard(switchboardAddresses[plugConfig.switchboardId]).processTrigger{value: value_}( plug_, triggerId, data_, plugOverrides ); emit AppGatewayCallRequested( triggerId, plugConfig.appGatewayId, plugConfig.switchboardId, toBytes32Format(plug_), plugOverrides, data_ ); } /** * @notice Fallback function that forwards all calls to Socket's callAppGateway * @dev The calldata is passed as-is to the gateways * @return The trigger id */ fallback(bytes calldata) external payable returns (bytes memory) { // return the trigger id return abi.encode(_triggerAppGateway(msg.sender, msg.value, msg.data)); } /** * @notice Sending ETH to the socket will revert */ receive() external payable { revert("Socket does not accept ETH"); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; /** * @title IPlug * @notice Interface for a plug contract that executes the payload received from a source chain. */ interface IPlug { /// @notice Initializes the socket /// @param appGatewayId_ The app gateway id /// @param socket_ The socket address /// @param switchboardId_ The switchboard id function initSocket(bytes32 appGatewayId_, address socket_, uint64 switchboardId_) external; /// @notice Gets the overrides /// @dev encoding format depends on the watcher system /// @return overrides_ The overrides function overrides() external view returns (bytes memory overrides_); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {ExecuteParams, TransmissionParams, ExecutionStatus} from "../../utils/common/Structs.sol"; /** * @title ISocket * @notice An interface for a Chain Abstraction contract * @dev This interface provides methods for transmitting and executing payloads, * connecting a plug to a remote chain and setting up switchboards for the payload transmission * This interface also emits events for important operations such as payload transmission, execution status, * and plug connection */ interface ISocket { /** * @notice emits the status of payload after external call * @param payloadId payload id which is executed */ event ExecutionSuccess(bytes32 payloadId, bool exceededMaxCopy, bytes returnData); /** * @notice emits the status of payload after external call * @param payloadId payload id which is executed */ event ExecutionFailed(bytes32 payloadId, bool exceededMaxCopy, bytes returnData); /** * @notice emits the config set by a plug for a remoteChainSlug * @param plug The address of plug on current chain * @param appGatewayId The address of plug on sibling chain * @param switchboardId The outbound switchboard (select from registered options) */ event PlugConnected(address plug, bytes32 appGatewayId, uint64 switchboardId); /** * @notice emits the config set by a plug for a remoteChainSlug * @param plug The address of plug on current chain */ event PlugDisconnected(address plug); /** * @notice emits the payload details when a new payload arrives at outbound * @param triggerId trigger id * @param switchboardId switchboard id * @param plug local plug address * @param overrides params, for specifying details like fee pool chain, fee pool token and max fees if required * @param payload the data which will be used by contracts on chain */ event AppGatewayCallRequested( bytes32 triggerId, bytes32 appGatewayId, uint64 switchboardId, bytes32 plug, bytes overrides, bytes payload ); /** * @notice Executes a payload * @param executeParams_ The execution parameters * @param transmissionParams_ The transmission parameters * @return success True if the payload was executed successfully * @return returnData The return data from the execution */ function execute( ExecuteParams calldata executeParams_, TransmissionParams calldata transmissionParams_ ) external payable returns (bool, bytes memory); /** * @notice sets the config specific to the plug * @param appGatewayId_ The address of plug present at sibling chain * @param switchboardId_ The id of switchboard to use for executing payloads */ function connect(bytes32 appGatewayId_, uint64 switchboardId_) external; /** * @notice Disconnects Plug from Socket */ function disconnect() external; /** * @notice Registers a switchboard for the socket * @return switchboardId The id of the switchboard */ function registerSwitchboard() external returns (uint64); /** * @notice Returns the config for given `plugAddress_` and `siblingChainSlug_` * @param plugAddress_ The address of plug present at current chain * @return appGatewayId The address of plug on sibling chain * @return switchboardId The id of the switchboard */ function getPlugConfig( address plugAddress_ ) external view returns (bytes32 appGatewayId, uint64 switchboardId); /** * @notice Returns the execution status of a payload * @param payloadId_ The payload id * @return executionStatus The execution status */ function payloadExecuted(bytes32 payloadId_) external view returns (ExecutionStatus); /** * @notice Returns the chain slug * @return chainSlug The chain slug */ function chainSlug() external view returns (uint32); /** * @notice Returns the digest of a payload * @param payloadId_ The payload id * @return digest The digest */ function payloadIdToDigest(bytes32 payloadId_) external view returns (bytes32); /** * @notice Returns the current trigger counter * @return triggerCounter The trigger counter */ function triggerCounter() external view returns (uint64); /** * @notice Returns the switchboard address for a given switchboard id * @param switchboardId_ The switchboard id * @return switchboardAddress The switchboard address */ function switchboardAddresses(uint64 switchboardId_) external view returns (address); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {ExecuteParams, TransmissionParams} from "../../utils/common/Structs.sol"; /** * @title ISocketFeeManager * @notice Interface for the socket fee manager */ interface ISocketFeeManager { /** * @notice Pays and validates fees for execution * @param executeParams_ The execution parameters * @param transmissionParams_ The transmission parameters */ function payAndCheckFees( ExecuteParams memory executeParams_, TransmissionParams memory transmissionParams_ ) external payable; /** * @notice Gets minimum fees required for execution * @return nativeFees The minimum native token fees required */ function getMinSocketFees() external view returns (uint256 nativeFees); /** * @notice Sets socket fees * @param socketFees_ The new socket fees amount */ function setSocketFees(uint256 socketFees_) external; /** * @notice Gets current socket fees * @return socketFees The current socket fees amount */ function socketFees() external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; /** * @title ISwitchboard * @dev The interface for a switchboard contract that is responsible for verification of payloads if the correct * digest is executed. */ interface ISwitchboard { /** * @notice Checks if a payloads can be allowed to go through the switchboard. * @param digest_ the payloads digest. * @param payloadId_ The unique identifier for the payloads. * @return A boolean indicating whether the payloads is allowed to go through the switchboard or not. */ function allowPayload(bytes32 digest_, bytes32 payloadId_) external view returns (bool); /** * @notice Processes a trigger and creates payload * @dev This function is called by the socket to process a trigger * @dev sb can override this function to add additional logic * @param triggerId_ Trigger ID from socket * @param plug_ Source plug address * @param payload_ Payload data * @param overrides_ Overrides for the trigger */ function processTrigger( address plug_, bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ ) external payable; /** * @notice Gets the transmitter for a given payload * @notice Switchboard are required to implement this function to allow for the verification of the transmitters * @param sender_ The sender of the payload * @param payloadId_ The payload ID * @param transmitterSignature_ The transmitter signature * @return The transmitter address */ function getTransmitter( address sender_, bytes32 payloadId_, bytes calldata transmitterSignature_ ) external view returns (address); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./interfaces/ISocket.sol"; import "./interfaces/ISwitchboard.sol"; import {IPlug} from "./interfaces/IPlug.sol"; import "./interfaces/ISocketFeeManager.sol"; import "../utils/AccessControl.sol"; import {GOVERNANCE_ROLE, RESCUE_ROLE, SWITCHBOARD_DISABLER_ROLE} from "../utils/common/AccessRoles.sol"; import {PlugConfigEvm, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol"; import "../utils/common/Errors.sol"; import {MAX_COPY_BYTES} from "../utils/common/Constants.sol"; /** * @title SocketConfig * @notice An abstract contract for configuring socket connections for plugs, * manages plug configs and switchboard registrations * @dev This contract is meant to be inherited by other contracts that require socket configuration functionality */ abstract contract SocketConfig is ISocket, AccessControl { // socket fee manager ISocketFeeManager public socketFeeManager; // @notice mapping of switchboard address to its status, helps socket to block invalid switchboards mapping(uint64 => SwitchboardStatus) public isValidSwitchboard; // @notice mapping of plug address to its config mapping(address => PlugConfigEvm) internal _plugConfigs; // @notice max copy bytes for socket uint16 public maxCopyBytes = 2048; // 2KB // @notice counter for switchboard ids uint64 public switchboardIdCounter = 1; // @notice mapping of switchboard id to its address mapping(uint64 => address) public switchboardAddresses; // @notice mapping of switchboard address to its id mapping(address => uint64) public switchboardIds; // @notice buffer to account for gas used by current contract execution uint256 public gasLimitBuffer; // @notice error triggered when a switchboard already exists error SwitchboardExists(); // @notice error triggered when a plug is not connected error PlugNotConnected(); // @notice event triggered when a new switchboard is added event SwitchboardAdded(address switchboard, uint64 switchboardId); // @notice event triggered when a switchboard is disabled event SwitchboardDisabled(uint64 switchboardId); // @notice event triggered when a switchboard is enabled event SwitchboardEnabled(uint64 switchboardId); // @notice event triggered when a socket fee manager is updated event SocketFeeManagerUpdated(address oldSocketFeeManager, address newSocketFeeManager); // @notice event triggered when the gas limit buffer is updated event GasLimitBufferUpdated(uint256 gasLimitBuffer); // @notice event triggered when the max copy bytes is updated event MaxCopyBytesUpdated(uint16 maxCopyBytes); /** * @notice Registers a switchboard on the socket * @dev This function is called by the switchboard to register itself on the socket * @dev This function will revert if the switchboard already exists * @return switchboardId The id of the switchboard */ function registerSwitchboard() external returns (uint64 switchboardId) { switchboardId = switchboardIds[msg.sender]; if (switchboardId != 0) revert SwitchboardExists(); // increment the switchboard id counter switchboardId = switchboardIdCounter++; // set the switchboard id and address switchboardIds[msg.sender] = switchboardId; switchboardAddresses[switchboardId] = msg.sender; // set the switchboard status to registered isValidSwitchboard[switchboardId] = SwitchboardStatus.REGISTERED; emit SwitchboardAdded(msg.sender, switchboardId); } /** * @notice Disables a switchboard * @dev This function is called by the governance role to disable a switchboard * @param switchboardId_ The id of the switchboard to disable */ function disableSwitchboard( uint64 switchboardId_ ) external onlyRole(SWITCHBOARD_DISABLER_ROLE) { isValidSwitchboard[switchboardId_] = SwitchboardStatus.DISABLED; emit SwitchboardDisabled(switchboardId_); } /** * @notice Enables a switchboard if disabled * @dev This function is called by the governance role to enable a switchboard * @param switchboardId_ The id of the switchboard to enable */ function enableSwitchboard(uint64 switchboardId_) external onlyRole(GOVERNANCE_ROLE) { isValidSwitchboard[switchboardId_] = SwitchboardStatus.REGISTERED; emit SwitchboardEnabled(switchboardId_); } /** * @notice Sets the socket fee manager * @dev This function is called by the governance role to set the socket fee manager * @param socketFeeManager_ The address of the socket fee manager */ function setSocketFeeManager(address socketFeeManager_) external onlyRole(GOVERNANCE_ROLE) { socketFeeManager = ISocketFeeManager(socketFeeManager_); emit SocketFeeManagerUpdated(address(socketFeeManager), socketFeeManager_); } /** * @notice Connects a plug to socket * @dev This function is called by the plug to connect itself to the socket * @param appGatewayId_ The app gateway id * @param switchboardId_ The switchboard id */ function connect(bytes32 appGatewayId_, uint64 switchboardId_) external override { if (isValidSwitchboard[switchboardId_] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); PlugConfigEvm storage _plugConfig = _plugConfigs[msg.sender]; _plugConfig.appGatewayId = appGatewayId_; _plugConfig.switchboardId = switchboardId_; emit PlugConnected(msg.sender, appGatewayId_, switchboardId_); } /** * @notice Disconnects a plug from socket * @dev This function is called by the plug to disconnect itself from the socket */ function disconnect() external override { PlugConfigEvm storage _plugConfig = _plugConfigs[msg.sender]; if (_plugConfig.appGatewayId == bytes32(0)) revert PlugNotConnected(); _plugConfig.appGatewayId = bytes32(0); _plugConfig.switchboardId = 0; emit PlugDisconnected(msg.sender); } /** * @notice Sets the gas limit buffer for socket * @dev This function is called by the governance role to set the gas limit buffer for socket * @param gasLimitBuffer_ The gas limit buffer for socket */ function setGasLimitBuffer(uint256 gasLimitBuffer_) external onlyRole(GOVERNANCE_ROLE) { gasLimitBuffer = gasLimitBuffer_; emit GasLimitBufferUpdated(gasLimitBuffer_); } /** * @notice Sets the max copy bytes for socket * @dev This function is called by the governance role to set the max copy bytes for socket * @param maxCopyBytes_ The max copy bytes for socket */ function setMaxCopyBytes(uint16 maxCopyBytes_) external onlyRole(GOVERNANCE_ROLE) { maxCopyBytes = maxCopyBytes_; emit MaxCopyBytesUpdated(maxCopyBytes_); } /** * @notice Returns the config for given `plugAddress_` * @param plugAddress_ The address of the plug present at current chain * @return appGatewayId The app gateway id * @return switchboardId The switchboard id */ function getPlugConfig( address plugAddress_ ) external view returns (bytes32 appGatewayId, uint64 switchboardId) { PlugConfigEvm memory _plugConfig = _plugConfigs[plugAddress_]; return (_plugConfig.appGatewayId, _plugConfig.switchboardId); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {ECDSA} from "lib/solady/src/utils/ECDSA.sol"; import "../utils/RescueFundsLib.sol"; import "./SocketConfig.sol"; import {LibCall} from "lib/solady/src/utils/LibCall.sol"; import {toBytes32Format} from "../utils/common/Converters.sol"; /** * @title SocketUtils * @notice Utility functions for socket */ abstract contract SocketUtils is SocketConfig { using LibCall for address; //////////////////////////////////////////////////////////// ////////////////////// State Vars ////////////////////////// //////////////////////////////////////////////////////////// struct SimulateParams { address target; uint256 value; uint256 gasLimit; bytes payload; } // address of the off-chain caller address public constant OFF_CHAIN_CALLER = address(0xDEAD); // prefix for trigger ID containing chain slug and address bits uint256 private immutable triggerPrefix; // version string for this socket instance bytes32 public immutable version; // chain slug for this deployed socket instance uint32 public immutable chainSlug; // counter for trigger id uint64 public triggerCounter; /// @notice Thrown when the caller is not off-chain error OnlyOffChain(); /// @notice Thrown when the simulation fails error SimulationFailed(); /// @notice Modifier to check if the caller is off-chain modifier onlyOffChain() { if (msg.sender != OFF_CHAIN_CALLER) revert OnlyOffChain(); _; } /** * @notice constructor for creating a new Socket contract instance * @param chainSlug_ The unique identifier of the chain this socket is deployed on * @param owner_ The address of the owner who has the initial admin role * @param version_ The version string which is hashed and stored in socket */ constructor(uint32 chainSlug_, address owner_, string memory version_) { chainSlug = chainSlug_; version = keccak256(bytes(version_)); triggerPrefix = (uint256(chainSlug_) << 224) | (uint256(uint160(address(this))) << 64); _initializeOwner(owner_); } /** * @notice Creates the digest for the payload * @param transmitter_ The address of the transmitter * @param payloadId_ The ID of the payload * @param appGatewayId_ The id of the app gateway * @param executeParams_ The parameters of the payload * @return The packed payload as a bytes32 hash * @dev This function is used to create the digest for the payload */ function _createDigest( address transmitter_, bytes32 payloadId_, bytes32 appGatewayId_, ExecuteParams calldata executeParams_ ) internal view returns (bytes32) { return keccak256( abi.encodePacked( toBytes32Format(address(this)), toBytes32Format(transmitter_), payloadId_, executeParams_.deadline, executeParams_.callType, executeParams_.gasLimit, executeParams_.value, executeParams_.payload, toBytes32Format(executeParams_.target), appGatewayId_, executeParams_.prevBatchDigestHash, executeParams_.extraData ) ); } /** * @notice Encodes the trigger ID with the chain slug, socket address and nonce * @return The trigger ID * @dev This function is used to encode the trigger ID with the chain slug, socket address and nonce */ function _encodeTriggerId() internal returns (bytes32) { return bytes32(triggerPrefix | triggerCounter++); } /** * @notice Simulation result * @param success True if the simulation was successful * @param returnData The return data from the simulation * @param exceededMaxCopy True if the simulation exceeded the max copy bytes */ struct SimulationResult { bool success; bytes returnData; bool exceededMaxCopy; } /** * @notice Simulates the payload * @dev This function is used to simulate the payload offchain for gas estimation and checking reverts * @param params The parameters of the simulation * @return The simulation results */ function simulate( SimulateParams[] calldata params ) external payable onlyOffChain returns (SimulationResult[] memory) { SimulationResult[] memory results = new SimulationResult[](params.length); for (uint256 i = 0; i < params.length; i++) { (bool success, bool exceededMaxCopy, bytes memory returnData) = params[i] .target .tryCall(params[i].value, params[i].gasLimit, maxCopyBytes, params[i].payload); results[i] = SimulationResult(success, returnData, exceededMaxCopy); } return results; } ////////////////////////////////////////////// //////////// Rescue role actions //////////// ///////////////////////////////////////////// /** * @notice Rescues funds from the contract if they are locked by mistake. This contract does not * theoretically need this function but it is added for safety. * @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_); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "lib/solady/src/auth/Ownable.sol"; /** * @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. * @dev slot 0 */ mapping(bytes32 => mapping(address => bool)) private _permits; // slots 1-50: gap for future storage variables uint256[49] _gap_access_control; /** * @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 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_]; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; // contains role hashes used in socket for various different operation // used to rescue funds bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); // used by governance bytes32 constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE"); // used by transmitters who execute payloads 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 to disable switchboard bytes32 constant SWITCHBOARD_DISABLER_ROLE = keccak256("SWITCHBOARD_DISABLER_ROLE"); // used by fees manager to withdraw native tokens bytes32 constant FEE_MANAGER_ROLE = keccak256("FEE_MANAGER_ROLE");
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; address constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); bytes32 constant FORWARD_CALL = keccak256("FORWARD_CALL"); bytes32 constant DISTRIBUTE_FEE = keccak256("DISTRIBUTE_FEE"); bytes32 constant DEPLOY = keccak256("DEPLOY"); bytes4 constant READ = bytes4(keccak256("READ")); bytes4 constant WRITE = bytes4(keccak256("WRITE")); bytes4 constant SCHEDULE = bytes4(keccak256("SCHEDULE")); bytes32 constant CALLBACK = keccak256("CALLBACK"); bytes32 constant FAST = keccak256("FAST"); bytes32 constant CCTP = keccak256("CCTP"); uint256 constant PAYLOAD_SIZE_LIMIT = 24_500; uint16 constant MAX_COPY_BYTES = 2048; // 2KB uint32 constant CHAIN_SLUG_SOLANA_MAINNET = 10000001; uint32 constant CHAIN_SLUG_SOLANA_DEVNET = 10000002; // Constant appGatewayId used on all chains bytes32 constant APP_GATEWAY_ID = 0xdeadbeefcafebabe1234567890abcdef1234567890abcdef1234567890abcdef;
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.21; error NotAnEvmAddress(bytes32 bytes32FormatAddress); function toBytes32Format(address addr) pure returns (bytes32) { return bytes32(uint256(uint160(addr))); } function fromBytes32Format(bytes32 bytes32FormatAddress) pure returns (address) { if (uint256(bytes32FormatAddress) >> 160 != 0) { revert NotAnEvmAddress(bytes32FormatAddress); } return address(uint160(uint256(bytes32FormatAddress))); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; error ZeroAddress(); error InvalidTransmitter(); error InvalidTokenAddress(); error InvalidSwitchboard(); error SocketAlreadyInitialized(); // Socket error NotSocket(); error PlugNotFound(); // EVMx error ResolvingScheduleTooEarly(); error CallFailed(); error InvalidAppGateway(); error AppGatewayAlreadyCalled(); error InvalidCallerTriggered(); error InvalidPromise(); error InvalidWatcherSignature(); error NonceUsed(); error AsyncModifierNotSet(); error WatcherNotSet(); error InvalidTarget(); error InvalidIndex(); error InvalidChainSlug(); error InvalidPayloadSize(); error InvalidOnChainAddress(); error InvalidScheduleDelay(); /// @notice Error thrown when trying to start or bid a closed auction error AuctionClosed(); /// @notice Error thrown when trying to start or bid an auction that is not open error AuctionNotOpen(); /// @notice Error thrown if fees exceed the maximum set fees error BidExceedsMaxFees(); /// @notice Error thrown if a lower bid already exists error LowerBidAlreadyExists(); /// @notice Error thrown when request count mismatch error RequestCountMismatch(); error InvalidAmount(); error InsufficientCreditsAvailable(); error InsufficientBalance(); /// @notice Error thrown when a caller is invalid error InvalidCaller(); /// @notice Error thrown when a gateway is invalid error InvalidGateway(); /// @notice Error thrown when a request is already cancelled error RequestAlreadyCancelled(); error DeadlineNotPassedForOnChainRevert(); error InvalidBid(); error MaxReAuctionCountReached(); error MaxMsgValueLimitExceeded(); /// @notice Error thrown when an invalid address attempts to call the Watcher only function error OnlyWatcherAllowed(); error InvalidPrecompileData(); error InvalidCallType(); error NotRequestHandler(); error NotInvoker(); error NotPromiseResolver(); error RequestPayloadCountLimitExceeded(); error InsufficientFees(); error RequestAlreadySettled(); error NoWriteRequest(); error AlreadyAssigned(); error OnlyAppGateway(); error NewMaxFeesLowerThanCurrent(uint256 currentMaxFees, uint256 newMaxFees); error InvalidContract(); error InvalidData(); error InvalidSignature(); error DeadlinePassed(); // Only Watcher can call functions error OnlyRequestHandlerAllowed(); error OnlyPromiseResolverAllowed(); error InvalidReceiver();
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; /// @notice Creates a payload ID from the given parameters /// @param payloadPointer_ The payload pointer /// @param switchboardId_ The switchboard id /// @param chainSlug_ The chain slug /// @return The created payload ID function createPayloadId( uint160 payloadPointer_, uint64 switchboardId_, uint32 chainSlug_ ) pure returns (bytes32) { return bytes32( (uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | uint256(payloadPointer_) ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; //// ENUMS //// enum IsPlug { YES, NO } enum Parallel { OFF, ON } enum Read { OFF, ON } enum WriteFinality { LOW, MEDIUM, HIGH } enum SwitchboardStatus { NOT_REGISTERED, REGISTERED, DISABLED } /// @notice The state of the async promise enum AsyncPromiseState { WAITING_FOR_CALLBACK_SELECTOR, WAITING_FOR_CALLBACK_EXECUTION, CALLBACK_REVERTING, ONCHAIN_REVERTING, RESOLVED } enum ExecutionStatus { NotExecuted, Executed, Reverted } struct AppGatewayApprovals { address appGateway; bool approval; } //// STRUCTS //// struct AppGatewayConfig { PlugConfigGeneric plugConfig; bytes32 plug; uint32 chainSlug; } // Plug config: struct PlugConfigGeneric { bytes32 appGatewayId; uint64 switchboardId; } // Plug config: struct PlugConfigEvm { bytes32 appGatewayId; uint64 switchboardId; } //trigger: struct TriggerParams { bytes32 triggerId; bytes32 plug; bytes32 appGatewayId; uint32 chainSlug; bytes overrides; bytes payload; } struct PromiseReturnData { bool exceededMaxCopy; bytes32 payloadId; bytes returnData; } // AM struct ExecuteParams { bytes4 callType; uint160 payloadPointer; uint256 deadline; uint256 gasLimit; uint256 value; bytes32 prevBatchDigestHash; address target; bytes payload; bytes extraData; } struct TransmissionParams { uint256 socketFees; address refundAddress; bytes extraData; bytes transmitterProof; } struct WatcherMultiCallParams { address contractAddress; bytes data; uint256 nonce; bytes signature; } struct CreateRequestResult { uint256 totalEstimatedWatcherFees; uint256 writeCount; address[] promiseList; PayloadParams[] payloadParams; } struct Bid { uint256 fee; address transmitter; bytes extraData; } struct UserCredits { uint256 totalCredits; uint256 blockedCredits; } // digest: struct DigestParams { bytes32 socket; bytes32 transmitter; bytes32 payloadId; uint256 deadline; bytes4 callType; uint256 gasLimit; uint256 value; bytes payload; bytes32 target; bytes32 appGatewayId; bytes32 prevBatchDigestHash; bytes extraData; } // App gateway base: struct OverrideParams { bytes4 callType; Parallel isParallelCall; WriteFinality writeFinality; uint256 gasLimit; uint256 value; uint256 readAtBlockNumber; uint256 delayInSeconds; } struct Transaction { uint32 chainSlug; bytes32 target; bytes payload; } struct QueueParams { OverrideParams overrideParams; Transaction transaction; address asyncPromise; bytes32 switchboardType; } struct PayloadParams { bytes4 callType; uint160 payloadPointer; address asyncPromise; address appGateway; bytes32 payloadId; uint256 resolvedAt; uint256 deadline; bytes precompileData; } // request struct RequestTrackingParams { bool isRequestCancelled; bool isRequestExecuted; uint40 currentBatch; uint256 currentBatchPayloadsLeft; uint256 payloadsRemaining; } struct RequestFeesDetails { uint256 maxFees; address consumeFrom; Bid winningBid; } struct RequestParams { RequestTrackingParams requestTrackingParams; RequestFeesDetails requestFeesDetails; address appGateway; address auctionManager; uint256 writeCount; bytes onCompleteData; } struct CCTPExecutionParams { ExecuteParams executeParams; bytes32 digest; bytes proof; bytes transmitterSignature; address refundAddress; } struct CCTPBatchParams { bytes32[] previousPayloadIds; uint32[] nextBatchRemoteChainSlugs; bytes[] messages; bytes[] attestations; } struct SolanaInstruction { SolanaInstructionData data; SolanaInstructionDataDescription description; } struct SolanaInstructionData { bytes32 programId; bytes32[] accounts; bytes8 instructionDiscriminator; // for now we assume the all functionArguments are simple types (uint256, address, bool, etc.) not complex types (struct, array, etc.) bytes[] functionArguments; } struct SolanaInstructionDataDescription { // flags for accounts, we only need isWritable for now // 0 bit - isWritable (0|1) bytes1[] accountFlags; // names for function argument types used later in data decoding in watcher and transmitter string[] functionArgumentTypeNames; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "lib/solady/src/utils/SafeTransferLib.sol"; import {ZeroAddress, InvalidTokenAddress} from "./common/Errors.sol"; import {ETH_ADDRESS} from "./common/Constants.sol"; interface IERC721 { function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; } interface IERC165 { function supportsInterface(bytes4 interfaceId) external view returns (bool); } /** * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ library RescueFundsLib { /** * @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.forceSafeTransferETH(rescueTo_, amount_); } else { if (token_.code.length == 0) revert InvalidTokenAddress(); // Identify if a token is an NFT (ERC721) by checking if it supports the ERC721 interface try IERC165(token_).supportsInterface(0x80ac58cd) returns (bool isERC721) { if (isERC721) IERC721(token_).safeTransferFrom(address(this), rescueTo_, amount_, ""); else { SafeTransferLib.safeTransfer(token_, rescueTo_, amount_); } } catch { SafeTransferLib.safeTransfer(token_, rescueTo_, amount_); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. bytes32 internal constant _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (bool guard) {} /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(ownerSlot) { mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. revert(0x1c, 0x04) } // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } else { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(_OWNER_SLOT, newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) } } else { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(_OWNER_SLOT))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized ECDSA wrapper. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) /// /// @dev Note: /// - The recovery functions use the ecrecover precompile (0x1). /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure. /// This is for more safety by default. /// Use the `tryRecover` variants if you need to get the zero address back /// upon recovery failure instead. /// - As of Solady version 0.0.134, all `bytes signature` variants accept both /// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. /// See: https://eips.ethereum.org/EIPS/eip-2098 /// This is for calldata efficiency on smart accounts prevalent on L2s. /// /// WARNING! Do NOT directly use signatures as unique identifiers: /// - The recovery operations do NOT check if a signature is non-malleable. /// - Use a nonce in the digest to prevent replay attacks on the same contract. /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. /// EIP-712 also enables readable signing of typed data for better user safety. /// - If you need a unique hash from a signature, please use the `canonicalHash` functions. library ECDSA { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The order of the secp256k1 elliptic curve. uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; /// @dev `N/2 + 1`. Used for checking the malleability of the signature. uint256 private constant _HALF_N_PLUS_1 = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The signature is invalid. error InvalidSignature(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RECOVERY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } { switch mload(signature) case 64 { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. } default { continue } mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if returndatasize() { break } } } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } { switch signature.length case 64 { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. } default { continue } mstore(0x00, hash) result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if returndatasize() { break } } } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* TRY-RECOVER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // WARNING! // These functions will NOT revert upon recovery failure. // Instead, they will return the zero address upon recovery failure. // It is critical that the returned address is NEVER compared against // a zero address (e.g. an uninitialized address variable). /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 {} { switch mload(signature) case 64 { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. } default { break } mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. break } } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 {} { switch signature.length case 64 { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. } default { break } mstore(0x00, hash) pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. break } } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an Ethereum Signed Message, created from a `hash`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, hash) // Store into scratch space for keccak256. mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. } } /// @dev Returns an Ethereum Signed Message, created from `s`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. /// Note: Supports lengths of `s` up to 999999 bytes. function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let sLength := mload(s) let o := 0x20 mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. mstore(0x00, 0x00) // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. for { let temp := sLength } 1 {} { o := sub(o, 1) mstore8(o, add(48, mod(temp, 10))) temp := div(temp, 10) if iszero(temp) { break } } let n := sub(0x3a, o) // Header length: `26 + 32 - o`. // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) mstore(s, sLength) // Restore the length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CANONICAL HASH FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // The following functions returns the hash of the signature in it's canonicalized format, // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28. // If `s` is greater than `N / 2` then it will be converted to `N - s` // and the `v` value will be flipped. // If the signature has an invalid length, or if `v` is invalid, // a uniquely corrupt hash will be returned. // These functions are useful for "poor-mans-VRF". /// @dev Returns the canonical hash of `signature`. function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { let l := mload(signature) for {} 1 {} { mstore(0x00, mload(add(signature, 0x20))) // `r`. let s := mload(add(signature, 0x40)) let v := mload(add(signature, 0x41)) if eq(l, 64) { v := add(shr(255, s), 27) s := shr(1, shl(1, s)) } if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. break } // If the length is neither 64 nor 65, return a uniquely corrupted hash. if iszero(lt(sub(l, 64), 2)) { // `bytes4(keccak256("InvalidSignatureLength"))`. result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2) } } } /// @dev Returns the canonical hash of `signature`. function canonicalHashCalldata(bytes calldata signature) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { for {} 1 {} { mstore(0x00, calldataload(signature.offset)) // `r`. let s := calldataload(add(signature.offset, 0x20)) let v := calldataload(add(signature.offset, 0x21)) if eq(signature.length, 64) { v := add(shr(255, s), 27) s := shr(1, shl(1, s)) } if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. break } // If the length is neither 64 nor 65, return a uniquely corrupted hash. if iszero(lt(sub(signature.length, 64), 2)) { calldatacopy(mload(0x40), signature.offset, signature.length) // `bytes4(keccak256("InvalidSignatureLength"))`. result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2) } } } /// @dev Returns the canonical hash of `signature`. function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { mstore(0x00, r) // `r`. let v := add(shr(255, vs), 27) let s := shr(1, shl(1, vs)) mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the canonical hash of `signature`. function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) { // @solidity memory-safe-assembly assembly { mstore(0x00, r) // `r`. if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes. function emptySignature() internal pure returns (bytes calldata signature) { /// @solidity memory-safe-assembly assembly { signature.length := 0 } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for making calls. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibCall.sol) /// @author Modified from ExcessivelySafeCall (https://github.com/nomad-xyz/ExcessivelySafeCall) /// /// @dev Note: /// - The arguments of the functions may differ from the libraries. /// Please read the functions carefully before use. library LibCall { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The target of the call is not a contract. error TargetIsNotContract(); /// @dev The data is too short to contain a function selector. error DataTooShort(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONTRACT CALL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // These functions will revert if called on a non-contract // (i.e. address without code). // They will bubble up the revert if the call fails. /// @dev Makes a call to `target`, with `data` and `value`. function callContract(address target, uint256 value, bytes memory data) internal returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) if iszero(call(gas(), target, value, add(data, 0x20), mload(data), codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } if iszero(returndatasize()) { if iszero(extcodesize(target)) { mstore(0x00, 0x5a836a5f) // `TargetIsNotContract()`. revert(0x1c, 0x04) } } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @dev Makes a call to `target`, with `data`. function callContract(address target, bytes memory data) internal returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) if iszero(call(gas(), target, 0, add(data, 0x20), mload(data), codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } if iszero(returndatasize()) { if iszero(extcodesize(target)) { mstore(0x00, 0x5a836a5f) // `TargetIsNotContract()`. revert(0x1c, 0x04) } } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @dev Makes a static call to `target`, with `data`. function staticCallContract(address target, bytes memory data) internal view returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) if iszero(staticcall(gas(), target, add(data, 0x20), mload(data), codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } if iszero(returndatasize()) { if iszero(extcodesize(target)) { mstore(0x00, 0x5a836a5f) // `TargetIsNotContract()`. revert(0x1c, 0x04) } } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /// @dev Makes a delegate call to `target`, with `data`. function delegateCallContract(address target, bytes memory data) internal returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) if iszero(delegatecall(gas(), target, add(data, 0x20), mload(data), codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } if iszero(returndatasize()) { if iszero(extcodesize(target)) { mstore(0x00, 0x5a836a5f) // `TargetIsNotContract()`. revert(0x1c, 0x04) } } mstore(result, returndatasize()) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* TRY CALL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // These functions enable gas limited calls to be performed, // with a cap on the number of return data bytes to be copied. // The can be used to ensure that the calling contract will not // run out-of-gas. /// @dev Makes a call to `target`, with `data` and `value`. /// The call is given a gas limit of `gasStipend`, /// and up to `maxCopy` bytes of return data can be copied. function tryCall( address target, uint256 value, uint256 gasStipend, uint16 maxCopy, bytes memory data ) internal returns (bool success, bool exceededMaxCopy, bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) success := call(gasStipend, target, value, add(data, 0x20), mload(data), codesize(), 0x00) let n := returndatasize() if gt(returndatasize(), and(0xffff, maxCopy)) { n := and(0xffff, maxCopy) exceededMaxCopy := 1 } mstore(result, n) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, n) // Copy the returndata. mstore(0x40, add(o, n)) // Allocate the memory. } } /// @dev Makes a call to `target`, with `data`. /// The call is given a gas limit of `gasStipend`, /// and up to `maxCopy` bytes of return data can be copied. function tryStaticCall(address target, uint256 gasStipend, uint16 maxCopy, bytes memory data) internal view returns (bool success, bool exceededMaxCopy, bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) success := staticcall(gasStipend, target, add(data, 0x20), mload(data), codesize(), 0x00) let n := returndatasize() if gt(returndatasize(), and(0xffff, maxCopy)) { n := and(0xffff, maxCopy) exceededMaxCopy := 1 } mstore(result, n) // Store the length. let o := add(result, 0x20) returndatacopy(o, 0x00, n) // Copy the returndata. mstore(0x40, add(o, n)) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Bubbles up the revert. function bubbleUpRevert(bytes memory revertReturnData) internal pure { /// @solidity memory-safe-assembly assembly { revert(add(0x20, revertReturnData), mload(revertReturnData)) } } /// @dev In-place replaces the function selector of encoded contract call data. function setSelector(bytes4 newSelector, bytes memory data) internal pure { /// @solidity memory-safe-assembly assembly { if iszero(gt(mload(data), 0x03)) { mstore(0x00, 0x0acec8bd) // `DataTooShort()`. revert(0x1c, 0x04) } let o := add(data, 0x20) mstore(o, or(shr(32, shl(32, mload(o))), newSelector)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol) /// /// @dev Note: /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /// @dev The ERC20 `totalSupply` query has failed. error TotalSupplyQueryFailed(); /// @dev The Permit2 operation has failed. error Permit2Failed(); /// @dev The Permit2 amount must be less than `2**160 - 1`. error Permit2AmountOverflow(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes. uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; /// @dev The unique EIP-712 domain domain separator for the DAI token contract. bytes32 internal constant DAI_DOMAIN_SEPARATOR = 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7; /// @dev The address for the WETH9 contract on Ethereum mainnet. address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; /// @dev The canonical Permit2 address. /// [Github](https://github.com/Uniswap/permit2) /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants. // // The regular variants: // - Forwards all remaining gas to the target. // - Reverts if the target reverts. // - Reverts if the current contract has insufficient balance. // // The force variants: // - Forwards with an optional gas stipend // (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases). // - If the target reverts, or if the gas stipend is exhausted, // creates a temporary contract to force send the ETH via `SELFDESTRUCT`. // Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758. // - Reverts if the current contract has insufficient balance. // // The try variants: // - Forwards with a mandatory gas stipend. // - Instead of reverting, returns whether the transfer succeeded. /// @dev Sends `amount` (in wei) ETH to `to`. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Sends all the ETH in the current contract to `to`. function safeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { // Transfer all the ETH and check if it succeeded or not. if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`. function forceSafeTransferAllETH(address to, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { // forgefmt: disable-next-item if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00) } } /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`. function trySafeTransferAllETH(address to, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// /// The `from` account must have at least `amount` approved for the current contract to manage. function trySafeTransferFrom(address token, address from, address to, uint256 amount) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { success := lt(or(iszero(extcodesize(token)), returndatasize()), success) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have their entire balance approved for the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`. amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it. // Perform the transfer, reverting upon failure. let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x14, to) // Store the `to` argument. amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// If the initial attempt to approve fails, attempts to reset the approved amount to zero, /// then retries the approval again (some tokens, e.g. USDT, requires this). /// Reverts upon failure. function safeApproveWithRetry(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, retrying upon failure. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x34, 0) // Store 0 for the `amount`. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval. mstore(0x34, amount) // Store back the original `amount`. // Retry the approval, reverting upon failure. success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { // Check the `extcodesize` again just in case the token selfdestructs lol. if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } } } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. amount := mul( // The arguments of `mul` are evaluated from right to left. mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } /// @dev Returns the total supply of the `token`. /// Reverts if the token does not exist or does not implement `totalSupply()`. function totalSupply(address token) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x18160ddd) // `totalSupply()`. if iszero( and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20)) ) { mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`. revert(0x1c, 0x04) } result := mload(0x00) } } /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// If the initial attempt fails, try to use Permit2 to transfer the token. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for the current contract to manage. function safeTransferFrom2(address token, address from, address to, uint256 amount) internal { if (!trySafeTransferFrom(token, from, to, amount)) { permit2TransferFrom(token, from, to, amount); } } /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2. /// Reverts upon failure. function permit2TransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(add(m, 0x74), shr(96, shl(96, token))) mstore(add(m, 0x54), amount) mstore(add(m, 0x34), to) mstore(add(m, 0x20), shl(96, from)) // `transferFrom(address,address,uint160,address)`. mstore(m, 0x36c78516000000000000000000000000) let p := PERMIT2 let exists := eq(chainid(), 1) if iszero(exists) { exists := iszero(iszero(extcodesize(p))) } if iszero( and( call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists. ) ) { mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`. revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04) } } } /// @dev Permit a user to spend a given amount of /// another user's tokens via native EIP-2612 permit if possible, falling /// back to Permit2 if native permit fails or is not implemented on the token. function permit2( address token, address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { bool success; /// @solidity memory-safe-assembly assembly { for {} shl(96, xor(token, WETH9)) {} { mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`. if iszero( and( // The arguments of `and` are evaluated from right to left. lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word. // Gas stipend to limit gas burn for tokens that don't refund gas when // an non-existing function is called. 5K should be enough for a SLOAD. staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20) ) ) { break } // After here, we can be sure that token is a contract. let m := mload(0x40) mstore(add(m, 0x34), spender) mstore(add(m, 0x20), shl(96, owner)) mstore(add(m, 0x74), deadline) if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) { mstore(0x14, owner) mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`. mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20)) mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`. // `nonces` is already at `add(m, 0x54)`. // `1` is already stored at `add(m, 0x94)`. mstore(add(m, 0xb4), and(0xff, v)) mstore(add(m, 0xd4), r) mstore(add(m, 0xf4), s) success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00) break } mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`. mstore(add(m, 0x54), amount) mstore(add(m, 0x94), and(0xff, v)) mstore(add(m, 0xb4), r) mstore(add(m, 0xd4), s) success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00) break } } if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s); } /// @dev Simple permit on the Permit2 contract. function simplePermit2( address token, address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(m, 0x927da105) // `allowance(address,address,address)`. { let addressMask := shr(96, not(0)) mstore(add(m, 0x20), and(addressMask, owner)) mstore(add(m, 0x40), and(addressMask, token)) mstore(add(m, 0x60), and(addressMask, spender)) mstore(add(m, 0xc0), and(addressMask, spender)) } let p := mul(PERMIT2, iszero(shr(160, amount))) if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`. staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60) ) ) { mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`. revert(add(0x18, shl(2, iszero(p))), 0x04) } mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant). // `owner` is already `add(m, 0x20)`. // `token` is already at `add(m, 0x40)`. mstore(add(m, 0x60), amount) mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`. // `nonce` is already at `add(m, 0xa0)`. // `spender` is already at `add(m, 0xc0)`. mstore(add(m, 0xe0), deadline) mstore(add(m, 0x100), 0x100) // `signature` offset. mstore(add(m, 0x120), 0x41) // `signature` length. mstore(add(m, 0x140), r) mstore(add(m, 0x160), s) mstore(add(m, 0x180), shl(248, v)) if iszero( // Revert if token does not have code, or if the call fails. mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) { mstore(0x00, 0x6b836e6b) // `Permit2Failed()`. revert(0x1c, 0x04) } } } }
{ "evmVersion": "paris", "optimizer": { "enabled": true, "runs": 1, "details": { "yul": true, "yulDetails": { "stackAllocation": true } } }, "viaIR": false, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint32","name":"chainSlug_","type":"uint32"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"string","name":"version_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"DeadlinePassed","type":"error"},{"inputs":[],"name":"InsufficientMsgValue","type":"error"},{"inputs":[],"name":"InvalidCallType","type":"error"},{"inputs":[],"name":"InvalidSwitchboard","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"LowGasLimit","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"NoPermit","type":"error"},{"inputs":[],"name":"OnlyOffChain","type":"error"},{"inputs":[{"internalType":"enum ExecutionStatus","name":"status","type":"uint8"}],"name":"PayloadAlreadyExecuted","type":"error"},{"inputs":[],"name":"PlugNotConnected","type":"error"},{"inputs":[],"name":"PlugNotFound","type":"error"},{"inputs":[],"name":"SimulationFailed","type":"error"},{"inputs":[],"name":"SwitchboardExists","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VerificationFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"triggerId","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"appGatewayId","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"switchboardId","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"plug","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"overrides","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"payload","type":"bytes"}],"name":"AppGatewayCallRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"payloadId","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"exceededMaxCopy","type":"bool"},{"indexed":false,"internalType":"bytes","name":"returnData","type":"bytes"}],"name":"ExecutionFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"payloadId","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"exceededMaxCopy","type":"bool"},{"indexed":false,"internalType":"bytes","name":"returnData","type":"bytes"}],"name":"ExecutionSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gasLimitBuffer","type":"uint256"}],"name":"GasLimitBufferUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"maxCopyBytes","type":"uint16"}],"name":"MaxCopyBytesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"plug","type":"address"},{"indexed":false,"internalType":"bytes32","name":"appGatewayId","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"switchboardId","type":"uint64"}],"name":"PlugConnected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"plug","type":"address"}],"name":"PlugDisconnected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"grantee","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"revokee","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSocketFeeManager","type":"address"},{"indexed":false,"internalType":"address","name":"newSocketFeeManager","type":"address"}],"name":"SocketFeeManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"switchboard","type":"address"},{"indexed":false,"internalType":"uint64","name":"switchboardId","type":"uint64"}],"name":"SwitchboardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"switchboardId","type":"uint64"}],"name":"SwitchboardDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"switchboardId","type":"uint64"}],"name":"SwitchboardEnabled","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"OFF_CHAIN_CALLER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chainSlug","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"appGatewayId_","type":"bytes32"},{"internalType":"uint64","name":"switchboardId_","type":"uint64"}],"name":"connect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"switchboardId_","type":"uint64"}],"name":"disableSwitchboard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disconnect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"switchboardId_","type":"uint64"}],"name":"enableSwitchboard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes4","name":"callType","type":"bytes4"},{"internalType":"uint160","name":"payloadPointer","type":"uint160"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes32","name":"prevBatchDigestHash","type":"bytes32"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ExecuteParams","name":"executeParams_","type":"tuple"},{"components":[{"internalType":"uint256","name":"socketFees","type":"uint256"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"bytes","name":"transmitterProof","type":"bytes"}],"internalType":"struct TransmissionParams","name":"transmissionParams_","type":"tuple"}],"name":"execute","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gasLimitBuffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"plugAddress_","type":"address"}],"name":"getPlugConfig","outputs":[{"internalType":"bytes32","name":"appGatewayId","type":"bytes32"},{"internalType":"uint64","name":"switchboardId","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"grantee_","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"address_","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"isValidSwitchboard","outputs":[{"internalType":"enum SwitchboardStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxCopyBytes","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"payloadExecuted","outputs":[{"internalType":"enum ExecutionStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"payloadIdToDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registerSwitchboard","outputs":[{"internalType":"uint64","name":"switchboardId","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"rescueTo_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"revokee_","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasLimitBuffer_","type":"uint256"}],"name":"setGasLimitBuffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"maxCopyBytes_","type":"uint16"}],"name":"setMaxCopyBytes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"socketFeeManager_","type":"address"}],"name":"setSocketFeeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct SocketUtils.SimulateParams[]","name":"params","type":"tuple[]"}],"name":"simulate","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"},{"internalType":"bool","name":"exceededMaxCopy","type":"bool"}],"internalType":"struct SocketUtils.SimulationResult[]","name":"","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"socketFeeManager","outputs":[{"internalType":"contract ISocketFeeManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"switchboardAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"switchboardIdCounter","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"switchboardIds","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"triggerAppGateway","outputs":[{"internalType":"bytes32","name":"triggerId","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"triggerCounter","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60e0604052603580546001600160501b031916620108001790553480156200002657600080fd5b5060405162002972380380620029728339810160408190526200004991620000e6565b63ffffffff831660c0528051602082012060a0526001600160e01b031960e084901b163060401b17608052828282620000828262000094565b5050606960385550620001f992505050565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b634e487b7160e01b600052604160045260246000fd5b600080600060608486031215620000fc57600080fd5b835163ffffffff811681146200011157600080fd5b602085810151919450906001600160a01b03811681146200013157600080fd5b60408601519093506001600160401b03808211156200014f57600080fd5b818701915087601f8301126200016457600080fd5b815181811115620001795762000179620000d0565b604051601f8201601f19908116603f01168101908382118183101715620001a457620001a4620000d0565b816040528281528a86848701011115620001bd57600080fd5b600093505b82841015620001e15784840186015181850187015292850192620001c2565b60008684830101528096505050505050509250925092565b60805160a05160c05161274262000230600039600081816105a70152610cf3015260006103a8015260006114ea01526127426000f3fe6080604052600436106101a35760003560e01c80631f0cfa7814610232578063212249d414610254578063256929621461028757806325bd97e51461028f57806325e94caf146102af57806329e654e1146102cf5780632e4d89fb146102f05780632f2ff15d146103115780633eaeac3d146103315780634fc7d6e91461036e57806354d1f13d1461038e57806354fd4d50146103965780635f850dfd146103ca5780636ccae054146103fd578063715018a61461041d57806374f5b1fc146104255780637c8552b21461043a5780638b0021de146104675780638da5cb5b1461048757806391bf8275146104a957806391d14854146104c957806391db23d3146104f95780639cf0af931461052f578063b30fe8ff14610565578063b349ba6514610595578063cb2cb4f6146105de578063d547741f146105f4578063d9374bff14610614578063de5b883814610629578063e4d728f014610649578063ea072f061461065f578063f04e283e1461067f578063f2fde38b14610692578063f3aebe4d146106a5578063f9778ee0146106c5578063fee81cf41461073e576101f2565b366101f25760405162461bcd60e51b815260206004820152601a6024820152790a6dec6d6cae840c8decae640dcdee840c2c6c6cae0e8408aa8960331b60448201526064015b60405180910390fd5b60003660606102043334600036610771565b60405160200161021691815260200190565b6040516020818303038152906040529050915050805190602001f35b34801561023e57600080fd5b5061025261024d366004611d67565b610977565b005b34801561026057600080fd5b5060355461026f9061ffff1681565b60405161ffff90911681526020015b60405180910390f35b610252610a04565b34801561029b57600080fd5b506102526102aa366004611da5565b610a53565b3480156102bb57600080fd5b506102526102ca366004611dd9565b610af8565b6102e26102dd366004611df4565b610bbf565b60405190815260200161027e565b6103036102fe366004611e65565b610bd4565b60405161027e929190611f28565b34801561031d57600080fd5b5061025261032c366004611f4b565b610d82565b34801561033d57600080fd5b5061036161034c366004611d67565b603a6020526000908152604090205460ff1681565b60405161027e9190611fa4565b34801561037a57600080fd5b50610252610389366004611fb7565b610d98565b610252610e2c565b3480156103a257600080fd5b506102e27f000000000000000000000000000000000000000000000000000000000000000081565b3480156103d657600080fd5b506035546103f0906201000090046001600160401b031681565b60405161027e9190611fdb565b34801561040957600080fd5b50610252610418366004611fef565b610e68565b610252610eed565b34801561043157600080fd5b506103f0610f01565b34801561044657600080fd5b506102e2610455366004611d67565b603b6020526000908152604090205481565b34801561047357600080fd5b506039546103f0906001600160401b031681565b34801561049357600080fd5b50638b78c6d819545b60405161027e919061203d565b6104bc6104b7366004612051565b611009565b60405161027e91906120b3565b3480156104d557600080fd5b506104e96104e4366004611f4b565b611215565b604051901515815260200161027e565b34801561050557600080fd5b506103f0610514366004611da5565b6037602052600090815260409020546001600160401b031681565b34801561053b57600080fd5b5061049c61054a366004611dd9565b6036602052600090815260409020546001600160a01b031681565b34801561057157600080fd5b50610361610580366004611dd9565b60336020526000908152604090205460ff1681565b3480156105a157600080fd5b506105c97f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff909116815260200161027e565b3480156105ea57600080fd5b5061049c61dead81565b34801561060057600080fd5b5061025261060f366004611f4b565b61123e565b34801561062057600080fd5b50610252611250565b34801561063557600080fd5b5060325461049c906001600160a01b031681565b34801561065557600080fd5b506102e260385481565b34801561066b57600080fd5b5061025261067a366004611dd9565b6112cf565b61025261068d366004611da5565b611372565b6102526106a0366004611da5565b6113b2565b3480156106b157600080fd5b506102526106c036600461213a565b6113d9565b3480156106d157600080fd5b506107216106e0366004611da5565b6001600160a01b0316600090815260346020908152604091829020825180840190935280548084526001909101546001600160401b03169290910182905291565b604080519283526001600160401b0390911660208301520161027e565b34801561074a57600080fd5b506102e2610759366004611da5565b63389a75e1600c908152600091909152602090205490565b6001600160a01b0384166000908152603460209081526040808320815180830190925280548083526001909101546001600160401b031692820192909252906107cd57604051632f8d63b560e11b815260040160405180910390fd5b60016020808301516001600160401b031660009081526033909152604090205460ff16600281111561080157610801611f70565b1461081f5760405163f63c9e4d60e01b815260040160405180910390fd5b6000866001600160a01b0316634a85f0416040518163ffffffff1660e01b8152600401600060405180830381865afa15801561085f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610887919081019061217c565b905061089161149e565b6020808401516001600160401b031660009081526036909152604090819020549051631fccd4af60e21b81529194506001600160a01b031690637f3352bc9088906108e8908b9088908b908b908990600401612251565b6000604051808303818588803b15801561090157600080fd5b505af1158015610915573d6000803e3d6000fd5b50505050507f8ff0599581fd62c5733e52cea3abd7874731f4a9f86ebb929e5e4afe103f74d4838360000151846020015161094f8b611512565b858a8a6040516109659796959493929190612297565b60405180910390a15050949350505050565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff166109c75760405163962f633360e01b8152600481018290526024016101e9565b60388290556040518281527fd0e3eb5d0d212f0a08af2be98373721fc901ed26fbac645e08bd664fef818366906020015b60405180910390a15050565b60006202a3006001600160401b03164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff16610aa35760405163962f633360e01b8152600481018290526024016101e9565b603280546001600160a01b0319166001600160a01b0384169081179091556040805182815260208101929092527fdcb02e10d5220346a4638aa2826eaab1897306623bc40a427049e4ebd12255b491016109f8565b3360009081527fb5c50a38e6916e731a3226b7d5e5c3b91233b8f8e2aed2cd335b91f819685d4460205260409020547f26cb9fcdfa98911bffb8f428f25753d4023cb719e648f062ccd7d210f02d79879060ff16610b6c5760405163962f633360e01b8152600481018290526024016101e9565b6001600160401b03821660009081526033602052604090819020805460ff19166002179055517f9ab25a32266417ee52a390121ebca0463374e76cecb25596a0f68e9d96a9e0ff906109f8908490611fdb565b6000610bcd33348585610771565b9392505050565b600060604284604001351015610bfd5760405163387b2e5560e11b815260040160405180910390fd5b6307905faf60e51b610c126020860186612303565b6001600160e01b03191614610c3a576040516339d2eb5560e01b815260040160405180910390fd5b6000603481610c4f60e0880160c08901611da5565b6001600160a01b0316815260208101919091526040016000208054909150610c8a57604051632f8d63b560e11b815260040160405180910390fd5b610c9984356080870135612334565b341015610cb957604051633c79c7bb60e11b815260040160405180910390fd5b6000610d24610cce6040880160208901611da5565b60018401546001600160a01b039190911660a09190911b600160a01b600160e01b03167f000000000000000000000000000000000000000000000000000000000000000060e01b6001600160e01b031916171790565b9050610d2f8161151e565b604080518082019091528254815260018301546001600160401b03166020820152610d6990829088610d6460608a018a612347565b611590565b610d7481878761174b565b9350935050505b9250929050565b610d8a61194e565b610d948282611969565b5050565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff16610de85760405163962f633360e01b8152600481018290526024016101e9565b6035805461ffff191661ffff84169081179091556040519081527f294d0c11af52572317e5a0e1362cbf85b3b7c1f7b3f6c7b7e3e5c29c76da33e2906020016109f8565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b3360009081527f266cad2a07820380da249144ee0922486396c59c16dc4ab5f4c00611d746e9b760205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16610edc5760405163962f633360e01b8152600481018290526024016101e9565b610ee78484846119c2565b50505050565b610ef561194e565b610eff6000611b4a565b565b336000908152603760205260409020546001600160401b03168015610f3957604051632dff855560e01b815260040160405180910390fd5b603580546201000090046001600160401b0316906002610f588361238d565b82546101009290920a6001600160401b0381810219909316918316021790915533600081815260376020908152604080832080546001600160401b03191695871695861790558483526036825280832080546001600160a01b031916851790556033825291829020805460ff19166001179055815192835282019290925281519293507f2f945cce0a82eacc4841d996b3d0429e01c7c603f3f900253d21b428c760dce1929081900390910190a190565b60603361dead1461102d57604051634e5ff03360e11b815260040160405180910390fd5b6000826001600160401b0381111561104757611047612166565b6040519080825280602002602001820160405280156110a057816020015b61108d6040518060600160405280600015158152602001606081526020016000151581525090565b8152602001906001900390816110655790505b50905060005b8381101561120b5760008060006111be8888868181106110c8576110c86123bb565b90506020028101906110da91906123d1565b602001358989878181106110f0576110f06123bb565b905060200281019061110291906123d1565b603554604091909101359061ffff168b8b89818110611123576111236123bb565b905060200281019061113591906123d1565b611143906060810190612347565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508f92508e91508b905081811061118c5761118c6123bb565b905060200281019061119e91906123d1565b6111ac906020810190611da5565b6001600160a01b031693929190611b88565b925092509250604051806060016040528084151581526020018281526020018315158152508585815181106111f5576111f56123bb565b60209081029190910101525050506001016110a6565b5090505b92915050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610bcd565b61124661194e565b610d948282611bd7565b336000908152603460205260409020805461127e5760405163411d025560e01b815260040160405180910390fd5b600081556001810180546001600160401b03191690556040517f474a53f61630e976f47075b6029ba8d55d0563151bdb9222c5dbdc88f7af6f51906112c490339061203d565b60405180910390a150565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff1661131f5760405163962f633360e01b8152600481018290526024016101e9565b6001600160401b03821660009081526033602052604090819020805460ff19166001179055517fa1cea6c3e73c288db1f2e2d7f04d9fd5f12463c30019b4ed354ba8bc7bc26f28906109f8908490611fdb565b61137a61194e565b63389a75e1600c52806000526020600c2080544211156113a257636f5e88186000526004601cfd5b600090556113af81611b4a565b50565b6113ba61194e565b8060601b6113d057637448fbae6000526004601cfd5b6113af81611b4a565b60016001600160401b03821660009081526033602052604090205460ff16600281111561140857611408611f70565b146114265760405163f63c9e4d60e01b815260040160405180910390fd5b336000818152603460209081526040918290208581556001810180546001600160401b0319166001600160401b038716908117909155835194855291840186905291830152907fb2a45daaee4fc6ced936700efec176684095e7b77c4cb1419b578ecc6f2ebce69060600160405180910390a1505050565b603980546000916001600160401b0390911690826114bb8361238d565b91906101000a8154816001600160401b0302191690836001600160401b031602179055506001600160401b03167f00000000000000000000000000000000000000000000000000000000000000001760001b905090565b6001600160a01b031690565b60016000828152603a602052604090205460ff16600281111561154357611543611f70565b03611575576000818152603a6020526040908190205490516310e6a4a360e21b81526101e99160ff1690600401611fa4565b6000908152603a60205260409020805460ff19166001179055565b60016020808601516001600160401b031660009081526033909152604090205460ff1660028111156115c4576115c4611f70565b146115e25760405163f63c9e4d60e01b815260040160405180910390fd5b6020808501516001600160401b031660009081526036909152604080822054905162e7cfb160e71b81526001600160a01b03909116906373e7d880906116329033908a90889088906004016123f1565b602060405180830381865afa15801561164f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116739190612423565b905060006116878288886000015188611c2d565b6000888152603b60209081526040808320849055898201516001600160401b031683526036909152908190205490516318e11fb360e11b815260048101839052602481018a90529192506001600160a01b0316906331c23f6690604401602060405180830381865afa158015611701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117259190612440565b6117425760405163439cc0cd60e01b815260040160405180910390fd5b50505050505050565b60006060606460385485606001356117639190612462565b61176d9190612479565b5a101561178d5760405163069c76d760e51b815260040160405180910390fd5b6035546000906117f99060808701359060608801359061ffff166117b460e08a018a612347565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506111ac9250505060e08b0160c08c01611da5565b919450909250905082156118c0577f324d63a433b21a12b90e79cd2ba736b2a5238be6165e03b750fa4a7d5193d5d986828460405161183a9392919061249b565b60405180910390a16032546001600160a01b0316156118bb57603254604051630b2b48ed60e01b81526001600160a01b0390911690630b2b48ed908635906118889089908990600401612571565b6000604051808303818588803b1580156118a157600080fd5b505af11580156118b5573d6000803e3d6000fd5b50505050505b611945565b6000868152603a60209081526040808320805460ff191660021790556118eb91908701908701611da5565b90506001600160a01b0381166118fe5750335b6119088134611cd4565b7f385334bc68a32c4d164625189adc7633e6074eb1b837fb4d11d768245151e4ce87838560405161193b9392919061249b565b60405180910390a1505b50935093915050565b638b78c6d819543314610eff576382b429006000526004601cfd5b6000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001600160a01b0382166119e95760405163d92e233d60e01b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611a1d57611a188282611cd4565b505050565b826001600160a01b03163b600003611a4857604051630f58058360e11b815260040160405180910390fd5b6040516301ffc9a760e01b81526380ac58cd60e01b60048201526001600160a01b038416906301ffc9a790602401602060405180830381865afa925050508015611aaf575060408051601f3d908101601f19168201909252611aac91810190612440565b60015b611abe57611a18838383611d17565b8015611b3f57604051635c46a7ef60e11b81523060048201526001600160a01b03848116602483015260448201849052608060648301526000608483015285169063b88d4fde9060a401600060405180830381600087803b158015611b2257600080fd5b505af1158015611b36573d6000803e3d6000fd5b50505050610ee7565b610ee7848484611d17565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600080606060405190506000388551602087018a8c8bf192503d8561ffff163d1115611bb957506001915061ffff85165b80825260208201816000823e01604052919790965090945092505050565b6000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b6000611c3830611512565b611c4186611512565b856040850135611c546020870187612303565b60608701356080880135611c6b60e08a018a612347565b611c83611c7e60e08d0160c08e01611da5565b611512565b8c60a08d0135611c976101008f018f612347565b604051602001611cb49e9d9c9b9a99989796959493929190612656565b604051602081830303815290604052805190602001209050949350505050565b80471015611cea5763b12d13eb6000526004601cfd5b6000386000388486620186a0f1610d9457816000526073600b5360ff6020536016600b82f0610d94573838fd5b816014528060345263a9059cbb60601b60005260206000604460106000875af18060016000511416611d5c57803d853b151710611d5c576390b8ec186000526004601cfd5b506000603452505050565b600060208284031215611d7957600080fd5b5035919050565b6001600160a01b03811681146113af57600080fd5b8035611da081611d80565b919050565b600060208284031215611db757600080fd5b8135610bcd81611d80565b80356001600160401b0381168114611da057600080fd5b600060208284031215611deb57600080fd5b610bcd82611dc2565b60008060208385031215611e0757600080fd5b82356001600160401b0380821115611e1e57600080fd5b818501915085601f830112611e3257600080fd5b813581811115611e4157600080fd5b866020828501011115611e5357600080fd5b60209290920196919550909350505050565b60008060408385031215611e7857600080fd5b82356001600160401b0380821115611e8f57600080fd5b908401906101208287031215611ea457600080fd5b90925060208401359080821115611eba57600080fd5b50830160808186031215611ecd57600080fd5b809150509250929050565b60005b83811015611ef3578181015183820152602001611edb565b50506000910152565b60008151808452611f14816020860160208601611ed8565b601f01601f19169290920160200192915050565b8215158152604060208201526000611f436040830184611efc565b949350505050565b60008060408385031215611f5e57600080fd5b823591506020830135611ecd81611d80565b634e487b7160e01b600052602160045260246000fd5b600381106113af57634e487b7160e01b600052602160045260246000fd5b60208101611fb183611f86565b91905290565b600060208284031215611fc957600080fd5b813561ffff81168114610bcd57600080fd5b6001600160401b0391909116815260200190565b60008060006060848603121561200457600080fd5b833561200f81611d80565b9250602084013561201f81611d80565b929592945050506040919091013590565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b6000806020838503121561206457600080fd5b82356001600160401b038082111561207b57600080fd5b818501915085601f83011261208f57600080fd5b81358181111561209e57600080fd5b8660208260051b8501011115611e5357600080fd5b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b8381101561212c57603f198984030185528151606081511515855288820151818a87015261210c82870182611efc565b9289015115159589019590955250948701949250908601906001016120dc565b509098975050505050505050565b6000806040838503121561214d57600080fd5b8235915061215d60208401611dc2565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561218e57600080fd5b81516001600160401b03808211156121a557600080fd5b818401915084601f8301126121b957600080fd5b8151818111156121cb576121cb612166565b604051601f8201601f19908116603f011681019083821181831017156121f3576121f3612166565b8160405282815287602084870101111561220c57600080fd5b61221d836020830160208801611ed8565b979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60018060a01b0386168152846020820152608060408201526000612279608083018587612228565b828103606084015261228b8185611efc565b98975050505050505050565b87815286602082015260018060401b038616604082015284606082015260c0608082015260006122ca60c0830186611efc565b82810360a08401526122dd818587612228565b9a9950505050505050505050565b80356001600160e01b031981168114611da057600080fd5b60006020828403121561231557600080fd5b610bcd826122eb565b634e487b7160e01b600052601160045260246000fd5b8082018082111561120f5761120f61231e565b6000808335601e1984360301811261235e57600080fd5b8301803591506001600160401b0382111561237857600080fd5b602001915036819003821315610d7b57600080fd5b60006001600160401b038281166002600160401b031981016123b1576123b161231e565b6001019392505050565b634e487b7160e01b600052603260045260246000fd5b60008235607e198336030181126123e757600080fd5b9190910192915050565b60018060a01b0385168152836020820152606060408201526000612419606083018486612228565b9695505050505050565b60006020828403121561243557600080fd5b8151610bcd81611d80565b60006020828403121561245257600080fd5b81518015158114610bcd57600080fd5b808202811582820484141761120f5761120f61231e565b60008261249657634e487b7160e01b600052601260045260246000fd5b500490565b83815282151560208201526060604082015260006124bc6060830184611efc565b95945050505050565b6000808335601e198436030181126124dc57600080fd5b83016020810192503590506001600160401b038111156124fb57600080fd5b803603821315610d7b57600080fd5b803582526000602082013561251e81611d80565b6001600160a01b0316602084015261253960408301836124c5565b6080604086015261254e608086018284612228565b91505061255e60608401846124c5565b8583036060870152612419838284612228565b6040815261259360408201612585856122eb565b6001600160e01b0319169052565b60006125a160208501611d95565b6125ae6060840182612030565b5060408401356080830152606084013560a0830152608084013560c083015260a084013560e08301526125e360c08501611d95565b6101006125f281850183612030565b6125ff60e08701876124c5565b61012086810152925061261761016086018483612228565b925050612626818701876124c5565b858403603f19016101408701529150612640838383612228565b9250505082810360208401526124bc818561250a565b8e81528d60208201528c60408201528b606082015263ffffffff60e01b8b1660808201528960848201528860a4820152868860c483013760008782018760c48201528660e4820152856101048201526101248486828401376000949091010192835250909e9d505050505050505050505050505056fe71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb1316a51056d8b2401ab04c48d9973add294eadde229c785c6d19cde93785467efa26469706673582212201bd6cad191a27c6473d37cbac5a541b99b4602fc8b5bf9fdbc05109528eb8c9a64736f6c6343000816003300000000000000000000000000000000000000000000000000000000000b67d20000000000000000000000007bd61c667f869fb21b77626f0ac0acee51e4be7c0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000445564d5800000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106101a35760003560e01c80631f0cfa7814610232578063212249d414610254578063256929621461028757806325bd97e51461028f57806325e94caf146102af57806329e654e1146102cf5780632e4d89fb146102f05780632f2ff15d146103115780633eaeac3d146103315780634fc7d6e91461036e57806354d1f13d1461038e57806354fd4d50146103965780635f850dfd146103ca5780636ccae054146103fd578063715018a61461041d57806374f5b1fc146104255780637c8552b21461043a5780638b0021de146104675780638da5cb5b1461048757806391bf8275146104a957806391d14854146104c957806391db23d3146104f95780639cf0af931461052f578063b30fe8ff14610565578063b349ba6514610595578063cb2cb4f6146105de578063d547741f146105f4578063d9374bff14610614578063de5b883814610629578063e4d728f014610649578063ea072f061461065f578063f04e283e1461067f578063f2fde38b14610692578063f3aebe4d146106a5578063f9778ee0146106c5578063fee81cf41461073e576101f2565b366101f25760405162461bcd60e51b815260206004820152601a6024820152790a6dec6d6cae840c8decae640dcdee840c2c6c6cae0e8408aa8960331b60448201526064015b60405180910390fd5b60003660606102043334600036610771565b60405160200161021691815260200190565b6040516020818303038152906040529050915050805190602001f35b34801561023e57600080fd5b5061025261024d366004611d67565b610977565b005b34801561026057600080fd5b5060355461026f9061ffff1681565b60405161ffff90911681526020015b60405180910390f35b610252610a04565b34801561029b57600080fd5b506102526102aa366004611da5565b610a53565b3480156102bb57600080fd5b506102526102ca366004611dd9565b610af8565b6102e26102dd366004611df4565b610bbf565b60405190815260200161027e565b6103036102fe366004611e65565b610bd4565b60405161027e929190611f28565b34801561031d57600080fd5b5061025261032c366004611f4b565b610d82565b34801561033d57600080fd5b5061036161034c366004611d67565b603a6020526000908152604090205460ff1681565b60405161027e9190611fa4565b34801561037a57600080fd5b50610252610389366004611fb7565b610d98565b610252610e2c565b3480156103a257600080fd5b506102e27fd37a307785b025da02406bd67d6fec7013b6629c2d9bcd83d2211c8307ebe3d281565b3480156103d657600080fd5b506035546103f0906201000090046001600160401b031681565b60405161027e9190611fdb565b34801561040957600080fd5b50610252610418366004611fef565b610e68565b610252610eed565b34801561043157600080fd5b506103f0610f01565b34801561044657600080fd5b506102e2610455366004611d67565b603b6020526000908152604090205481565b34801561047357600080fd5b506039546103f0906001600160401b031681565b34801561049357600080fd5b50638b78c6d819545b60405161027e919061203d565b6104bc6104b7366004612051565b611009565b60405161027e91906120b3565b3480156104d557600080fd5b506104e96104e4366004611f4b565b611215565b604051901515815260200161027e565b34801561050557600080fd5b506103f0610514366004611da5565b6037602052600090815260409020546001600160401b031681565b34801561053b57600080fd5b5061049c61054a366004611dd9565b6036602052600090815260409020546001600160a01b031681565b34801561057157600080fd5b50610361610580366004611dd9565b60336020526000908152604090205460ff1681565b3480156105a157600080fd5b506105c97f00000000000000000000000000000000000000000000000000000000000b67d281565b60405163ffffffff909116815260200161027e565b3480156105ea57600080fd5b5061049c61dead81565b34801561060057600080fd5b5061025261060f366004611f4b565b61123e565b34801561062057600080fd5b50610252611250565b34801561063557600080fd5b5060325461049c906001600160a01b031681565b34801561065557600080fd5b506102e260385481565b34801561066b57600080fd5b5061025261067a366004611dd9565b6112cf565b61025261068d366004611da5565b611372565b6102526106a0366004611da5565b6113b2565b3480156106b157600080fd5b506102526106c036600461213a565b6113d9565b3480156106d157600080fd5b506107216106e0366004611da5565b6001600160a01b0316600090815260346020908152604091829020825180840190935280548084526001909101546001600160401b03169290910182905291565b604080519283526001600160401b0390911660208301520161027e565b34801561074a57600080fd5b506102e2610759366004611da5565b63389a75e1600c908152600091909152602090205490565b6001600160a01b0384166000908152603460209081526040808320815180830190925280548083526001909101546001600160401b031692820192909252906107cd57604051632f8d63b560e11b815260040160405180910390fd5b60016020808301516001600160401b031660009081526033909152604090205460ff16600281111561080157610801611f70565b1461081f5760405163f63c9e4d60e01b815260040160405180910390fd5b6000866001600160a01b0316634a85f0416040518163ffffffff1660e01b8152600401600060405180830381865afa15801561085f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610887919081019061217c565b905061089161149e565b6020808401516001600160401b031660009081526036909152604090819020549051631fccd4af60e21b81529194506001600160a01b031690637f3352bc9088906108e8908b9088908b908b908990600401612251565b6000604051808303818588803b15801561090157600080fd5b505af1158015610915573d6000803e3d6000fd5b50505050507f8ff0599581fd62c5733e52cea3abd7874731f4a9f86ebb929e5e4afe103f74d4838360000151846020015161094f8b611512565b858a8a6040516109659796959493929190612297565b60405180910390a15050949350505050565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff166109c75760405163962f633360e01b8152600481018290526024016101e9565b60388290556040518281527fd0e3eb5d0d212f0a08af2be98373721fc901ed26fbac645e08bd664fef818366906020015b60405180910390a15050565b60006202a3006001600160401b03164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff16610aa35760405163962f633360e01b8152600481018290526024016101e9565b603280546001600160a01b0319166001600160a01b0384169081179091556040805182815260208101929092527fdcb02e10d5220346a4638aa2826eaab1897306623bc40a427049e4ebd12255b491016109f8565b3360009081527fb5c50a38e6916e731a3226b7d5e5c3b91233b8f8e2aed2cd335b91f819685d4460205260409020547f26cb9fcdfa98911bffb8f428f25753d4023cb719e648f062ccd7d210f02d79879060ff16610b6c5760405163962f633360e01b8152600481018290526024016101e9565b6001600160401b03821660009081526033602052604090819020805460ff19166002179055517f9ab25a32266417ee52a390121ebca0463374e76cecb25596a0f68e9d96a9e0ff906109f8908490611fdb565b6000610bcd33348585610771565b9392505050565b600060604284604001351015610bfd5760405163387b2e5560e11b815260040160405180910390fd5b6307905faf60e51b610c126020860186612303565b6001600160e01b03191614610c3a576040516339d2eb5560e01b815260040160405180910390fd5b6000603481610c4f60e0880160c08901611da5565b6001600160a01b0316815260208101919091526040016000208054909150610c8a57604051632f8d63b560e11b815260040160405180910390fd5b610c9984356080870135612334565b341015610cb957604051633c79c7bb60e11b815260040160405180910390fd5b6000610d24610cce6040880160208901611da5565b60018401546001600160a01b039190911660a09190911b600160a01b600160e01b03167f00000000000000000000000000000000000000000000000000000000000b67d260e01b6001600160e01b031916171790565b9050610d2f8161151e565b604080518082019091528254815260018301546001600160401b03166020820152610d6990829088610d6460608a018a612347565b611590565b610d7481878761174b565b9350935050505b9250929050565b610d8a61194e565b610d948282611969565b5050565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff16610de85760405163962f633360e01b8152600481018290526024016101e9565b6035805461ffff191661ffff84169081179091556040519081527f294d0c11af52572317e5a0e1362cbf85b3b7c1f7b3f6c7b7e3e5c29c76da33e2906020016109f8565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b3360009081527f266cad2a07820380da249144ee0922486396c59c16dc4ab5f4c00611d746e9b760205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16610edc5760405163962f633360e01b8152600481018290526024016101e9565b610ee78484846119c2565b50505050565b610ef561194e565b610eff6000611b4a565b565b336000908152603760205260409020546001600160401b03168015610f3957604051632dff855560e01b815260040160405180910390fd5b603580546201000090046001600160401b0316906002610f588361238d565b82546101009290920a6001600160401b0381810219909316918316021790915533600081815260376020908152604080832080546001600160401b03191695871695861790558483526036825280832080546001600160a01b031916851790556033825291829020805460ff19166001179055815192835282019290925281519293507f2f945cce0a82eacc4841d996b3d0429e01c7c603f3f900253d21b428c760dce1929081900390910190a190565b60603361dead1461102d57604051634e5ff03360e11b815260040160405180910390fd5b6000826001600160401b0381111561104757611047612166565b6040519080825280602002602001820160405280156110a057816020015b61108d6040518060600160405280600015158152602001606081526020016000151581525090565b8152602001906001900390816110655790505b50905060005b8381101561120b5760008060006111be8888868181106110c8576110c86123bb565b90506020028101906110da91906123d1565b602001358989878181106110f0576110f06123bb565b905060200281019061110291906123d1565b603554604091909101359061ffff168b8b89818110611123576111236123bb565b905060200281019061113591906123d1565b611143906060810190612347565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508f92508e91508b905081811061118c5761118c6123bb565b905060200281019061119e91906123d1565b6111ac906020810190611da5565b6001600160a01b031693929190611b88565b925092509250604051806060016040528084151581526020018281526020018315158152508585815181106111f5576111f56123bb565b60209081029190910101525050506001016110a6565b5090505b92915050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16610bcd565b61124661194e565b610d948282611bd7565b336000908152603460205260409020805461127e5760405163411d025560e01b815260040160405180910390fd5b600081556001810180546001600160401b03191690556040517f474a53f61630e976f47075b6029ba8d55d0563151bdb9222c5dbdc88f7af6f51906112c490339061203d565b60405180910390a150565b3360009081526000805160206126ed83398151915260205260409020546000805160206126cd8339815191529060ff1661131f5760405163962f633360e01b8152600481018290526024016101e9565b6001600160401b03821660009081526033602052604090819020805460ff19166001179055517fa1cea6c3e73c288db1f2e2d7f04d9fd5f12463c30019b4ed354ba8bc7bc26f28906109f8908490611fdb565b61137a61194e565b63389a75e1600c52806000526020600c2080544211156113a257636f5e88186000526004601cfd5b600090556113af81611b4a565b50565b6113ba61194e565b8060601b6113d057637448fbae6000526004601cfd5b6113af81611b4a565b60016001600160401b03821660009081526033602052604090205460ff16600281111561140857611408611f70565b146114265760405163f63c9e4d60e01b815260040160405180910390fd5b336000818152603460209081526040918290208581556001810180546001600160401b0319166001600160401b038716908117909155835194855291840186905291830152907fb2a45daaee4fc6ced936700efec176684095e7b77c4cb1419b578ecc6f2ebce69060600160405180910390a1505050565b603980546000916001600160401b0390911690826114bb8361238d565b91906101000a8154816001600160401b0302191690836001600160401b031602179055506001600160401b03167f000b67d2b94742b094f89a8d53e15a45cdbf9810a5b090eb00000000000000001760001b905090565b6001600160a01b031690565b60016000828152603a602052604090205460ff16600281111561154357611543611f70565b03611575576000818152603a6020526040908190205490516310e6a4a360e21b81526101e99160ff1690600401611fa4565b6000908152603a60205260409020805460ff19166001179055565b60016020808601516001600160401b031660009081526033909152604090205460ff1660028111156115c4576115c4611f70565b146115e25760405163f63c9e4d60e01b815260040160405180910390fd5b6020808501516001600160401b031660009081526036909152604080822054905162e7cfb160e71b81526001600160a01b03909116906373e7d880906116329033908a90889088906004016123f1565b602060405180830381865afa15801561164f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116739190612423565b905060006116878288886000015188611c2d565b6000888152603b60209081526040808320849055898201516001600160401b031683526036909152908190205490516318e11fb360e11b815260048101839052602481018a90529192506001600160a01b0316906331c23f6690604401602060405180830381865afa158015611701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117259190612440565b6117425760405163439cc0cd60e01b815260040160405180910390fd5b50505050505050565b60006060606460385485606001356117639190612462565b61176d9190612479565b5a101561178d5760405163069c76d760e51b815260040160405180910390fd5b6035546000906117f99060808701359060608801359061ffff166117b460e08a018a612347565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506111ac9250505060e08b0160c08c01611da5565b919450909250905082156118c0577f324d63a433b21a12b90e79cd2ba736b2a5238be6165e03b750fa4a7d5193d5d986828460405161183a9392919061249b565b60405180910390a16032546001600160a01b0316156118bb57603254604051630b2b48ed60e01b81526001600160a01b0390911690630b2b48ed908635906118889089908990600401612571565b6000604051808303818588803b1580156118a157600080fd5b505af11580156118b5573d6000803e3d6000fd5b50505050505b611945565b6000868152603a60209081526040808320805460ff191660021790556118eb91908701908701611da5565b90506001600160a01b0381166118fe5750335b6119088134611cd4565b7f385334bc68a32c4d164625189adc7633e6074eb1b837fb4d11d768245151e4ce87838560405161193b9392919061249b565b60405180910390a1505b50935093915050565b638b78c6d819543314610eff576382b429006000526004601cfd5b6000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001600160a01b0382166119e95760405163d92e233d60e01b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611a1d57611a188282611cd4565b505050565b826001600160a01b03163b600003611a4857604051630f58058360e11b815260040160405180910390fd5b6040516301ffc9a760e01b81526380ac58cd60e01b60048201526001600160a01b038416906301ffc9a790602401602060405180830381865afa925050508015611aaf575060408051601f3d908101601f19168201909252611aac91810190612440565b60015b611abe57611a18838383611d17565b8015611b3f57604051635c46a7ef60e11b81523060048201526001600160a01b03848116602483015260448201849052608060648301526000608483015285169063b88d4fde9060a401600060405180830381600087803b158015611b2257600080fd5b505af1158015611b36573d6000803e3d6000fd5b50505050610ee7565b610ee7848484611d17565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600080606060405190506000388551602087018a8c8bf192503d8561ffff163d1115611bb957506001915061ffff85165b80825260208201816000823e01604052919790965090945092505050565b6000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b6000611c3830611512565b611c4186611512565b856040850135611c546020870187612303565b60608701356080880135611c6b60e08a018a612347565b611c83611c7e60e08d0160c08e01611da5565b611512565b8c60a08d0135611c976101008f018f612347565b604051602001611cb49e9d9c9b9a99989796959493929190612656565b604051602081830303815290604052805190602001209050949350505050565b80471015611cea5763b12d13eb6000526004601cfd5b6000386000388486620186a0f1610d9457816000526073600b5360ff6020536016600b82f0610d94573838fd5b816014528060345263a9059cbb60601b60005260206000604460106000875af18060016000511416611d5c57803d853b151710611d5c576390b8ec186000526004601cfd5b506000603452505050565b600060208284031215611d7957600080fd5b5035919050565b6001600160a01b03811681146113af57600080fd5b8035611da081611d80565b919050565b600060208284031215611db757600080fd5b8135610bcd81611d80565b80356001600160401b0381168114611da057600080fd5b600060208284031215611deb57600080fd5b610bcd82611dc2565b60008060208385031215611e0757600080fd5b82356001600160401b0380821115611e1e57600080fd5b818501915085601f830112611e3257600080fd5b813581811115611e4157600080fd5b866020828501011115611e5357600080fd5b60209290920196919550909350505050565b60008060408385031215611e7857600080fd5b82356001600160401b0380821115611e8f57600080fd5b908401906101208287031215611ea457600080fd5b90925060208401359080821115611eba57600080fd5b50830160808186031215611ecd57600080fd5b809150509250929050565b60005b83811015611ef3578181015183820152602001611edb565b50506000910152565b60008151808452611f14816020860160208601611ed8565b601f01601f19169290920160200192915050565b8215158152604060208201526000611f436040830184611efc565b949350505050565b60008060408385031215611f5e57600080fd5b823591506020830135611ecd81611d80565b634e487b7160e01b600052602160045260246000fd5b600381106113af57634e487b7160e01b600052602160045260246000fd5b60208101611fb183611f86565b91905290565b600060208284031215611fc957600080fd5b813561ffff81168114610bcd57600080fd5b6001600160401b0391909116815260200190565b60008060006060848603121561200457600080fd5b833561200f81611d80565b9250602084013561201f81611d80565b929592945050506040919091013590565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b6000806020838503121561206457600080fd5b82356001600160401b038082111561207b57600080fd5b818501915085601f83011261208f57600080fd5b81358181111561209e57600080fd5b8660208260051b8501011115611e5357600080fd5b600060208083018184528085518083526040925060408601915060408160051b87010184880160005b8381101561212c57603f198984030185528151606081511515855288820151818a87015261210c82870182611efc565b9289015115159589019590955250948701949250908601906001016120dc565b509098975050505050505050565b6000806040838503121561214d57600080fd5b8235915061215d60208401611dc2565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561218e57600080fd5b81516001600160401b03808211156121a557600080fd5b818401915084601f8301126121b957600080fd5b8151818111156121cb576121cb612166565b604051601f8201601f19908116603f011681019083821181831017156121f3576121f3612166565b8160405282815287602084870101111561220c57600080fd5b61221d836020830160208801611ed8565b979650505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60018060a01b0386168152846020820152608060408201526000612279608083018587612228565b828103606084015261228b8185611efc565b98975050505050505050565b87815286602082015260018060401b038616604082015284606082015260c0608082015260006122ca60c0830186611efc565b82810360a08401526122dd818587612228565b9a9950505050505050505050565b80356001600160e01b031981168114611da057600080fd5b60006020828403121561231557600080fd5b610bcd826122eb565b634e487b7160e01b600052601160045260246000fd5b8082018082111561120f5761120f61231e565b6000808335601e1984360301811261235e57600080fd5b8301803591506001600160401b0382111561237857600080fd5b602001915036819003821315610d7b57600080fd5b60006001600160401b038281166002600160401b031981016123b1576123b161231e565b6001019392505050565b634e487b7160e01b600052603260045260246000fd5b60008235607e198336030181126123e757600080fd5b9190910192915050565b60018060a01b0385168152836020820152606060408201526000612419606083018486612228565b9695505050505050565b60006020828403121561243557600080fd5b8151610bcd81611d80565b60006020828403121561245257600080fd5b81518015158114610bcd57600080fd5b808202811582820484141761120f5761120f61231e565b60008261249657634e487b7160e01b600052601260045260246000fd5b500490565b83815282151560208201526060604082015260006124bc6060830184611efc565b95945050505050565b6000808335601e198436030181126124dc57600080fd5b83016020810192503590506001600160401b038111156124fb57600080fd5b803603821315610d7b57600080fd5b803582526000602082013561251e81611d80565b6001600160a01b0316602084015261253960408301836124c5565b6080604086015261254e608086018284612228565b91505061255e60608401846124c5565b8583036060870152612419838284612228565b6040815261259360408201612585856122eb565b6001600160e01b0319169052565b60006125a160208501611d95565b6125ae6060840182612030565b5060408401356080830152606084013560a0830152608084013560c083015260a084013560e08301526125e360c08501611d95565b6101006125f281850183612030565b6125ff60e08701876124c5565b61012086810152925061261761016086018483612228565b925050612626818701876124c5565b858403603f19016101408701529150612640838383612228565b9250505082810360208401526124bc818561250a565b8e81528d60208201528c60408201528b606082015263ffffffff60e01b8b1660808201528960848201528860a4820152868860c483013760008782018760c48201528660e4820152856101048201526101248486828401376000949091010192835250909e9d505050505050505050505050505056fe71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb1316a51056d8b2401ab04c48d9973add294eadde229c785c6d19cde93785467efa26469706673582212201bd6cad191a27c6473d37cbac5a541b99b4602fc8b5bf9fdbc05109528eb8c9a64736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000b67d20000000000000000000000007bd61c667f869fb21b77626f0ac0acee51e4be7c0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000445564d5800000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : chainSlug_ (uint32): 747474
Arg [1] : owner_ (address): 0x7BD61c667f869FB21b77626f0Ac0ACEE51e4BE7C
Arg [2] : version_ (string): EVMX
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000b67d2
Arg [1] : 0000000000000000000000007bd61c667f869fb21b77626f0ac0acee51e4be7c
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [4] : 45564d5800000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.