waveygist

Safe MultiSendCallOnly - 23/10/2026 06:43 - HIGH

Summary

Stops future vesting in three YFI escrows and claws back unvested tokens to yChad, permanently clearing escrow ownership. Transfers Voting guardianship and Executor management to yChad, giving it control over operator permissions and arbitrary-call execution. Temporarily enables yChad as an operator to execute six downstream calls whose effects are not established by the supplied context, then disables that operator permission. Moves 401,000 yvUSDC-1 shares from TreasuryVault through yChad to Funding Distributor. Irreversible vesting cuts, broad administrative control changes, and unresolved downstream effects create material risk.

Execution Context

Outer call is DELEGATECALL from the Safe (0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52) into 0x40A2aCCbd92BCA938b02010E17A5b8929b49130D (Safe MultiSendCallOnly). Inner calls execute in the Safe's own storage context: msg.sender = 0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52. Every inner transaction is a plain CALL from the Safe, so the batch was simulated as one ordered bundle of those calls from the Safe — the same execution the multisend performs.

Analysis

Vesting revocations — calls 1–3

Each revoke() uses its default arguments: the execution timestamp and yChad as the clawback beneficiary. The verified source requires yChad to own the escrow and execution to occur before its end time. It transfers the amount returned by _locked(ts) to yChad, sets disabled_at to that timestamp, and permanently clears ownership.

The simulations show these YFI recoveries:

Each escrow’s token() getter identifies YFI with 18 decimals. Ownership changes from yChad → zero, preventing further owner-authorized revocations. The action terminates future vesting and redirects the unvested allocation to yChad; no onward transfer of the recovered YFI appears in the supplied calls.

Administrative handover and execution — calls 4–8

accept_guardian() makes yChad the Voting guardian and clears pending_guardian. accept_management() similarly makes yChad the Executor manager and clears pending_management. Both pending roles already name yChad, so these calls complete previously initiated handovers.

Executor management has a concrete control demonstrated by the source: it can add or remove operators. Operators can submit scripts that make arbitrary external calls from the Executor.

Calls 6–8 use that authority in sequence:

  1. Set yChad’s operator permission from False → True.
  2. Execute a script containing six downstream calls, each carrying the four-byte selector 0x4759be10.
  3. Set yChad’s operator permission back to False.

The script targets Staking Reward Distributor, Delegated Staking Reward Distributor, two unnamed contracts, Vote Boost Reward Distributor, and Funding Distributor. The Executor parses each target and calldata length, then invokes it using raw_call; downstream contracts therefore receive calls from the Executor rather than directly from yChad.

The script simulation succeeds. Removing operator permission afterward ends yChad’s immediate execution permission, but does not remove its management authority: yChad retains the ability to authorize operators again. The guardian handover also persists.

Treasury share movement — calls 9–10

toGovernance() transfers 401,000 yvUSDC-1 shares from TreasuryVault to its governance account, yChad. The source restricts this function to governance and sends the specified token directly to that same account.

The final transfer() forwards 401,000 yvUSDC-1 shares from yChad to Funding Distributor. Simulations confirm both legs, so yChad acts as the intermediate recipient for this allocation.

These are vault shares, not a transfer or redemption of 401,000 USDC. Ownership of the shares—and their associated claim on vault assets—moves from TreasuryVault to Funding Distributor. The two legs move the same allocation; they do not represent twice the funding.

Risk assessment

HIGH risk. The batch combines irreversible vesting cuts with persistent administrative handovers and a treasury allocation. Escrow recipients lose future vesting, while yChad receives the recovered YFI and becomes unable to reverse the ownership clearing through the shown escrow function.

Executor management gives yChad lasting control over who may perform arbitrary-call execution. The temporary operator grant limits the duration of that particular permission, but does not constrain the retained manager’s ability to grant it again. The six downstream calls also succeed without their effects being established in the supplied context.

All ten listed calls simulate successfully, supporting execution feasibility and the reported token movements. Success alone does not establish the safety of downstream behavior or the intended use of the transferred shares.

