ExecutionHelper

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/bridge/Base.sol": {
      "content": "// SPDX-License-Identifier: MIT
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_)
            );
        } else {
            transferInfo = transferInfo_;
        }
    }

    /**
     * @notice Executes post-bridge operations after completing a token bridge transfer.
     * @dev This internal function is called after completing a token bridge transfer.
     * It executes the source post-hook call if a hook contract is defined, calculates fees,
     * calls the outbound function of the connector, and emits an event for tokens withdrawn.
     * @param msgGasLimit_ The gas limit for the outbound call.
     * @param connector_ The address of the connector responsible for the transfer.
     * @param options_ Additional options for the outbound call.
     * @param postHookData_ Data returned from the source post-hook call.
     * @param transferInfo_ Information about the transfer.
     * @dev Reverts with `MessageIdMisMatched` if the returned message ID does not match the expected message ID.
     */
    function _afterBridge(
        uint256 msgGasLimit_,
        address connector_,
        bytes memory options_,
        bytes memory postHookData_,
        TransferInfo memory transferInfo_
    ) internal {
        TransferInfo memory transferInfo = transferInfo_;
        if (address(hook__) != address(0)) {
            transferInfo = hook__.srcPostHookCall(
                SrcPostHookCallParams(
                    connector_,
                    options_,
                    postHookData_,
                    transferInfo_
                )
            );
        }

        uint256 fees = token == ETH_ADDRESS
            ? msg.value - transferInfo.amount
            : msg.value;

        bytes32 messageId = IConnector(connector_).getMessageId();
        bytes32 returnedMessageId = IConnector(connector_).outbound{
            value: fees
        }(
            msgGasLimit_,
            abi.encode(
                transferInfo.receiver,
                transferInfo.amount,
                messageId,
                transferInfo.extraData
            ),
            options_
        );
        if (returnedMessageId != messageId) revert MessageIdMisMatched();

        emit BridgingTokens(
            connector_,
            msg.sender,
            transferInfo.receiver,
            transferInfo.amount,
            messageId
        );
    }

    /**
     * @notice Executes pre-mint operations before minting tokens.
     * @dev This internal function is called before minting tokens.
     * It validates the caller as a valid connector, checks if the receiver is not this contract, the bridge contract,
     * or the token contract, and executes the destination pre-hook call if a hook contract is defined.
     * @param transferInfo_ Information about the transfer.
     * @return postHookData Data returned from the destination pre-hook call.
     * @return transferInfo Information about the transfer after pre-mint operations.
     * @dev Reverts with `InvalidConnector` if the caller is not a valid connector.
     * Reverts with `CannotTransferOrExecuteOnBridgeContracts` if the receiver is this contract, the bridge contract,
     * or the token contract.
     */
    function _beforeMint(
        uint32,
        TransferInfo memory transferInfo_
    )
        internal
        returns (bytes memory postHookData, TransferInfo memory transferInfo)
    {
        if (!validConnectors[msg.sender]) revert InvalidConnector();

        // no need of source check here, as if invalid caller, will revert with InvalidPoolId
        if (
            transferInfo_.receiver == address(this) ||
            // transferInfo_.receiver == address(bridge__) ||
            transferInfo_.receiver == token
        ) revert CannotTransferOrExecuteOnBridgeContracts();

        if (address(hook__) != address(0)) {
            (postHookData, transferInfo) = hook__.dstPreHookCall(
                DstPreHookCallParams(
                    msg.sender,
                    connectorCache[msg.sender],
                    transferInfo_
                )
            );
        } else {
            transferInfo = transferInfo_;
        }
    }

    /**
     * @notice Executes post-mint operations after minting tokens.
     * @dev This internal function is called after minting tokens.
     * It executes the destination post-hook call if a hook contract is defined and updates cache data.
     * @param messageId_ The unique identifier for the mint transaction.
     * @param postHookData_ Data returned from the destination pre-hook call.
     * @param transferInfo_ Information about the mint transaction.
     */
    function _afterMint(
        uint256,
        bytes32 messageId_,
        bytes memory postHookData_,
        TransferInfo memory transferInfo_
    ) internal {
        if (address(hook__) != address(0)) {
            CacheData memory cacheData = hook__.dstPostHookCall(
                DstPostHookCallParams(
                    msg.sender,
                    messageId_,
                    connectorCache[msg.sender],
                    postHookData_,
                    transferInfo_
                )
            );

            identifierCache[messageId_] = cacheData.identifierCache;
            connectorCache[msg.sender] = cacheData.connectorCache;
        }

        emit TokensBridged(
            msg.sender,
            transferInfo_.receiver,
            transferInfo_.amount,
            messageId_
        );
    }

    /**
     * @notice Executes pre-retry operations before retrying a failed transaction.
     * @dev This internal function is called before retrying a failed transaction.
     * It validates the connector, retrieves cache data for the given message ID,
     * and executes the pre-retry hook if defined.
     * @param connector_ The address of the connector responsible for the failed transaction.
     * @param messageId_ The unique identifier for the failed transaction.
     * @return postHookData Data returned from the pre-retry hook call.
     * @return transferInfo Information about the transfer.
     * @dev Reverts with `InvalidConnector` if the connector is not valid.
     * Reverts with `NoPendingData` if there is no pending data for the given message ID.
     */
    function _beforeRetry(
        address connector_,
        bytes32 messageId_
    )
        internal
        returns (bytes memory postHookData, TransferInfo memory transferInfo)
    {
        if (!validConnectors[connector_]) revert InvalidConnector();

        CacheData memory cacheData = CacheData(
            identifierCache[messageId_],
            connectorCache[connector_]
        );

        if (cacheData.identifierCache.length == 0) revert NoPendingData();
        (postHookData, transferInfo) = hook__.preRetryHook(
            PreRetryHookCallParams(connector_, cacheData)
        );
    }

    /**
     * @notice Executes post-retry operations after retrying a failed transaction.
     * @dev This internal function is called after retrying a failed transaction.
     * It retrieves cache data for the given message ID, executes the post-retry hook if defined,
     * and updates cache data.
     * @param connector_ The address of the connector responsible for the failed transaction.
     * @param messageId_ The unique identifier for the failed transaction.
     * @param postHookData Data returned from the pre-retry hook call.
     */
    function _afterRetry(
        address connector_,
        bytes32 messageId_,
        bytes memory postHookData
    ) internal {
        CacheData memory cacheData = CacheData(
            identifierCache[messageId_],
            connectorCache[connector_]
        );

        (cacheData) = hook__.postRetryHook(
            PostRetryHookCallParams(
                connector_,
                messageId_,
                postHookData,
                cacheData
            )
        );
        identifierCache[messageId_] = cacheData.identifierCache;
        connectorCache[connector_] = cacheData.connectorCache;
    }

    /**
     * @notice Retrieves the minimum fees required for a transaction from a connector.
     * @dev This function returns the minimum fees required for a transaction from the specified connector,
     * based on the provided message gas limit and payload size.
     * @param connector_ The address of the connector.
     * @param msgGasLimit_ The gas limit for the transaction.
     * @param payloadSize_ The size of the payload for the transaction.
     * @return totalFees The total minimum fees required for the transaction.
     */
    function getMinFees(
        address connector_,
        uint256 msgGasLimit_,
        uint256 payloadSize_
    ) external view returns (uint256 totalFees) {
        return IConnector(connector_).getMinFees(msgGasLimit_, payloadSize_);
    }
}
"
    },
    "contracts/bridge/Controller.sol": {
      "content": "// SPDX-License-Identifier: MIT
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 extraData_ The extra data passed to hook functions.
     * @param options_ Additional options for the bridging process.
     */
    function bridge(
        address receiver_,
        uint256 amount_,
        uint256 msgGasLimit_,
        address connector_,
        bytes calldata extraData_,
        bytes calldata options_
    ) external payable nonReentrant {
        (
            TransferInfo memory transferInfo,
            bytes memory postHookData
        ) = _beforeBridge(
                connector_,
                TransferInfo(receiver_, amount_, extraData_)
            );

        // to maintain socket dl specific accounting for super token
        // re check this logic for mint and mint use cases and if other minter involved
        totalMinted -= transferInfo.amount;
        _burn(msg.sender, transferInfo.amount);
        _afterBridge(
            msgGasLimit_,
            connector_,
            options_,
            postHookData,
            transferInfo
        );
    }

    /**
     * @notice Receives inbound tokens from another chain.
     * @dev This function is used to receive tokens from another chain.
     * @param siblingChainSlug_ The identifier of the sibling chain.
     * @param payload_ The payload containing the inbound tokens.
     */
    function receiveInbound(
        uint32 siblingChainSlug_,
        bytes memory payload_
    ) external payable override nonReentrant {
        (
            address receiver,
            uint256 lockAmount,
            bytes32 messageId,
            bytes memory extraData
        ) = abi.decode(payload_, (address, uint256, bytes32, bytes));

        // convert to shares
        TransferInfo memory transferInfo = TransferInfo(
            receiver,
            lockAmount,
            extraData
        );
        bytes memory postHookData;
        (postHookData, transferInfo) = _beforeMint(
            siblingChainSlug_,
            transferInfo
        );

        _mint(transferInfo.receiver, transferInfo.amount);
        totalMinted += transferInfo.amount;

        _afterMint(lockAmount, messageId, postHookData, transferInfo);
    }

    /**
     * @notice Retry a failed transaction.
     * @dev This function allows retrying a failed transaction sent through a connector.
     * @param connector_ The address of the connector contract responsible for the failed transaction.
     * @param messageId_ The unique identifier of the failed transaction.
     */
    function retry(
        address connector_,
        bytes32 messageId_
    ) external nonReentrant {
        (
            bytes memory postHookData,
            TransferInfo memory transferInfo
        ) = _beforeRetry(connector_, messageId_);
        _mint(transferInfo.receiver, transferInfo.amount);
        totalMinted += transferInfo.amount;

        _afterRetry(connector_, messageId_, postHookData);
    }

    function _burn(address user_, uint256 burnAmount_) internal virtual {
        IMintableERC20(token).burn(user_, burnAmount_);
    }

    function _mint(address user_, uint256 mintAmount_) internal virtual {
        if (mintAmount_ == 0) return;
        IMintableERC20(token).mint(user_, mintAmount_);
    }
}
"
    },
    "contracts/bridge/FiatTokenV2_1/FiatTokenV2_1_Controller.sol": {
      "content": "// SPDX-License-Identifier: MIT
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_);
    }
}
"
    },
    "contracts/bridge/FiatTokenV2_1/IFiatTokenV2_1_Mintable.sol": {
      "content": "// 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;
}
"
    },
    "contracts/bridge/Vault.sol": {
      "content": "// SPDX-License-Identifier: MIT
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 extraData_ The extra data passed to hook functions.
     * @param options_ Additional options for the bridging process.
     */
    function bridge(
        address receiver_,
        uint256 amount_,
        uint256 msgGasLimit_,
        address connector_,
        bytes calldata extraData_,
        bytes calldata options_
    ) external payable nonReentrant {
        (
            TransferInfo memory transferInfo,
            bytes memory postHookData
        ) = _beforeBridge(
                connector_,
                TransferInfo(receiver_, amount_, extraData_)
            );

        _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 postHookData,
            TransferInfo memory transferInfo
        ) = _beforeRetry(connector_, messageId_);
        _transferTokens(transferInfo.receiver, transferInfo.amount);

        _afterRetry(connector_, messageId_, postHookData);
    }

    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_);
    }
}
"
    },
    "contracts/common/Constants.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

