waveygist

Yearn TimelockController - 25/09/2026 18:06 - MEDIUM

Revision 2 — corrected. The first version of this report did not identify the vaults or strategies by name. It called the Spark looper an Aave looper, treated the max_debt units and the add_to_queue flag as unknown, and wrongly said the debt-setting calls were unverified. Every figure below was read on-chain on 25/09/2026, and the four calls were re-simulated as one sequential batch.

Summary

Adds Flex WETH yVault as a strategy of the yETH Recovery Vault with a 500 WETH max debt, and puts it in the default queue. Adds the wstETH/WETH Spark Looper (wstETH looped about 8× on SparkLend) to yvWETH-2 with a 10,000 WETH max debt, and leaves it out of the default queue. The 10,000 WETH cap is the same as every other yvWETH-2 strategy and the vault's deposit limit, but it is about 8.6× the vault's current 1,164 WETH in assets, so it does not limit anything in practice. No funds move until a debt allocator calls update_debt. MEDIUM

Analysis

Call 1–2: Flex WETH yVault → yETH Recovery Vault

0xd7a540ba3626c0aa66e7DB4088971d0CD64695B6 is the Yearn yETH Recovery Vault (yETH-Recovery), a V3 vault on API 3.0.4 whose asset is WETH (18 decimals).

  • add_strategy(new_strategy, add_to_queue=True) registers 0xfaC55fAFD0b55BFb8dD41F735EfCc195adA9891F, the Flex WETH yVault (yvFlexWETH, V3 API 3.1.0, WETH asset). The vault asserts that strategy.asset() == vault.asset, which holds here. Because add_to_queue=True, the strategy is appended to the default queue. That queue is used for withdrawals and for allocators that read it.
  • update_max_debt_for_strategy(strategy, 500e18) sets the cap to 500 WETH. max_debt is denominated in the vault's asset.
yETH Recovery Vault Now
Total assets 2,445.35 WETH, fully deployed, 0 idle
Deposit limit 3,000 WETH
Default queue WETH-1 yVault (1,956.28 WETH debt, uncapped max_debt) → WETH-2 yVault (489.07 WETH debt, 500 WETH cap)
Flex WETH yVault params not active (activation = 0, max_debt = 0)

The new 500 WETH cap equals the vault's existing cap for yvWETH-2 and is about 20% of current assets.

The Flex WETH yVault is small, with 10.01 WETH in total assets. All of it sits in the Flex yvWETH-2/WETH Lender (0x7E4a6A89583e117C641aB3ce8897209800A3F2E3); the WETH-1 yVault is also in its queue with 0 debt. Judging by the lender's name, it lends WETH against yvWETH-2 collateral. If so, Recovery Vault funds sent to Flex would take credit exposure to yvWETH-2, a vault the Recovery Vault already allocates to directly. I haven't verified this beyond the contract name.

Call 3–4: wstETH/WETH Spark Looper → yvWETH-2

0xAc37729B76db6438CE62042AE1270ee574CA7571 is the WETH-2 yVault (yvWETH-2, V3 API 3.0.2, WETH asset).

  • add_strategy(new_strategy, add_to_queue=False) registers 0x68A14629cb07c74259f481382fE8b6cFD8970121, the wstETH/WETH Spark Looper (ysWETH). "LSTAaveLooper" is only its Etherscan contract name. With add_to_queue=False, the looper stays out of the default queue: it can receive debt through update_debt, but default-queue withdrawals will not pull from it unless the queue is changed or a custom queue is passed.
  • update_max_debt_for_strategy(strategy, 10_000e18) sets the cap to 10,000 WETH.
WETH-2 yVault Now
Total assets 1,163.81 WETH, fully deployed, 0 idle
Deposit limit 10,000 WETH
Default queue (debt / max debt) WETH-1 yVault 190.51 / 10,000 · Morpho Y-WETH Compounder 0 / 10,000 · Spark wstETH to yvUSD Lender Borrower Accumulator 921.82 / 10,000 · Spark WETH/USDS (yvUSD) Lender Borrower 51.49 / 10,000
Spark Looper params not active (activation = 0, max_debt = 0)

The 10,000 WETH cap matches the vault's existing convention: every current strategy is capped at 10,000 WETH, which is also the deposit limit. The cap therefore is not a real limit, and a debt allocator could move all of yvWETH-2 into the looper.

