GroveEthereum_20251030

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": {
    "src/proposals/20251030/GroveEthereum_20251030.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.25;

import { Ethereum } from "lib/grove-address-registry/src/Ethereum.sol";

import { GrovePayloadEthereum } from "src/libraries/GrovePayloadEthereum.sol";

/**
 * @title  October 30, 2025 Grove Ethereum Proposal
 * @author Grove Labs
 * Forum : https://forum.sky.money/t/october-30th-2025-proposed-changes-to-grove-for-upcoming-spell/27321
 */
contract GroveEthereum_20251030 is GrovePayloadEthereum {

    address internal constant CURVE_RLUSD_USDC = 0xD001aE433f254283FeCE51d4ACcE8c53263aa186;

    address internal constant AAVE_ATOKEN_CORE_USDC     = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c;
    address internal constant AAVE_ATOKEN_CORE_RLUSD    = 0xFa82580c16A31D0c1bC632A36F82e83EfEF3Eec0;
    address internal constant AAVE_ATOKEN_HORIZON_USDC  = 0x68215B6533c47ff9f7125aC95adf00fE4a62f79e;
    address internal constant AAVE_ATOKEN_HORIZON_RLUSD = 0xE3190143Eb552456F88464662f0c0C4aC67A77eB;

    uint256 internal constant CURVE_RLUSD_USDC_MAX_SLIPPAGE = 0.9990e18;
    uint256 internal constant CURVE_RLUSD_USDC_SWAP_MAX     = 20_000_000e18;
    uint256 internal constant CURVE_RLUSD_USDC_SWAP_SLOPE   = 100_000_000e18 / uint256(1 days);

    uint256 internal constant AAVE_ATOKEN_CORE_USDC_DEPOSIT_MAX   = 50_000_000e6;
    uint256 internal constant AAVE_ATOKEN_CORE_USDC_DEPOSIT_SLOPE = 25_000_000e6 / uint256(1 days);

    uint256 internal constant AAVE_ATOKEN_CORE_RLUSD_DEPOSIT_MAX   = 50_000_000e18;
    uint256 internal constant AAVE_ATOKEN_CORE_RLUSD_DEPOSIT_SLOPE = 25_000_000e18 / uint256(1 days);

    uint256 internal constant AAVE_ATOKEN_HORIZON_USDC_DEPOSIT_MAX   = 50_000_000e6;
    uint256 internal constant AAVE_ATOKEN_HORIZON_USDC_DEPOSIT_SLOPE = 25_000_000e6 / uint256(1 days);

    uint256 internal constant AAVE_ATOKEN_HORIZON_RLUSD_DEPOSIT_MAX   = 50_000_000e18;
    uint256 internal constant AAVE_ATOKEN_HORIZON_RLUSD_DEPOSIT_SLOPE = 25_000_000e18 / uint256(1 days);

    function _execute() internal override {
        // [Mainnet] Curve RLUSD/USDC Pool Onboarding
        //   Forum : https://forum.sky.money/t/october-30th-2025-proposed-changes-to-grove-for-upcoming-spell/27321
        _onboardCurvePoolRlusdUsdc();

        // [Mainnet] Aave Core USDC Onboarding
        //   Forum : https://forum.sky.money/t/october-30th-2025-proposed-changes-to-grove-for-upcoming-spell/27321
        _onboardAaveCoreUsdc();

        // [Mainnet] Aave Core RLUSD Onboarding
        //   Forum : https://forum.sky.money/t/october-30th-2025-proposed-changes-to-grove-for-upcoming-spell/27321
        _onboardAaveCoreRlusd();

        // [Mainnet] Aave Horizon USDC Onboarding
        //   Forum : https://forum.sky.money/t/october-30th-2025-proposed-changes-to-grove-for-upcoming-spell/27321
        _onboardAaveHorizonUsdc();

        // [Mainnet] Aave Horizon RLUSD Onboarding
        //   Forum : https://forum.sky.money/t/october-30th-2025-proposed-changes-to-grove-for-upcoming-spell/27321
        _onboardAaveHorizonRlusd();
    }

    function _onboardCurvePoolRlusdUsdc() internal {
        _onboardCurvePool({
            controller    : Ethereum.ALM_CONTROLLER,
            pool          : CURVE_RLUSD_USDC,
            maxSlippage   : CURVE_RLUSD_USDC_MAX_SLIPPAGE,
            swapMax       : CURVE_RLUSD_USDC_SWAP_MAX,
            swapSlope     : CURVE_RLUSD_USDC_SWAP_SLOPE,
            depositMax    : 0,
            depositSlope  : 0,
            withdrawMax   : 0,
            withdrawSlope : 0
        });
    }

    function _onboardAaveCoreUsdc() internal {
        _onboardAaveToken({
            token        : AAVE_ATOKEN_CORE_USDC,
            depositMax   : AAVE_ATOKEN_CORE_USDC_DEPOSIT_MAX,
            depositSlope : AAVE_ATOKEN_CORE_USDC_DEPOSIT_SLOPE
        });
    }

    function _onboardAaveCoreRlusd() internal {
        _onboardAaveToken({
            token        : AAVE_ATOKEN_CORE_RLUSD,
            depositMax   : AAVE_ATOKEN_CORE_RLUSD_DEPOSIT_MAX,
            depositSlope : AAVE_ATOKEN_CORE_RLUSD_DEPOSIT_SLOPE
        });
    }

    function _onboardAaveHorizonUsdc() internal {
        _onboardAaveToken({
            token        : AAVE_ATOKEN_HORIZON_USDC,
            depositMax   : AAVE_ATOKEN_HORIZON_USDC_DEPOSIT_MAX,
            depositSlope : AAVE_ATOKEN_HORIZON_USDC_DEPOSIT_SLOPE
        });
    }

    function _onboardAaveHorizonRlusd() internal {
        _onboardAaveToken({
            token        : AAVE_ATOKEN_HORIZON_RLUSD,
            depositMax   : AAVE_ATOKEN_HORIZON_RLUSD_DEPOSIT_MAX,
            depositSlope : AAVE_ATOKEN_HORIZON_RLUSD_DEPOSIT_SLOPE
        });
    }

}
"
    },
    "lib/grove-address-registry/src/Ethereum.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.0;