Not verified

  • The function corresponding to selector 0x4759be10 and its state or asset effects at each of the six downstream targets.
  • The Voting guardian’s powers beyond accepting the role; their enforcing code is not supplied.
  • Funding Distributor’s downstream handling of the 401,000 yvUSDC-1 shares and their underlying USDC redemption value.

Call Flow

From: 0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52 (yChad (Yearn multisig/daddy))

  1. revoke() on 0xFC1568689b020e2e54AF3F8DB94017F72b621B32 (Simple Vesting Escrow)

    • no inputs
    • Batch simulation: SUCCESS, gas 61,935 (run in batch order on the state left by the preceding calls)
  2. revoke() on 0x81854559F1633cF455259b8765d69f8a5A62aD89 (Simple Vesting Escrow)

    • no inputs
    • Batch simulation: SUCCESS, gas 61,935 (run in batch order on the state left by the preceding calls)
  3. revoke() on 0x450BFA6AaBC9341c52cCA82E8Fc7AAFfd3b6f0E5 (Simple Vesting Escrow)

    • no inputs
    • Batch simulation: SUCCESS, gas 61,935 (run in batch order on the state left by the preceding calls)
  4. accept_guardian() on 0x543e8871562a8C53E8B6a26835aeEcB3a5A13070 (Voting)

    • no inputs
    • Batch simulation: SUCCESS, gas 27,530 (run in batch order on the state left by the preceding calls)
  5. accept_management() on 0xac7D4A37Ba61C2CAc7f64d3e2b5773D85613Fe7b (Executor)

    • no inputs
    • Batch simulation: SUCCESS, gas 27,529 (run in batch order on the state left by the preceding calls)
  6. set_operator(address,bool) on 0xac7D4A37Ba61C2CAc7f64d3e2b5773D85613Fe7b (Executor)

    • address _operator: 0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52 (yChad (Yearn multisig/daddy))
    • bool _flag: True
    • Batch simulation: SUCCESS, gas 47,509 (run in batch order on the state left by the preceding calls)
  7. execute(bytes) on 0xac7D4A37Ba61C2CAc7f64d3e2b5773D85613Fe7b (Executor)

    • bytes _script: 0x95547ede56cf74b73dd78a37f547127dffda6113000000000000000000000004759be10c952b31960c97e76362ac340d07d183ada15e3d6e000000000000000000000004759be10c2548bf65916fdabb5a5673fc4225011ff29ee884000000000000000000000004759be10c7efc3953bed2fc20b9f825ebffab1cc8b072a000000000000000000000000004759be10ca139250bcb3880a97c75fd4287d5bf2a6caf98a3000000000000000000000004759be10cbcc932e4750c3e465a7e54a06a34f9edf8f6116b000000000000000000000004759be10c
    • Batch simulation: SUCCESS, gas 102,574 (run in batch order on the state left by the preceding calls)
  8. set_operator(address,bool) on 0xac7D4A37Ba61C2CAc7f64d3e2b5773D85613Fe7b (Executor)

    • address _operator: 0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52 (yChad (Yearn multisig/daddy))
    • bool _flag: False
    • Batch simulation: SUCCESS, gas 25,597 (run in batch order on the state left by the preceding calls)
  9. toGovernance(address,uint256) on 0x93A62dA5a14C80f265DAbC077fCEE437B1a0Efde (TreasuryVault)

    • address _token: 0xBe53A109B494E5c9f97b9Cd39Fe969BE68BF6204 (USDC-1 yVault (yvUSDC-1, 6 dec) — Yearn V3 Vault)
    • uint256 _amount: 401,000,000,000
    • Batch simulation: SUCCESS, gas 44,065 (run in batch order on the state left by the preceding calls)
  10. transfer(address,uint256) on 0xBe53A109B494E5c9f97b9Cd39Fe969BE68BF6204 (USDC-1 yVault (yvUSDC-1, 6 dec) — Yearn V3 Vault)

  • address receiver: 0xbCc932e4750C3E465A7E54A06A34F9EdF8f6116b (Funding Distributor)
  • uint256 amount: 401,000,000,000
  • Batch simulation: SUCCESS, gas 54,706 (run in batch order on the state left by the preceding calls)

Current State