address constant ETH_ADDRESS = address(
    0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
);

bytes32 constant NORMAL_CONTROLLER = keccak256("NORMAL_CONTROLLER");
bytes32 constant FIAT_TOKEN_CONTROLLER = keccak256("FIAT_TOKEN_CONTROLLER");

bytes32 constant LIMIT_HOOK = keccak256("LIMIT_HOOK");
bytes32 constant LIMIT_EXECUTION_HOOK = keccak256("LIMIT_EXECUTION_HOOK");
bytes32 constant LIMIT_EXECUTION_YIELD_HOOK = keccak256(
    "LIMIT_EXECUTION_YIELD_HOOK"
);
bytes32 constant LIMIT_EXECUTION_YIELD_TOKEN_HOOK = keccak256(
    "LIMIT_EXECUTION_YIELD_TOKEN_HOOK"
);

bytes32 constant ERC20_VAULT = keccak256("ERC20_VAULT");
bytes32 constant NATIVE_VAULT = keccak256("NATIVE_VAULT");
"
    },
    "contracts/common/Errors.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

error SiblingNotSupported();
error NotAuthorized();
error NotBridge();
error NotSocket();
error ConnectorUnavailable();
error InvalidPoolId();
error CannotTransferOrExecuteOnBridgeContracts();
error NoPendingData();
error MessageIdMisMatched();
error NotMessageBridge();
error InvalidSiblingChainSlug();
error InvalidTokenContract();
error InvalidExchangeRateContract();
error InvalidConnector();
error InvalidConnectorPoolId();
error ZeroAddressReceiver();
error ZeroAddress();
error ZeroAmount();
error DebtRatioTooHigh();
error NotEnoughAssets();
error VaultShutdown();
error InsufficientFunds();
error PermitDeadlineExpired();
error InvalidSigner();
error InsufficientMsgValue();
error InvalidOptionsLength();
"
    },
    "contracts/common/Structs.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