library Ethereum {

    /******************************************************************************************************************/
    /*** Token Addresses                                                                                            ***/
    /******************************************************************************************************************/

    address internal constant CBBTC  = 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf;
    address internal constant DAI    = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address internal constant EZETH  = 0xbf5495Efe5DB9ce00f80364C8B423567e58d2110;
    address internal constant GNO    = 0x6810e776880C02933D47DB1b9fc05908e5386b96;
    address internal constant LBTC   = 0x8236a87084f8B84306f72007F36F2618A5634494;
    address internal constant MKR    = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
    address internal constant RETH   = 0xae78736Cd615f374D3085123A210448E74Fc6393;
    address internal constant RSETH  = 0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7;
    address internal constant SDAI   = 0x83F20F44975D03b1b09e64809B757c47f942BEeA;
    address internal constant SUSDC  = 0xBc65ad17c5C0a2A4D159fa5a503f4992c7B545FE;
    address internal constant SUSDE  = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497;
    address internal constant SUSDS  = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD;
    address internal constant TBTC   = 0x18084fbA666a33d37592fA2633fD49a74DD93a88;
    address internal constant USDC   = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    address internal constant USDE   = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3;
    address internal constant USDS   = 0xdC035D45d973E3EC169d2276DDab16f1e407384F;
    address internal constant USCC   = 0x14d60E7FDC0D71d8611742720E4C50E7a974020c;
    address internal constant USDT   = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
    address internal constant USTB   = 0x43415eB6ff9DB7E26A15b704e7A3eDCe97d31C4e;
    address internal constant WBTC   = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address internal constant WEETH  = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee;
    address internal constant WETH   = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address internal constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;

    /******************************************************************************************************************/
    /*** MakerDAO Addresses                                                                                         ***/
    /******************************************************************************************************************/

    address internal constant AUTO_LINE   = 0xC7Bdd1F2B16447dcf3dE045C4a039A60EC2f0ba3;
    address internal constant CHIEF       = 0x0a3f6849f78076aefaDf113F5BED87720274dDC0;
    address internal constant DAI_USDS    = 0x3225737a9Bbb6473CB4a45b7244ACa2BeFdB276A;
    address internal constant PAUSE_PROXY = 0xBE8E3e3618f7474F8cB1d074A26afFef007E98FB;
    address internal constant POT         = 0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7;
    address internal constant PSM         = 0xf6e72Db5454dd049d0788e411b06CfAF16853042;  // Lite PSM
    address internal constant VAT         = 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B;

    /******************************************************************************************************************/
    /*** GroveDAO Addresses                                                                                         ***/
    /******************************************************************************************************************/

    address internal constant GROVE_PROXY = 0x1369f7b2b38c76B6478c0f0E66D94923421891Ba;

    /******************************************************************************************************************/
    /*** Grove Allocation System Addresses                                                                          ***/
    /******************************************************************************************************************/

    address internal constant ALLOCATOR_BUFFER   = 0x629aD4D779F46B8A1491D3f76f7E97Cb04D8b1Cd;
    address internal constant ALLOCATOR_ORACLE   = 0xc7B91C401C02B73CBdF424dFaaa60950d5040dB7;
    address internal constant ALLOCATOR_REGISTRY = 0xCdCFA95343DA7821fdD01dc4d0AeDA958051bB3B;
    address internal constant ALLOCATOR_ROLES	 = 0x9A865A710399cea85dbD9144b7a09C889e94E803;
    address internal constant ALLOCATOR_VAULT	 = 0x26512A41C8406800f21094a7a7A0f980f6e25d43;

    /******************************************************************************************************************/
    /*** Grove Liquidity Layer Addresses                                                                            ***/
    /******************************************************************************************************************/

    address internal constant ALM_CONTROLLER  = 0xB111E07c8B939b0Fe701710b365305F7F23B0edd;
    address internal constant ALM_PROXY       = 0x491EDFB0B8b608044e227225C715981a30F3A44E;
    address internal constant ALM_RATE_LIMITS = 0x5F5cfCB8a463868E37Ab27B5eFF3ba02112dF19a;

    address internal constant ALM_FREEZER = 0xB0113804960345fd0a245788b3423319c86940e5;
    address internal constant ALM_RELAYER = 0x0eEC86649E756a23CBc68d9EFEd756f16aD5F85f;

    /******************************************************************************************************************/
    /*** Ethena Addresses                                                                                           ***/
    /******************************************************************************************************************/

    address internal constant ETHENA_MINTER = 0xe3490297a08d6fC8Da46Edb7B6142E4F461b62D3;

    /******************************************************************************************************************/
    /*** Blackrock BUIDL Addresses                                                                                  ***/
    /******************************************************************************************************************/

    address internal constant BUIDL          = 0x7712c34205737192402172409a8F7ccef8aA2AEc;
    address internal constant BUIDL_REDEEM   = 0x31D3F59Ad4aAC0eeE2247c65EBE8Bf6E9E470a53;  // Circle redeem
    address internal constant BUIDLI         = 0x6a9DA2D710BB9B700acde7Cb81F10F1fF8C89041;
    address internal constant BUIDLI_DEPOSIT = 0xD1917664bE3FdAea377f6E8D5BF043ab5C3b1312;
    address internal constant BUIDLI_REDEEM  = 0x8780Dd016171B91E4Df47075dA0a947959C34200;  // Offchain redeem

    /******************************************************************************************************************/
    /*** Centrifuge Addresses                                                                                       ***/
    /******************************************************************************************************************/

    address internal constant CENTRIFUGE_JAAA  = 0x4880799eE5200fC58DA299e965df644fBf46780B;
    address internal constant CENTRIFUGE_JTRSY = 0xFE6920eB6C421f1179cA8c8d4170530CDBdfd77A;

    /******************************************************************************************************************/
    /*** Fluid Addresses                                                                                            ***/
    /******************************************************************************************************************/

    address internal constant FLUID_SUSDS = 0x2BBE31d63E6813E3AC858C04dae43FB2a72B0D11;

    /******************************************************************************************************************/
    /*** Morpho Addresses                                                                                           ***/
    /******************************************************************************************************************/

    address internal constant MORPHO = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;

    /******************************************************************************************************************/
    /*** Superstate Addresses                                                                                       ***/
    /******************************************************************************************************************/

    address internal constant SUPERSTATE_REDEMPTION = 0x4c21B7577C8FE8b0B0669165ee7C8f67fa1454Cf;

    /******************************************************************************************************************/
    /*** Cross-Domain Addresses                                                                                     ***/
    /******************************************************************************************************************/

    address internal constant CCTP_TOKEN_MESSENGER = 0xBd3fa81B58Ba92a82136038B25aDec7066af3155;

    /******************************************************************************************************************/
    /*** Pendle Addresses                                                                                           ***/
    /******************************************************************************************************************/

    address public constant PENDLE_ROUTER = 0x888888888889758F76e7103c6CbF23ABbF58F946;

}
"
    },
    "src/libraries/GrovePayloadEthereum.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

