F-2025-0016·wrong-approval-target

Silo_Module::silo_execute will revert as approval is to the wrong contract

Fixedvaultetfstrategyd2-contracts
TL;DR

silo_execute approves actions[0].silo for the input asset, but the Silo router (not the silo itself) is the one that pulls tokens for the batched execution. As written, the first action of the bundle fails approval-side and the gas savings the function was meant to deliver are lost.

Severity
LOW
Impact
LOW
Likelihood
HIGH
Method
MManual review
CAT.
Complexity
LOW
Exploitability
LOW
02Section · Description

Description

D2 integrates with the lending protocol Silo via the Silo_Module, which facilitates communication with the Silo contract.

One of the functions in this module is Silo_Module::silo_execute, which allows bundling multiple calls and sending them to the Silo Router to optimize gas usage:

solidity
function silo_execute(ISiloRouter.Action[] calldata actions) external onlyRole(EXECUTOR_ROLE)
nonReentrant {
// @audit approval should be to `address(router)` as it is the one facilitating the actions
IERC20(actions[0].asset).approve(actions[0].silo, actions[0].amount);
router.execute(actions);
}

The issue here is that the approval is incorrectly granted to actions[0].silo, whereas it should be granted to router, as this is the contract responsible for handling all token transfers when router.execute(actions) is invoked.

The audit included a Foundry PoC (test_silo_execute) that builds a bundle of (deposit, borrow, repay, withdraw) actions and shows the execution fails on the deposit step due to insufficient allowance for the router.

03Section · Impact

Impact

The silo_execute function will fail due to the incorrect approval. While this does not affect the overall functionality of Silo_Module (since all necessary actions can still be executed separately), the gas savings intended by bundling transactions will be lost, leading to higher transaction costs.

04Section · Recommendation

Recommendation

Consider approving router instead:

solidity
- IERC20(actions[0].asset).approve(actions[0].silo, actions[0].amount);
+ IERC20(actions[0].asset).approve(address(router), actions[0].amount);
05Section · Resolution

Resolution

D2: Fixed in ea03f4d.

Cyfrin: Verified.

Status
Fixed
Fix commit
ea03f4d
F-2025-0016