Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 335 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Bridge | 21470539 | 20 hrs ago | IN | 0.00554029 ETH | 0.00088707 | ||||
Bridge | 21469740 | 22 hrs ago | IN | 0.00646767 ETH | 0.00115196 | ||||
Bridge | 21467694 | 29 hrs ago | IN | 0.0079565 ETH | 0.00330126 | ||||
Bridge | 21465429 | 37 hrs ago | IN | 0.00579896 ETH | 0.00139452 | ||||
Bridge | 21457673 | 2 days ago | IN | 0.0055858 ETH | 0.00115245 | ||||
Bridge | 21457121 | 2 days ago | IN | 0.0055858 ETH | 0.00098217 | ||||
Bridge | 21450981 | 3 days ago | IN | 0.00692157 ETH | 0.00197088 | ||||
Bridge | 21444158 | 4 days ago | IN | 0.02339253 ETH | 0.00826223 | ||||
Bridge | 21443652 | 4 days ago | IN | 0.02641848 ETH | 0.00895332 | ||||
Bridge | 21410568 | 9 days ago | IN | 0.00774225 ETH | 0.00218484 | ||||
Bridge | 21394684 | 11 days ago | IN | 0.01489219 ETH | 0.00574288 | ||||
Bridge | 21392523 | 11 days ago | IN | 0.00836784 ETH | 0.00252851 | ||||
Bridge | 21391584 | 11 days ago | IN | 0.00836784 ETH | 0.00217516 | ||||
Bridge | 21386443 | 12 days ago | IN | 0.01128927 ETH | 0.00311328 | ||||
Bridge | 21383384 | 13 days ago | IN | 0.00908322 ETH | 0.00276792 | ||||
Bridge | 21383376 | 13 days ago | IN | 0.00908322 ETH | 0.00273941 | ||||
Bridge | 21363040 | 15 days ago | IN | 0.00796852 ETH | 0.00217288 | ||||
Bridge | 21360147 | 16 days ago | IN | 0.00869834 ETH | 0.00223534 | ||||
Bridge | 21359475 | 16 days ago | IN | 0.00869834 ETH | 0.00323602 | ||||
Bridge | 21355708 | 16 days ago | IN | 0.00747511 ETH | 0.00172152 | ||||
Bridge | 21338089 | 19 days ago | IN | 0.01477875 ETH | 0.00689888 | ||||
Bridge | 21334086 | 19 days ago | IN | 0.01376601 ETH | 0.00458194 | ||||
Bridge | 21332914 | 20 days ago | IN | 0.0137252 ETH | 0.00400541 | ||||
Bridge | 21321648 | 21 days ago | IN | 0.01127565 ETH | 0.00311887 | ||||
Bridge | 21310355 | 23 days ago | IN | 0.0140643 ETH | 0.0036352 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21470539 | 20 hrs ago | 0.00554029 ETH | ||||
21469740 | 22 hrs ago | 0.00646767 ETH | ||||
21467694 | 29 hrs ago | 0.0079565 ETH | ||||
21465429 | 37 hrs ago | 0.00579896 ETH | ||||
21457673 | 2 days ago | 0.0055858 ETH | ||||
21457121 | 2 days ago | 0.0055858 ETH | ||||
21450981 | 3 days ago | 0.00692157 ETH | ||||
21444158 | 4 days ago | 0.02339253 ETH | ||||
21443652 | 4 days ago | 0.02641848 ETH | ||||
21410568 | 9 days ago | 0.00774225 ETH | ||||
21394684 | 11 days ago | 0.01489219 ETH | ||||
21392523 | 11 days ago | 0.00836784 ETH | ||||
21391584 | 11 days ago | 0.00836784 ETH | ||||
21386443 | 12 days ago | 0.01128927 ETH | ||||
21383384 | 13 days ago | 0.00908322 ETH | ||||
21383376 | 13 days ago | 0.00908322 ETH | ||||
21363040 | 15 days ago | 0.00796852 ETH | ||||
21360147 | 16 days ago | 0.00869834 ETH | ||||
21359475 | 16 days ago | 0.00869834 ETH | ||||
21355708 | 16 days ago | 0.00747511 ETH | ||||
21338089 | 19 days ago | 0.01477875 ETH | ||||
21334086 | 19 days ago | 0.01376601 ETH | ||||
21332914 | 20 days ago | 0.0137252 ETH | ||||
21321648 | 21 days ago | 0.01127565 ETH | ||||
21310355 | 23 days ago | 0.0140643 ETH |
Loading...
Loading
Contract Name:
Controller
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.13; import "./Base.sol"; contract Controller is Base { uint256 public totalMinted; constructor(address token_) Base(token_) { bridgeType = NORMAL_CONTROLLER; } /** * @notice Bridges tokens between chains. * @dev This function allows bridging tokens between different chains. * @param receiver_ The address to receive the bridged tokens. * @param amount_ The amount of tokens to bridge. * @param msgGasLimit_ The gas limit for the execution of the bridging process. * @param connector_ The address of the connector contract responsible for the bridge. * @param execPayload_ The payload for executing the bridging process on the connector. * @param options_ Additional options for the bridging process. */ function bridge( address receiver_, uint256 amount_, uint256 msgGasLimit_, address connector_, bytes calldata execPayload_, bytes calldata options_ ) external payable nonReentrant { ( TransferInfo memory transferInfo, bytes memory postHookData ) = _beforeBridge( connector_, TransferInfo(receiver_, amount_, execPayload_) ); // to maintain socket dl specific accounting for super token // re check this logic for mint and mint use cases and if other minter involved totalMinted -= transferInfo.amount; _burn(msg.sender, transferInfo.amount); _afterBridge( msgGasLimit_, connector_, options_, postHookData, transferInfo ); } /** * @notice Receives inbound tokens from another chain. * @dev This function is used to receive tokens from another chain. * @param siblingChainSlug_ The identifier of the sibling chain. * @param payload_ The payload containing the inbound tokens. */ function receiveInbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { ( address receiver, uint256 lockAmount, bytes32 messageId, bytes memory extraData ) = abi.decode(payload_, (address, uint256, bytes32, bytes)); // convert to shares TransferInfo memory transferInfo = TransferInfo( receiver, lockAmount, extraData ); bytes memory postHookData; (postHookData, transferInfo) = _beforeMint( siblingChainSlug_, transferInfo ); _mint(transferInfo.receiver, transferInfo.amount); totalMinted += transferInfo.amount; _afterMint(lockAmount, messageId, postHookData, transferInfo); } /** * @notice Retry a failed transaction. * @dev This function allows retrying a failed transaction sent through a connector. * @param connector_ The address of the connector contract responsible for the failed transaction. * @param messageId_ The unique identifier of the failed transaction. */ function retry( address connector_, bytes32 messageId_ ) external nonReentrant { ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) = _beforeRetry(connector_, messageId_); _mint(transferInfo.receiver, transferInfo.amount); totalMinted += transferInfo.amount; _afterRetry(connector_, messageId_, postRetryHookData); } function _burn(address user_, uint256 burnAmount_) internal virtual { IMintableERC20(token).burn(user_, burnAmount_); } function _mint(address user_, uint256 mintAmount_) internal virtual { if (mintAmount_ == 0) return; IMintableERC20(token).mint(user_, mintAmount_); } }
pragma solidity 0.8.13; import {IMintableERC20} from "../interfaces/IMintableERC20.sol"; import {IConnector} from "../interfaces/IConnector.sol"; import "lib/solmate/src/utils/SafeTransferLib.sol"; import "../interfaces/IHook.sol"; import "../common/Errors.sol"; import "lib/solmate/src/utils/ReentrancyGuard.sol"; import "../interfaces/IBridge.sol"; import "../utils/RescueBase.sol"; import "../common/Constants.sol"; abstract contract Base is ReentrancyGuard, IBridge, RescueBase { address public immutable token; bytes32 public bridgeType; IHook public hook__; // message identifier => cache mapping(bytes32 => bytes) public identifierCache; // connector => cache mapping(address => bytes) public connectorCache; mapping(address => bool) public validConnectors; event ConnectorStatusUpdated(address connector, bool status); event HookUpdated(address newHook); event BridgingTokens( address connector, address sender, address receiver, uint256 amount, bytes32 messageId ); event TokensBridged( address connecter, address receiver, uint256 amount, bytes32 messageId ); constructor(address token_) AccessControl(msg.sender) { if (token_ != ETH_ADDRESS && token_.code.length == 0) revert InvalidTokenContract(); token = token_; _grantRole(RESCUE_ROLE, msg.sender); } /** * @notice this function is used to update hook * @dev it can only be updated by owner * @dev should be carefully migrated as it can risk user funds * @param hook_ new hook address */ function updateHook( address hook_, bool approve_ ) external virtual onlyOwner { // remove the approval from the old hook if (token != ETH_ADDRESS) { if (ERC20(token).allowance(address(this), address(hook__)) > 0) { SafeTransferLib.safeApprove(ERC20(token), address(hook__), 0); } if (approve_) { SafeTransferLib.safeApprove( ERC20(token), hook_, type(uint256).max ); } } hook__ = IHook(hook_); emit HookUpdated(hook_); } function updateConnectorStatus( address[] calldata connectors, bool[] calldata statuses ) external onlyOwner { uint256 length = connectors.length; for (uint256 i; i < length; i++) { validConnectors[connectors[i]] = statuses[i]; emit ConnectorStatusUpdated(connectors[i], statuses[i]); } } /** * @notice Executes pre-bridge operations before initiating a token bridge transfer. * @dev This internal function is called before initiating a token bridge transfer. * It validates the receiver address and the connector, and if a pre-hook contract is defined, * it executes the source pre-hook call. * @param connector_ The address of the connector responsible for the transfer. * @param transferInfo_ Information about the transfer. * @return transferInfo Information about the transfer after pre-bridge operations. * @return postHookData Data returned from the pre-hook call. * @dev Reverts with `ZeroAddressReceiver` if the receiver address is zero. * Reverts with `InvalidConnector` if the connector address is not valid. */ function _beforeBridge( address connector_, TransferInfo memory transferInfo_ ) internal returns (TransferInfo memory transferInfo, bytes memory postHookData) { if (transferInfo_.receiver == address(0)) revert ZeroAddressReceiver(); if (!validConnectors[connector_]) revert InvalidConnector(); if (token == ETH_ADDRESS && msg.value < transferInfo_.amount) revert InsufficientMsgValue(); if (address(hook__) != address(0)) { (transferInfo, postHookData) = hook__.srcPreHookCall( SrcPreHookCallParams(connector_, msg.sender, transferInfo_) ); } } /** * @notice Executes post-bridge operations after completing a token bridge transfer. * @dev This internal function is called after completing a token bridge transfer. * It executes the source post-hook call if a hook contract is defined, calculates fees, * calls the outbound function of the connector, and emits an event for tokens withdrawn. * @param msgGasLimit_ The gas limit for the outbound call. * @param connector_ The address of the connector responsible for the transfer. * @param options_ Additional options for the outbound call. * @param postSrcHookData_ Data returned from the source post-hook call. * @param transferInfo_ Information about the transfer. * @dev Reverts with `MessageIdMisMatched` if the returned message ID does not match the expected message ID. */ function _afterBridge( uint256 msgGasLimit_, address connector_, bytes memory options_, bytes memory postSrcHookData_, TransferInfo memory transferInfo_ ) internal { TransferInfo memory transferInfo = transferInfo_; if (address(hook__) != address(0)) { transferInfo = hook__.srcPostHookCall( SrcPostHookCallParams( connector_, options_, postSrcHookData_, transferInfo_ ) ); } uint256 fees = token == ETH_ADDRESS ? msg.value - transferInfo.amount : msg.value; bytes32 messageId = IConnector(connector_).getMessageId(); bytes32 returnedMessageId = IConnector(connector_).outbound{ value: fees }( msgGasLimit_, abi.encode( transferInfo.receiver, transferInfo.amount, messageId, transferInfo.data ), options_ ); if (returnedMessageId != messageId) revert MessageIdMisMatched(); emit BridgingTokens( connector_, msg.sender, transferInfo.receiver, transferInfo.amount, messageId ); } /** * @notice Executes pre-mint operations before minting tokens. * @dev This internal function is called before minting tokens. * It validates the caller as a valid connector, checks if the receiver is not this contract, the bridge contract, * or the token contract, and executes the destination pre-hook call if a hook contract is defined. * @param transferInfo_ Information about the transfer. * @return postHookData Data returned from the destination pre-hook call. * @return transferInfo Information about the transfer after pre-mint operations. * @dev Reverts with `InvalidConnector` if the caller is not a valid connector. * Reverts with `CannotTransferOrExecuteOnBridgeContracts` if the receiver is this contract, the bridge contract, * or the token contract. */ function _beforeMint( uint32, TransferInfo memory transferInfo_ ) internal returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (!validConnectors[msg.sender]) revert InvalidConnector(); // no need of source check here, as if invalid caller, will revert with InvalidPoolId if ( transferInfo_.receiver == address(this) || // transferInfo_.receiver == address(bridge__) || transferInfo_.receiver == token ) revert CannotTransferOrExecuteOnBridgeContracts(); if (address(hook__) != address(0)) { (postHookData, transferInfo) = hook__.dstPreHookCall( DstPreHookCallParams( msg.sender, connectorCache[msg.sender], transferInfo_ ) ); } } /** * @notice Executes post-mint operations after minting tokens. * @dev This internal function is called after minting tokens. * It executes the destination post-hook call if a hook contract is defined and updates cache data. * @param messageId_ The unique identifier for the mint transaction. * @param postHookData_ Data returned from the destination pre-hook call. * @param transferInfo_ Information about the mint transaction. */ function _afterMint( uint256, bytes32 messageId_, bytes memory postHookData_, TransferInfo memory transferInfo_ ) internal { if (address(hook__) != address(0)) { CacheData memory cacheData = hook__.dstPostHookCall( DstPostHookCallParams( msg.sender, messageId_, connectorCache[msg.sender], postHookData_, transferInfo_ ) ); identifierCache[messageId_] = cacheData.identifierCache; connectorCache[msg.sender] = cacheData.connectorCache; } emit TokensBridged( msg.sender, transferInfo_.receiver, transferInfo_.amount, messageId_ ); } /** * @notice Executes pre-retry operations before retrying a failed transaction. * @dev This internal function is called before retrying a failed transaction. * It validates the connector, retrieves cache data for the given message ID, * and executes the pre-retry hook if defined. * @param connector_ The address of the connector responsible for the failed transaction. * @param messageId_ The unique identifier for the failed transaction. * @return postRetryHookData Data returned from the pre-retry hook call. * @return transferInfo Information about the transfer. * @dev Reverts with `InvalidConnector` if the connector is not valid. * Reverts with `NoPendingData` if there is no pending data for the given message ID. */ function _beforeRetry( address connector_, bytes32 messageId_ ) internal returns ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) { if (!validConnectors[connector_]) revert InvalidConnector(); CacheData memory cacheData = CacheData( identifierCache[messageId_], connectorCache[connector_] ); if (cacheData.identifierCache.length == 0) revert NoPendingData(); (postRetryHookData, transferInfo) = hook__.preRetryHook( PreRetryHookCallParams(connector_, cacheData) ); } /** * @notice Executes post-retry operations after retrying a failed transaction. * @dev This internal function is called after retrying a failed transaction. * It retrieves cache data for the given message ID, executes the post-retry hook if defined, * and updates cache data. * @param connector_ The address of the connector responsible for the failed transaction. * @param messageId_ The unique identifier for the failed transaction. * @param postRetryHookData Data returned from the pre-retry hook call. */ function _afterRetry( address connector_, bytes32 messageId_, bytes memory postRetryHookData ) internal { CacheData memory cacheData = CacheData( identifierCache[messageId_], connectorCache[connector_] ); (cacheData) = hook__.postRetryHook( PostRetryHookCallParams( connector_, messageId_, postRetryHookData, cacheData ) ); identifierCache[messageId_] = cacheData.identifierCache; connectorCache[connector_] = cacheData.connectorCache; } /** * @notice Retrieves the minimum fees required for a transaction from a connector. * @dev This function returns the minimum fees required for a transaction from the specified connector, * based on the provided message gas limit and payload size. * @param connector_ The address of the connector. * @param msgGasLimit_ The gas limit for the transaction. * @param payloadSize_ The size of the payload for the transaction. * @return totalFees The total minimum fees required for the transaction. */ function getMinFees( address connector_, uint256 msgGasLimit_, uint256 payloadSize_ ) external view returns (uint256 totalFees) { return IConnector(connector_).getMinFees(msgGasLimit_, payloadSize_); } }
pragma solidity 0.8.13; import {IFiatTokenV2_1_Mintable} from "./IFiatTokenV2_1_Mintable.sol"; import "../Controller.sol"; contract FiatTokenV2_1_Controller is Controller { using SafeTransferLib for ERC20; constructor(address token_) Controller(token_) { bridgeType = FIAT_TOKEN_CONTROLLER; } function _burn(address user_, uint256 burnAmount_) internal override { ERC20(token).safeTransferFrom(user_, address(this), burnAmount_); IFiatTokenV2_1_Mintable(address(token)).burn(burnAmount_); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "lib/solmate/src/tokens/ERC20.sol"; // USDC's standard token abstract contract IFiatTokenV2_1_Mintable is ERC20 { function mint(address receiver_, uint256 amount_) external virtual; function burn(uint256 _amount) external virtual; }
pragma solidity 0.8.13; import "./Base.sol"; import "../interfaces/IConnector.sol"; import "lib/solmate/src/tokens/ERC20.sol"; /** * @title SuperToken * @notice A contract which enables bridging a token to its sibling chains. * @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts. */ contract Vault is Base { using SafeTransferLib for ERC20; // /** // * @notice constructor for creating a new SuperTokenVault. // * @param token_ token contract address which is to be bridged. // */ constructor(address token_) Base(token_) { bridgeType = token_ == ETH_ADDRESS ? NATIVE_VAULT : ERC20_VAULT; } /** * @notice Bridges tokens between chains. * @dev This function allows bridging tokens between different chains. * @param receiver_ The address to receive the bridged tokens. * @param amount_ The amount of tokens to bridge. * @param msgGasLimit_ The gas limit for the execution of the bridging process. * @param connector_ The address of the connector contract responsible for the bridge. * @param execPayload_ The payload for executing the bridging process on the connector. * @param options_ Additional options for the bridging process. */ function bridge( address receiver_, uint256 amount_, uint256 msgGasLimit_, address connector_, bytes calldata execPayload_, bytes calldata options_ ) external payable nonReentrant { ( TransferInfo memory transferInfo, bytes memory postHookData ) = _beforeBridge( connector_, TransferInfo(receiver_, amount_, execPayload_) ); _receiveTokens(transferInfo.amount); _afterBridge( msgGasLimit_, connector_, options_, postHookData, transferInfo ); } /** * @notice Receives inbound tokens from another chain. * @dev This function is used to receive tokens from another chain. * @param siblingChainSlug_ The identifier of the sibling chain. * @param payload_ The payload containing the inbound tokens. */ function receiveInbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { ( address receiver, uint256 unlockAmount, bytes32 messageId, bytes memory extraData ) = abi.decode(payload_, (address, uint256, bytes32, bytes)); TransferInfo memory transferInfo = TransferInfo( receiver, unlockAmount, extraData ); bytes memory postHookData; (postHookData, transferInfo) = _beforeMint( siblingChainSlug_, transferInfo ); _transferTokens(transferInfo.receiver, transferInfo.amount); _afterMint(unlockAmount, messageId, postHookData, transferInfo); } /** * @notice Retry a failed transaction. * @dev This function allows retrying a failed transaction sent through a connector. * @param connector_ The address of the connector contract responsible for the failed transaction. * @param messageId_ The unique identifier of the failed transaction. */ function retry( address connector_, bytes32 messageId_ ) external nonReentrant { ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) = _beforeRetry(connector_, messageId_); _transferTokens(transferInfo.receiver, transferInfo.amount); _afterRetry(connector_, messageId_, postRetryHookData); } function _transferTokens(address receiver_, uint256 amount_) internal { if (amount_ == 0) return; if (address(token) == ETH_ADDRESS) { SafeTransferLib.safeTransferETH(receiver_, amount_); } else { ERC20(token).safeTransfer(receiver_, amount_); } } function _receiveTokens(uint256 amount_) internal { if (amount_ == 0 || address(token) == ETH_ADDRESS) return; ERC20(token).safeTransferFrom(msg.sender, address(this), amount_); } }
pragma solidity 0.8.13; address constant ETH_ADDRESS = address( 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ); bytes32 constant NORMAL_CONTROLLER = keccak256("NORMAL_CONTROLLER"); bytes32 constant FIAT_TOKEN_CONTROLLER = keccak256("FIAT_TOKEN_CONTROLLER"); bytes32 constant LIMIT_HOOK = keccak256("LIMIT_HOOK"); bytes32 constant LYRA_VAULT_ZAP_HOOK = keccak256("LYRA_VAULT_ZAP_HOOK"); bytes32 constant LIMIT_EXECUTION_HOOK = keccak256("LIMIT_EXECUTION_HOOK"); bytes32 constant LIMIT_EXECUTION_YIELD_HOOK = keccak256( "LIMIT_EXECUTION_YIELD_HOOK" ); bytes32 constant LIMIT_EXECUTION_YIELD_TOKEN_HOOK = keccak256( "LIMIT_EXECUTION_YIELD_TOKEN_HOOK" ); bytes32 constant ERC20_VAULT = keccak256("ERC20_VAULT"); bytes32 constant NATIVE_VAULT = keccak256("NATIVE_VAULT");
pragma solidity 0.8.13; error SiblingNotSupported(); error NotAuthorized(); error NotBridge(); error NotSocket(); error ConnectorUnavailable(); error InvalidPoolId(); error CannotTransferOrExecuteOnBridgeContracts(); error NoPendingData(); error MessageIdMisMatched(); error NotMessageBridge(); error InvalidSiblingChainSlug(); error InvalidTokenContract(); error InvalidExchangeRateContract(); error InvalidConnector(); error InvalidConnectorPoolId(); error ZeroAddressReceiver(); error ZeroAddress(); error ZeroAmount(); error DebtRatioTooHigh(); error NotEnoughAssets(); error VaultShutdown(); error InsufficientFunds(); error PermitDeadlineExpired(); error InvalidSigner(); error InsufficientMsgValue();
pragma solidity 0.8.13; struct UpdateLimitParams { bool isMint; address connector; uint256 maxLimit; uint256 ratePerSecond; } struct SrcPreHookCallParams { address connector; address msgSender; TransferInfo transferInfo; } struct SrcPostHookCallParams { address connector; bytes options; bytes postSrcHookData; TransferInfo transferInfo; } struct DstPreHookCallParams { address connector; bytes connectorCache; TransferInfo transferInfo; } struct DstPostHookCallParams { address connector; bytes32 messageId; bytes connectorCache; bytes postHookData; TransferInfo transferInfo; } struct PreRetryHookCallParams { address connector; CacheData cacheData; } struct PostRetryHookCallParams { address connector; bytes32 messageId; bytes postRetryHookData; CacheData cacheData; } struct TransferInfo { address receiver; uint256 amount; bytes data; } struct CacheData { bytes identifierCache; bytes connectorCache; } struct LimitParams { uint256 lastUpdateTimestamp; uint256 ratePerSecond; uint256 maxLimit; uint256 lastUpdateLimit; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "./utils/RescueBase.sol"; import {ISocket} from "./interfaces/ISocket.sol"; import {IPlug} from "./interfaces/IPlug.sol"; import {IConnector} from "./interfaces/IConnector.sol"; import {IBridge} from "./interfaces/IBridge.sol"; import "./common/Errors.sol"; contract ConnectorPlug is IConnector, IPlug, RescueBase { IBridge public immutable bridge__; ISocket public immutable socket__; uint32 public immutable siblingChainSlug; uint256 public messageIdPart; event ConnectorPlugDisconnected(); constructor( address bridge_, address socket_, uint32 siblingChainSlug_ ) AccessControl(msg.sender) { bridge__ = IBridge(bridge_); socket__ = ISocket(socket_); siblingChainSlug = siblingChainSlug_; _grantRole(RESCUE_ROLE, msg.sender); } function outbound( uint256 msgGasLimit_, bytes memory payload_, bytes memory ) external payable override returns (bytes32 messageId_) { if (msg.sender != address(bridge__)) revert NotBridge(); return socket__.outbound{value: msg.value}( siblingChainSlug, msgGasLimit_, bytes32(0), bytes32(0), payload_ ); } function inbound( uint32 siblingChainSlug_, // cannot be connected for any other slug, immutable variable bytes calldata payload_ ) external payable override { if (msg.sender != address(socket__)) revert NotSocket(); bridge__.receiveInbound(siblingChainSlug_, payload_); } /** * @notice this function calculates the fees needed to send the message to Socket. * @param msgGasLimit_ min gas limit needed at destination chain to execute the message. */ function getMinFees( uint256 msgGasLimit_, uint256 payloadSize_ ) external view returns (uint256 totalFees) { return socket__.getMinFees( msgGasLimit_, payloadSize_, bytes32(0), bytes32(0), siblingChainSlug, address(this) ); } function connect( address siblingPlug_, address switchboard_ ) external onlyOwner { messageIdPart = (uint256(socket__.chainSlug()) << 224) | (uint256(uint160(siblingPlug_)) << 64); socket__.connect( siblingChainSlug, siblingPlug_, switchboard_, switchboard_ ); } function disconnect() external onlyOwner { messageIdPart = 0; ( , address inboundSwitchboard, address outboundSwitchboard, , ) = socket__.getPlugConfig(address(this), siblingChainSlug); socket__.connect( siblingChainSlug, address(0), inboundSwitchboard, outboundSwitchboard ); emit ConnectorPlugDisconnected(); } /** * @notice this function is used to calculate message id before sending outbound(). * @return messageId */ function getMessageId() external view returns (bytes32) { return bytes32(messageIdPart | (socket__.globalMessageCount())); } }
// // // SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "lib/openzeppelin-contracts/contracts/utils/math/Math.sol"; import {FixedPointMathLib} from "lib/solmate/src/utils/FixedPointMathLib.sol"; import {IStrategy} from "../interfaces/IStrategy.sol"; import {IMintableERC20} from "../interfaces/IMintableERC20.sol"; import "lib/solmate/src/utils/SafeTransferLib.sol"; import {IConnector} from "../ConnectorPlug.sol"; import "./LimitExecutionHook.sol"; interface IYieldToken { function updateTotalUnderlyingAssets(uint256 amount_) external; function calculateMintAmount(uint256 amount_) external returns (uint256); function convertToShares( uint256 underlyingAssets ) external view returns (uint256); function transfer(address to_, uint256 amount_) external returns (bool); function convertToAssets(uint256 shares) external view returns (uint256); } // limits on underlying or visible tokens contract Controller_YieldLimitExecHook is LimitExecutionHook { using SafeTransferLib for IMintableERC20; using FixedPointMathLib for uint256; uint256 private constant MAX_BPS = 10_000; IYieldToken public immutable yieldToken__; // total yield uint256 public totalUnderlyingAssets; // if true, no funds can be invested in the strategy bool public emergencyShutdown; event ShutdownStateUpdated(bool shutdownState); modifier notShutdown() { if (emergencyShutdown) revert VaultShutdown(); _; } constructor( address underlyingAsset_, address controller_, address executionHelper_ ) LimitExecutionHook(msg.sender, controller_, executionHelper_, true) { yieldToken__ = IYieldToken(underlyingAsset_); hookType = LIMIT_EXECUTION_YIELD_TOKEN_HOOK; _grantRole(LIMIT_UPDATER_ROLE, msg.sender); } // assumed transfer info inputs are validated at controller // transfer info data is untrusted function srcPreHookCall( SrcPreHookCallParams calldata params_ ) public override notShutdown returns (TransferInfo memory transferInfo, bytes memory postSrcHookData) { super.srcPreHookCall(params_); uint256 amount = params_.transferInfo.amount; postSrcHookData = abi.encode(amount); totalUnderlyingAssets -= amount; transferInfo = params_.transferInfo; transferInfo.amount = yieldToken__.convertToShares(amount); } function srcPostHookCall( SrcPostHookCallParams memory srcPostHookCallParams_ ) public override isVaultOrController returns (TransferInfo memory transferInfo) { yieldToken__.updateTotalUnderlyingAssets(totalUnderlyingAssets); transferInfo.receiver = srcPostHookCallParams_.transferInfo.receiver; transferInfo.data = abi.encode( srcPostHookCallParams_.options, srcPostHookCallParams_.transferInfo.data ); transferInfo.amount = abi.decode( srcPostHookCallParams_.postSrcHookData, (uint256) ); } /** * @notice This function is called before the execution of a destination hook. * @dev It checks if the sibling chain is supported, consumes a part of the limit, and prepares post-hook data. */ function dstPreHookCall( DstPreHookCallParams calldata params_ ) public override notShutdown isVaultOrController returns (bytes memory postHookData, TransferInfo memory transferInfo) { (uint256 increasedUnderlying, bytes memory payload) = abi.decode( params_.transferInfo.data, (uint256, bytes) ); _poolDstHook(params_.connector, increasedUnderlying); totalUnderlyingAssets += increasedUnderlying; yieldToken__.updateTotalUnderlyingAssets(totalUnderlyingAssets); yieldToken__.updateTotalUnderlyingAssets(totalUnderlyingAssets); if (params_.transferInfo.amount == 0) return (abi.encode(0, 0, 0, address(0)), transferInfo); (uint256 consumedUnderlying, uint256 pendingUnderlying) = _limitDstHook( params_.connector, params_.transferInfo.amount ); uint256 sharesToMint = yieldToken__.calculateMintAmount( params_.transferInfo.amount ); postHookData = abi.encode( consumedUnderlying, pendingUnderlying, params_.transferInfo.amount, params_.transferInfo.receiver ); transferInfo = params_.transferInfo; if (pendingUnderlying != 0) transferInfo.receiver = address(this); transferInfo.amount = sharesToMint; transferInfo.data = payload; } /** * @notice Handles post-hook logic after the execution of a destination hook. * @dev This function processes post-hook data to update the identifier cache and sibling chain cache. */ function dstPostHookCall( DstPostHookCallParams calldata params_ ) public override isVaultOrController notShutdown returns (CacheData memory cacheData) { ( uint256 consumedUnderlying, uint256 pendingUnderlying, uint256 depositUnderlying, address receiver ) = abi.decode( params_.postHookData, (uint256, uint256, uint256, address) ); bytes memory execPayload = params_.transferInfo.data; uint256 connectorPendingShares = _getConnectorPendingAmount( params_.connectorCache ); uint256 pendingShares; if (pendingUnderlying > 0) { // totalShares * consumedU / totalU uint256 consumedShares = (params_.transferInfo.amount * pendingUnderlying) / depositUnderlying; pendingShares = params_.transferInfo.amount - consumedShares; cacheData.identifierCache = abi.encode( params_.transferInfo.receiver, pendingShares, params_.connector, execPayload ); yieldToken__.transfer(receiver, consumedUnderlying); emit TokensPending( params_.connector, params_.transferInfo.receiver, consumedShares, pendingShares, params_.messageId ); } else { if (execPayload.length > 0) { // execute bool success = executionHelper__.execute( params_.transferInfo.receiver, execPayload, params_.messageId, depositUnderlying ); if (success) { emit MessageExecuted( params_.messageId, params_.transferInfo.receiver ); cacheData.identifierCache = new bytes(0); } else cacheData.identifierCache = abi.encode( params_.transferInfo.receiver, 0, params_.connector, execPayload ); } else cacheData.identifierCache = new bytes(0); } cacheData.connectorCache = abi.encode( connectorPendingShares + pendingShares ); } // /** // * @notice Handles pre-retry hook logic before execution. // * @dev This function can be used to mint funds which were in a pending state due to limits. // * @param siblingChainSlug_ The unique identifier of the sibling chain. // * @param identifierCache_ Identifier cache containing pending mint information. // * @param connectorCache_ Sibling chain cache containing pending amount information. // * @return updatedReceiver The updated receiver of the funds. // * @return consumedUnderlying The amount consumed from the limit. // * @return postRetryHookData The post-hook data to be processed after the retry hook execution. // */ function preRetryHook( PreRetryHookCallParams calldata params_ ) public override isVaultOrController notShutdown returns ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) { ( address receiver, uint256 totalPendingShares, address connector, ) = abi.decode( params_.cacheData.identifierCache, (address, uint256, address, bytes) ); if (connector != params_.connector) revert InvalidConnector(); (uint256 consumedShares, uint256 pendingShares) = _limitDstHook( params_.connector, totalPendingShares ); postRetryHookData = abi.encode(receiver, consumedShares, pendingShares); uint256 consumedUnderlying = yieldToken__.convertToAssets( consumedShares ); yieldToken__.transfer(receiver, consumedUnderlying); transferInfo = TransferInfo(transferInfo.receiver, 0, bytes("")); } // /** // * @notice Handles post-retry hook logic after execution. // * @dev This function updates the identifier cache and sibling chain cache based on the post-hook data. // * @param siblingChainSlug_ The unique identifier of the sibling chain. // * @param identifierCache_ Identifier cache containing pending mint information. // * @param connectorCache_ Sibling chain cache containing pending amount information. // * @param postRetryHookData_ The post-hook data containing updated receiver and consumed/pending amounts. // * @return newIdentifierCache The updated identifier cache. // * @return newConnectorCache The updated sibling chain cache. // */ function postRetryHook( PostRetryHookCallParams calldata params_ ) public override returns (CacheData memory cacheData) { return super.postRetryHook(params_); } function updateEmergencyShutdownState( bool shutdownState_ ) external onlyOwner { emergencyShutdown = shutdownState_; emit ShutdownStateUpdated(shutdownState_); } }
pragma solidity 0.8.13; import "lib/solmate/src/utils/ReentrancyGuard.sol"; import "../common/Errors.sol"; import "../common/Constants.sol"; import "../interfaces/IHook.sol"; import "../utils/RescueBase.sol"; /** * @title Base contract for super token and vault * @notice It contains relevant execution payload storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ abstract contract HookBase is ReentrancyGuard, IHook, RescueBase { address public immutable vaultOrController; bytes32 public hookType; /** * @notice Constructor for creating a new SuperToken. */ constructor( address owner_, address vaultOrController_ ) AccessControl(owner_) { vaultOrController = vaultOrController_; _grantRole(RESCUE_ROLE, owner_); } modifier isVaultOrController() { if (msg.sender != vaultOrController) revert NotAuthorized(); _; } }
pragma solidity 0.8.13; import "./plugins/LimitPlugin.sol"; import "./plugins/ExecutionHelper.sol"; import "./plugins/ConnectorPoolPlugin.sol"; import "../interfaces/IController.sol"; contract LimitExecutionHook is LimitPlugin, ConnectorPoolPlugin { bool public useControllerPools; ExecutionHelper executionHelper__; event MessageExecuted(bytes32 indexed messageId, address indexed receiver); /** * @notice Constructor for creating a new SuperToken. * @param owner_ Owner of this contract. */ constructor( address owner_, address controller_, address executionHelper_, bool useControllerPools_ ) HookBase(owner_, controller_) { useControllerPools = useControllerPools_; executionHelper__ = ExecutionHelper(executionHelper_); hookType = LIMIT_EXECUTION_HOOK; _grantRole(LIMIT_UPDATER_ROLE, owner_); } function setExecutionHelper(address executionHelper_) external onlyOwner { executionHelper__ = ExecutionHelper(executionHelper_); } function srcPreHookCall( SrcPreHookCallParams calldata params_ ) public virtual isVaultOrController returns (TransferInfo memory, bytes memory) { if (useControllerPools) _poolSrcHook(params_.connector, params_.transferInfo.amount); _limitSrcHook(params_.connector, params_.transferInfo.amount); return (params_.transferInfo, bytes("")); } function srcPostHookCall( SrcPostHookCallParams memory params_ ) public virtual isVaultOrController returns (TransferInfo memory) { return params_.transferInfo; } function dstPreHookCall( DstPreHookCallParams calldata params_ ) public virtual isVaultOrController returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (useControllerPools) _poolDstHook(params_.connector, params_.transferInfo.amount); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, params_.transferInfo.amount ); postHookData = abi.encode( consumedAmount, pendingAmount, params_.transferInfo.amount ); transferInfo = params_.transferInfo; transferInfo.amount = consumedAmount; } function dstPostHookCall( DstPostHookCallParams calldata params_ ) public virtual isVaultOrController returns (CacheData memory cacheData) { bytes memory execPayload = params_.transferInfo.data; ( uint256 consumedAmount, uint256 pendingAmount, uint256 bridgeAmount ) = abi.decode(params_.postHookData, (uint256, uint256, uint256)); uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.connectorCache ); cacheData.connectorCache = abi.encode( connectorPendingAmount + pendingAmount ); cacheData.identifierCache = abi.encode( params_.transferInfo.receiver, pendingAmount, bridgeAmount, params_.connector, execPayload ); if (pendingAmount > 0) { emit TokensPending( params_.connector, params_.transferInfo.receiver, consumedAmount, pendingAmount, params_.messageId ); } else { if (execPayload.length > 0) { // execute bool success = executionHelper__.execute( params_.transferInfo.receiver, execPayload, params_.messageId, bridgeAmount ); if (success) { emit MessageExecuted( params_.messageId, params_.transferInfo.receiver ); cacheData.identifierCache = new bytes(0); } } else cacheData.identifierCache = new bytes(0); } } function preRetryHook( PreRetryHookCallParams calldata params_ ) public virtual isVaultOrController returns ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) { ( address receiver, uint256 pendingMint, , address connector, bytes memory execPayload ) = abi.decode( params_.cacheData.identifierCache, (address, uint256, uint256, address, bytes) ); if (connector != params_.connector) revert InvalidConnector(); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, pendingMint ); postRetryHookData = abi.encode(receiver, consumedAmount, pendingAmount); transferInfo = TransferInfo(receiver, consumedAmount, bytes("")); } function postRetryHook( PostRetryHookCallParams calldata params_ ) public virtual isVaultOrController returns (CacheData memory cacheData) { ( , uint256 pendingMint, uint256 bridgeAmount, address connector, bytes memory execPayload ) = abi.decode( params_.cacheData.identifierCache, (address, uint256, uint256, address, bytes) ); (address receiver, uint256 consumedAmount, uint256 pendingAmount) = abi .decode(params_.postRetryHookData, (address, uint256, uint256)); uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.cacheData.connectorCache ); cacheData.connectorCache = abi.encode( connectorPendingAmount - consumedAmount ); cacheData.identifierCache = abi.encode( receiver, pendingAmount, bridgeAmount, connector, execPayload ); emit PendingTokensBridged( params_.connector, receiver, consumedAmount, pendingAmount, params_.messageId ); if (pendingAmount == 0) { // receiver is not an input from user, can receiver check // no connector check required here, as already done in preRetryHook call in same tx // execute bool success = executionHelper__.execute( receiver, execPayload, params_.messageId, bridgeAmount ); if (success) { emit MessageExecuted(params_.messageId, receiver); cacheData.identifierCache = new bytes(0); } } } function getConnectorPendingAmount( address connector_ ) external returns (uint256) { bytes memory cache = IController(vaultOrController).connectorCache( connector_ ); return _getConnectorPendingAmount(cache); } function _getIdentifierPendingAmount( bytes memory identifierCache_ ) internal view returns (uint256) { if (identifierCache_.length > 0) { (, uint256 pendingAmount, , , ) = abi.decode( identifierCache_, (address, uint256, uint256, address, bytes) ); return pendingAmount; } else return 0; } function getIdentifierPendingAmount( bytes32 messageId_ ) external returns (uint256) { bytes memory cache = IController(vaultOrController).identifierCache( messageId_ ); return _getIdentifierPendingAmount(cache); } }
pragma solidity 0.8.13; import "./plugins/LimitPlugin.sol"; import "../interfaces/IController.sol"; import "./plugins/ConnectorPoolPlugin.sol"; contract LimitHook is LimitPlugin, ConnectorPoolPlugin { bool public immutable useControllerPools; /** * @notice Constructor for creating a new SuperToken. * @param owner_ Owner of this contract. */ constructor( address owner_, address controller_, bool useControllerPools_ ) HookBase(owner_, controller_) { useControllerPools = useControllerPools_; hookType = LIMIT_HOOK; _grantRole(LIMIT_UPDATER_ROLE, owner_); } function srcPreHookCall( SrcPreHookCallParams memory params_ ) external isVaultOrController returns (TransferInfo memory transferInfo, bytes memory) { if (useControllerPools) _poolSrcHook(params_.connector, params_.transferInfo.amount); _limitSrcHook(params_.connector, params_.transferInfo.amount); transferInfo = params_.transferInfo; } function srcPostHookCall( SrcPostHookCallParams memory params_ ) external view isVaultOrController returns (TransferInfo memory) { return params_.transferInfo; } function dstPreHookCall( DstPreHookCallParams memory params_ ) external virtual isVaultOrController returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (useControllerPools) _poolDstHook(params_.connector, params_.transferInfo.amount); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, params_.transferInfo.amount ); postHookData = abi.encode(consumedAmount, pendingAmount); transferInfo = params_.transferInfo; transferInfo.amount = consumedAmount; } function dstPostHookCall( DstPostHookCallParams memory params_ ) external virtual isVaultOrController returns (CacheData memory cacheData) { (uint256 consumedAmount, uint256 pendingAmount) = abi.decode( params_.postHookData, (uint256, uint256) ); uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.connectorCache ); if (pendingAmount > 0) { cacheData = CacheData( abi.encode( params_.transferInfo.receiver, pendingAmount, params_.connector ), abi.encode(connectorPendingAmount + pendingAmount) ); emit TokensPending( params_.connector, params_.transferInfo.receiver, consumedAmount, pendingAmount, params_.messageId ); } else { cacheData = CacheData( bytes(""), abi.encode(connectorPendingAmount + pendingAmount) ); } } function preRetryHook( PreRetryHookCallParams memory params_ ) external nonReentrant isVaultOrController returns ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) { (address updatedReceiver, uint256 pendingMint, address connector) = abi .decode( params_.cacheData.identifierCache, (address, uint256, address) ); if (connector != params_.connector) revert InvalidConnector(); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, pendingMint ); postRetryHookData = abi.encode( updatedReceiver, consumedAmount, pendingAmount ); transferInfo = TransferInfo(updatedReceiver, consumedAmount, bytes("")); } function postRetryHook( PostRetryHookCallParams calldata params_ ) external isVaultOrController nonReentrant returns (CacheData memory cacheData) { ( address updatedReceiver, uint256 consumedAmount, uint256 pendingAmount ) = abi.decode(params_.postRetryHookData, (address, uint256, uint256)); // code reaches here after minting/unlocking the pending amount emit PendingTokensBridged( params_.connector, updatedReceiver, consumedAmount, pendingAmount, params_.messageId ); uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.cacheData.connectorCache ); cacheData.connectorCache = abi.encode( connectorPendingAmount - consumedAmount ); cacheData.identifierCache = abi.encode( updatedReceiver, pendingAmount, params_.connector ); if (pendingAmount == 0) { cacheData.identifierCache = new bytes(0); } } function getConnectorPendingAmount( address connector_ ) external returns (uint256) { bytes memory cache = IController(vaultOrController).connectorCache( connector_ ); return _getConnectorPendingAmount(cache); } function _getIdentifierPendingAmount( bytes memory identifierCache_ ) internal view returns (uint256) { if (identifierCache_.length > 0) { (, uint256 pendingAmount, ) = abi.decode( identifierCache_, (address, uint256, address) ); return pendingAmount; } else return 0; } function getIdentifierPendingAmount( bytes32 messageId_ ) external returns (uint256) { bytes memory cache = IController(vaultOrController).identifierCache( messageId_ ); return _getIdentifierPendingAmount(cache); } }
pragma solidity 0.8.13; import "./plugins/LimitPlugin.sol"; import "../interfaces/IController.sol"; import "./plugins/ConnectorPoolPlugin.sol"; import "./LimitHook.sol"; import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IBridge.sol"; import {IConnector} from "../interfaces/IConnector.sol"; interface LyraTSA is IERC20 { function underlying() external view returns (IERC20); function withdrawTo( address account, uint256 amount ) external returns (bool); function depositFor( address account, uint256 amount ) external returns (bool); } interface IBridgeExt is IBridge { function token() external view returns (address); } interface IConnectorPlugExt is IConnector { function bridge__() external returns (IBridge); } abstract contract LyraTSAHookBase is LimitHook { struct PackedAddresses { address returnRecipient; address fallbackReceiver; address withdrawConnector; IBridgeExt withdrawVault; IERC20 withdrawToken; } uint withdrawalMinGasLimit = 500000; /** * @notice Constructor for creating a new SuperToken. * @param owner_ Owner of this contract. */ constructor( address owner_, address controller_, bool useControllerPools_ ) LimitHook(owner_, controller_, useControllerPools_) { hookType = LYRA_VAULT_ZAP_HOOK; } receive() external payable {} /////////// // Admin // /////////// function setWithdrawalMinGasLimit(uint limit) external onlyOwner { withdrawalMinGasLimit = limit; } function recoverEth(address payable recipient) external onlyOwner { recipient.transfer(address(this).balance); } function recoverERC20(IERC20 token, address recipient) external onlyOwner { token.transfer(recipient, token.balanceOf(address(this))); } //////////////// // Hook calls // //////////////// function dstPreHookCall( DstPreHookCallParams memory params_ ) external override isVaultOrController returns (bytes memory postHookData, TransferInfo memory transferInfo) { if (useControllerPools) _poolDstHook(params_.connector, params_.transferInfo.amount); (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook( params_.connector, params_.transferInfo.amount ); transferInfo = params_.transferInfo; transferInfo.amount = consumedAmount; // No data provided, process normally OR if not all amount is consumed, process normally if (params_.transferInfo.data.length == 64 && pendingAmount == 0) { (address returnRecipient, address withdrawConnector) = abi.decode( params_.transferInfo.data, (address, address) ); if ( returnRecipient == address(0) || withdrawConnector == address(0) ) { // In the case of an invalid/zero withdrawConnector still deposit to TSA and send to original receiver postHookData = abi.encode(consumedAmount, pendingAmount); } else { postHookData = abi.encode( consumedAmount, pendingAmount, params_.transferInfo.receiver, returnRecipient, withdrawConnector ); transferInfo.receiver = address(this); } } else { // Any invalid data shape will be treated as a normal transfer postHookData = abi.encode(consumedAmount, pendingAmount); } } function dstPostHookCall( DstPostHookCallParams memory params_ ) external override isVaultOrController returns (CacheData memory cacheData) { ( uint256 consumedAmount, uint256 pendingAmount, PackedAddresses memory addrs, bool attemptToWithdraw ) = _parseParameters(params_); if (attemptToWithdraw) { IERC20 mintedToken = IERC20(IBridgeExt(vaultOrController).token()); uint balance = mintedToken.balanceOf(address(this)); if (balance != consumedAmount) { revert("MINTED_BALANCE_MISMATCH"); } bool conversionSucceeded = _convertToken( mintedToken, addrs.withdrawToken, balance ); if (conversionSucceeded) { bool withdrew = _withdrawToRecipient(addrs); if (!withdrew) { // Withdraw failed, send withdrawToken to fallback addrs.withdrawToken.transfer( addrs.fallbackReceiver, addrs.withdrawToken.balanceOf(address(this)) ); } } else { // Deposit failed, send minted tokens to fallback mintedToken.transfer(addrs.fallbackReceiver, balance); } } uint256 connectorPendingAmount = _getConnectorPendingAmount( params_.connectorCache ); if (pendingAmount > 0) { cacheData = CacheData( abi.encode( params_.transferInfo.receiver, pendingAmount, params_.connector ), abi.encode(connectorPendingAmount + pendingAmount) ); emit TokensPending( params_.connector, params_.transferInfo.receiver, consumedAmount, pendingAmount, params_.messageId ); } else { cacheData = CacheData( bytes(""), abi.encode(connectorPendingAmount + pendingAmount) ); } } function _parseParameters( DstPostHookCallParams memory params_ ) internal returns ( uint256 consumedAmount, uint256 pendingAmount, PackedAddresses memory addrs, bool attemptToWithdraw ) { attemptToWithdraw = false; if (params_.postHookData.length == 64) { (consumedAmount, pendingAmount) = abi.decode( params_.postHookData, (uint256, uint256) ); return (consumedAmount, pendingAmount, addrs, false); } else if (params_.postHookData.length == 160) { // If the data is 160 bytes, it means we want to attempt to deposit to the TSA // and withdraw the shares immediately IERC20 mintedToken = IERC20(IBridgeExt(vaultOrController).token()); ( consumedAmount, pendingAmount, addrs.fallbackReceiver, addrs.returnRecipient, addrs.withdrawConnector ) = abi.decode( params_.postHookData, (uint256, uint256, address, address, address) ); if (pendingAmount != 0) { revert("INVALID_PENDING_AMOUNT"); } addrs.withdrawVault = tryGetWithdrawVault(addrs.withdrawConnector); if (address(addrs.withdrawVault) == address(0)) { mintedToken.transfer(addrs.fallbackReceiver, consumedAmount); return (consumedAmount, pendingAmount, addrs, false); } addrs.withdrawToken = tryGetToken(addrs.withdrawVault); if (address(addrs.withdrawToken) == address(0)) { mintedToken.transfer(addrs.fallbackReceiver, consumedAmount); return (consumedAmount, pendingAmount, addrs, false); } return (consumedAmount, pendingAmount, addrs, true); } else { revert("parse: INVALID_DATA_LENGTH"); } } function _withdrawToRecipient( PackedAddresses memory addrs ) internal returns (bool success) { uint256 amount = addrs.withdrawToken.balanceOf(address(this)); addrs.withdrawToken.approve(address(addrs.withdrawVault), amount); uint256 fees = IConnectorPlugExt(addrs.withdrawConnector).getMinFees( withdrawalMinGasLimit, 0 ); if (fees > address(this).balance) { revert("INSUFFICIENT_ETH_BALANCE"); } try addrs.withdrawVault.bridge{value: fees}( addrs.returnRecipient, amount, withdrawalMinGasLimit, addrs.withdrawConnector, new bytes(0), new bytes(0) ) { return true; } catch { return false; } } /// @dev Returns zero address if bridge is not found or connector is invalid function tryGetWithdrawVault( address connector ) internal returns (IBridgeExt withdrawVault) { (bool success, bytes memory data) = connector.call( abi.encodeWithSignature("bridge__()") ); if (!success || data.length == 0) { return IBridgeExt(address(0)); } return IBridgeExt(abi.decode(data, (address))); } /// @dev Returns zero address if not found function tryGetToken( IBridgeExt withdrawVault ) internal returns (IERC20 withdrawToken) { (bool success, bytes memory data) = address(withdrawVault).call( abi.encodeWithSignature("token()") ); if (!success || data.length == 0) { return IERC20(address(0)); } return IERC20(abi.decode(data, (address))); } function _convertToken( IERC20 depositToken, IERC20 withdrawToken, uint256 amount ) internal virtual returns (bool success); } contract LyraTSADepositHook is LyraTSAHookBase { constructor( address owner_, address controller_, bool useControllerPools_ ) LyraTSAHookBase(owner_, controller_, useControllerPools_) {} function _convertToken( IERC20 depositToken, IERC20 withdrawToken, uint256 amount ) internal override returns (bool success) { LyraTSA tsa = LyraTSA(address(withdrawToken)); depositToken.approve(address(tsa), amount); try tsa.depositFor(address(this), amount) returns (bool) { return true; } catch { return false; } } } contract LyraTSAWithdrawHook is LyraTSAHookBase { constructor( address owner_, address controller_, bool useControllerPools_ ) LyraTSAHookBase(owner_, controller_, useControllerPools_) {} function _convertToken( IERC20 depositToken, IERC20 withdrawToken, uint256 amount ) internal override returns (bool success) { LyraTSA tsa = LyraTSA(address(depositToken)); if (tsa.underlying() != withdrawToken) { return false; } try tsa.withdrawTo(address(this), amount) returns (bool) { return true; } catch { return false; } } }
pragma solidity 0.8.13; import "../HookBase.sol"; abstract contract ConnectorPoolPlugin is HookBase { // connectorPoolId => totalLockedAmount mapping(uint256 => uint256) public poolLockedAmounts; // connector => connectorPoolId mapping(address => uint256) public connectorPoolIds; event ConnectorPoolIdUpdated(address connector, uint256 poolId); event PoolLockedAmountUpdated(uint256 poolId, uint256 amount); function updateConnectorPoolId( address[] calldata connectors, uint256[] calldata poolIds_ ) external onlyOwner { uint256 length = connectors.length; for (uint256 i; i < length; i++) { if (poolIds_[i] == 0) revert InvalidPoolId(); connectorPoolIds[connectors[i]] = poolIds_[i]; emit ConnectorPoolIdUpdated(connectors[i], poolIds_[i]); } } function updatePoolLockedAmounts( uint256[] calldata poolIds_, uint256[] calldata amounts_ ) external onlyOwner { uint256 length = poolIds_.length; for (uint256 i; i < length; i++) { if (poolIds_[i] == 0) revert InvalidPoolId(); poolLockedAmounts[poolIds_[i]] = amounts_[i]; emit PoolLockedAmountUpdated(poolIds_[i], amounts_[i]); } } function _poolSrcHook(address connector_, uint256 amount_) internal { uint256 connectorPoolId = connectorPoolIds[connector_]; if (connectorPoolId == 0) revert InvalidPoolId(); if (amount_ > poolLockedAmounts[connectorPoolId]) revert InsufficientFunds(); poolLockedAmounts[connectorPoolId] -= amount_; } function _poolDstHook( address connector_, uint256 amount_ ) internal returns (uint256 oldLockedAmount) { uint256 connectorPoolId = connectorPoolIds[connector_]; if (connectorPoolId == 0) revert InvalidPoolId(); oldLockedAmount = poolLockedAmounts[connectorPoolId]; poolLockedAmounts[connectorPoolId] += amount_; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "../../libraries/ExcessivelySafeCall.sol"; import "../../utils/RescueBase.sol"; import "../../common/Errors.sol"; /** * @title ExecutionHelper * @notice It is an untrusted contract used for payload execution by Super token and Vault. */ contract ExecutionHelper is RescueBase { using ExcessivelySafeCall for address; uint16 private constant MAX_COPY_BYTES = 0; address public hook; bytes32 public messageId; uint256 public bridgeAmount; constructor(address owner_) AccessControl(owner_) { _grantRole(RESCUE_ROLE, owner_); } modifier onlyHook() { require(msg.sender == hook, "ExecutionHelper: only hook"); _; } function setHook(address hook_) external onlyOwner { hook = hook_; } /** * @notice this function is used to execute a payload at target_ * @dev receiver address cannot be this contract address. * @param target_ address of target. * @param payload_ payload to be executed at target. */ function execute( address target_, bytes memory payload_, bytes32 messageId_, uint256 bridgeAmount_ ) external onlyHook returns (bool success) { if (target_ == address(this)) return false; messageId = messageId_; bridgeAmount = bridgeAmount_; (success, ) = target_.excessivelySafeCall( gasleft(), MAX_COPY_BYTES, payload_ ); messageId = bytes32(0); bridgeAmount = 0; } }
pragma solidity 0.8.13; import "../HookBase.sol"; import {Gauge} from "../../utils/Gauge.sol"; abstract contract LimitPlugin is Gauge, HookBase { bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE"); // connector => receivingLimitParams mapping(address => LimitParams) _receivingLimitParams; // connector => sendingLimitParams mapping(address => LimitParams) _sendingLimitParams; //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // Emitted when limit parameters are updated event LimitParamsUpdated(UpdateLimitParams[] updates); // Emitted when pending tokens are minted to the receiver event PendingTokensBridged( address connector, address receiver, uint256 consumedAmount, uint256 pendingAmount, bytes32 messageId ); // Emitted when the transfer reaches the limit, and the token mint is added to the pending queue event TokensPending( address connector, address receiver, uint256 consumedAmount, uint256 pendingAmount, bytes32 messageId ); /** * @notice This function is used to set bridge limits. * @dev It can only be updated by the owner. * @param updates An array of structs containing update parameters. */ function updateLimitParams( UpdateLimitParams[] calldata updates ) external onlyRole(LIMIT_UPDATER_ROLE) { for (uint256 i = 0; i < updates.length; i++) { if (updates[i].isMint) { _consumePartLimit( 0, _receivingLimitParams[updates[i].connector] ); // To keep the current limit in sync _receivingLimitParams[updates[i].connector].maxLimit = updates[ i ].maxLimit; _receivingLimitParams[updates[i].connector] .ratePerSecond = updates[i].ratePerSecond; } else { _consumePartLimit(0, _sendingLimitParams[updates[i].connector]); // To keep the current limit in sync _sendingLimitParams[updates[i].connector].maxLimit = updates[i] .maxLimit; _sendingLimitParams[updates[i].connector] .ratePerSecond = updates[i].ratePerSecond; } } emit LimitParamsUpdated(updates); } function getCurrentReceivingLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_receivingLimitParams[connector_]); } function getCurrentSendingLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_sendingLimitParams[connector_]); } function getReceivingLimitParams( address connector_ ) external view returns (LimitParams memory) { return _receivingLimitParams[connector_]; } function getSendingLimitParams( address connector_ ) external view returns (LimitParams memory) { return _sendingLimitParams[connector_]; } function _limitSrcHook(address connector_, uint256 amount_) internal { if (_sendingLimitParams[connector_].maxLimit == 0) revert SiblingNotSupported(); _consumeFullLimit(amount_, _sendingLimitParams[connector_]); // Reverts on limit hit } function _limitDstHook( address connector_, uint256 amount_ ) internal returns (uint256 consumedAmount, uint256 pendingAmount) { if (_receivingLimitParams[connector_].maxLimit == 0) revert SiblingNotSupported(); (consumedAmount, pendingAmount) = _consumePartLimit( amount_, _receivingLimitParams[connector_] ); } function _getConnectorPendingAmount( bytes memory connectorCache_ ) internal pure returns (uint256) { if (connectorCache_.length > 0) { return abi.decode(connectorCache_, (uint256)); } else return 0; } }
// // // SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "lib/openzeppelin-contracts/contracts/utils/math/Math.sol"; import {FixedPointMathLib} from "lib/solmate/src/utils/FixedPointMathLib.sol"; import {IStrategy} from "../interfaces/IStrategy.sol"; import "lib/solmate/src/tokens/ERC20.sol"; import "lib/solmate/src/utils/SafeTransferLib.sol"; import {IConnector} from "../ConnectorPlug.sol"; import "./LimitExecutionHook.sol"; contract Vault_YieldLimitExecHook is LimitExecutionHook { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; uint256 private constant MAX_BPS = 10_000; IStrategy public strategy; // address of the strategy contract ERC20 public immutable underlyingAsset__; uint256 public totalLockedInStrategy; // total funds deposited in strategy uint256 public totalIdle; // Amount of tokens that are in the vault uint256 public totalDebt; // Amount of tokens that strategy have borrowed uint128 public lastRebalanceTimestamp; // Timestamp of last rebalance uint128 public rebalanceDelay; // Delay between rebalance uint256 public debtRatio; // Debt ratio for the Vault (in BPS, <= 10k) bool public emergencyShutdown; // if true, no funds can be invested in the strategy uint256 public lastTotalUnderlyingAssetsSynced; event WithdrawFromStrategy(uint256 withdrawn); event Rebalanced( uint256 totalIdle, uint256 totalDebt, uint256 credit, uint256 debtOutstanding ); event ShutdownStateUpdated(bool shutdownState); event DebtRatioUpdated(uint256 debtRatio); event StrategyUpdated(address strategy); event RebalanceDelayUpdated(uint128 rebalanceDelay); modifier notShutdown() { if (emergencyShutdown) revert VaultShutdown(); _; } constructor( uint256 debtRatio_, uint128 rebalanceDelay_, address strategy_, address underlyingAsset_, address vault_, address executionHelper_, bool useControllerPools_ ) LimitExecutionHook( msg.sender, vault_, executionHelper_, useControllerPools_ ) { underlyingAsset__ = ERC20(underlyingAsset_); debtRatio = debtRatio_; rebalanceDelay = rebalanceDelay_; strategy = IStrategy(strategy_); hookType = LIMIT_EXECUTION_YIELD_HOOK; _grantRole(LIMIT_UPDATER_ROLE, msg.sender); } /** * @dev This function calls the srcHookCall function of the connector contract, * passing in the receiver, amount, siblingChainSlug, extradata, and msg.sender, and returns * the updated receiver, amount, and extradata. */ function srcPreHookCall( SrcPreHookCallParams calldata params_ ) public override notShutdown returns (TransferInfo memory, bytes memory) { totalIdle += params_.transferInfo.amount; return super.srcPreHookCall(params_); } function srcPostHookCall( SrcPostHookCallParams memory srcPostHookCallParams_ ) public override isVaultOrController returns (TransferInfo memory transferInfo) { _checkDelayAndRebalance(); uint256 totalUnderlyingAsset = strategy.estimatedTotalAssets() + totalIdle; uint256 totalYieldSync = totalUnderlyingAsset - lastTotalUnderlyingAssetsSynced; lastTotalUnderlyingAssetsSynced = totalUnderlyingAsset; transferInfo = srcPostHookCallParams_.transferInfo; if (srcPostHookCallParams_.transferInfo.amount == 0) { transferInfo.data = abi.encode(totalYieldSync, bytes("")); } else { transferInfo.data = abi.encode( totalYieldSync, srcPostHookCallParams_.transferInfo.data ); } } /** * @notice This function is called before the execution of a destination hook. * @dev It checks if the sibling chain is supported, consumes a part of the limit, and prepares post-hook data. */ function dstPreHookCall( DstPreHookCallParams calldata params_ ) public override notShutdown returns (bytes memory postHookData, TransferInfo memory transferInfo) { (postHookData, transferInfo) = super.dstPreHookCall(params_); // ensure vault have enough idle underlyingAssets if (transferInfo.amount > totalUnderlyingAssets()) revert NotEnoughAssets(); (bytes memory options_, bytes memory payload_) = abi.decode( params_.transferInfo.data, (bytes, bytes) ); bool pullFromStrategy = abi.decode(options_, (bool)); if (transferInfo.amount > totalIdle) { if (pullFromStrategy) { _withdrawFromStrategy(transferInfo.amount - totalIdle); } else { ( uint256 consumedUnderlying, uint256 pendingUnderlying, uint256 bridgeUnderlying ) = abi.decode(postHookData, (uint256, uint256, uint256)); pendingUnderlying += transferInfo.amount - totalIdle; postHookData = abi.encode( transferInfo.amount, pendingUnderlying, bridgeUnderlying ); transferInfo.amount = totalIdle; // Update the lastUpdateLimit as consumedAmount is reduced to totalIdle. This is to ensure that the // receiving limit is updated by correct transferred amount. LimitParams storage receivingParams = _receivingLimitParams[ params_.connector ]; receivingParams.lastUpdateLimit += consumedUnderlying - transferInfo.amount; } totalIdle = 0; } else totalIdle -= transferInfo.amount; transferInfo.data = payload_; transferInfo.receiver = params_.transferInfo.receiver; } function dstPostHookCall( DstPostHookCallParams calldata params_ ) public override returns (CacheData memory cacheData) { return super.dstPostHookCall(params_); } /** * @notice Handles pre-retry hook logic before execution. * @dev This function can be used to mint funds which were in a pending state due to limits. */ function preRetryHook( PreRetryHookCallParams calldata params_ ) public override notShutdown returns ( bytes memory postRetryHookData, TransferInfo memory transferInfo ) { (postRetryHookData, transferInfo) = super.preRetryHook(params_); if (transferInfo.amount > totalIdle) { _withdrawFromStrategy(transferInfo.amount - totalIdle); totalIdle = 0; } else totalIdle -= transferInfo.amount; } function postRetryHook( PostRetryHookCallParams calldata params_ ) public override returns (CacheData memory cacheData) { return super.postRetryHook(params_); } function withdrawFromStrategy( uint256 underlyingAsset_ ) external onlyOwner returns (uint256) { return _withdrawFromStrategy(underlyingAsset_); } function _withdrawFromStrategy( uint256 underlyingAsset_ ) internal returns (uint256 withdrawn) { uint256 preBalance = underlyingAsset__.balanceOf(address(this)); strategy.withdraw(underlyingAsset_); withdrawn = underlyingAsset__.balanceOf(address(this)) - preBalance; totalIdle += withdrawn; totalDebt -= withdrawn; underlyingAsset__.transfer(vaultOrController, withdrawn); emit WithdrawFromStrategy(withdrawn); } function _withdrawAllFromStrategy() internal returns (uint256) { uint256 preBalance = underlyingAsset__.balanceOf(address(this)); strategy.withdrawAll(); uint256 withdrawn = underlyingAsset__.balanceOf(address(this)) - preBalance; totalIdle += withdrawn; totalDebt = 0; underlyingAsset__.transfer(vaultOrController, withdrawn); emit WithdrawFromStrategy(withdrawn); return withdrawn; } function rebalance() external notShutdown { _rebalance(); } function _checkDelayAndRebalance() internal { uint128 timeElapsed = uint128(block.timestamp) - lastRebalanceTimestamp; if (timeElapsed >= rebalanceDelay) { _rebalance(); } } function _rebalance() internal { if (address(strategy) == address(0)) return; lastRebalanceTimestamp = uint128(block.timestamp); // Compute the line of credit the Vault is able to offer the Strategy (if any) uint256 credit = _creditAvailable(); uint256 pendingDebt = _debtOutstanding(); if (credit > 0) { // Credit surplus, give to Strategy totalIdle -= credit; totalDebt += credit; totalLockedInStrategy += credit; underlyingAsset__.safeTransferFrom( vaultOrController, address(strategy), credit ); strategy.invest(); } else if (pendingDebt > 0) { // Credit deficit, take from Strategy _withdrawFromStrategy(pendingDebt); } emit Rebalanced(totalIdle, totalDebt, credit, pendingDebt); } /// @notice Returns the total quantity of all underlyingAssets under control of this /// Vault, whether they're loaned out to a Strategy, or currently held in /// the Vault. /// @return total quantity of all underlyingAssets under control of this Vault function totalUnderlyingAssets() public view returns (uint256) { return strategy.estimatedTotalAssets() + totalIdle; } function _creditAvailable() internal view returns (uint256) { uint256 vaultTotalAssets = totalUnderlyingAssets(); uint256 vaultDebtLimit = (debtRatio * vaultTotalAssets) / MAX_BPS; uint256 vaultTotalDebt = totalDebt; if (vaultDebtLimit <= vaultTotalDebt) return 0; // Start with debt limit left for the Strategy uint256 availableCredit = vaultDebtLimit - vaultTotalDebt; // Can only borrow up to what the contract has in reserve // NOTE: Running near 100% is discouraged return Math.min(availableCredit, totalIdle); } function creditAvailable() external view returns (uint256) { // @notice // Amount of tokens in Vault a Strategy has access to as a credit line. // This will check the Strategy's debt limit, as well as the tokens // available in the Vault, and determine the maximum amount of tokens // (if any) the Strategy may draw on. // In the rare case the Vault is in emergency shutdown this will return 0. // @param strategy The Strategy to check. Defaults to caller. // @return The quantity of tokens available for the Strategy to draw on. return _creditAvailable(); } function _debtOutstanding() internal view returns (uint256) { // See note on `debtOutstanding()`. if (debtRatio == 0) { return totalDebt; } uint256 debtLimit = ((debtRatio * totalUnderlyingAssets()) / MAX_BPS); if (totalDebt <= debtLimit) return 0; else return totalDebt - debtLimit; } function debtOutstanding() external view returns (uint256) { // @notice // Determines if `strategy` is past its debt limit and if any tokens // should be withdrawn to the Vault. // @return The quantity of tokens to withdraw. return _debtOutstanding(); } function updateEmergencyShutdownState( bool shutdownState_, bool detachStrategy ) external onlyOwner { if (shutdownState_ && detachStrategy) { // If we're exiting emergency shutdown, we need to empty strategy _withdrawAllFromStrategy(); strategy = IStrategy(address(0)); } emergencyShutdown = shutdownState_; emit ShutdownStateUpdated(shutdownState_); } //////////////////////////////////////////////////////// ////////////////////// SETTERS ////////////////////////// //////////////////////////////////////////////////////// function setDebtRatio(uint256 debtRatio_) external onlyOwner { if (debtRatio_ > MAX_BPS) revert DebtRatioTooHigh(); debtRatio = debtRatio_; emit DebtRatioUpdated(debtRatio_); } function setStrategy(address strategy_) external onlyOwner { strategy = IStrategy(strategy_); emit StrategyUpdated(strategy_); } function setRebalanceDelay(uint128 rebalanceDelay_) external onlyOwner { rebalanceDelay = rebalanceDelay_; emit RebalanceDelayUpdated(rebalanceDelay_); } }
pragma solidity ^0.8.3; interface IBridge { function bridge( address receiver_, uint256 amount_, uint256 msgGasLimit_, address connector_, bytes calldata execPayload_, bytes calldata options_ ) external payable; function receiveInbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable; function retry(address connector_, bytes32 messageId_) external; }
pragma solidity ^0.8.13; interface IConnector { function outbound( uint256 msgGasLimit_, bytes memory payload_, bytes memory options_ ) external payable returns (bytes32 messageId_); function siblingChainSlug() external view returns (uint32); function getMinFees( uint256 msgGasLimit_, uint256 payloadSize_ ) external view returns (uint256 totalFees); function getMessageId() external view returns (bytes32); }
pragma solidity ^0.8.13; interface IController { function identifierCache( bytes32 messageId_ ) external payable returns (bytes memory cache); function connectorCache( address connector_ ) external payable returns (bytes memory cache); }
pragma solidity ^0.8.3; import "../common/Structs.sol"; interface IHook { /** * @notice Executes pre-hook call for source underlyingAsset. * @dev This function is used to execute a pre-hook call for the source underlyingAsset before initiating a transfer. * @param params_ Parameters for the pre-hook call. * @return transferInfo Information about the transfer. * @return postSrcHookData returned from the pre-hook call. */ function srcPreHookCall( SrcPreHookCallParams calldata params_ ) external returns ( TransferInfo memory transferInfo, bytes memory postSrcHookData ); function srcPostHookCall( SrcPostHookCallParams calldata params_ ) external returns (TransferInfo memory transferInfo); /** * @notice Executes pre-hook call for destination underlyingAsset. * @dev This function is used to execute a pre-hook call for the destination underlyingAsset before initiating a transfer. * @param params_ Parameters for the pre-hook call. */ function dstPreHookCall( DstPreHookCallParams calldata params_ ) external returns (bytes memory postHookData, TransferInfo memory transferInfo); /** * @notice Executes post-hook call for destination underlyingAsset. * @dev This function is used to execute a post-hook call for the destination underlyingAsset after completing a transfer. * @param params_ Parameters for the post-hook call. * @return cacheData Cached data for the post-hook call. */ function dstPostHookCall( DstPostHookCallParams calldata params_ ) external returns (CacheData memory cacheData); /** * @notice Executes a pre-retry hook for a failed transaction. * @dev This function is used to execute a pre-retry hook for a failed transaction. * @param params_ Parameters for the pre-retry hook. * @return postRetryHookData Data from the post-retry hook. * @return transferInfo Information about the transfer. */ function preRetryHook( PreRetryHookCallParams calldata params_ ) external returns ( bytes memory postRetryHookData, TransferInfo memory transferInfo ); /** * @notice Executes a post-retry hook for a failed transaction. * @dev This function is used to execute a post-retry hook for a failed transaction. * @param params_ Parameters for the post-retry hook. * @return cacheData Cached data for the post-retry hook. */ function postRetryHook( PostRetryHookCallParams calldata params_ ) external returns (CacheData memory cacheData); }
pragma solidity 0.8.13; import "./IHook.sol"; interface ILimitHook is IHook { function updateLimitParams(UpdateLimitParams[] calldata updates) external; function checkLimit(address user, uint256 amount) external view; function getIdentifierPendingAmount( bytes32 messageId_ ) external returns (uint256); function getConnectorPendingAmount( address connector_ ) external returns (uint256); function getCurrentReceivingLimit( address connector_ ) external view returns (uint256); function getCurrentSendingLimit( address connector_ ) external view returns (uint256); function getReceivingLimitParams( address connector_ ) external view returns (LimitParams memory); function getSendingLimitParams( address connector_ ) external view returns (LimitParams memory); }
pragma solidity 0.8.13; interface IMintableERC20 { function mint(address receiver_, uint256 amount_) external; function burn(address burner_, uint256 amount_) external; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title IPlug * @notice Interface for a plug contract that executes the message received from a source chain. */ interface IPlug { /** * @dev this should be only executable by socket * @notice executes the message received from source chain * @notice It is expected to have original sender checks in the destination plugs using payload * @param srcChainSlug_ chain slug of source * @param payload_ the data which is needed by plug at inbound call on remote */ function inbound( uint32 srcChainSlug_, bytes calldata payload_ ) external payable; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title ISocket * @notice An interface for a cross-chain communication contract * @dev This interface provides methods for transmitting and executing messages between chains, * connecting a plug to a remote chain and setting up switchboards for the message transmission * This interface also emits events for important operations such as message transmission, execution status, * and plug connection */ interface ISocket { /** * @notice A struct containing fees required for message transmission and execution * @param transmissionFees fees needed for transmission * @param switchboardFees fees needed by switchboard * @param executionFee fees needed for execution */ struct Fees { uint128 transmissionFees; uint128 executionFee; uint128 switchboardFees; } /** * @title MessageDetails * @dev This struct defines the details of a message to be executed in a Decapacitor contract. */ struct MessageDetails { // A unique identifier for the message. bytes32 msgId; // The fee to be paid for executing the message. uint256 executionFee; // The maximum amount of gas that can be used to execute the message. uint256 minMsgGasLimit; // The extra params which provides msg value and additional info needed for message exec bytes32 executionParams; // The payload data to be executed in the message. bytes payload; } /** * @title ExecutionDetails * @dev This struct defines the execution details */ struct ExecutionDetails { // packet id bytes32 packetId; // proposal count uint256 proposalCount; // gas limit needed to execute inbound uint256 executionGasLimit; // proof data required by the Decapacitor contract to verify the message's authenticity bytes decapacitorProof; // signature of executor bytes signature; } /** * @notice emits the message details when a new message arrives at outbound * @param localChainSlug local chain slug * @param localPlug local plug address * @param dstChainSlug remote chain slug * @param dstPlug remote plug address * @param msgId message id packed with remoteChainSlug and nonce * @param minMsgGasLimit gas limit needed to execute the inbound at remote * @param payload the data which will be used by inbound at remote */ event MessageOutbound( uint32 localChainSlug, address localPlug, uint32 dstChainSlug, address dstPlug, bytes32 msgId, uint256 minMsgGasLimit, bytes32 executionParams, bytes32 transmissionParams, bytes payload, Fees fees ); /** * @notice emits the status of message after inbound call * @param msgId msg id which is executed */ event ExecutionSuccess(bytes32 msgId); /** * @notice emits the config set by a plug for a remoteChainSlug * @param plug address of plug on current chain * @param siblingChainSlug sibling chain slug * @param siblingPlug address of plug on sibling chain * @param inboundSwitchboard inbound switchboard (select from registered options) * @param outboundSwitchboard outbound switchboard (select from registered options) * @param capacitor capacitor selected based on outbound switchboard * @param decapacitor decapacitor selected based on inbound switchboard */ event PlugConnected( address plug, uint32 siblingChainSlug, address siblingPlug, address inboundSwitchboard, address outboundSwitchboard, address capacitor, address decapacitor ); /** * @notice registers a message * @dev Packs the message and includes it in a packet with capacitor * @param remoteChainSlug_ the remote chain slug * @param minMsgGasLimit_ the gas limit needed to execute the payload on remote * @param payload_ the data which is needed by plug at inbound call on remote */ function outbound( uint32 remoteChainSlug_, uint256 minMsgGasLimit_, bytes32 executionParams_, bytes32 transmissionParams_, bytes memory payload_ ) external payable returns (bytes32 msgId); /** * @notice executes a message * @param executionDetails_ the packet details, proof and signature needed for message execution * @param messageDetails_ the message details */ function execute( ISocket.ExecutionDetails calldata executionDetails_, ISocket.MessageDetails calldata messageDetails_ ) external payable; /** * @notice sets the config specific to the plug * @param siblingChainSlug_ the sibling chain slug * @param siblingPlug_ address of plug present at sibling chain to call inbound * @param inboundSwitchboard_ the address of switchboard to use for receiving messages * @param outboundSwitchboard_ the address of switchboard to use for sending messages */ function connect( uint32 siblingChainSlug_, address siblingPlug_, address inboundSwitchboard_, address outboundSwitchboard_ ) external; /** * @notice Retrieves the minimum fees required for a message with a specified gas limit and destination chain. * @param minMsgGasLimit_ The gas limit of the message. * @param remoteChainSlug_ The slug of the destination chain for the message. * @param plug_ The address of the plug through which the message is sent. * @return totalFees The minimum fees required for the specified message. */ function getMinFees( uint256 minMsgGasLimit_, uint256 payloadSize_, bytes32 executionParams_, bytes32 transmissionParams_, uint32 remoteChainSlug_, address plug_ ) external view returns (uint256 totalFees); /** * @notice returns chain slug * @return chainSlug current chain slug */ function chainSlug() external view returns (uint32 chainSlug); function globalMessageCount() external view returns (uint64); /** * @notice returns the config for given `plugAddress_` and `siblingChainSlug_` * @param siblingChainSlug_ the sibling chain slug * @param plugAddress_ address of plug present at current chain */ function getPlugConfig( address plugAddress_, uint32 siblingChainSlug_ ) external view returns ( address siblingPlug, address inboundSwitchboard__, address outboundSwitchboard__, address capacitor__, address decapacitor__ ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title IStrategy * @notice Interface for strategy contract which interacts with other protocols */ interface IStrategy { function withdraw(uint256 amount_) external returns (uint256 loss_); function withdrawAll() external; function estimatedTotalAssets() external view returns (uint256 totalUnderlyingAssets_); function invest() external; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.13; library ExcessivelySafeCall { uint constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeCall( address _target, uint _gas, uint16 _maxCopy, bytes memory _calldata ) internal returns (bool, bytes memory) { // set up for assembly call uint _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := call( _gas, // gas _target, // recipient 0, // ether value add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeStaticCall( address _target, uint _gas, uint16 _maxCopy, bytes memory _calldata ) internal view returns (bool, bytes memory) { // set up for assembly call uint _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := staticcall( _gas, // gas _target, // recipient add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /** * @notice Swaps function selectors in encoded contract calls * @dev Allows reuse of encoded calldata for functions with identical * argument types but different names. It simply swaps out the first 4 bytes * for the new selector. This function modifies memory in place, and should * only be used with caution. * @param _newSelector The new 4-byte selector * @param _buf The encoded contract args */ function swapSelector( bytes4 _newSelector, bytes memory _buf ) internal pure { require(_buf.length >= 4); uint _mask = LOW_28_MASK; assembly { // load the first word of let _word := mload(add(_buf, 0x20)) // mask out the top 4 bytes // /x _word := and(_word, _mask) _word := or(_newSelector, _word) mstore(add(_buf, 0x20), _word) } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "lib/solmate/src/utils/SafeTransferLib.sol"; import "lib/solmate/src/tokens/ERC20.sol"; error ZeroAddress(); /** * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ library RescueFundsLib { /** * @dev The address used to identify ETH. */ address public constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); /** * @dev thrown when the given token address don't have any code */ error InvalidTokenAddress(); /** * @dev Rescues funds from a contract. * @param token_ The address of the token contract. * @param rescueTo_ The address of the user. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) internal { if (rescueTo_ == address(0)) revert ZeroAddress(); if (token_ == ETH_ADDRESS) { SafeTransferLib.safeTransferETH(rescueTo_, amount_); } else { if (token_.code.length == 0) revert InvalidTokenAddress(); SafeTransferLib.safeTransfer(ERC20(token_), rescueTo_, amount_); } } }
pragma solidity 0.8.13; import "lib/solmate/src/tokens/ERC20.sol"; import "../utils/RescueBase.sol"; import "../interfaces/IHook.sol"; /** * @title SuperToken * @notice An ERC20 contract which enables bridging a token to its sibling chains. * @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts. */ contract SuperToken is ERC20, RescueBase { // for all controller access (mint, burn) bytes32 constant CONTROLLER_ROLE = keccak256("CONTROLLER_ROLE"); /** * @notice constructor for creating a new SuperToken. * @param name_ token name * @param symbol_ token symbol * @param decimals_ token decimals (should be same on all chains) * @param initialSupplyHolder_ address to which initial supply will be minted * @param owner_ owner of this contract * @param initialSupply_ initial supply of super token */ constructor( string memory name_, string memory symbol_, uint8 decimals_, address initialSupplyHolder_, address owner_, uint256 initialSupply_ ) ERC20(name_, symbol_, decimals_) AccessControl(owner_) { _mint(initialSupplyHolder_, initialSupply_); _grantRole(RESCUE_ROLE, owner_); } function burn( address user_, uint256 amount_ ) external onlyRole(CONTROLLER_ROLE) { _burn(user_, amount_); } function mint( address receiver_, uint256 amount_ ) external onlyRole(CONTROLLER_ROLE) { _mint(receiver_, amount_); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "./YieldTokenBase.sol"; import {IStrategy} from "../../interfaces/IStrategy.sol"; import {IConnector} from "../../interfaces/IConnector.sol"; import {IHook} from "../../interfaces/IHook.sol"; // add shutdown contract YieldToken is YieldTokenBase { using FixedPointMathLib for uint256; bytes32 constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 constant HOOK_ROLE = keccak256("HOOK_ROLE"); constructor( string memory name_, string memory symbol_, uint8 decimals_ ) YieldTokenBase(name_, symbol_, decimals_) AccessControl(msg.sender) { _grantRole(RESCUE_ROLE, msg.sender); } // move to hook // fix to round up and check other cases function calculateMintAmount( uint256 underlyingAssets_ ) external view returns (uint256) { // total supply -> total shares // total yield -> total underlying from all chains // yield sent from src chain includes new amount hence subtracted here uint256 supply = _totalSupply; // Saves an extra SLOAD if _totalSupply is non-zero. return supply == 0 ? underlyingAssets_ : underlyingAssets_.mulDivDown( supply, totalUnderlyingAssets - underlyingAssets_ ); } function burn( address user_, uint256 shares_ ) external nonReentrant onlyRole(MINTER_ROLE) { _burn(user_, shares_); } // minter role function mint( address receiver_, uint256 amount_ ) external nonReentrant onlyRole(MINTER_ROLE) { _mint(receiver_, amount_); } // hook role function updateTotalUnderlyingAssets( uint256 amount_ ) external onlyRole(HOOK_ROLE) { _updateTotalUnderlyingAssets(amount_); } function _updateTotalUnderlyingAssets(uint256 amount_) internal { totalUnderlyingAssets = amount_; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; import {FixedPointMathLib} from "lib/solmate/src/utils/FixedPointMathLib.sol"; import "../../utils/RescueBase.sol"; import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {PermitDeadlineExpired, InvalidSigner} from "../../common/Errors.sol"; abstract contract YieldTokenBase is RescueBase, ReentrancyGuard, IERC20 { using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal _totalSupply; mapping(address => uint256) internal _balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// YIELD STORAGE //////////////////////////////////////////////////////////////*/ // total yield from all siblings uint256 public totalUnderlyingAssets; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol, uint8 _decimals) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function convertToShares( uint256 underlyingAssets ) public view virtual returns (uint256) { uint256 supply = _totalSupply; // Saves an extra SLOAD if _totalSupply is non-zero. return supply == 0 ? underlyingAssets : underlyingAssets.mulDivDown(supply, totalUnderlyingAssets); } function convertToAssets( uint256 shares ) public view virtual returns (uint256) { uint256 supply = _totalSupply; // Saves an extra SLOAD if _totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalUnderlyingAssets, supply); } function balanceOf(address user_) external view returns (uint256) { uint256 balance = _balanceOf[user_]; if (balance == 0) return 0; return convertToAssets(balance); } // recheck for multi yield function totalSupply() external view returns (uint256) { if (_totalSupply == 0) return 0; return totalUnderlyingAssets; } function approve( address spender_, uint256 amount_ ) public virtual returns (bool) { uint256 shares = convertToShares(amount_); allowance[msg.sender][spender_] = shares; emit Approval(msg.sender, spender_, shares); return true; } function transfer( address to_, uint256 amount_ ) public override returns (bool) { uint256 sharesToTransfer = convertToShares(amount_); _balanceOf[msg.sender] -= sharesToTransfer; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { _balanceOf[to_] += sharesToTransfer; } emit Transfer(msg.sender, to_, amount_); return true; } // transfer changes shares balance but reduces the amount function transferFrom( address from_, address to_, uint256 amount_ ) public override returns (bool) { uint256 sharesToTransfer = convertToShares(amount_); uint256 allowed = allowance[from_][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from_][msg.sender] = allowed - sharesToTransfer; _balanceOf[from_] -= sharesToTransfer; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { _balanceOf[to_] += sharesToTransfer; } emit Transfer(from_, to_, amount_); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { if (deadline < block.timestamp) revert PermitDeadlineExpired(); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, convertToShares(value), nonces[owner]++, deadline ) ) ) ), v, r, s ); if (recoveredAddress == address(0) || recoveredAddress != owner) revert InvalidSigner(); allowance[recoveredAddress][spender] = convertToShares(value); } emit Approval(owner, spender, convertToShares(value)); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { _totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { _balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { _balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { _totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "./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. */ mapping(bytes32 => mapping(address => bool)) private _permits; /** * @dev Emitted when a role is granted to an address. */ event RoleGranted(bytes32 indexed role, address indexed grantee); /** * @dev Emitted when a role is revoked from an address. */ event RoleRevoked(bytes32 indexed role, address indexed revokee); /** * @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier. */ error NoPermit(bytes32 role); /** * @dev Constructor that sets the owner of the contract. */ constructor(address owner_) Ownable(owner_) {} /** * @dev Modifier that restricts access to addresses having roles * Throws an error if the caller do not have permit */ modifier onlyRole(bytes32 role) { if (!_permits[role][msg.sender]) revert NoPermit(role); _; } /** * @dev Checks and reverts if an address do not have a specific role. * @param role_ The role to check. * @param address_ The address to check. */ function _checkRole(bytes32 role_, address address_) internal virtual { if (!_hasRole(role_, address_)) revert NoPermit(role_); } /** * @dev Grants a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. * Can only be called by the owner of the contract. */ function grantRole( bytes32 role_, address grantee_ ) external virtual onlyOwner { _grantRole(role_, grantee_); } /** * @dev Revokes a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. * Can only be called by the owner of the contract. */ function revokeRole( bytes32 role_, address revokee_ ) external virtual onlyOwner { _revokeRole(role_, revokee_); } /** * @dev Internal function to grant a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. */ function _grantRole(bytes32 role_, address grantee_) internal { _permits[role_][grantee_] = true; emit RoleGranted(role_, grantee_); } /** * @dev Internal function to revoke a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. */ function _revokeRole(bytes32 role_, address revokee_) internal { _permits[role_][revokee_] = false; emit RoleRevoked(role_, revokee_); } /** * @dev Checks whether an address has a specific role. * @param role_ The role to check. * @param address_ The address to check. * @return A boolean value indicating whether or not the address has the role. */ function hasRole( bytes32 role_, address address_ ) external view returns (bool) { return _hasRole(role_, address_); } function _hasRole( bytes32 role_, address address_ ) internal view returns (bool) { return _permits[role_][address_]; } }
pragma solidity 0.8.13; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval( address indexed owner, address indexed spender, uint256 amount ); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol, uint8 _decimals) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve( address spender, uint256 amount ) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer( address to, uint256 amount ) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require( recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER" ); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore( freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000 ) mstore( add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff) ) // Append and mask the "from" argument. mstore( add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or( and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()) ), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer(ERC20 token, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore( freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000 ) mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or( and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()) ), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove(ERC20 token, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore( freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000 ) mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or( and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()) ), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } } error ZeroAddress(); /** * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ library RescueFundsLib { /** * @dev The address used to identify ETH. */ address public constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); /** * @dev thrown when the given token address don't have any code */ error InvalidTokenAddress(); /** * @dev Rescues funds from a contract. * @param token_ The address of the token contract. * @param rescueTo_ The address of the user. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) internal { if (rescueTo_ == address(0)) revert ZeroAddress(); if (token_ == ETH_ADDRESS) { SafeTransferLib.safeTransferETH(rescueTo_, amount_); } else { if (token_.code.length == 0) revert InvalidTokenAddress(); SafeTransferLib.safeTransfer(ERC20(token_), rescueTo_, amount_); } } } /** * @title Ownable * @dev The Ownable contract provides a simple way to manage ownership of a contract * and allows for ownership to be transferred to a nominated address. */ abstract contract Ownable { address private _owner; address private _nominee; event OwnerNominated(address indexed nominee); event OwnerClaimed(address indexed claimer); error OnlyOwner(); error OnlyNominee(); /** * @dev Sets the contract's owner to the address that is passed to the constructor. */ constructor(address owner_) { _claimOwner(owner_); } /** * @dev Modifier that restricts access to only the contract's owner. * Throws an error if the caller is not the owner. */ modifier onlyOwner() { if (msg.sender != _owner) revert OnlyOwner(); _; } /** * @dev Returns the current owner of the contract. */ function owner() external view returns (address) { return _owner; } /** * @dev Returns the current nominee for ownership of the contract. */ function nominee() external view returns (address) { return _nominee; } /** * @dev Allows the current owner to nominate a new owner for the contract. * Throws an error if the caller is not the owner. * Emits an `OwnerNominated` event with the address of the nominee. */ function nominateOwner(address nominee_) external { if (msg.sender != _owner) revert OnlyOwner(); _nominee = nominee_; emit OwnerNominated(_nominee); } /** * @dev Allows the nominated owner to claim ownership of the contract. * Throws an error if the caller is not the nominee. * Sets the nominated owner as the new owner of the contract. * Emits an `OwnerClaimed` event with the address of the new owner. */ function claimOwner() external { if (msg.sender != _nominee) revert OnlyNominee(); _claimOwner(msg.sender); } /** * @dev Internal function that sets the owner of the contract to the specified address * and sets the nominee to address(0). */ function _claimOwner(address claimer_) internal { _owner = claimer_; _nominee = address(0); emit OwnerClaimed(claimer_); } } /** * @title AccessControl * @dev This abstract contract implements access control mechanism based on roles. * Each role can have one or more addresses associated with it, which are granted * permission to execute functions with the onlyRole modifier. */ abstract contract AccessControl is Ownable { /** * @dev A mapping of roles to a mapping of addresses to boolean values indicating whether or not they have the role. */ mapping(bytes32 => mapping(address => bool)) private _permits; /** * @dev Emitted when a role is granted to an address. */ event RoleGranted(bytes32 indexed role, address indexed grantee); /** * @dev Emitted when a role is revoked from an address. */ event RoleRevoked(bytes32 indexed role, address indexed revokee); /** * @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier. */ error NoPermit(bytes32 role); /** * @dev Constructor that sets the owner of the contract. */ constructor(address owner_) Ownable(owner_) {} /** * @dev Modifier that restricts access to addresses having roles * Throws an error if the caller do not have permit */ modifier onlyRole(bytes32 role) { if (!_permits[role][msg.sender]) revert NoPermit(role); _; } /** * @dev Checks and reverts if an address do not have a specific role. * @param role_ The role to check. * @param address_ The address to check. */ function _checkRole(bytes32 role_, address address_) internal virtual { if (!_hasRole(role_, address_)) revert NoPermit(role_); } /** * @dev Grants a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. * Can only be called by the owner of the contract. */ function grantRole( bytes32 role_, address grantee_ ) external virtual onlyOwner { _grantRole(role_, grantee_); } /** * @dev Revokes a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. * Can only be called by the owner of the contract. */ function revokeRole( bytes32 role_, address revokee_ ) external virtual onlyOwner { _revokeRole(role_, revokee_); } /** * @dev Internal function to grant a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. */ function _grantRole(bytes32 role_, address grantee_) internal { _permits[role_][grantee_] = true; emit RoleGranted(role_, grantee_); } /** * @dev Internal function to revoke a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. */ function _revokeRole(bytes32 role_, address revokee_) internal { _permits[role_][revokee_] = false; emit RoleRevoked(role_, revokee_); } /** * @dev Checks whether an address has a specific role. * @param role_ The role to check. * @param address_ The address to check. * @return A boolean value indicating whether or not the address has the role. */ function hasRole( bytes32 role_, address address_ ) external view returns (bool) { return _hasRole(role_, address_); } function _hasRole( bytes32 role_, address address_ ) internal view returns (bool) { return _permits[role_][address_]; } } /** * @title Base contract for super token and vault * @notice It contains relevant execution payload storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ abstract contract RescueBase is AccessControl { bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyRole(RESCUE_ROLE) { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } } contract Faucet is RescueBase { using SafeTransferLib for ERC20; constructor(address token_) AccessControl(msg.sender) { _grantRole(RESCUE_ROLE, msg.sender); } function getTokens(address receiver_, address[] calldata tokens_) external { for (uint256 i = 0; i < tokens_.length; i++) { ERC20 token = ERC20(tokens_[i]); uint256 amount = 10 ** token.decimals() * 1000; token.safeTransfer(receiver_, amount); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "../common/Structs.sol"; abstract contract Gauge { error AmountOutsideLimit(); function _getCurrentLimit( LimitParams storage _params ) internal view returns (uint256 _limit) { uint256 timeElapsed = block.timestamp - _params.lastUpdateTimestamp; uint256 limitIncrease = timeElapsed * _params.ratePerSecond; if (limitIncrease + _params.lastUpdateLimit > _params.maxLimit) { _limit = _params.maxLimit; } else { _limit = limitIncrease + _params.lastUpdateLimit; } } function _consumePartLimit( uint256 amount_, LimitParams storage _params ) internal returns (uint256 consumedAmount, uint256 pendingAmount) { uint256 currentLimit = _getCurrentLimit(_params); _params.lastUpdateTimestamp = block.timestamp; if (currentLimit >= amount_) { _params.lastUpdateLimit = currentLimit - amount_; consumedAmount = amount_; pendingAmount = 0; } else { _params.lastUpdateLimit = 0; consumedAmount = currentLimit; pendingAmount = amount_ - currentLimit; } } function _consumeFullLimit( uint256 amount_, LimitParams storage _params ) internal { uint256 currentLimit = _getCurrentLimit(_params); if (currentLimit >= amount_) { _params.lastUpdateTimestamp = block.timestamp; _params.lastUpdateLimit = currentLimit - amount_; } else { revert AmountOutsideLimit(); } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title Ownable * @dev The Ownable contract provides a simple way to manage ownership of a contract * and allows for ownership to be transferred to a nominated address. */ abstract contract Ownable { address private _owner; address private _nominee; event OwnerNominated(address indexed nominee); event OwnerClaimed(address indexed claimer); error OnlyOwner(); error OnlyNominee(); /** * @dev Sets the contract's owner to the address that is passed to the constructor. */ constructor(address owner_) { _claimOwner(owner_); } /** * @dev Modifier that restricts access to only the contract's owner. * Throws an error if the caller is not the owner. */ modifier onlyOwner() { if (msg.sender != _owner) revert OnlyOwner(); _; } /** * @dev Returns the current owner of the contract. */ function owner() external view returns (address) { return _owner; } /** * @dev Returns the current nominee for ownership of the contract. */ function nominee() external view returns (address) { return _nominee; } /** * @dev Allows the current owner to nominate a new owner for the contract. * Throws an error if the caller is not the owner. * Emits an `OwnerNominated` event with the address of the nominee. */ function nominateOwner(address nominee_) external { if (msg.sender != _owner) revert OnlyOwner(); _nominee = nominee_; emit OwnerNominated(_nominee); } /** * @dev Allows the nominated owner to claim ownership of the contract. * Throws an error if the caller is not the nominee. * Sets the nominated owner as the new owner of the contract. * Emits an `OwnerClaimed` event with the address of the new owner. */ function claimOwner() external { if (msg.sender != _nominee) revert OnlyNominee(); _claimOwner(msg.sender); } /** * @dev Internal function that sets the owner of the contract to the specified address * and sets the nominee to address(0). */ function _claimOwner(address claimer_) internal { _owner = claimer_; _nominee = address(0); emit OwnerClaimed(claimer_); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; import {AccessControl} from "./AccessControl.sol"; /** * @title Base contract for super token and vault * @notice It contains relevant execution payload storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ abstract contract RescueBase is AccessControl { bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyRole(RESCUE_ROLE) { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
{ "optimizer": { "enabled": true, "runs": 999999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"token_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CannotTransferOrExecuteOnBridgeContracts","type":"error"},{"inputs":[],"name":"InsufficientMsgValue","type":"error"},{"inputs":[],"name":"InvalidConnector","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"InvalidTokenContract","type":"error"},{"inputs":[],"name":"MessageIdMisMatched","type":"error"},{"inputs":[],"name":"NoPendingData","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"NoPermit","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAddressReceiver","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"messageId","type":"bytes32"}],"name":"BridgingTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"ConnectorStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newHook","type":"address"}],"name":"HookUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"OwnerClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nominee","type":"address"}],"name":"OwnerNominated","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":"connecter","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"messageId","type":"bytes32"}],"name":"TokensBridged","type":"event"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"},{"internalType":"address","name":"connector_","type":"address"},{"internalType":"bytes","name":"execPayload_","type":"bytes"},{"internalType":"bytes","name":"options_","type":"bytes"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"bridgeType","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"connectorCache","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"},{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"},{"internalType":"uint256","name":"payloadSize_","type":"uint256"}],"name":"getMinFees","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"}],"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":[],"name":"hook__","outputs":[{"internalType":"contract IHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"identifierCache","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nominee_","type":"address"}],"name":"nominateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"},{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"receiveInbound","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":"address","name":"connector_","type":"address"},{"internalType":"bytes32","name":"messageId_","type":"bytes32"}],"name":"retry","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":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"connectors","type":"address[]"},{"internalType":"bool[]","name":"statuses","type":"bool[]"}],"name":"updateConnectorStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hook_","type":"address"},{"internalType":"bool","name":"approve_","type":"bool"}],"name":"updateHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"validConnectors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405260016000553480156200001657600080fd5b506040516200358e3803806200358e8339810160408190526200003991620001b2565b803380620000478162000103565b50506001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee148015906200007f57506001600160a01b0381163b155b156200009e57604051630a6f7ecd60e21b815260040160405180910390fd5b6001600160a01b038116608052620000d77fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292533362000157565b50507ff429e6a62fa0281dc1be2d92b346977a47bde9521f323f08f996b64bdbabd5f3600455620001e4565b600180546001600160a01b0383166001600160a01b031991821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60008281526003602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b600060208284031215620001c557600080fd5b81516001600160a01b0381168114620001dd57600080fd5b9392505050565b60805161334f6200023f6000396000818161042e01528181610b0b01528181610bab01528181610c2501528181610c6d015281816111c9015281816113a601528181611503015281816118bf0152611b14015261334f6000f3fe60806040526004361061016a5760003560e01c80638da5cb5b116100cb578063d547741f1161007f578063f290aafa11610059578063f290aafa146103fc578063fc0c546a1461041c578063fc3a7b981461045057600080fd5b8063d547741f1461038c578063e272ad3f146103ac578063e9ee1eaf146103cc57600080fd5b80639dc7b023116100b05780639dc7b02314610336578063a2309ff814610356578063aad48d801461036c57600080fd5b80638da5cb5b146102db57806391d148541461030657600080fd5b80634b0a8854116101225780636ccae054116101075780636ccae0541461027b57806370bab2c01461029b578063873ea755146102c857600080fd5b80634b0a88541461022e5780635b94db271461025b57600080fd5b80632f2ff15d116101535780632f2ff15d146101e45780633bd1adec14610206578063405e720a1461021b57600080fd5b806320f99c0a1461016f5780632421e155146101c0575b600080fd5b34801561017b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101cc57600080fd5b506101d660045481565b6040519081526020016101b7565b3480156101f057600080fd5b506102046101ff366004612696565b610470565b005b34801561021257600080fd5b506102046104cf565b610204610229366004612708565b61052b565b34801561023a57600080fd5b5061024e6102493660046127b0565b61069a565b6040516101b79190612843565b34801561026757600080fd5b506102046102763660046127b0565b610734565b34801561028757600080fd5b50610204610296366004612856565b6107f4565b3480156102a757600080fd5b506005546101969073ffffffffffffffffffffffffffffffffffffffff1681565b6102046102d636600461295b565b610892565b3480156102e757600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16610196565b34801561031257600080fd5b50610326610321366004612696565b6109b7565b60405190151581526020016101b7565b34801561034257600080fd5b506102046103513660046129f7565b6109f2565b34801561036257600080fd5b506101d660095481565b34801561037857600080fd5b50610204610387366004612a38565b610ab8565b34801561039857600080fd5b506102046103a7366004612696565b610d2d565b3480156103b857600080fd5b506102046103c7366004612ab2565b610d88565b3480156103d857600080fd5b506103266103e73660046127b0565b60086020526000908152604090205460ff1681565b34801561040857600080fd5b5061024e610417366004612b1e565b610f42565b34801561042857600080fd5b506101967f000000000000000000000000000000000000000000000000000000000000000081565b34801561045c57600080fd5b506101d661046b366004612b37565b610f5b565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104c1576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282610ffc565b5050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610520576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61052933611082565b565b60005460011461059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b60026000819055506000806106158760405180606001604052808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c815260200189898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506110fb565b9150915081602001516009600082825461062f9190612b9b565b9250508190555061064433836020015161135a565b610689888886868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508792508891506113ff9050565b505060016000555050505050505050565b600760205260009081526040902080546106b390612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546106df90612bb2565b801561072c5780601f106107015761010080835404028352916020019161072c565b820191906000526020600020905b81548152906001019060200180831161070f57829003601f168201915b505050505081565b60015473ffffffffffffffffffffffffffffffffffffffff163314610785576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b3360009081527f271b3e2292ab6fd3ff496cd98d6d375af02f11568a701741f48bba7789f13a7060205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16610881576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610593565b61088c848484611744565b50505050565b6000546001146108fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600081905550600080600080848060200190518101906109209190612c4a565b9350935093509350600060405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200185815260200183815250905060606109698883611839565b805160208201519194509192506109809190611abc565b8160200151600960008282546109969190612caf565b909155506109a8905085858385611b43565b50506001600055505050505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff165b9392505050565b600054600114610a5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600090815580610a708484611d7f565b91509150610a8681600001518260200151611abc565b806020015160096000828254610a9c9190612caf565b90915550610aad9050848484612087565b505060016000555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b09576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610cb3576005546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91821660248201526000917f0000000000000000000000000000000000000000000000000000000000000000169063dd62ed3e90604401602060405180830381865afa158015610bf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c169190612cc7565b1115610c6257600554610c62907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff166000612342565b8015610cb357610cb37f0000000000000000000000000000000000000000000000000000000000000000837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612342565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527fe816c20840d998c8612f9b624b91687a80510eeb293cb09f7637379f6d73342d9060200160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d7e576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282612411565b60015473ffffffffffffffffffffffffffffffffffffffff163314610dd9576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610f3a57838382818110610df757610df7612ce0565b9050602002016020810190610e0c9190612d0f565b60086000888885818110610e2257610e22612ce0565b9050602002016020810190610e3791906127b0565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790557f857309f1a328784e9fc9749624be5d32fd8e7afab58e7fe9a218dd613a37f15c868683818110610ebd57610ebd612ce0565b9050602002016020810190610ed291906127b0565b858584818110610ee457610ee4612ce0565b9050602002016020810190610ef99190612d0f565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835290151560208301520160405180910390a180610f3281612d2a565b915050610ddd565b505050505050565b600660205260009081526040902080546106b390612bb2565b6040517f666758ca000000000000000000000000000000000000000000000000000000008152600481018390526024810182905260009073ffffffffffffffffffffffffffffffffffffffff85169063666758ca90604401602060405180830381865afa158015610fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff49190612cc7565b949350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60408051606080820183526000808352602083015291810191909152815160609073ffffffffffffffffffffffffffffffffffffffff16611168576040517f96bbcf1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff166111c7576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480156112235750826020015134105b1561125a576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611353576005546040805160608101825273ffffffffffffffffffffffffffffffffffffffff878116825233602083015281830187905291517ff59ad990000000000000000000000000000000000000000000000000000000008152919092169163f59ad990916112e89190600401612da1565b6000604051808303816000875af1158015611307573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d9190810190612e6f565b90925090505b9250929050565b6040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f00000000000000000000000000000000000000000000000000000000000000001690639dc29fac906044015b600060405180830381600087803b1580156113eb57600080fd5b505af1158015610f3a573d6000803e3d6000fd5b600554819073ffffffffffffffffffffffffffffffffffffffff16156114ff576005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820186905291517f62811bf200000000000000000000000000000000000000000000000000000000815291909216916362811bf2916114979190600401612ed3565b6000604051808303816000875af11580156114b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526114fc9190810190612f72565b90505b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146115585734611567565b60208201516115679034612b9b565b905060008673ffffffffffffffffffffffffffffffffffffffff166374fa24a66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115da9190612cc7565b905060008773ffffffffffffffffffffffffffffffffffffffff1663ac0710cb848b87600001518860200151878a6040015160405160200161161f9493929190612fa7565b6040516020818303038152906040528b6040518563ffffffff1660e01b815260040161164d93929190612fec565b60206040518083038185885af115801561166b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116909190612cc7565b90508181146116cb576040517f7b7bbbe000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83516020808601516040805173ffffffffffffffffffffffffffffffffffffffff8d811682523394820194909452929093168284015260608201526080810184905290517fc74a00177d2c63e6eead5ea7936974ad9d0121f86140723b8909f8ec9662cc619181900360a00190a1505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216611791576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8416016117dd576117d88282612494565b505050565b8273ffffffffffffffffffffffffffffffffffffffff163b60000361182e576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117d8838383612509565b6040805160608181018352600080835260208301529181018290523360009081526008602052604090205460ff1661189d576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825173ffffffffffffffffffffffffffffffffffffffff1630148061191157507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff16145b15611948576040517f285c601600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611353576005546040805160608101825233808252600090815260076020908152929020805473ffffffffffffffffffffffffffffffffffffffff9094169363cf36b91793830191906119b490612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546119e090612bb2565b8015611a2d5780601f10611a0257610100808354040283529160200191611a2d565b820191906000526020600020905b815481529060010190602001808311611a1057829003601f168201915b50505050508152602001868152506040518263ffffffff1660e01b8152600401611a579190613017565b6000604051808303816000875af1158015611a76573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d919081019061308e565b80600003611ac8575050565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f000000000000000000000000000000000000000000000000000000000000000016906340c10f19906044016113d1565b60055473ffffffffffffffffffffffffffffffffffffffff1615611d15576005546040805160a0810182523380825260208083018890526000918252600790528281208054919473ffffffffffffffffffffffffffffffffffffffff169363dd19fe3893929083019190611bb690612bb2565b80601f0160208091040260200160405190810160405280929190818152602001828054611be290612bb2565b8015611c2f5780601f10611c0457610100808354040283529160200191611c2f565b820191906000526020600020905b815481529060010190602001808311611c1257829003601f168201915b50505050508152602001868152602001858152506040518263ffffffff1660e01b8152600401611c5f91906130e8565b6000604051808303816000875af1158015611c7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611cc49190810190613188565b805160008681526006602090815260409091208251939450611cec93909291909101906125d8565b5060208082015133600090815260078352604090208151611d12939192909101906125d8565b50505b80516020808301516040805133815273ffffffffffffffffffffffffffffffffffffffff909416928401929092528282015260608201859052517f9afd47907e25028cdaca89d193518c302bbb128617d5a992c5abd458155265939181900360800190a150505050565b60408051606081810183526000808352602083015291810182905273ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff16611df9576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518082018252600085815260066020529182208054829190611e1d90612bb2565b80601f0160208091040260200160405190810160405280929190818152602001828054611e4990612bb2565b8015611e965780601f10611e6b57610100808354040283529160200191611e96565b820191906000526020600020905b815481529060010190602001808311611e7957829003601f168201915b50505050508152602001600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054611eea90612bb2565b80601f0160208091040260200160405190810160405280929190818152602001828054611f1690612bb2565b8015611f635780601f10611f3857610100808354040283529160200191611f63565b820191906000526020600020905b815481529060010190602001808311611f4657829003601f168201915b50505050508152509050806000015151600003611fac576040517fd3d38f6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460408051808201825273ffffffffffffffffffffffffffffffffffffffff88811682526020820185905291517f7afb99530000000000000000000000000000000000000000000000000000000081529190921691637afb995391612016919060040161325d565b6000604051808303816000875af1158015612035573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261207b919081019061308e565b90969095509350505050565b6040805180820182526000848152600660205291822080548291906120ab90612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546120d790612bb2565b80156121245780601f106120f957610100808354040283529160200191612124565b820191906000526020600020905b81548152906001019060200180831161210757829003601f168201915b50505050508152602001600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805461217890612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546121a490612bb2565b80156121f15780601f106121c6576101008083540402835291602001916121f1565b820191906000526020600020905b8154815290600101906020018083116121d457829003601f168201915b5050509190925250506005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820185905291517f1306ac3b000000000000000000000000000000000000000000000000000000008152939450911691631306ac3b9161227291600401613298565b6000604051808303816000875af1158015612291573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526122d79190810190613188565b8051600085815260066020908152604090912082519394506122ff93909291909101906125d8565b5060208082015173ffffffffffffffffffffffffffffffffffffffff861660009081526007835260409020815161233b939192909101906125d8565b5050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610593565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b600080600080600085875af19050806117d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610593565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610593565b8280546125e490612bb2565b90600052602060002090601f016020900481019282612606576000855561264c565b82601f1061261f57805160ff191683800117855561264c565b8280016001018555821561264c579182015b8281111561264c578251825591602001919060010190612631565b5061265892915061265c565b5090565b5b80821115612658576000815560010161265d565b73ffffffffffffffffffffffffffffffffffffffff8116811461269357600080fd5b50565b600080604083850312156126a957600080fd5b8235915060208301356126bb81612671565b809150509250929050565b60008083601f8401126126d857600080fd5b50813567ffffffffffffffff8111156126f057600080fd5b60208301915083602082850101111561135357600080fd5b60008060008060008060008060c0898b03121561272457600080fd5b883561272f81612671565b97506020890135965060408901359550606089013561274d81612671565b9450608089013567ffffffffffffffff8082111561276a57600080fd5b6127768c838d016126c6565b909650945060a08b013591508082111561278f57600080fd5b5061279c8b828c016126c6565b999c989b5096995094979396929594505050565b6000602082840312156127c257600080fd5b81356109eb81612671565b60005b838110156127e85781810151838201526020016127d0565b8381111561088c5750506000910152565b600081518084526128118160208601602086016127cd565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006109eb60208301846127f9565b60008060006060848603121561286b57600080fd5b833561287681612671565b9250602084013561288681612671565b929592945050506040919091013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561290d5761290d612897565b604052919050565b600067ffffffffffffffff82111561292f5761292f612897565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000806040838503121561296e57600080fd5b823563ffffffff8116811461298257600080fd5b9150602083013567ffffffffffffffff81111561299e57600080fd5b8301601f810185136129af57600080fd5b80356129c26129bd82612915565b6128c6565b8181528660208385010111156129d757600080fd5b816020840160208301376000602083830101528093505050509250929050565b60008060408385031215612a0a57600080fd5b8235612a1581612671565b946020939093013593505050565b80358015158114612a3357600080fd5b919050565b60008060408385031215612a4b57600080fd5b8235612a5681612671565b9150612a6460208401612a23565b90509250929050565b60008083601f840112612a7f57600080fd5b50813567ffffffffffffffff811115612a9757600080fd5b6020830191508360208260051b850101111561135357600080fd5b60008060008060408587031215612ac857600080fd5b843567ffffffffffffffff80821115612ae057600080fd5b612aec88838901612a6d565b90965094506020870135915080821115612b0557600080fd5b50612b1287828801612a6d565b95989497509550505050565b600060208284031215612b3057600080fd5b5035919050565b600080600060608486031215612b4c57600080fd5b8335612b5781612671565b95602085013595506040909401359392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612bad57612bad612b6c565b500390565b600181811c90821680612bc657607f821691505b602082108103612bff577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f830112612c1657600080fd5b8151612c246129bd82612915565b818152846020838601011115612c3957600080fd5b610ff48260208301602087016127cd565b60008060008060808587031215612c6057600080fd5b8451612c6b81612671565b809450506020850151925060408501519150606085015167ffffffffffffffff811115612c9757600080fd5b612ca387828801612c05565b91505092959194509250565b60008219821115612cc257612cc2612b6c565b500190565b600060208284031215612cd957600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215612d2157600080fd5b6109eb82612a23565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612d5b57612d5b612b6c565b5060010190565b73ffffffffffffffffffffffffffffffffffffffff8151168252602081015160208301526000604082015160606040850152610ff460608501826127f9565b60208152600073ffffffffffffffffffffffffffffffffffffffff808451166020840152806020850151166040840152506040830151606080840152610ff46080840182612d62565b600060608284031215612dfc57600080fd5b6040516060810167ffffffffffffffff8282108183111715612e2057612e20612897565b8160405282935084519150612e3482612671565b818352602085015160208401526040850151915080821115612e5557600080fd5b50612e6285828601612c05565b6040830152505092915050565b60008060408385031215612e8257600080fd5b825167ffffffffffffffff80821115612e9a57600080fd5b612ea686838701612dea565b93506020850151915080821115612ebc57600080fd5b50612ec985828601612c05565b9150509250929050565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526000602083015160806040840152612f0f60a08401826127f9565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080858403016060860152612f4b83836127f9565b9250606086015191508085840301608086015250612f698282612d62565b95945050505050565b600060208284031215612f8457600080fd5b815167ffffffffffffffff811115612f9b57600080fd5b610ff484828501612dea565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152826040820152608060608201526000612fe260808301846127f9565b9695505050505050565b83815260606020820152600061300560608301856127f9565b8281036040840152612fe281856127f9565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152600060208301516060604084015261305360808401826127f9565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016060850152612f698282612d62565b600080604083850312156130a157600080fd5b825167ffffffffffffffff808211156130b957600080fd5b6130c586838701612c05565b935060208501519150808211156130db57600080fd5b50612ec985828601612dea565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152602082015160408201526000604083015160a0606084015261312e60c08401826127f9565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08085840301608086015261316a83836127f9565b925060808601519150808584030160a086015250612f698282612d62565b60006020828403121561319a57600080fd5b815167ffffffffffffffff808211156131b257600080fd5b90830190604082860312156131c657600080fd5b6040516040810181811083821117156131e1576131e1612897565b6040528251828111156131f357600080fd5b6131ff87828601612c05565b82525060208301518281111561321457600080fd5b61322087828601612c05565b60208301525095945050505050565b600081516040845261324460408501826127f9565b905060208301518482036020860152612f6982826127f9565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152610ff4606084018261322f565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526020820151604082015260006040830151608060608401526132de60a08401826127f9565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016080850152612f69828261322f56fea2646970667358221220f9ab5ef8b398d2fedef050a1e75c4cac22bdcce7f3e95e073dd82deb5340699264736f6c634300080d0033000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d7
Deployed Bytecode
0x60806040526004361061016a5760003560e01c80638da5cb5b116100cb578063d547741f1161007f578063f290aafa11610059578063f290aafa146103fc578063fc0c546a1461041c578063fc3a7b981461045057600080fd5b8063d547741f1461038c578063e272ad3f146103ac578063e9ee1eaf146103cc57600080fd5b80639dc7b023116100b05780639dc7b02314610336578063a2309ff814610356578063aad48d801461036c57600080fd5b80638da5cb5b146102db57806391d148541461030657600080fd5b80634b0a8854116101225780636ccae054116101075780636ccae0541461027b57806370bab2c01461029b578063873ea755146102c857600080fd5b80634b0a88541461022e5780635b94db271461025b57600080fd5b80632f2ff15d116101535780632f2ff15d146101e45780633bd1adec14610206578063405e720a1461021b57600080fd5b806320f99c0a1461016f5780632421e155146101c0575b600080fd5b34801561017b57600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101cc57600080fd5b506101d660045481565b6040519081526020016101b7565b3480156101f057600080fd5b506102046101ff366004612696565b610470565b005b34801561021257600080fd5b506102046104cf565b610204610229366004612708565b61052b565b34801561023a57600080fd5b5061024e6102493660046127b0565b61069a565b6040516101b79190612843565b34801561026757600080fd5b506102046102763660046127b0565b610734565b34801561028757600080fd5b50610204610296366004612856565b6107f4565b3480156102a757600080fd5b506005546101969073ffffffffffffffffffffffffffffffffffffffff1681565b6102046102d636600461295b565b610892565b3480156102e757600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff16610196565b34801561031257600080fd5b50610326610321366004612696565b6109b7565b60405190151581526020016101b7565b34801561034257600080fd5b506102046103513660046129f7565b6109f2565b34801561036257600080fd5b506101d660095481565b34801561037857600080fd5b50610204610387366004612a38565b610ab8565b34801561039857600080fd5b506102046103a7366004612696565b610d2d565b3480156103b857600080fd5b506102046103c7366004612ab2565b610d88565b3480156103d857600080fd5b506103266103e73660046127b0565b60086020526000908152604090205460ff1681565b34801561040857600080fd5b5061024e610417366004612b1e565b610f42565b34801561042857600080fd5b506101967f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d781565b34801561045c57600080fd5b506101d661046b366004612b37565b610f5b565b60015473ffffffffffffffffffffffffffffffffffffffff1633146104c1576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282610ffc565b5050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610520576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61052933611082565b565b60005460011461059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b60026000819055506000806106158760405180606001604052808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c815260200189898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506110fb565b9150915081602001516009600082825461062f9190612b9b565b9250508190555061064433836020015161135a565b610689888886868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508792508891506113ff9050565b505060016000555050505050505050565b600760205260009081526040902080546106b390612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546106df90612bb2565b801561072c5780601f106107015761010080835404028352916020019161072c565b820191906000526020600020905b81548152906001019060200180831161070f57829003601f168201915b505050505081565b60015473ffffffffffffffffffffffffffffffffffffffff163314610785576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b3360009081527f271b3e2292ab6fd3ff496cd98d6d375af02f11568a701741f48bba7789f13a7060205260409020547fc4c453d647953c0fd35db5a34ee76e60fb4abc3a8fb891a25936b70b38f292539060ff16610881576040517f962f633300000000000000000000000000000000000000000000000000000000815260048101829052602401610593565b61088c848484611744565b50505050565b6000546001146108fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600081905550600080600080848060200190518101906109209190612c4a565b9350935093509350600060405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200185815260200183815250905060606109698883611839565b805160208201519194509192506109809190611abc565b8160200151600960008282546109969190612caf565b909155506109a8905085858385611b43565b50506001600055505050505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff165b9392505050565b600054600114610a5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610593565b6002600090815580610a708484611d7f565b91509150610a8681600001518260200151611abc565b806020015160096000828254610a9c9190612caf565b90915550610aad9050848484612087565b505060016000555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b09576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d773ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610cb3576005546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91821660248201526000917f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d7169063dd62ed3e90604401602060405180830381865afa158015610bf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c169190612cc7565b1115610c6257600554610c62907f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d79073ffffffffffffffffffffffffffffffffffffffff166000612342565b8015610cb357610cb37f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d7837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612342565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527fe816c20840d998c8612f9b624b91687a80510eeb293cb09f7637379f6d73342d9060200160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d7e576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104cb8282612411565b60015473ffffffffffffffffffffffffffffffffffffffff163314610dd9576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b81811015610f3a57838382818110610df757610df7612ce0565b9050602002016020810190610e0c9190612d0f565b60086000888885818110610e2257610e22612ce0565b9050602002016020810190610e3791906127b0565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790557f857309f1a328784e9fc9749624be5d32fd8e7afab58e7fe9a218dd613a37f15c868683818110610ebd57610ebd612ce0565b9050602002016020810190610ed291906127b0565b858584818110610ee457610ee4612ce0565b9050602002016020810190610ef99190612d0f565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835290151560208301520160405180910390a180610f3281612d2a565b915050610ddd565b505050505050565b600660205260009081526040902080546106b390612bb2565b6040517f666758ca000000000000000000000000000000000000000000000000000000008152600481018390526024810182905260009073ffffffffffffffffffffffffffffffffffffffff85169063666758ca90604401602060405180830381865afa158015610fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff49190612cc7565b949350505050565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905551909184917f2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f39190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556002805490911690556040517ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8790600090a250565b60408051606080820183526000808352602083015291810191909152815160609073ffffffffffffffffffffffffffffffffffffffff16611168576040517f96bbcf1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff166111c7576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d773ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480156112235750826020015134105b1561125a576040517f78f38f7600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611353576005546040805160608101825273ffffffffffffffffffffffffffffffffffffffff878116825233602083015281830187905291517ff59ad990000000000000000000000000000000000000000000000000000000008152919092169163f59ad990916112e89190600401612da1565b6000604051808303816000875af1158015611307573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d9190810190612e6f565b90925090505b9250929050565b6040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d71690639dc29fac906044015b600060405180830381600087803b1580156113eb57600080fd5b505af1158015610f3a573d6000803e3d6000fd5b600554819073ffffffffffffffffffffffffffffffffffffffff16156114ff576005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820186905291517f62811bf200000000000000000000000000000000000000000000000000000000815291909216916362811bf2916114979190600401612ed3565b6000604051808303816000875af11580156114b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526114fc9190810190612f72565b90505b60007f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d773ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146115585734611567565b60208201516115679034612b9b565b905060008673ffffffffffffffffffffffffffffffffffffffff166374fa24a66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115da9190612cc7565b905060008773ffffffffffffffffffffffffffffffffffffffff1663ac0710cb848b87600001518860200151878a6040015160405160200161161f9493929190612fa7565b6040516020818303038152906040528b6040518563ffffffff1660e01b815260040161164d93929190612fec565b60206040518083038185885af115801561166b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116909190612cc7565b90508181146116cb576040517f7b7bbbe000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83516020808601516040805173ffffffffffffffffffffffffffffffffffffffff8d811682523394820194909452929093168284015260608201526080810184905290517fc74a00177d2c63e6eead5ea7936974ad9d0121f86140723b8909f8ec9662cc619181900360a00190a1505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216611791576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8416016117dd576117d88282612494565b505050565b8273ffffffffffffffffffffffffffffffffffffffff163b60000361182e576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117d8838383612509565b6040805160608181018352600080835260208301529181018290523360009081526008602052604090205460ff1661189d576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825173ffffffffffffffffffffffffffffffffffffffff1630148061191157507f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d773ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff16145b15611948576040517f285c601600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055473ffffffffffffffffffffffffffffffffffffffff1615611353576005546040805160608101825233808252600090815260076020908152929020805473ffffffffffffffffffffffffffffffffffffffff9094169363cf36b91793830191906119b490612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546119e090612bb2565b8015611a2d5780601f10611a0257610100808354040283529160200191611a2d565b820191906000526020600020905b815481529060010190602001808311611a1057829003601f168201915b50505050508152602001868152506040518263ffffffff1660e01b8152600401611a579190613017565b6000604051808303816000875af1158015611a76573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261134d919081019061308e565b80600003611ac8575050565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390527f000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d716906340c10f19906044016113d1565b60055473ffffffffffffffffffffffffffffffffffffffff1615611d15576005546040805160a0810182523380825260208083018890526000918252600790528281208054919473ffffffffffffffffffffffffffffffffffffffff169363dd19fe3893929083019190611bb690612bb2565b80601f0160208091040260200160405190810160405280929190818152602001828054611be290612bb2565b8015611c2f5780601f10611c0457610100808354040283529160200191611c2f565b820191906000526020600020905b815481529060010190602001808311611c1257829003601f168201915b50505050508152602001868152602001858152506040518263ffffffff1660e01b8152600401611c5f91906130e8565b6000604051808303816000875af1158015611c7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611cc49190810190613188565b805160008681526006602090815260409091208251939450611cec93909291909101906125d8565b5060208082015133600090815260078352604090208151611d12939192909101906125d8565b50505b80516020808301516040805133815273ffffffffffffffffffffffffffffffffffffffff909416928401929092528282015260608201859052517f9afd47907e25028cdaca89d193518c302bbb128617d5a992c5abd458155265939181900360800190a150505050565b60408051606081810183526000808352602083015291810182905273ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604090205460ff16611df9576040517f5b0a758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518082018252600085815260066020529182208054829190611e1d90612bb2565b80601f0160208091040260200160405190810160405280929190818152602001828054611e4990612bb2565b8015611e965780601f10611e6b57610100808354040283529160200191611e96565b820191906000526020600020905b815481529060010190602001808311611e7957829003601f168201915b50505050508152602001600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054611eea90612bb2565b80601f0160208091040260200160405190810160405280929190818152602001828054611f1690612bb2565b8015611f635780601f10611f3857610100808354040283529160200191611f63565b820191906000526020600020905b815481529060010190602001808311611f4657829003601f168201915b50505050508152509050806000015151600003611fac576040517fd3d38f6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460408051808201825273ffffffffffffffffffffffffffffffffffffffff88811682526020820185905291517f7afb99530000000000000000000000000000000000000000000000000000000081529190921691637afb995391612016919060040161325d565b6000604051808303816000875af1158015612035573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261207b919081019061308e565b90969095509350505050565b6040805180820182526000848152600660205291822080548291906120ab90612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546120d790612bb2565b80156121245780601f106120f957610100808354040283529160200191612124565b820191906000526020600020905b81548152906001019060200180831161210757829003601f168201915b50505050508152602001600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805461217890612bb2565b80601f01602080910402602001604051908101604052809291908181526020018280546121a490612bb2565b80156121f15780601f106121c6576101008083540402835291602001916121f1565b820191906000526020600020905b8154815290600101906020018083116121d457829003601f168201915b5050509190925250506005546040805160808101825273ffffffffffffffffffffffffffffffffffffffff8881168252602082018890528183018790526060820185905291517f1306ac3b000000000000000000000000000000000000000000000000000000008152939450911691631306ac3b9161227291600401613298565b6000604051808303816000875af1158015612291573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526122d79190810190613188565b8051600085815260066020908152604090912082519394506122ff93909291909101906125d8565b5060208082015173ffffffffffffffffffffffffffffffffffffffff861660009081526007835260409020815161233b939192909101906125d8565b5050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610593565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551909184917f155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a529190a35050565b600080600080600085875af19050806117d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152606401610593565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061088c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610593565b8280546125e490612bb2565b90600052602060002090601f016020900481019282612606576000855561264c565b82601f1061261f57805160ff191683800117855561264c565b8280016001018555821561264c579182015b8281111561264c578251825591602001919060010190612631565b5061265892915061265c565b5090565b5b80821115612658576000815560010161265d565b73ffffffffffffffffffffffffffffffffffffffff8116811461269357600080fd5b50565b600080604083850312156126a957600080fd5b8235915060208301356126bb81612671565b809150509250929050565b60008083601f8401126126d857600080fd5b50813567ffffffffffffffff8111156126f057600080fd5b60208301915083602082850101111561135357600080fd5b60008060008060008060008060c0898b03121561272457600080fd5b883561272f81612671565b97506020890135965060408901359550606089013561274d81612671565b9450608089013567ffffffffffffffff8082111561276a57600080fd5b6127768c838d016126c6565b909650945060a08b013591508082111561278f57600080fd5b5061279c8b828c016126c6565b999c989b5096995094979396929594505050565b6000602082840312156127c257600080fd5b81356109eb81612671565b60005b838110156127e85781810151838201526020016127d0565b8381111561088c5750506000910152565b600081518084526128118160208601602086016127cd565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006109eb60208301846127f9565b60008060006060848603121561286b57600080fd5b833561287681612671565b9250602084013561288681612671565b929592945050506040919091013590565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561290d5761290d612897565b604052919050565b600067ffffffffffffffff82111561292f5761292f612897565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000806040838503121561296e57600080fd5b823563ffffffff8116811461298257600080fd5b9150602083013567ffffffffffffffff81111561299e57600080fd5b8301601f810185136129af57600080fd5b80356129c26129bd82612915565b6128c6565b8181528660208385010111156129d757600080fd5b816020840160208301376000602083830101528093505050509250929050565b60008060408385031215612a0a57600080fd5b8235612a1581612671565b946020939093013593505050565b80358015158114612a3357600080fd5b919050565b60008060408385031215612a4b57600080fd5b8235612a5681612671565b9150612a6460208401612a23565b90509250929050565b60008083601f840112612a7f57600080fd5b50813567ffffffffffffffff811115612a9757600080fd5b6020830191508360208260051b850101111561135357600080fd5b60008060008060408587031215612ac857600080fd5b843567ffffffffffffffff80821115612ae057600080fd5b612aec88838901612a6d565b90965094506020870135915080821115612b0557600080fd5b50612b1287828801612a6d565b95989497509550505050565b600060208284031215612b3057600080fd5b5035919050565b600080600060608486031215612b4c57600080fd5b8335612b5781612671565b95602085013595506040909401359392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612bad57612bad612b6c565b500390565b600181811c90821680612bc657607f821691505b602082108103612bff577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f830112612c1657600080fd5b8151612c246129bd82612915565b818152846020838601011115612c3957600080fd5b610ff48260208301602087016127cd565b60008060008060808587031215612c6057600080fd5b8451612c6b81612671565b809450506020850151925060408501519150606085015167ffffffffffffffff811115612c9757600080fd5b612ca387828801612c05565b91505092959194509250565b60008219821115612cc257612cc2612b6c565b500190565b600060208284031215612cd957600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215612d2157600080fd5b6109eb82612a23565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612d5b57612d5b612b6c565b5060010190565b73ffffffffffffffffffffffffffffffffffffffff8151168252602081015160208301526000604082015160606040850152610ff460608501826127f9565b60208152600073ffffffffffffffffffffffffffffffffffffffff808451166020840152806020850151166040840152506040830151606080840152610ff46080840182612d62565b600060608284031215612dfc57600080fd5b6040516060810167ffffffffffffffff8282108183111715612e2057612e20612897565b8160405282935084519150612e3482612671565b818352602085015160208401526040850151915080821115612e5557600080fd5b50612e6285828601612c05565b6040830152505092915050565b60008060408385031215612e8257600080fd5b825167ffffffffffffffff80821115612e9a57600080fd5b612ea686838701612dea565b93506020850151915080821115612ebc57600080fd5b50612ec985828601612c05565b9150509250929050565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526000602083015160806040840152612f0f60a08401826127f9565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080858403016060860152612f4b83836127f9565b9250606086015191508085840301608086015250612f698282612d62565b95945050505050565b600060208284031215612f8457600080fd5b815167ffffffffffffffff811115612f9b57600080fd5b610ff484828501612dea565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152826040820152608060608201526000612fe260808301846127f9565b9695505050505050565b83815260606020820152600061300560608301856127f9565b8281036040840152612fe281856127f9565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152600060208301516060604084015261305360808401826127f9565b905060408401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016060850152612f698282612d62565b600080604083850312156130a157600080fd5b825167ffffffffffffffff808211156130b957600080fd5b6130c586838701612c05565b935060208501519150808211156130db57600080fd5b50612ec985828601612dea565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152602082015160408201526000604083015160a0606084015261312e60c08401826127f9565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08085840301608086015261316a83836127f9565b925060808601519150808584030160a086015250612f698282612d62565b60006020828403121561319a57600080fd5b815167ffffffffffffffff808211156131b257600080fd5b90830190604082860312156131c657600080fd5b6040516040810181811083821117156131e1576131e1612897565b6040528251828111156131f357600080fd5b6131ff87828601612c05565b82525060208301518281111561321457600080fd5b61322087828601612c05565b60208301525095945050505050565b600081516040845261324460408501826127f9565b905060208301518482036020860152612f6982826127f9565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152610ff4606084018261322f565b6020815273ffffffffffffffffffffffffffffffffffffffff82511660208201526020820151604082015260006040830151608060608401526132de60a08401826127f9565b905060608401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016080850152612f69828261322f56fea2646970667358221220f9ab5ef8b398d2fedef050a1e75c4cac22bdcce7f3e95e073dd82deb5340699264736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d7
-----Decoded View---------------
Arg [0] : token_ (address): 0xE9A12fB15cC00b59867E4E2f0aCbdCebfd32b3d7
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000e9a12fb15cc00b59867e4e2f0acbdcebfd32b3d7
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ 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.