import { Ethereum }  from "lib/grove-address-registry/src/Ethereum.sol";
import { Avalanche } from "lib/grove-address-registry/src/Avalanche.sol";
import { Plume }     from "lib/grove-address-registry/src/Plume.sol";

import { IExecutor } from "lib/grove-gov-relay/src/interfaces/IExecutor.sol";

import { CCTPForwarder }          from "xchain-helpers/forwarders/CCTPForwarder.sol";
import { ArbitrumERC20Forwarder } from "xchain-helpers/forwarders/ArbitrumERC20Forwarder.sol";

import { GroveLiquidityLayerHelpers } from "./GroveLiquidityLayerHelpers.sol";

/**
 * @dev Base smart contract for Ethereum.
 * @author Steakhouse Financial
 */
abstract contract GrovePayloadEthereum {

    // ADD SUPPORTED FOREIGN PAYLOADS HERE

    // These need to be immutable (delegatecall) and can only be set in constructor
    address public immutable PAYLOAD_AVALANCHE;
    address public immutable PAYLOAD_PLUME;

    function execute() external {
        _execute();

        if (PAYLOAD_AVALANCHE != address(0)) {
            CCTPForwarder.sendMessage({
                messageTransmitter:  CCTPForwarder.MESSAGE_TRANSMITTER_CIRCLE_ETHEREUM,
                destinationDomainId: CCTPForwarder.DOMAIN_ID_CIRCLE_AVALANCHE,
                recipient:           Avalanche.GROVE_RECEIVER,
                messageBody:         _encodePayloadQueue(PAYLOAD_AVALANCHE)
            });
        }

        if (PAYLOAD_PLUME != address(0)) {
            ArbitrumERC20Forwarder.sendMessageL1toL2({
                l1CrossDomain: ArbitrumERC20Forwarder.L1_CROSS_DOMAIN_PLUME,
                target:        Plume.GROVE_RECEIVER,
                message:       _encodePayloadQueue(PAYLOAD_PLUME),
                gasLimit:      1_000_0000,
                maxFeePerGas:  5_000e9,
                baseFee:       block.basefee
            });
        }
    }

    function _execute() internal virtual;

    function _encodePayloadQueue(address _payload) internal pure returns (bytes memory) {
        address[] memory targets        = new address[](1);
        uint256[] memory values         = new uint256[](1);
        string[] memory signatures      = new string[](1);
        bytes[] memory calldatas        = new bytes[](1);
        bool[] memory withDelegatecalls = new bool[](1);

        targets[0]           = _payload;
        values[0]            = 0;
        signatures[0]        = 'execute()';
        calldatas[0]         = '';
        withDelegatecalls[0] = true;

        return abi.encodeCall(IExecutor.queue, (
            targets,
            values,
            signatures,
            calldatas,
            withDelegatecalls
        ));
    }

    function _onboardERC4626Vault(address vault, uint256 depositMax, uint256 depositSlope) internal {
        GroveLiquidityLayerHelpers.onboardERC4626Vault(
            Ethereum.ALM_RATE_LIMITS,
            vault,
            depositMax,
            depositSlope
        );
    }

    function _onboardERC7540Vault(address vault, uint256 depositMax, uint256 depositSlope) internal {
        GroveLiquidityLayerHelpers.onboardERC7540Vault(
            Ethereum.ALM_RATE_LIMITS,
            vault,
            depositMax,
            depositSlope
        );
    }

    function _offboardERC7540Vault(address vault) internal {
        GroveLiquidityLayerHelpers.offboardERC7540Vault(
            Ethereum.ALM_RATE_LIMITS,
            vault
        );
    }

    function _setUSDSMintRateLimit(uint256 maxAmount, uint256 slope) internal {
        GroveLiquidityLayerHelpers.setUSDSMintRateLimit(
            Ethereum.ALM_RATE_LIMITS,
            maxAmount,
            slope
        );
    }

    function _setUSDSToUSDCRateLimit(uint256 maxAmount, uint256 slope) internal {
        GroveLiquidityLayerHelpers.setUSDSToUSDCRateLimit(
            Ethereum.ALM_RATE_LIMITS,
            maxAmount,
            slope
        );
    }

    function _setCentrifugeCrosschainTransferRateLimit(address centrifugeVault, uint16 destinationCentrifugeId, uint256 maxAmount, uint256 slope) internal {
        GroveLiquidityLayerHelpers.setCentrifugeCrosschainTransferRateLimit(
            Ethereum.ALM_RATE_LIMITS,
            centrifugeVault,
            destinationCentrifugeId,
            maxAmount,
            slope
        );
    }

    function _onboardAaveToken(address token, uint256 depositMax, uint256 depositSlope) internal {
        GroveLiquidityLayerHelpers.onboardAaveToken(
            Ethereum.ALM_RATE_LIMITS,
            token,
            depositMax,
            depositSlope
        );
    }

    function _onboardCurvePool(
        address controller,
        address pool,
        uint256 maxSlippage,
        uint256 swapMax,
        uint256 swapSlope,
        uint256 depositMax,
        uint256 depositSlope,
        uint256 withdrawMax,
        uint256 withdrawSlope
    ) internal {
        GroveLiquidityLayerHelpers.onboardCurvePool(
            controller,
            Ethereum.ALM_RATE_LIMITS,
            pool,
            maxSlippage,
            swapMax,
            swapSlope,
            depositMax,
            depositSlope,
            withdrawMax,
            withdrawSlope
        );
    }

}
"
    },
    "lib/grove-address-registry/src/Avalanche.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.0;