struct UpdateLimitParams {
    bool isMint;
    address connector;
    uint256 maxLimit;
    uint256 ratePerSecond;
}

struct SrcPreHookCallParams {
    address connector;
    address msgSender;
    TransferInfo transferInfo;
}

struct SrcPostHookCallParams {
    address connector;
    bytes options;
    bytes postHookData;
    TransferInfo transferInfo;
}

struct DstPreHookCallParams {
    address connector;
    bytes connectorCache;
    TransferInfo transferInfo;
}

struct DstPostHookCallParams {
    address connector;
    bytes32 messageId;
    bytes connectorCache;
    bytes postHookData;
    TransferInfo transferInfo;
}

struct PreRetryHookCallParams {
    address connector;
    CacheData cacheData;
}

struct PostRetryHookCallParams {
    address connector;
    bytes32 messageId;
    bytes postHookData;
    CacheData cacheData;
}

struct TransferInfo {
    address receiver;
    uint256 amount;
    bytes extraData;
}

struct CacheData {
    bytes identifierCache;
    bytes connectorCache;
}

struct LimitParams {
    uint256 lastUpdateTimestamp;
    uint256 ratePerSecond;
    uint256 maxLimit;
    uint256 lastUpdateLimit;
}
"
    },
    "contracts/ConnectorPlug.sol": {
      "content": "// 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;
    bytes32 public immutable transmissionParams;
    uint256 public messageIdPart;

    event ConnectorPlugDisconnected();

    constructor(
        address bridge_,
        address socket_,
        uint32 siblingChainSlug_,
        bytes32 transmissionParams_
    ) AccessControl(msg.sender) {
        bridge__ = IBridge(bridge_);
        socket__ = ISocket(socket_);
        siblingChainSlug = siblingChainSlug_;
        transmissionParams = transmissionParams_;
        _grantRole(RESCUE_ROLE, msg.sender);
    }

    function outbound(
        uint256 msgGasLimit_,
        bytes memory payload_,
        bytes memory options_
    ) external payable override returns (bytes32 messageId_) {
        if (msg.sender != address(bridge__)) revert NotBridge();
        if (options_.length == 0) {
            return
                socket__.outbound{value: msg.value}(
                    siblingChainSlug,
                    msgGasLimit_,
                    bytes32(0),
                    transmissionParams,
                    payload_
                );
        } else {
            if (options_.length != 32) revert InvalidOptionsLength();
            bytes32 executionParams = abi.decode(options_, (bytes32));

            return
                socket__.outbound{value: msg.value}(
                    siblingChainSlug,
                    msgGasLimit_,
                    executionParams,
                    transmissionParams,
                    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()));
    }
}
"
    },
    "contracts/hooks/Controller_YieldLimitExecHook.sol": {
      "content": "// SPDX-License-Identifier: MIT
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 postHookData)
    {
        super.srcPreHookCall(params_);
        uint256 amount = params_.transferInfo.amount;
        postHookData = 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.extraData = abi.encode(
            srcPostHookCallParams_.options,
            srcPostHookCallParams_.transferInfo.extraData
        );
        transferInfo.amount = abi.decode(
            srcPostHookCallParams_.postHookData,
            (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.extraData,
            (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.extraData = 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.extraData;

        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 postHookData The post-hook data to be processed after the retry hook execution.
    //  */
    function preRetryHook(
        PreRetryHookCallParams calldata params_
    )
        public
        override
        isVaultOrController
        notShutdown
        returns (bytes memory postHookData, 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
        );

        postHookData = 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 postHookData_ 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_);
    }
}
"
    },
    "contracts/hooks/HookBase.sol": {
      "content": "// SPDX-License-Identifier: MIT
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();
        _;
    }
}
"
    },
    "contracts/hooks/LimitExecutionHook.sol": {
      "content": "// SPDX-License-Identifier: MIT
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.extraData;

        (
            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 postHookData, TransferInfo memory transferInfo)
    {
        (address receiver, uint256 pendingMint, , address connector, ) = abi
            .decode(
                params_.cacheData.identifierCache,
                (address, uint256, uint256, address, bytes)
            );

        if (connector != params_.connector) revert InvalidConnector();

        (uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook(
            params_.connector,
            pendingMint
        );

        postHookData = abi.encode(receiver, consumedAmount, pendingAmount);
        transferInfo = TransferInfo(receiver, consumedAmount, bytes(""));
    }

    function postRetryHook(
        PostRetryHookCallParams calldata params_
    ) public virtual isVaultOrController returns (CacheData memory cacheData) {
        (
            ,
            ,
            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_.postHookData, (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 pure 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);
    }
}
"
    },
    "contracts/hooks/LimitHook.sol": {
      "content": "// SPDX-License-Identifier: MIT
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 postHookData)
    {
        if (useControllerPools)
            _poolSrcHook(params_.connector, params_.transferInfo.amount);

        _limitSrcHook(params_.connector, params_.transferInfo.amount);
        transferInfo = params_.transferInfo;
        postHookData = hex"";
    }

    function srcPostHookCall(
        SrcPostHookCallParams memory params_
    ) external view isVaultOrController returns (TransferInfo memory) {
        return params_.transferInfo;
    }

    function dstPreHookCall(
        DstPreHookCallParams memory params_
    )
        external
        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 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 postHookData, TransferInfo memory transferInfo)
    {
        (address receiver, 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
        );

        postHookData = abi.encode(receiver, consumedAmount, pendingAmount);
        transferInfo = TransferInfo(receiver, consumedAmount, bytes(""));
    }

    function postRetryHook(
        PostRetryHookCallParams calldata params_
    )
        external
        isVaultOrController
        nonReentrant
        returns (CacheData memory cacheData)
    {
        (address receiver, uint256 consumedAmount, uint256 pendingAmount) = abi
            .decode(params_.postHookData, (address, uint256, uint256));

        // code reaches here after minting/unlocking the pending amount
        emit PendingTokensBridged(
            params_.connector,
            receiver,
            consumedAmount,
            pendingAmount,
            params_.messageId
        );

        uint256 connectorPendingAmount = _getConnectorPendingAmount(
            params_.cacheData.connectorCache
        );
        cacheData.connectorCache = abi.encode(
            connectorPendingAmount - consumedAmount
        );
        cacheData.identifierCache = abi.encode(
            receiver,
            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 pure 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);
    }
}
"
    },
    "contracts/hooks/plugins/ConnectorPoolPlugin.sol": {
      "content": "// SPDX-License-Identifier: MIT
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_;
    }
}
"
    },
    "contracts/hooks/plugins/ExecutionHelper.sol": {
      "content": "// 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;

    event ExecutionFailed(bytes reason);

    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_;

        bytes memory returnData;
        (success, returnData) = target_.excessivelySafeCall(
            gasleft(),
            MAX_COPY_BYTES,
            payload_
        );

        if (!success) emit ExecutionFailed(_getRevertMsg(returnData));

        messageId = bytes32(0);
        bridgeAmount = 0;
    }

    function _getRevertMsg(
        bytes memory _returnData
    ) internal pure returns (bytes memory) {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (_returnData.length < 68) return bytes("");
        return _returnData;
    }
}
"
    },
    "contracts/hooks/plugins/LimitPlugin.sol": {
      "content": "// SPDX-License-Identifier: MIT
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;
    }
}
"
    },
    "contracts/hooks/Vault_YieldLimitExecHook.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "lib/openzeppelin-contracts/contracts/utils/math/Math.sol

Tags:
ERC20, Multisig, Mintable, Burnable, Swap, Yield, Voting, Upgradeable, Multi-Signature, Factory|addr:0x6d8ed2e3b5ff82dd0dd97125a6b2a599713e29cb|verified:true|block:23726637|tx:0xf4f75a4f1f026deac9491752bbcf08b32fc363d9b62772d3722d94c2bcab4a00|first_check:1762285552

Submitted on: 2025-11-04 20:45:52

Comments

Log in to comment.

No comments yet.