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 ***/
/**********************************************************************************************/
Submitted on: 2025-10-23 19:39:19
Comments
Log in to comment.
No comments yet.