library Avalanche {

    /******************************************************************************************************************/
    /*** Token Addresses                                                                                            ***/
    /******************************************************************************************************************/

    address internal constant USDC = 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E;

    /******************************************************************************************************************/
    /*** Bridging Addresses                                                                                         ***/
    /******************************************************************************************************************/

    address internal constant CCTP_TOKEN_MESSENGER = 0x6B25532e1060CE10cc3B0A99e5683b91BFDe6982;

    /******************************************************************************************************************/
    /*** Grove Liquidity Layer Addresses                                                                            ***/
    /******************************************************************************************************************/

    address internal constant ALM_CONTROLLER  = 0x734266cE1E49b148eF633f2E0358382488064999;
    address internal constant ALM_PROXY       = 0x7107DD8F56642327945294a18A4280C78e153644;
    address internal constant ALM_RATE_LIMITS = 0x6ba2e6bCCe3d2A31F1e3e1d3e11CDffBaA002A21;

    address internal constant ALM_FREEZER = 0xB0113804960345fd0a245788b3423319c86940e5;
    address internal constant ALM_RELAYER = 0x0eEC86649E756a23CBc68d9EFEd756f16aD5F85f;

    /******************************************************************************************************************/
    /*** Governance Relay Addresses                                                                                 ***/
    /******************************************************************************************************************/

    address internal constant GROVE_EXECUTOR = 0x4b803781828b76EaBF21AaF02e5ce23596b4d60c;
    address internal constant GROVE_RECEIVER = 0x26e9512547feC1906C55256e491DfB6673D8C23f;

    /******************************************************************************************************************/
    /*** Centrifuge Addresses                                                                                       ***/
    /******************************************************************************************************************/

    address internal constant CENTRIFUGE_JAAA  = 0x1121F4e21eD8B9BC1BB9A2952cDD8639aC897784;
    address internal constant CENTRIFUGE_JTRSY = 0xFE6920eB6C421f1179cA8c8d4170530CDBdfd77A;

}
"
    },
    "lib/grove-address-registry/src/Plume.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.0;

library Plume {

    /******************************************************************************************************************/
    /*** Token Addresses                                                                                            ***/
    /******************************************************************************************************************/

    address internal constant USDC = 0x222365EF19F7947e5484218551B56bb3965Aa7aF;

    /******************************************************************************************************************/
    /*** Grove Liquidity Layer Addresses                                                                            ***/
    /******************************************************************************************************************/

    address internal constant ALM_CONTROLLER  = 0x0C462Fff7Cc975bC9F2B0aEB8270febA5FD71e1B;
    address internal constant ALM_PROXY       = 0x1DB91ad50446a671e2231f77e00948E68876F812;
    address internal constant ALM_RATE_LIMITS = 0x7f8408eBbBC3504F83eeDa52910dd75Eba92C955;

    address internal constant ALM_FREEZER = 0xB0113804960345fd0a245788b3423319c86940e5;
    address internal constant ALM_RELAYER = 0x0eEC86649E756a23CBc68d9EFEd756f16aD5F85f;

    /******************************************************************************************************************/
    /*** Governance Relay Addresses                                                                                 ***/
    /******************************************************************************************************************/

    address internal constant GROVE_EXECUTOR = 0x3048386E09c72C20FB268a37d2B630D7f2Ee9138;
    address internal constant GROVE_RECEIVER = 0x7D592085847558A97695DDC1CD6E8FEe818510E8;

    /******************************************************************************************************************/
    /*** Centrifuge Addresses                                                                                       ***/
    /******************************************************************************************************************/

    address internal constant CENTRIFUGE_ACRDX = 0x354a9222571259457B2e98b2285B62e6a9bf4eD3;
    address internal constant CENTRIFUGE_JTRSY = 0x818A3593340622c1D6A51B039e191F2f8C99A1F2;

}
"
    },
    "lib/grove-gov-relay/src/interfaces/IExecutor.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

import { IAccessControl } from 'openzeppelin-contracts/contracts/access/IAccessControl.sol';

/**
 * @title  IExecutor
 * @author Aave
 * @notice Defines the interface for the Executor
 */
interface IExecutor is IAccessControl {

    /******************************************************************************************************************/
    /*** Errors                                                                                                     ***/
    /******************************************************************************************************************/

    error GracePeriodTooShort();
    error OnlyQueuedActions();
    error TimelockNotFinished();
    error InvalidActionsSetId();
    error EmptyTargets();
    error InconsistentParamsLength();
    error InsufficientBalance();

    /******************************************************************************************************************/
    /*** Enums                                                                                                      ***/
    /******************************************************************************************************************/

    /**
     * @notice This enum contains all possible actions set states
     */
    enum ActionsSetState {
        Queued,
        Executed,
        Canceled,
        Expired
    }

