Routed event for on-chain attribution. No fees are deducted — 100% of the user’s tokens reach the vault.
Current version: V3.2.0 on all chains (Ethereum, Base, Arbitrum, Optimism, HyperEVM, Katana). Mainnet upgraded 2026-04-25 in tx 0x2f7370658006e87972488159bc97dfc5cfef8067039e5127c8060c384ecbce21.
What Changed in V3.2.0
depositForAvailable(...)— new entry point that pullsmin(allowance, balance)frommsg.senderinstead of an explicitamountargument. Eliminates the calldata-amount-mismatch revert that bridge-fee underflows would otherwise cause: any 1-wei underdelivery from Across/Stargate would previously revert the whole composer call. Now the router sweeps whatever actually arrived.- ERC-4626 void-return fallback — vaults whose
deposit()returns no value (some forks of OpenZeppelin’s older 4626 reference) are detected viavrd.lengthand fall back to abalanceOfdelta to compute shares. Removes a class of “Adapter: shares not delivered” reverts on niche forks. - All V3.1.1 changes (open caller model, no whitelist) carried forward.
What Changed in V3.1.1
- Removed the
authorizedCallersgate ondepositFor/depositRequestFor. Any address can now call with an arbitraryuserargument, which is used purely for theRoutedevent and as the share recipient. This removes ops friction of whitelisting every new LiFi bridge-receiver contract (ReceiverAcrossV3/V4, ReceiverStargateV2, ReceiverChainflip, per-chain Executors, etc.) and keeps the composer flow working across bridge updates without a router upgrade. - Security note: funds are pulled from
msg.sender(the caller must own the tokens or have been approved), so no one can spend a third party’s balance. The remaining risk is attribution spoofing — anyone can emit aRoutedevent with an unrelateduser/partnerId. Attribution consumers (revenue share, referrer tiering) should treatRoutedevents as advisory and cross-check against on-chain share balances where strict correctness matters. authorizedCallersstorage slot and the admin functions (setAuthorizedCaller,setAuthorizedCallerBatch) are preserved for forward compatibility but are not consulted by the current implementation.
What Changed in V3.1.0
- 8-arg
depositForwithminSharesOut— on-chain slippage floor. The 7-arg form is kept as a compatibility shim (forwards withminSharesOut=0). - Shares returned explicitly —
_executeVaultCallnow returns the exact shares minted, cross-checked againstbalanceOf(recipient)to detect silent failures in downstream adapters. Routedevent now includesshares— previously indexers had to read vault share balances to compute this.- Introduced
authorizedCallerswhitelist (removed again in V3.1.1 — see above).
Contract Addresses
All chains on V3.2.0 as of 2026-04-25. Same proxy address on Katana and HyperEVM is coincidental (CREATE2 on separate chains).| Chain | Chain ID | Proxy Address |
|---|---|---|
| Ethereum | 1 | 0x85f76c1685046Ea226E1148EE1ab81a8a15C385d |
| Base | 8453 | 0xF6B7723661d52E8533c77479d3cad534B4D147Aa |
| Arbitrum | 42161 | 0xC5700f4D8054BA982C39838D7C33442f54688bd2 |
| Optimism | 10 | 0x7554937Aa95195D744A6c45E0fd7D4F95A2F8F72 |
| Monad | 143 | 0xCD8dfD627A3712C9a2B079398e0d524970D5E73F |
| HyperEVM | 999 | 0xa682CD1c2Fd7c8545b401824096A600C2bD98F69 |
| Katana | 747474 | 0xa682CD1c2Fd7c8545b401824096A600C2bD98F69 |
How Deposits Work
Same-Chain Deposit
Cross-Chain Deposit (Two-Step)
Used for vault types that aren’t composable with LiFi’s Composer (Midas, Veda, Custom, IPOR, Lido):Cross-Chain Deposit (Single-Step, Composer)
Used for Morpho and other standard ERC-4626 vaults. LiFi bridges + callsdepositFor atomically on the destination:
user. V3.1.1 accepts any msg.sender — only msg.sender’s own balance can be pulled.
depositForAvailable (V3.2.0+)
Cross-chain composer entry point — pullsmin(allowance, balance) from msg.sender instead of an explicit amount. Used by the /v1/quote/build API for any composer route so the destination call cannot revert on bridge underdelivery.
depositFor would revert at safeTransferFrom. depositForAvailable sweeps the actual balance and proceeds.
depositFor
Primary entry point for same-chain user-initiated deposits. Two overloaded forms are exposed for backward compatibility:msg.sender is still the token source (safeTransferFrom(msg.sender, ...)), so only the caller’s own balance can ever be spent.
depositRequestFor
For vaults with async deposit queues (Yearn-stylerequestDeposit):
DepositRequestRouted(partnerId, partnerType, user, vault, asset, amount, requestId).
Events
Vault Dispatch Priority
_executeVaultCall picks the deposit path in this order:
| Priority | Mapping | Call emitted |
|---|---|---|
| 1 | vaultAdapters[vault] | IVaultAdapter(adapter).deposit(...) |
| 2 | midasVaults[vault] | midasIssuance.depositInstant(asset, amt18, 0, 0) |
| 3 | vedaTellers[vault] | teller.deposit(asset, amount, 0) |
| 4 | lidoDepositQueues[vault][asset] | queue.deposit(uint224(amount), address(0), []) |
| 5 | isERC4626 == true | vault.deposit(amount, recipient) |
| 6 | fallback | vault.syncDeposit(amount, recipient, address(0)) |
balanceOf(recipient) deltas to fail closed.
Vault Adapter Pattern
New vault protocols can be added without a router upgrade. Deploy an adapter implementingIVaultAdapter:
amount of asset to the adapter and calls adapter.deposit(...). The adapter must (a) return the actual share count and (b) deliver those shares to recipient. Retained assets on the adapter revert the deposit.
Register an adapter:
Caller Model (V3.1.1)
V3.1.1 removes the caller whitelist. Any address may calldepositFor(..., user=<anyone>). The admin setters (setAuthorizedCaller, setAuthorizedCallerBatch) and the authorizedCallers mapping still exist in storage but are not consulted by the current implementation — they remain in place so the whitelist can be re-enabled in a future release without a storage-layout migration.
Why the gate was removed: LiFi routes composer deposits through several different bridge-specific receivers (ReceiverAcrossV3, ReceiverAcrossV4, ReceiverStargateV2, ReceiverChainflip, plus per-chain Executors), and each new bridge LiFi supports would otherwise require a whitelist update on every router proxy. V3.1.0’s Whitelist model couldn’t keep up.
Trade-off: anyone can emit a Routed event with a spoofed user / partnerId. Funds are safe (safeTransferFrom pulls from msg.sender only), but attribution consumers should treat Routed events as informational and verify with on-chain share balances when exact attribution matters.
Admin Functions
Owner-only:| Function | Purpose |
|---|---|
setVaultAdapter(vault, adapter) | Register a vault protocol integration |
setVaultAdapterBatch(vaults, adapters) | Batch variant |
setMidasVault(token, iv) | Map a Midas share token to its issuance vault |
setVedaTeller(vault, teller) | Map a Veda BoringVault to its teller |
setLidoDepositQueue(vault, asset, q) | Map a Lido vault to its SyncDepositQueue |
setAuthorizedCaller(caller, ok) | Whitelist a composer caller |
setVaultAllowed(vault, ok) | Optional vault-level whitelist |
setVaultWhitelistEnabled(bool) | Enable/disable the vault allowlist |
pause() / unpause() | Circuit breaker |
rescueERC20(token, to, amt) | Recover any stuck ERC-20 (should never be needed) |
withdrawETH() | Withdraw any native ETH that landed on the contract |
transferOwnership(newOwner) | Step 1 of two-step ownership transfer |
acceptOwnership() | Step 2 — pending owner accepts |
Security Properties
- No custody — contract never holds a positive balance between transactions. Any stuck balance is rescuable by owner.
- No fees — 100% of the deposited amount is forwarded to the vault. Attribution is event-only.
- Access-controlled
userspoofing — non-authorized callers can only passuser == msg.sender. - Reentrancy protected — all state-mutating deposit paths use the OpenZeppelin
ReentrancyGuard. - Optional vault allowlist —
vaultWhitelistEnabledcan gate which vaults can be deposited into. Off by default. - Two-step ownership — prevents accidental transfer to dead addresses.
- UUPS upgradeable — owner-controlled, storage layout preserved across all upgrades via
_deprecated_padding.
API Integration Notes
The Yieldo API (/v1/quote/build) returns pre-encoded depositFor calldata using the 7-arg form for maximum backward compat during the rollout window. After all integrators have migrated, the API will switch to the 8-arg form with a computed minSharesOut for real slippage protection.
Users never sign an intent or typed-data message for deposits anymore — they only sign the approval (for ERC-20) and the deposit transaction itself. Withdrawals are handled by the vault protocols directly (ERC-4626 redeem / withdraw) and are outside the router’s scope.