Platform: Code4rena
Start Date: 10/11/2023
Pot Size: $28,000 USDC
Total HM: 5
Participants: 185
Period: 5 days
Judge: 0xDjango
Id: 305
League: ETH
Rank: 142/185
Findings: 1
Award: $2.76
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: m_Rassska
Also found by: 0x1337, 0xAadi, 0xHelium, 0xLeveler, 0xblackskull, 0xbrett8571, 0xepley, 0xffchain, 0xluckhu, 0xmystery, 0xrugpull_detector, 0xvj, ABAIKUNANBAEV, Aamir, AerialRaider, Amithuddar, Bauchibred, Bauer, CatsSecurity, Cryptor, Daniel526, Draiakoo, Eigenvectors, ElCid, GREY-HAWK-REACH, Inspecktor, Juntao, King_, LinKenji, Madalad, MaslarovK, Matin, MatricksDeCoder, McToady, Noro, PENGUN, Pechenite, Phantasmagoria, RaoulSchaffranek, SBSecurity, SandNallani, Shaheen, Soul22, Stormreckson, T1MOH, Tadev, TeamSS, TheSchnilch, Topmark, Tumelo_Crypto, Udsen, Yanchuan, ZanyBonzy, _thanos1, adeolu, adriro, alexfilippov314, almurhasan, amaechieth, anarcheuz, ayden, baice, bareli, boredpukar, bronze_pickaxe, btk, cartlex_, catellatech, chaduke, cheatc0d3, circlelooper, codynhat, crack-the-kelp, critical-or-high, debo, deepkin, desaperh, dipp, eeshenggoh, evmboi32, ge6a, gesha17, glcanvas, gumgumzum, hals, hihen, hunter_w3b, jasonxiale, joaovwfreire, ke1caM, leegh, lsaudit, marchev, merlinboii, niser93, osmanozdemir1, paritomarrr, passion, pep7siup, phoenixV110, pipidu83, poneta, ro1sharkm, rouhsamad, rvierdiiev, sakshamguruji, seerether, shealtielanz, soliditytaker, spark, squeaky_cactus, stackachu, supersizer0x, tallo, taner2344, turvy_fuzz, twcctop, ubl4nk, wisdomn_, xAriextz, zach, zhaojie, zhaojohnson, ziyou-
2.7592 USDC - $2.76
ChainlinkPriceOracle
.The ChainlinkPriceOracle
contract inherits from IPriceFetcher
instead of directly implementing the ILRTOracle
interface. This is confusing and doesn't fully capture the intent that this is an oracle implementation.
/// @title ChainlinkPriceOracle Contract /// @notice contract that fetches the exchange rate of assets from chainlink price feeds contract ChainlinkPriceOracle is IPriceFetcher, LRTConfigRoleChecker, Initializable { // ... }
This shows ChainlinkPriceOracle
inheriting from IPriceFetcher
instead of ILRTOracle
.
The ChainlinkPriceOracle
contract currently inherits from:
IPriceFetcher
LRTConfigRoleChecker
Initializable
But since this is a specific implementation of the ILRTOracle
interface, it would be clearer to do:
contract ChainlinkPriceOracle is ILRTOracle, LRTConfigRoleChecker, Initializable { // ... }
The benefits:
Makes the intent more clear - this implements the oracle interface
Avoids confusing naming with IPriceFetcher
Allows directly implementing ILRTOracle
methods
So in summary, changing the inheritance to directly implement the oracle interface better captures the intent and simplifies the contract API.
Change the inheritance to:
contract ChainlinkPriceOracle is ILRTOracle, LRTConfigRoleChecker, Initializable { // ... }
Directly inheriting from the oracle interface better conveys that this is an implementation contract.
The maxApproveToEigenStrategyManager
function approves the EigenLayer manager contract to spend max uint256 tokens from the NodeDelegator. However, depositAssetIntoStrategy
then only deposits a specific balance
amount.
This max approval is unnecessary and increases the damage if the EigenLayer manager contract is compromised.
maxApproveToEigenStrategyManager
approves max tokens: https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/NodeDelegator.sol#L35-L68
The depositAssetIntoStrategy
function in NodeDelegator approving the maximum token amount to the EigenLayer manager does seem unnecessary.
Specifically, this line: L45
IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);
Is approving the EigenLayer manager contract to spend up to 2^256 - 1 tokens.
However, it seems this large approval is not needed, since depositAssetIntoStrategy
then deposits a specific balance
amount: L67
IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy( IStrategy(strategy), token, balance );
So it would be better to approve only the balance
amount that is actually being deposited:
IERC20(asset).approve(eigenlayerStrategyManagerAddress, balance);
The risk of approving max is it would allow the EigenLayer manager to potentially drain all tokens if compromised, rather than only the deposit amount.
So approving the specific deposit amount is likely sufficient and reduces unnecessary over-approval.
maxApproveToEigenStrategyManager
, approve only the balance
amount instead of max tokens:IERC20(asset).approve(eigenlayerStrategyManagerAddress, balance);
balance
inline in depositAssetIntoStrategy
.This reduces the excess token approval and limits damages in case of an EigenLayer manager compromise.
Currently, the DEFAULT_ADMIN_ROLE
in LRTConfig
has full privileges including:
So it has a mix of administrative, governance and upgrade related responsibilities.
My suggestion is to split the DEFAULT_ADMIN_ROLE
into two separate roles:
Admin Role: This will be responsible for urgent admin functions like pausing, upgrades, and changing admin accounts.
Governance Role: This will be responsible for governance related activities like adding assets, changing parameters, risk management etc.
Some benefits of splitting the single admin role:
Clear separation between urgent admin tasks and governance activities.
Different account/parties can manage each role.
Admin role has minimum privileges needed for emergency scenarios.
Governance party cannot perform sensitive tasks like upgrading contracts or changing admin accounts.
Governance activities like adding assets require social consensus so may need broader oversight.
To implement this, the AccessControl
roles can be split as:
// Admin role bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); // Governance role bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
The privileges would be divided between the two roles.
#0 - c4-pre-sort
2023-11-18T01:02:39Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-12-01T18:50:45Z
fatherGoose1 marked the issue as grade-b