    /******************************************************************************************************************/
    /*** Events                                                                                                     ***/
    /******************************************************************************************************************/

    /**
     * @notice This struct contains the data needed to execute a specified set of actions.
     * @param  targets           Array of targets to call.
     * @param  values            Array of values to pass in each call.
     * @param  signatures        Array of function signatures to encode in each call by the actions which can be empty strings.
     * @param  calldatas         Array of calldatas to pass in each call, appended to the signature at the same array index if not empty.
     * @param  withDelegateCalls Array of whether to delegatecall for each call.
     * @param  executionTime     Timestamp starting from which the actions set can be executed.
     * @param  executed          True if the actions set has been executed, false otherwise.
     * @param  canceled          True if the actions set has been canceled, false otherwise.
     */
    struct ActionsSet {
        address[] targets;
        uint256[] values;
        string[] signatures;
        bytes[] calldatas;
        bool[] withDelegatecalls;
        uint256 executionTime;
        bool executed;
        bool canceled;
    }

    /**
     * @dev   Emitted when an ActionsSet is queued.
     * @param id                Id of the ActionsSet.
     * @param targets           Array of targets to be called by the actions set.
     * @param values            Array of values to pass in each call by the actions set.
     * @param signatures        Array of function signatures to encode in each call by the actions set.
     * @param calldatas         Array of calldata to pass in each call by the actions set.
     * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set.
     * @param executionTime     The timestamp at which this actions set can be executed.
     **/
    event ActionsSetQueued(
        uint256 indexed id,
        address[] targets,
        uint256[] values,
        string[] signatures,
        bytes[] calldatas,
        bool[] withDelegatecalls,
        uint256 executionTime
    );

    /**
     * @dev   Emitted when an ActionsSet is successfully executed.
     * @param id                 Id of the ActionsSet.
     * @param initiatorExecution The address that triggered the ActionsSet execution.
     * @param returnedData       The returned data from the ActionsSet execution.
     **/
    event ActionsSetExecuted(
        uint256 indexed id,
        address indexed initiatorExecution,
        bytes[] returnedData
    );

    /**
     * @dev   Emitted when an ActionsSet is cancelled by the guardian.
     * @param id Id of the ActionsSet.
     **/
    event ActionsSetCanceled(uint256 indexed id);

    /**
     * @dev   Emitted when the delay (between queueing and execution) is updated.
     * @param oldDelay The value of the old delay.
     * @param newDelay The value of the new delay.
     **/
    event DelayUpdate(uint256 oldDelay, uint256 newDelay);

    /**
     * @dev   Emitted when the grace period (between executionTime and expiration) is updated.
     * @param oldGracePeriod The value of the old grace period.
     * @param newGracePeriod The value of the new grace period.
     **/
    event GracePeriodUpdate(uint256 oldGracePeriod, uint256 newGracePeriod);

    /******************************************************************************************************************/
    /*** State variables                                                                                            ***/
    /******************************************************************************************************************/

    /**
     * @notice The role that allows submission of a queued action.
     **/
    function SUBMISSION_ROLE() external view returns (bytes32);

    /**
     * @notice The role that allows a guardian to cancel a pending action.
     **/
    function GUARDIAN_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the minimum grace period that can be set, 10 minutes.
     */
    function MINIMUM_GRACE_PERIOD() external view returns (uint256);

     /**
     * @notice Returns the total number of actions sets of the executor.
     * @return The number of actions sets.
     **/
    function actionsSetCount() external view returns (uint256);

    /**
     * @notice Returns the delay (between queuing and execution)
     * @return The value of the delay (in seconds)
     **/
    function delay() external view returns (uint256);

    /**
     * @notice Time after the execution time during which the actions set can be executed.
     * @return The value of the grace period (in seconds)
     **/
    function gracePeriod() external view returns (uint256);

    /******************************************************************************************************************/
    /*** ActionSet functions                                                                                        ***/
    /******************************************************************************************************************/

    /**
     * @notice Queue an ActionsSet.
     * @dev    If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise.
     * @param  targets           Array of targets to be called by the actions set.
     * @param  values            Array of values to pass in each call by the actions set.
     * @param  signatures        Array of function signatures to encode in each call by the actions which can be empty strings.
     * @param  calldatas         Array of calldata to pass in each call by the actions set.
     * @param  withDelegatecalls Array of whether to delegatecall for each call of the actions set.
     **/
    function queue(
        address[] memory targets,
        uint256[] memory values,
        string[] memory signatures,
        bytes[] memory calldatas,
        bool[] memory withDelegatecalls
    ) external;

    /**
     * @notice Execute the ActionsSet
     * @param  actionsSetId The id of the ActionsSet to execute
     **/
    function execute(uint256 actionsSetId) external payable;

    /**
     * @notice Cancel the ActionsSet.
     * @param  actionsSetId The id of the ActionsSet to cancel.
     **/
    function cancel(uint256 actionsSetId) external;

    /******************************************************************************************************************/
    /*** Admin functions                                                                                            ***/
    /******************************************************************************************************************/

    /**
     * @notice Update the delay, time between queueing and execution of ActionsSet.
     * @dev    It does not affect to actions set that are already queued.
     * @param  delay The value of the delay (in seconds).
     **/
    function updateDelay(uint256 delay) external;

    /**
     * @notice Update the grace period, the period after the execution time during which an actions set can be executed
     * @param  gracePeriod The value of the grace period (in seconds).
     **/
    function updateGracePeriod(uint256 gracePeriod) external;

    /******************************************************************************************************************/
    /*** Misc functions                                                                                             ***/
    /******************************************************************************************************************/

    /**
     * @notice Allows to delegatecall a given target with an specific amount of value.
     * @dev    This function is external so it allows to specify a defined msg.value for the delegate call, reducing
     *         the risk that a delegatecall gets executed with more value than intended.
     * @return The bytes returned by the delegate call.
     **/
    function executeDelegateCall(address target, bytes calldata data)
        external
        payable
        returns (bytes memory);

