- Organization 
- @euler
- Engagement Type 
- Cantina Reviews 
- Period 
- - 
- Repositories 
Findings
Low Risk
3 findings
3 fixed
0 acknowledged
Informational
6 findings
5 fixed
1 acknowledged
Low Risk3 findings
- Default fee recipient can be set to zero address- Severity 
- Severity: Low
- Submitted by 
- slowfi 
 - Description- The function - setDefaultRecipientfrom contract- EulerSwapProtocolFeeConfigassigns- defaultRecipient = recipientwithout rejecting- address(0). If- defaultRecipientis set to zero, any pool without an override may resolve to a zero recipient, which can burn fees or break downstream transfers depending on token behavior.- Also it is important to know that this breaks the flow from the previous implementation. Previously if protocol fee recipient is address(0), the fees are deposited into the euler account vs now they are burned. As a result, the account's NAV won't increase (as expected). - Recommendation- Consider to enforce a non-zero - recipientin- setDefaultRecipient. If you need a “reset” control path, consider to add an explicit- unsetDefaultRecipient()(with a safe fallback recipient) or track a separate boolean sentinel rather than using- address(0).
- Missing events for administrative state changes- Severity 
- Severity: Low
- Submitted by 
- slowfi 
 - Description- The functions - setAdmin,- setDefault,- setOverride, and- removeOverridefrom contract- EulerSwapProtocolFeeConfigupdate critical configuration without emitting events (- src/EulerSwapProtocolFeeConfig.sol:48+). The absence of events reduces on-chain observability for indexers, off-chain monitoring, audits, and incident response, and makes it harder to track configuration history per pool.- The function setAdminfrom contractEulerSwapProtocolFeeConfigchanges the admin without an event.
- The function setDefaultfrom contractEulerSwapProtocolFeeConfigupdates the global default fee recipient/fee without an event.
- The function setOverridefrom contractEulerSwapProtocolFeeConfigsets a per-pool override without an event.
- The function removeOverridefrom contractEulerSwapProtocolFeeConfigclears a per-pool override without an event.
 - Recommendation- Consider to emit explicit events for each state transition, including previous and new values and indexed fields for efficient querying. For example: - event AdminUpdated(address indexed oldAdmin, address indexed newAdmin);event DefaultUpdated(address indexed oldRecipient, address indexed newRecipient, uint64 oldFee, uint64 newFee);event OverrideSet(address indexed pool, address indexed recipient, uint64 fee);event OverrideRemoved(address indexed pool);- Emit the corresponding event in - setAdmin,- setDefault,- setOverride, and- removeOverride. Also consider to emit events during contract initialization to establish the baseline configuration in logs.
- Activation marks pool active before initialization completes- Severity 
- Severity: Low
- Submitted by 
- slowfi 
 - Description- The function - activatefrom contract- EulerSwapManagementsets- s.status = 1at- src/EulerSwapManagement.sol:68before completing all initialization. If status- 1represents “active,” any external call made afterward (directly or via hooks) could observe the pool as active and enter flows that assume full initialization is finished.- Recommendation- Consider to mark the pool as “initializing” first (e.g., - s.status = 2) and only set- s.status = 1at the very end, after all external calls and state are finalized. Also consider to replace magic numbers with an- enumfor clarity and to enforce status checks (- require(s.status == Initializing/Inactive/Active)) at critical entry points.
Informational6 findings
- Remove unused code- Severity 
- Severity: Informational
- Submitted by 
- Sujith S 
 - Description- Multiple files under the scope of the audit contain unused file imports, error declarations, or event declarations. This dead code will affect code quality. Consider deleting any unused file imports, error, and event declarations referenced in this finding. 
