Execution-level enforcement for LayerZero V2 OVault.
- What is YZEnforcedComposer?
- When Does It Work?
- How Does It Work?
- Architecture Diagrams
- Flow Diagrams
- Reference
YZEnforcedComposer is a smart contract extension for LayerZero's VaultComposerSync. It adds risk management controls to cross-chain vault deposits and redemptions.
User (Spoke Chain) βββΊ Deposit 1000 USDC βββΊ Vault (Hub Chain)
β
βΌ
No limits!
No caps!
No control!
Without enforcement:
- Whale can deposit unlimited amount β TVL risk
- Single user can dominate vault β centralization risk
- No emergency pause β no circuit breaker
- Anyone can deposit β no access control
User (Spoke Chain) βββΊ Deposit 1000 USDC
β
βΌ
YZEnforcedComposer
β
βββΊ Check: TVL cap OK?
βββΊ Check: User cap OK?
βββΊ Check: Not paused?
βββΊ Check: Whitelisted?
β
βΌ
Vault (Hub Chain)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LayerZero V2 OVault Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β SPOKE CHAIN (eg, Ethereum) β HUB CHAIN (eg, Arbitrum) β
β βββββββββββββββββββββββββββββ β βββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββ β ββββββββββββββββββββββββ β
β β AssetOFT β β β AssetOFT (OFT) β β
β β (OFT) β β β β β
β β β β β Part of Asset Mesh β β
β β Part of Asset β β ββββββββββββββββββββββββ β
β β Mesh β β β β
β ββββββββββ¬βββββββββ β β β
β β β βΌ β
β β 1. send() with composeMsg β ββββββββββββββββββββββββ β
β β β β LayerZero Endpoint β β
β βΌ β β β β
β βββββββββββββββββββββββ β β 2. lzReceive() β β
β β LayerZero Endpoint β β β - OFT mints β β
β β β β β assets to β β
β β β β β composer β β
β βββββββββββββββββββββββ β β - Calls β β
β β β β lzCompose() β β
β β β ββββββββββββ¬ββββββββββββ β
β β β β β
β βββββββββ cross-chain message ββββΌβββββββββββββββ β
β β β β
β β βΌ β
β β ββββββββββββββββββββββββ β
β β β VaultComposerSync β β
β β β (ReentrancyGuard) β β
β β β β β β
β β β βΌ β β
β β β ββββββββββββββββββββ β β
β β β βYZEnforcedComposerβ β β
β β β β β β β
β β β β 3. Enforcement β β β
β β β β Checks β β β
β β β ββββββββββ¬ββββββββββ β β
β β β β β β
β β β βΌ β β
β β β ββββββββββββββββββββ β β
β β β β ERC4626 Vault β β β
β β β ββββββββββββββββββββ β β
β β ββββββββββββββββββββββββ β
β β β
β β ββββββββββββββββββββββββ β
β β β ShareOFTAdapter β β
β β β (Lockbox Model) ββββ Share OFT wraps
β β β β β vault shares
β β β Locks vault shares β β
β β β for cross-chain β β
β β ββββββββββββββββββββββββ β
β β β
ββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββ
β KEY ARCHITECTURE POINTS β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β’ Two OFT Meshes: Asset Mesh + Share Mesh β
β β’ ShareOFTAdapter uses lockbox model on hub β
β β’ Sequential flow: lzReceive β lzCompose β
β β’ Permissionless recovery: anyone can retry β
β β’ YZEnforcedComposer adds: caps, pause, whitelist β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β YZEnforcedComposer β
β β
β βββββββββββββββββββ βββββββββββββββββββ β
β β depositAndSend()β β redeemAndSend() β β
β β β β β β
β β Called when: β β Called when: β β
β β User deposits β β User redeems β β
β β assets to vaultβ β shares for β β
β β β β assets β β
β ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ β
β β β β
β βΌ βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β _depositAndSend() β β
β β β β
β β Enforcement happens HERE: β β
β β β’ TVL cap check β β
β β β’ User cap check β β
β β β’ Pause check β β
β β β’ Whitelist check β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Also triggered via lzCompose() for cross-chain operations β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Context | Entry Point | Flow |
|---|---|---|
| Local deposit | depositAndSend() |
User β Composer β Vault |
| Local redeem | redeemAndSend() |
User β Composer β Vault |
| Cross-chain deposit | lzCompose() β _depositAndSend() |
OFT β Composer β Vault |
| Cross-chain redeem | lzCompose() β _redeemAndSend() |
OFT β Composer β Vault |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Enforcement Flow β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β User Action β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Check: Paused? β β
β β β β
β β paused == true βββΊ REVERT βββΊ YZ_Paused β β
β βββββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Check: Whitelist? β β
β β β β
β β whitelistEnabled && !whitelist[user] β β
β β β β β
β β βΌ β β
β β REVERT βββΊ "YZ: not whitelisted" β β
β βββββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Check: TVL Cap? β β
β β β β
β β tvlCap > 0 && β β
β β currentTVL + amount > tvlCap β β
β β β β β
β β βΌ β β
β β REVERT βββΊ YZ_TVLCapExceeded β β
β βββββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Check: User Cap? β β
β β β β
β β userCap[user] > 0 && β β
β β userDeposits[user] + amount > userCap β β
β β β β β
β β βΌ β β
β β REVERT βββΊ YZ_UserCapExceeded β β
β βββββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Check: Vault Capacity? β β
β β β β
β β amount > vault.maxDeposit(this) β β
β β β β β
β β βΌ β β
β β REVERT βββΊ ERC4626ExceededMaxDeposit β β
β βββββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β ALL CHECKS PASSED β β
β β β β
β β Execute: super._depositAndSend() β β
β β β β
β β Update: userDeposits[user] += actual β β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
OVault operations have only two possible final outcomes:
- Success - Assets deposited, shares minted, cross-chain transfer completed
- Failed (but Refunded) - Assets returned to user on source chain
This is enforced by LayerZero's architecture. No partial state, no stuck funds.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Automatic Refund on Enforcement Revert β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β lzCompose() called by LayerZero Endpoint β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β try handleCompose() β β
β β β β β
β β βΌ β β
β β _depositAndSend() β β
β β β β β
β β βΌ β β
β β YZ Enforcement Check β β
β β β β β
β β βββΊ REVERT (cap exceeded) β β
β β β β β β
β β β βΌ β β
β β β βββββββββββββββββββ β β
β β β β Caught by try/ β β β
β β β β catch in β β β
β β β β lzCompose() β β β
β β β ββββββββββ¬βββββββββ β β
β β β β β β
β β β βΌ β β
β β β βββββββββββββββββββ β β
β β β β _refund() called β β β
β β β β β β β
β β β β Assets sent backβ β β
β β β β to user on β β β
β β β β source chain β β β
β β β βββββββββββββββββββ β β
β β β β β
β β βββΊ SUCCESS β β
β β β β β
β β βΌ β β
β β Vault deposit proceeds β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Failure Mode | What Happens | User Action |
|---|---|---|
Insufficient msg.value |
Revert propagates to Endpoint | Retry with correct msg.value |
| Slippage exceeded | Caught by try-catch β _refund() |
Adjust slippage tolerance, retry |
| YZ Enforcement (cap/pause) | Caught by try-catch β _refund() |
Wait for cap increase or unpause |
| Vault error | Caught by try-catch β _refund() |
Check vault state |
Note: Slippage failures are retriable with adjusted tolerance. Enforcement failures (TVL cap, pause) require admin intervention before retry.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Inheritance Tree β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββ β
β β ReentrancyGuard β β
β β (OpenZeppelin) β β
β ββββββββββββ¬βββββββββββ β
β β β
β β protects against reentrant calls β
β β β
β βΌ β
β βββββββββββββββββββββββ β
β β VaultComposerSync β β
β β (LayerZero) β β
β β β β
β β - VAULT β immutable β
β β - ASSET_OFT β immutable β
β β - SHARE_OFT β immutable β
β β - ENDPOINT β immutable β
β β β β
β β + depositAndSend() β public β
β β + redeemAndSend() β public β
β β + lzCompose() β external β
β β β β
β β # _depositAndSend()β virtual βββ OVERRIDE β
β β # _redeemAndSend() β virtual βββ OVERRIDE β
β β # _deposit() β virtual β
β β # _redeem() β virtual β
β β # _assertSlippage()β virtual β
β ββββββββββββ¬βββββββββββ β
β β β
β β extends β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β YZEnforcedComposer β β
β β β β
β β State: β β
β β - admin address β β
β β - pendingAdmin address β β
β β - tvlCap uint256 β β
β β - userDepositCap mapping(address=>uint256)β β
β β - userDeposits mapping(address=>uint256)β β
β β - userShares mapping(address=>uint256)β β
β β - depositsPaused bool β β
β β - redemptionsPaused bool β β
β β - whitelistEnabled bool β β
β β - whitelist mapping(address=>bool) β β
β β - _tempSharesMinted uint256 β β
β β β β
β β Admin Functions: β β
β β + setTVLCap() β β
β β + setUserCap() β β
β β + batchSetUserCaps() β β
β β + pauseDeposits() / unpauseDeposits() β β
β β + pauseRedemptions() / unpauseRedemptions() β β
β β + pauseAll() / unpauseAll() β β
β β + setWhitelistEnabled() β β
β β + setWhitelist() / batchSetWhitelist() β β
β β + setAdmin() / proposeAdmin() / acceptAdmin() β β
β β + emergencyWithdraw() / emergencyWithdrawNative() β β
β β β β
β β View Functions: β β
β β + canDeposit() β β
β β + getUserDepositInfo() β β
β β + getTotalValueLocked() β β
β β + getUserShares() / getUserAssets() β β
β β β β
β β Overrides: β β
β β # _depositAndSend() βββ adds enforcement β β
β β # _redeemAndSend() βββ adds tracking β β
β β # _deposit() βββ tracks actual shares β β
β β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Contract States β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β βββββββββββββββ pause() βββββββββββββ β β
β β β ACTIVE β ββββββββββββββββββββΊ β PAUSED β β β
β β β β β β β β
β β β - Deposits β ββββββββββββββββββββ β - Blocked β β β
β β β - Redempts β unpause() β - Redemptsβ β β
β β β allowed β β blocked β β β
β β βββββββββββββββ βββββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Whitelist Mode β β
β β β β
β β βββββββββββββββ setWhitelistEnabled(true) β β
β β β OPEN β ββββββββββββββββββββββΊ βββββββββββββ β β
β β β β β WHITELIST β β β
β β β Anyone can β βββββββββββββββββββββ β Only β β β
β β β deposit β setWhitelistEnabled β whitelistedβ β β
β β β β (false) β users β β β
β β βββββββββββββββ βββββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Cap States β β
β β β β
β β tvlCap = 0 βββΊ Unlimited TVL β β
β β tvlCap > 0 βββΊ Limited to tvlCap β β
β β β β
β β userCap[user] = 0 βββΊ Unlimited for user β β
β β userCap[user] > 0 βββΊ Limited to userCap β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cross-Chain Deposit Flow β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β SPOKE CHAIN (eg, Ethereum) HUB CHAIN (eg, Arbitrum) β
β β
β βββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ
β β User β β ββ
β β β β βββββββββββββββββββββββββββ ββ
β β 1. approve() β β β LayerZero Endpoint β ββ
β β assetOFT β β β β ββ
β β β β β 4. lzReceive() β ββ
β β 2. send() β β β - verify DVN quorum β ββ
β β composeMsg β β β - mint OFT to β ββ
β β = SendParam β β β composer β ββ
β β + minMsgVal β β β β ββ
β ββββββββββ¬βββββββββ β βββββββββββββ¬ββββββββββββββ ββ
β β β β ββ
β β β βΌ ββ
β β β βββββββββββββββββββββββββββ ββ
β β β β lzCompose() β ββ
β β β β β ββ
β β β β 5. try handleCompose() β ββ
β β β β β β ββ
β β β β βΌ β ββ
β β β β βββββββββββββββββββββ β ββ
β β β β β YZEnforcedComposerβ β ββ
β β β β β β β ββ
β β β β β 6. _depositAndSendβ β ββ
β β β β β β β β ββ
β β β β β βΌ β β ββ
β β β β β βββββββββββββββββ β β ββ
β β β β β β ENFORCEMENT β β β ββ
β β β β β β β β β ββ
β β β β β β β Not paused β β β ββ
β β β β β β β Whitelisted β β β ββ
β β β β β β β TVL OK β β β ββ
β β β β β β β User cap OK β β β ββ
β β β β β β β Vault OK β β β ββ
β β β β β βββββββββ¬ββββββββ β β ββ
β β β β β β β β ββ
β β β β β βΌ β β ββ
β β β β β 7. vault.deposit()β β ββ
β β β β β 8. _assertSlippageβ β ββ
β β β β β 9. _send() shares β β ββ
β β β β β10. Update trackingβ β ββ
β β β β βββββββββββββββββββββ β ββ
β β β β β β ββ
β β β β βΌ β ββ
β β β β SUCCESS ββββββββββββββββ€ ββ
β β β β β ββ
β β β βββββββββββββββββββββββββββ ββ
β β β ββ
β β If REVERT: β βββββββββββββββββββββββββββ ββ
β β β β catch β _refund() β ββ
β β βββββββββββββββββββββββββββββ β ββ
β β Assets returned to user β β 11. Send assets back β ββ
β β on spoke chain β β to source chain β ββ
β β β βββββββββββββββββββββββββββ ββ
β β β ββ
β βΌ ββββββββββββββββββββββββββββββββββββ
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Local Deposit Flow β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β User on Hub Chain β
β β β
β β 1. approve(assetOFT, amount) β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β composer.depositAndSend(amount, sendParam, refund) β β
β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β 2. transferFrom(user, composer, amount) β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β
β β βΌ β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β 3. _depositAndSend() β β β
β β β β β β
β β β βββΊ Check: paused? β β β
β β β βββΊ Check: whitelisted? β β β
β β β βββΊ Check: TVL cap? β β β
β β β βββΊ Check: user cap? β β β
β β β βββΊ Check: vault.maxDeposit? β β β
β β β β β β β
β β β βββΊ Record TVL before β β β
β β β βββΊ super._depositAndSend() β β β
β β β β βββΊ _deposit() β vault.deposit() β β β
β β β β βββΊ _assertSlippage() β β β
β β β β βββΊ _send() β local/remote β β β
β β β β β β β
β β β βββΊ Update userDeposits β β β
β β β β β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β User receives shares (local or cross-chain) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Function | Description | Access |
|---|---|---|
setTVLCap(uint256 cap) |
Set maximum TVL | Admin only |
setUserCap(address user, uint256 cap) |
Set user deposit limit | Admin only |
batchSetUserCaps(address[], uint256[]) |
Batch set user caps | Admin only |
pauseDeposits() |
Pause deposits only | Admin only |
unpauseDeposits() |
Unpause deposits | Admin only |
pauseRedemptions() |
Pause redemptions (extreme caution) | Admin only |
unpauseRedemptions() |
Unpause redemptions | Admin only |
pauseAll() |
Pause deposits and redemptions | Admin only |
unpauseAll() |
Unpause all operations | Admin only |
setWhitelistEnabled(bool) |
Enable/disable whitelist | Admin only |
setWhitelist(address, bool) |
Set whitelist status | Admin only |
batchSetWhitelist(address[], bool[]) |
Batch whitelist | Admin only |
setAdmin(address) |
Transfer admin role directly | Admin only |
proposeAdmin(address) |
Propose admin role (2-step setup) | Admin only |
acceptAdmin() |
Accept admin role (2-step setup) | Pending admin |
emergencyWithdraw(uint256, address) |
Withdraw stuck assets | Admin only |
emergencyWithdrawShares(uint256, address) |
Withdraw stuck shares | Admin only |
emergencyWithdrawNative(uint256, address) |
Withdraw stuck native ETH | Admin only |
| Function | Returns | Description |
|---|---|---|
admin() |
address | Current admin |
pendingAdmin() |
address | Pending admin |
tvlCap() |
uint256 | TVL limit (0 = unlimited) |
userDepositCap(address) |
uint256 | User's deposit limit |
userDeposits(address) |
uint256 | User's tracked deposits |
userShares(address) |
uint256 | User's tracked shares |
depositsPaused() |
bool | Deposit pause state |
redemptionsPaused() |
bool | Redemption pause state |
whitelistEnabled() |
bool | Whitelist mode |
whitelist(address) |
bool | User whitelist status |
canDeposit(address, uint256) |
(bool, string) | Check if deposit allowed |
getUserDepositInfo(address) |
(uint256, uint256, uint256) | deposit, cap, remaining |
getUserShares(address) |
uint256 | User's exact vault shares balance |
getUserAssets(address) |
uint256 | User's exact asset value equivalent |
getTotalValueLocked() |
uint256 | Current vault TVL |
| Error | Trigger |
|---|---|
YZ_DepositsPaused() |
Deposits are paused |
YZ_RedemptionsPaused() |
Redemptions are paused |
YZ_NotAdmin() |
Caller not admin |
YZ_ZeroAddress() |
Zero address provided |
YZ_TVLCapExceeded(currentTVL, amount, cap) |
TVL cap exceeded |
YZ_UserCapExceeded(user, current, amount, cap) |
User cap exceeded |
The _redeemAndSend() override replicates parent logic instead of calling super._redeemAndSend(). This is necessary to track user deposits BEFORE assets are sent cross-chain.
Why this matters:
Parent flow: _redeem() β _assertSlippage() β _send()
β
βββΊ After _send(), assets have LEFT the contract
Balance delta would be 0 or negative
Trade-off:
- β Correctly tracks assets before they leave
- β Does NOT automatically inherit future
VaultComposerSyncupdates
Mitigation:
- Pin
@layerzerolabs/ovault-evmto a specific version - Monitor releases for
_redeemAndSendchanges - Update override when parent changes
# foundry.toml
[dependencies]
"@layerzerolabs/ovault-evm" = "1.0.0" # Pin specific versionThe exposed_setUserDeposit() function is for testing only. Remove before mainnet deployment.
YZEnforcedComposer uses vault.totalAssets() for TVL cap enforcement. This is valid because:
OVault uses deterministic pricing:
sharePrice = totalAssets / totalSupply
Per LayerZero docs: "OVault eliminates the need for oracles" β the ERC-4626 vault's accounting is self-contained and deterministic.
Why this matters:
- No oracle manipulation risk
- No stale price feeds
- No external dependencies for cap enforcement
- TVL is always accurate from vault state
Note: Future versions may add Chainlink price feeds for multi-asset vaults or cross-chain TVL aggregation. Marked for v0.2 consideration.
There's a tension between admin-only emergencyWithdraw() and LayerZero's permissionless recovery principle:
| Mechanism | Who Can Call | Purpose |
|---|---|---|
emergencyWithdraw() |
Admin only | Withdraw stuck assets from composer |
LayerZero _refund() |
Automatic | Return assets on failed compose |
| Endpoint recovery | Anyone | Clear failed messages |
Clarification:
emergencyWithdraw()is for assets stuck in the composer (eg, failed transfers, dust accumulation)- It does NOT override LayerZero's recovery flows
- Failed cross-chain operations still go through
_refund()β assets return to source chain emergencyWithdraw()should only be used for edge cases, not to intercept user funds
Best Practice: Document all emergencyWithdraw() calls with on-chain evidence of stuck assets.
YZEnforcedComposer does NOT interfere with LayerZero's built-in permissionless recovery mechanisms.
| Function | Who Can Call | Purpose |
|---|---|---|
lzCompose() retry |
Anyone | Retry failed compose operations |
_refund() |
Automatic via try-catch | Returns assets on failure |
| Endpoint recovery | Anyone | Message recovery from endpoint |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LayerZero Permissionless Recovery β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Failed Transaction β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β Payload stays in Endpoint β β
β β β β
β β Anyone can: β β
β β β’ Retry lzCompose() with correct msg.valueβ β
β β β’ Trigger refund via _refund() β β
β β β’ Clear failed messages β β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β
β YZEnforcedComposer does NOT: β
β β’ Block retry attempts β
β β’ Require admin approval for refunds β
β β’ Add custom recovery logic β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Admin Only | Permissionless (Anyone) |
|---|---|
pause() / unpause() |
Retry failed lzCompose() |
setTVLCap() |
Trigger refund |
setUserCap() |
Endpoint message recovery |
setWhitelist() |
|
emergencyWithdraw() |
Key Insight: Admin controls affect new transactions only. Failed transactions can always be recovered permissionlessly.
When the contract is paused, retry attempts will still revert:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Paused State + Retry Interaction β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β User retries lzCompose() while paused β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β whenNotPaused modifier β β
β β β β β
β β βΌ β β
β β paused == true β β
β β β β β
β β βΌ β β
β β REVERT YZ_Paused β β
β β β β β
β β βΌ β β
β β Caught by try-catch β β
β β β β β
β β βΌ β β
β β _refund() triggered β β
β β β β β
β β βΌ β β
β β Assets returned to user β β
β βββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Result: Pause forces all retries into refunds β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Implication: Pausing doesn't block recovery β it forces recovery via refund. Users get their assets back, but cannot complete deposits until unpaused.
import {YZEnforcedComposer} from "./src/YZEnforcedComposer.sol";
// Deploy
YZEnforcedComposer composer = new YZEnforcedComposer(
address(vault), // ERC4626 vault
address(assetOFT), // Asset OFT
address(shareOFT), // Share OFT Adapter
admin // Admin address
);
// Configure
composer.setTVLCap(100_000_000 * 1e6); // 100M cap
composer.setUserCap(user, 100_000 * 1e6); // 100K per user
composer.setWhitelistEnabled(false); // Open access
// Emergency
composer.pauseDeposits(); // Pause deposits only
composer.unpauseDeposits(); // Resume depositsMIT