    /**
     * @notice Allows to receive funds into the executor.
     * @dev    Useful for actionsSet that needs funds to gets executed.
     */
    function receiveFunds() external payable;

    /******************************************************************************************************************/
    /*** View functions                                                                                             ***/
    /******************************************************************************************************************/


    /**
     * @notice Returns the data of an actions set.
     * @param  actionsSetId The id of the ActionsSet.
     * @return The data of the ActionsSet.
     **/
     function getActionsSetById(uint256 actionsSetId) external view returns (ActionsSet memory);

     /**
      * @notice Returns the current state of an actions set.
      * @param  actionsSetId The id of the ActionsSet.
      * @return The current state of theI ActionsSet.
      **/
     function getCurrentState(uint256 actionsSetId) external view returns (ActionsSetState);

}
"
    },
    "lib/xchain-helpers/src/forwarders/CCTPForwarder.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;

interface IMessageTransmitter {
    function sendMessage(
        uint32 destinationDomain,
        bytes32 recipient,
        bytes calldata messageBody
    ) external;
}

library CCTPForwarder {

    address constant internal MESSAGE_TRANSMITTER_CIRCLE_ETHEREUM     = 0x0a992d191DEeC32aFe36203Ad87D7d289a738F81;
    address constant internal MESSAGE_TRANSMITTER_CIRCLE_AVALANCHE    = 0x8186359aF5F57FbB40c6b14A588d2A59C0C29880;
    address constant internal MESSAGE_TRANSMITTER_CIRCLE_OPTIMISM     = 0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8;
    address constant internal MESSAGE_TRANSMITTER_CIRCLE_ARBITRUM_ONE = 0xC30362313FBBA5cf9163F0bb16a0e01f01A896ca;
    address constant internal MESSAGE_TRANSMITTER_CIRCLE_BASE         = 0xAD09780d193884d503182aD4588450C416D6F9D4;
    address constant internal MESSAGE_TRANSMITTER_CIRCLE_POLYGON_POS  = 0xF3be9355363857F3e001be68856A2f96b4C39Ba9;
    address constant internal MESSAGE_TRANSMITTER_CIRCLE_UNICHAIN     = 0x353bE9E2E38AB1D19104534e4edC21c643Df86f4;

    uint32 constant internal DOMAIN_ID_CIRCLE_ETHEREUM     = 0;
    uint32 constant internal DOMAIN_ID_CIRCLE_AVALANCHE    = 1;
    uint32 constant internal DOMAIN_ID_CIRCLE_OPTIMISM     = 2;
    uint32 constant internal DOMAIN_ID_CIRCLE_ARBITRUM_ONE = 3;
    uint32 constant internal DOMAIN_ID_CIRCLE_NOBLE        = 4;
    uint32 constant internal DOMAIN_ID_CIRCLE_SOLANA       = 5;
    uint32 constant internal DOMAIN_ID_CIRCLE_BASE         = 6;
    uint32 constant internal DOMAIN_ID_CIRCLE_POLYGON_POS  = 7;
    uint32 constant internal DOMAIN_ID_CIRCLE_UNICHAIN     = 10;

    function sendMessage(
        address messageTransmitter,
        uint32 destinationDomainId,
        bytes32 recipient,
        bytes memory messageBody
    ) internal {
        IMessageTransmitter(messageTransmitter).sendMessage(
            destinationDomainId,
            recipient,
            messageBody
        );
    }

    function sendMessage(
        address messageTransmitter,
        uint32 destinationDomainId,
        address recipient,
        bytes memory messageBody
    ) internal {
        sendMessage(
            messageTransmitter,
            destinationDomainId,
            bytes32(uint256(uint160(recipient))),
            messageBody
        );
    }
    
}
"
    },
    "lib/xchain-helpers/src/forwarders/ArbitrumERC20Forwarder.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;

import { IERC20 } from "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

import { SafeERC20 } from "../../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

interface ICrossDomainArbitrum {
    function createRetryableTicket(
        address to,
        uint256 l2CallValue,
        uint256 maxSubmissionCost,
        address excessFeeRefundAddress,
        address callValueRefundAddress,
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 tokenTotalFeeAmount,
        bytes calldata data
    ) external returns (uint256);
    function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) external view returns (uint256);
}

interface IArbSys {
    function sendTxToL1(address target, bytes calldata message) external;
}

library ArbitrumERC20Forwarder {

    address constant internal L1_CROSS_DOMAIN_PLUME = 0x943fc691242291B74B105e8D19bd9E5DC2fcBa1D;
    address constant internal PLUME_GAS_TOKEN       = 0x4C1746A800D224393fE2470C70A35717eD4eA5F1;
    address constant internal L2_CROSS_DOMAIN       = 0x0000000000000000000000000000000000000064;

    function sendMessageL1toL2(
        address l1CrossDomain,
        address target,
        bytes memory message,
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 baseFee
    ) internal {
        IERC20 gasToken;

        if (l1CrossDomain == L1_CROSS_DOMAIN_PLUME) gasToken = IERC20(PLUME_GAS_TOKEN);
        else revert("ArbitrumERC20Forwarder/invalid-l1-cross-domain");

        uint256 maxSubmission = ICrossDomainArbitrum(l1CrossDomain).calculateRetryableSubmissionFee(message.length, baseFee);

        SafeERC20.forceApprove(gasToken, l1CrossDomain, maxSubmission + gasLimit * maxFeePerGas);

        ICrossDomainArbitrum(l1CrossDomain).createRetryableTicket(
            target,
            0, // we always assume that l2CallValue = 0
            maxSubmission,
            address(0), // burn the excess gas
            address(0), // burn the excess gas
            gasLimit,
            maxFeePerGas,
            maxSubmission + gasLimit * maxFeePerGas, // max redemption fee
            message
        );

        SafeERC20.forceApprove(gasToken, l1CrossDomain, 0);
    }

    function sendMessageL2toL1(
        address target,
        bytes memory message
    ) internal {
        IArbSys(L2_CROSS_DOMAIN).sendTxToL1(
            target,
            message
        );
    }

}
"
    },
    "src/libraries/GroveLiquidityLayerHelpers.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