- Improve code documentation- Severity 
- Severity: Informational
- Submitted by 
- Sujith S 
 - Description- In FundsLib.depositAssets(), the inline documentation mentions that only E_ZeroShares is handled by setting the amount to zero, but the code actually manages both E_ZeroShares and ZeroShares errors.
 - - /// @dev If the deposit fails with E_ZeroShares error, it safely returns 0 (this happens with very small amounts).+ /// @dev If the deposit fails with E_ZeroShares or ZeroShares error, it safely returns 0 (this happens with very small amounts).- All non-abstract contracts in the repository have inline documentation and a security contact before declaration, except the new EulerSwapManagementandEulerSwapcontracts.
 - + /// @title EulerSwapManagement contract+ /// @custom:security-contact [email protected]+ /// @author Euler Labs (https://www.eulerlabs.com/) contract EulerSwapManagement is EulerSwapBase {- The function eulerSwapCall()from contractIEulerSwapCalleedefines the flash-swap callback, but its parameters are sparsely documented. This can hinder integrators from implementing correct settlement logic, leading to subtle bugs. Consider to expand the NatSpec for eulerSwapCall to clarify integrators on received parameters.
 
- Internal function delegateToManagementImpl violates conventions- Severity 
- Severity: Informational
- Submitted by 
- Sujith S 
 - Description- The internal function - delegateToManagementImpl()violates standard Solidity conventions in two ways:- 
Naming Convention: Internal functions should be prefixed with an underscore _ to distinguish them from external/public functions clearly. The function is named delegateToManagementImpl()instead of_delegateToManagementImpl().
- 
File Organization: Internal and private functions are conventionally placed at the bottom of the contract, after all external and public functions. 
 - Consider fixing the above-mentioned convention issue. 
- Do not use EVC sub-accounts as fee recipients- Severity 
- Severity: Informational
- Submitted by 
- slowfi 
 - Description- The functions - setDefaultand- setOverridefrom contract- EulerSwapProtocolFeeConfig, and the fee transfer paths in- SwapLib(LP and protocol fee handling), do not remap EVC sub-accounts to their owners before ERC20 transfers. Unlike the registry’s bond redemption flow, which calls- evc.getAccountOwner(recipient)to resolve the owner, fee recipients are used as-is. As a result, configuring LP or protocol fee recipients to an EVC sub-account can direct fees to an address without control keys. Additionally, the activation guard only forbids- feeRecipient == eulerAccountfor the pool; other EVC sub-accounts are still permitted.- Recommendation- Consider to explicitly document that LP and protocol fee recipients must be externally owned addresses or controllable contract addresses, and should not be EVC sub-accounts. Include this constraint in deployment runbooks, admin UI validations, and configuration examples. Optionally, consider to note that the registry remaps bond recipients to owners, but fee flows currently do not, to avoid assumptions of parity. 
- Admin address must not be an EVC sub-account- Severity 
- Severity: Informational
- Submitted by 
- slowfi 
 - Description- The function - setAdminfrom contract- EulerSwapProtocolFeeConfigupdates the- adminwithout restricting it to a non-EVC sub-account. Subsequent admin-only calls use- onlyAdmin, which invokes- _authenticateCallerWithStandardContextState(true). If the stored- adminis an EVC sub-account, or if calls are routed through EVC with- onBehalfOfAccountset to a sub-account, authentication can fail, effectively bricking administrative functions for this contract.- Recommendation- Consider to document that - adminmust be a controllable EOA or standard contract address, not an EVC sub-account. Include this in deployment runbooks and any admin tooling. Optionally, consider to validate in ops tooling that- newAdminis not an EVC sub-account (e.g., warn when- evc.getAccountOwner(newAdmin) != address(0)).
- Protocol fee override can target non existent pool- State 
- Acknowledged
- Severity 
- Severity: Informational
- Submitted by 
- slowfi 
 - Description- The function - setOverridefrom contract- EulerSwapProtocolFeeConfigsets- overrides[pool]without validating that- poolis a real EulerSwap pool. This enables typos or stale addresses to create dead configuration entries that will never be used, complicating ops and audits.- Recommendation- Consider to verify the - poolagainst the factory/registry before storing the override (e.g.,- IEulerSwapFactory.isPool(pool)or equivalent), and revert if the pool is unknown. Also consider to emit an event so indexers can track successful validations.- Euler: There may exist a use case for overriding the fees before the pool creation to have the existing data at pool time creation. - Cantina managed: Acknowledged by Euler team.