Looper position:

Spark Looper Now
Lending pool SparkLend Pool 0xC13e21B648A5Ee794902342038FF3aDAB66BE987 (addresses provider 0x02C3eA4e34C0cBd694D2adFa2c690EECbC1793eE)
Collateral wstETH 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
Leverage 7.98× current, 8.0× target, 8.5× max
LTV 87.47% current vs 93% liquidation collateral factor
Total assets 1,034.03 WETH
Existing depositor WETH-1 yVault 0xc56413869c6CDf96496f2b1eF801fEDBdFA7dDB0, 1,033.63 WETH debt with a 2,000 WETH cap; it holds all of the looper's shares
Performance fee 0

yvWETH-2 already has indirect exposure to this looper through its 190.51 WETH allocation to WETH-1. This change would give it direct exposure, with a cap five times the one WETH-1 uses.

Execution check

A Tenderly sequential bundle simulation, with all calls sent from the timelock in batch order, succeeds for all four calls. Gas per call: 114,879 / 53,526 / 88,019 / 53,469. The earlier "reverted" diagnostics for calls 2 and 4 came from simulating each call on its own, where update_max_debt_for_strategy runs before its strategy exists and fails assert activation != 0, "inactive strategy". The timelock holds the required ADD_STRATEGY_MANAGER and MAX_DEBT_MANAGER roles on both vaults.

Risk assessment

MEDIUM. No assets move when this executes. It opens two new allocation paths:

  1. Recovery Vault → Flex WETH (500 WETH, in default queue). The Flex vault is new and small (10 WETH), and it may lend against yvWETH-2 collateral, which would add a layer of credit exposure to a vault the Recovery Vault already uses.
  2. yvWETH-2 → Spark Looper (10,000 WETH, not in queue). The looper runs an ~8× leveraged wstETH/WETH position on SparkLend. The main risks are liquidation from a wstETH/ETH depeg (87.5% LTV vs 93% liquidation), WETH borrow-rate spikes above staking yield, and exit slippage when unwinding. In practice the cap does not bind. Keeping the looper out of the default queue means ordinary withdrawals will not force an unwind.

Both paths depend on later update_debt calls, so real exposure is set by the debt allocator, not by this transaction.

Call Flow

From: 0x88Ba032be87d5EF1fbE87336B7090767F367BF73

  1. add_strategy(address,bool) on 0xd7a540ba3626c0aa66e7DB4088971d0CD64695B6 (Yearn yETH Recovery Vault, yETH-Recovery)
  2. update_max_debt_for_strategy(address,uint256) on 0xd7a540ba3626c0aa66e7DB4088971d0CD64695B6 (Yearn yETH Recovery Vault)
  3. add_strategy(address,bool) on 0xAc37729B76db6438CE62042AE1270ee574CA7571 (WETH-2 yVault, yvWETH-2)
  4. update_max_debt_for_strategy(address,uint256) on 0xAc37729B76db6438CE62042AE1270ee574CA7571 (WETH-2 yVault)

Reference

Address Label Role
0x88Ba032be87d5EF1fbE87336B7090767F367BF73 Yearn TimelockController Executor
0xd7a540ba3626c0aa66e7DB4088971d0CD64695B6 Yearn yETH Recovery Vault (yETH-Recovery) Call target, calls 1–2
0xfaC55fAFD0b55BFb8dD41F735EfCc195adA9891F Flex WETH yVault (yvFlexWETH) New strategy, calls 1–2
0xAc37729B76db6438CE62042AE1270ee574CA7571 WETH-2 yVault (yvWETH-2) Call target, calls 3–4
0x68A14629cb07c74259f481382fE8b6cFD8970121 wstETH/WETH Spark Looper (ysWETH) New strategy, calls 3–4
0xc56413869c6CDf96496f2b1eF801fEDBdFA7dDB0 WETH-1 yVault Existing looper depositor
0x7E4a6A89583e117C641aB3ce8897209800A3F2E3 Flex yvWETH-2/WETH Lender Flex WETH yVault's strategy
0xC13e21B648A5Ee794902342038FF3aDAB66BE987 SparkLend Pool Looper lending market
0xb3bd6B2E61753C311EFbCF0111f75D29706D9a41 RoleManager role_manager of all three vaults