import { MainnetController } from "grove-alm-controller/src/MainnetController.sol";
import { RateLimitHelpers }  from "grove-alm-controller/src/RateLimitHelpers.sol";

import { IRateLimits } from "grove-alm-controller/src/interfaces/IRateLimits.sol";

/**
 * @notice Helper functions for Grove Liquidity Layer
 */
library GroveLiquidityLayerHelpers {

    // This is the same on all chains
    address private constant MORPHO = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;

    address public constant BLANK_ADDRESS_PLACEHOLDER = 0x00000000000000000000000000000000DeaDBeef;

    bytes32 public constant LIMIT_4626_DEPOSIT        = keccak256("LIMIT_4626_DEPOSIT");
    bytes32 public constant LIMIT_4626_WITHDRAW       = keccak256("LIMIT_4626_WITHDRAW");
    bytes32 public constant LIMIT_7540_DEPOSIT        = keccak256("LIMIT_7540_DEPOSIT");
    bytes32 public constant LIMIT_7540_REDEEM         = keccak256("LIMIT_7540_REDEEM");
    bytes32 public constant LIMIT_USDS_MINT           = keccak256("LIMIT_USDS_MINT");
    bytes32 public constant LIMIT_USDS_TO_USDC        = keccak256("LIMIT_USDS_TO_USDC");
    bytes32 public constant LIMIT_CENTRIFUGE_TRANSFER = keccak256("LIMIT_CENTRIFUGE_TRANSFER");
    bytes32 public constant LIMIT_AAVE_DEPOSIT        = keccak256("LIMIT_AAVE_DEPOSIT");
    bytes32 public constant LIMIT_AAVE_WITHDRAW       = keccak256("LIMIT_AAVE_WITHDRAW");
    bytes32 public constant LIMIT_CURVE_DEPOSIT       = keccak256("LIMIT_CURVE_DEPOSIT");
    bytes32 public constant LIMIT_CURVE_SWAP          = keccak256("LIMIT_CURVE_SWAP");
    bytes32 public constant LIMIT_CURVE_WITHDRAW      = keccak256("LIMIT_CURVE_WITHDRAW");

    uint16 public constant ETHEREUM_DESTINATION_CENTRIFUGE_ID  = 1;
    uint16 public constant PLUME_DESTINATION_CENTRIFUGE_ID     = 4;
    uint16 public constant AVALANCHE_DESTINATION_CENTRIFUGE_ID = 5;

    /**
     * @notice Onboard an ERC4626 vault
     * @dev This will set the deposit to the given numbers with
     *      the withdraw limit set to unlimited.
     */
    function onboardERC4626Vault(
        address rateLimits,
        address vault,
        uint256 depositMax,
        uint256 depositSlope
    ) internal {
        bytes32 depositKey = RateLimitHelpers.makeAssetKey(
            LIMIT_4626_DEPOSIT,
            vault
        );
        bytes32 withdrawKey = RateLimitHelpers.makeAssetKey(
            LIMIT_4626_WITHDRAW,
            vault
        );

        IRateLimits(rateLimits).setRateLimitData(depositKey, depositMax, depositSlope);

        IRateLimits(rateLimits).setUnlimitedRateLimitData(withdrawKey);
    }

    /**
     * @notice Onboard an ERC7540 vault
     * @dev This will set the deposit to the given numbers with
     *      the redeem limit set to unlimited.
     */
    function onboardERC7540Vault(
        address rateLimits,
        address vault,
        uint256 depositMax,
        uint256 depositSlope
    ) internal {
        bytes32 depositKey = RateLimitHelpers.makeAssetKey(
            LIMIT_7540_DEPOSIT,
            vault
        );
        bytes32 redeemKey = RateLimitHelpers.makeAssetKey(
            LIMIT_7540_REDEEM,
            vault
        );

        IRateLimits(rateLimits).setRateLimitData(depositKey, depositMax, depositSlope);
        IRateLimits(rateLimits).setUnlimitedRateLimitData(redeemKey);
    }

    function offboardERC7540Vault(
        address rateLimits,
        address vault
    ) internal {
        bytes32 depositKey = RateLimitHelpers.makeAssetKey(
            LIMIT_7540_DEPOSIT,
            vault
        );
        bytes32 redeemKey = RateLimitHelpers.makeAssetKey(
            LIMIT_7540_REDEEM,
            vault
        );

        IRateLimits(rateLimits).setRateLimitData(depositKey, 0, 0);
        IRateLimits(rateLimits).setRateLimitData(redeemKey,  0, 0);
    }

    /**
     * @notice Onboard an Aave token
     * @dev This will set the deposit to the given numbers with
     *      the withdraw limit set to unlimited.
     */
    function onboardAaveToken(
        address rateLimits,
        address token,
        uint256 depositMax,
        uint256 depositSlope
    ) internal {
        bytes32 depositKey = RateLimitHelpers.makeAssetKey(
            LIMIT_AAVE_DEPOSIT,
            token
        );
        bytes32 withdrawKey = RateLimitHelpers.makeAssetKey(
            LIMIT_AAVE_WITHDRAW,
            token
        );

        IRateLimits(rateLimits).setRateLimitData(depositKey, depositMax, depositSlope);
        IRateLimits(rateLimits).setUnlimitedRateLimitData(withdrawKey);
    }

    /**
     * @notice Onboard a Curve pool
     */
    function onboardCurvePool(
        address controller,
        address rateLimits,
        address pool,
        uint256 maxSlippage,
        uint256 swapMax,
        uint256 swapSlope,
        uint256 depositMax,
        uint256 depositSlope,
        uint256 withdrawMax,
        uint256 withdrawSlope
    ) internal {
        MainnetController(controller).setMaxSlippage(pool, maxSlippage);

        if (swapMax != 0) {
            bytes32 swapKey = RateLimitHelpers.makeAssetKey(
                LIMIT_CURVE_SWAP,
                pool
            );
            IRateLimits(rateLimits).setRateLimitData(swapKey, swapMax, swapSlope);
        }

        if (depositMax != 0) {
            bytes32 depositKey = RateLimitHelpers.makeAssetKey(
                LIMIT_CURVE_DEPOSIT,
                pool
            );
            IRateLimits(rateLimits).setRateLimitData(depositKey, depositMax, depositSlope);
        }

        if (withdrawMax != 0) {
            bytes32 withdrawKey = RateLimitHelpers.makeAssetKey(
                LIMIT_CURVE_WITHDRAW,
                pool
            );
            IRateLimits(rateLimits).setRateLimitData(withdrawKey, withdrawMax, withdrawSlope);
        }
    }

    function setUSDSMintRateLimit(
        address rateLimits,
        uint256 maxAmount,
        uint256 slope
    ) internal {
        bytes32 mintKey = RateLimitHelpers.makeAssetKey(
            LIMIT_USDS_MINT,
            MORPHO
        );

        IRateLimits(rateLimits).setRateLimitData(mintKey, maxAmount, slope);
    }

    function setUSDSToUSDCRateLimit(
        address rateLimits,
        uint256 maxUsdcAmount,
        uint256 slope
    ) internal {
        bytes32 usdsToUsdcKey = RateLimitHelpers.makeAssetKey(
            LIMIT_USDS_TO_USDC,
            MORPHO
        );

        IRateLimits(rateLimits).setRateLimitData(usdsToUsdcKey, maxUsdcAmount, slope);
    }

    function setCentrifugeCrosschainTransferRateLimit(
        address rateLimits,
        address centrifugeVault,
        uint16  destinationCentrifugeId,
        uint256 maxAmount,
        uint256 slope
    ) internal {
        bytes32 centrifugeCrosschainTransferKey = keccak256(abi.encode(LIMIT_CENTRIFUGE_TRANSFER, centrifugeVault, destinationCentrifugeId));

        IRateLimits(rateLimits).setRateLimitData(centrifugeCrosschainTransferKey, maxAmount, slope);
    }

}
"
    },
    "lib/xchain-helpers/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
"
    },
    "lib/xchain-helpers/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "lib/xchain-helpers/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "lib/grove-alm-controller/src/MainnetController.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.21;

import { IAToken }            from "aave-v3-origin/src/core/contracts/interfaces/IAToken.sol";
import { IPool as IAavePool } from "aave-v3-origin/src/core/contracts/interfaces/IPool.sol";

import { IERC7540 } from "forge-std/interfaces/IERC7540.sol";

import { AccessControl } from "openzeppelin-contracts/contracts/access/AccessControl.sol";

import { IERC20 }   from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import { IERC4626 } from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol";

import { Ethereum } from "grove-address-registry/Ethereum.sol";

import { IALMProxy }   from "./interfaces/IALMProxy.sol";
import { ICCTPLike }   from "./interfaces/CCTPInterfaces.sol";
import { IRateLimits } from "./interfaces/IRateLimits.sol";

import "./interfaces/ILayerZero.sol";

import { CCTPLib }                        from "./libraries/CCTPLib.sol";
import { CentrifugeLib }                  from "./libraries/CentrifugeLib.sol";
import { CurveLib }                       from "./libraries/CurveLib.sol";
import { IDaiUsdsLike, IPSMLike, PSMLib } from "./libraries/PSMLib.sol";

import { OptionsBuilder } from "layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";

import { RateLimitHelpers } from "./RateLimitHelpers.sol";

interface IATokenWithPool is IAToken {
    function POOL() external view returns(address);
}

interface IEthenaMinterLike {
    function setDelegatedSigner(address delegateSigner) external;
    function removeDelegatedSigner(address delegateSigner) external;
}

interface IMapleTokenLike is IERC4626 {
    function requestRedeem(uint256 shares, address receiver) external;
    function removeShares(uint256 shares, address receiver) external;
}

interface ISUSDELike is IERC4626 {
    function cooldownAssets(uint256 usdeAmount) external;
    function cooldownShares(uint256 susdeAmount) external;
    function unstake(address receiver) external;
}

interface IVaultLike {
    function buffer() external view returns (address);
    function draw(uint256 usdsAmount) external;
    function wipe(uint256 usdsAmount) external;
}

contract MainnetController is AccessControl {

    using OptionsBuilder for bytes;

    /**********************************************************************************************/
    /*** Events                                                                                 ***/
    /**********************************************************************************************/

    event CentrifugeRecipientSet(uint16 indexed centrifugeId, bytes32 recipient);
    event LayerZeroRecipientSet(uint32 indexed destinationEndpointId, bytes32 layerZeroRecipient);
    event MaxSlippageSet(address indexed pool, uint256 maxSlippage);
    event MintRecipientSet(uint32 indexed destinationDomain, bytes32 mintRecipient);
    event RelayerRemoved(address indexed relayer);

    /**********************************************************************************************/
    /*** State variables                                                                        ***/
    /**********************************************************************************************/

 

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Swap, Liquidity, Staking, Yield, Voting, Timelock, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x8b4a92f8375ef89165aef4639e640e077d7c656b|verified:true|block:23632464|tx:0x7de5ef7429c6c5344415af118db441584fa7a3d82905620b7d84942f5c1f1070|first_check:1761241156

Submitted on: 2025-10-23 19:39:19

Comments

Log in to comment.

No comments yet.