Skip to content

Commit 4a60403

Browse files
committed
Update docs and comments
1 parent 97f6f1a commit 4a60403

3 files changed

Lines changed: 24 additions & 16 deletions

File tree

contracts/contracts/strategies/crosschainV3/FLOWS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ sequenceDiagram
532532
Master->>Master: _processWithdrawClaimAck(N+2, claimed, body)
533533
Note over Master: store nonceProcessed[N+2] = true<br/>store pendingWithdrawalAmount = 0<br/>store remoteStrategyBalance = yieldBaseline
534534
Note over Master: transfers full WETH balance from Master Strategy to L2 Vault
535-
else queue not yet matured (NACK)
535+
else NACK (outstandingRequestId != EMPTY / amount == 0 / bridgeAssetHeld < amount / ship out of [min,max])
536536
Note over Remote: currentBalance = _yieldOnlyBaseline()<br/>body = encode(currentBalance, false, 0)<br/>payload = packPayload(WITHDRAW_CLAIM_ACK, N+2, body)
537537
Remote->>SuperEth: sendMessage(payload)
538538
SuperEth->>Bridge: ccipSend{value:fee}(BASE_SELECTOR, ccipMessage)
@@ -563,7 +563,7 @@ message comes from Master's local ETH pool (`_send (userFunded=false)` uses
563563
leg-2 claim ack lands.
564564

565565
For `withdrawAll` (vault or governor sweep), `_withdrawRequest` is called with
566-
`min(remoteStrategyBalance, inboundAdapter.maxTransferAmount())` so a sweep
566+
`min(_drawableRemoteBalance(), inboundAdapter.maxTransferAmount())` so a sweep
567567
larger than the bridge's per-tx limit lands as a partial withdrawal rather
568568
than reverting.
569569

@@ -727,7 +727,7 @@ sequenceDiagram
727727
Master->>Master: _processWithdrawClaimAck(N+2, landed, body)
728728
Note over Master: store nonceProcessed[N+2] = true<br/>store pendingWithdrawalAmount = 0<br/>store remoteStrategyBalance = yieldBaseline
729729
Note over Master: transfers full USDC balance from Master Strategy to Spoke sub-OUSD Vault
730-
else queue not yet matured (NACK)
730+
else NACK (outstandingRequestId != EMPTY / amount == 0 / bridgeAssetHeld < amount / ship out of [min,max])
731731
Note over Remote: currentBalance = _yieldOnlyBaseline()<br/>body = encode(currentBalance, false, 0)<br/>payload = packPayload(WITHDRAW_CLAIM_ACK, N+2, body)
732732
Remote->>AdapterEth: sendMessage(payload)
733733
AdapterEth->>CCTP: sendMessage(message)

contracts/contracts/strategies/crosschainV3/MasterWOTokenStrategy.sol

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,14 @@ contract MasterWOTokenStrategy is AbstractWOTokenStrategy {
164164
/// must equal the vault (enforced by the require below); Master always forwards the
165165
/// received bridgeAsset to `vaultAddress` on the leg-2 ack.
166166
///
167-
/// Only the `remoteStrategyBalance` slice is drawable here: `_amount` must be
168-
/// `<= remoteStrategyBalance` even though `checkBalance` can report more (local
169-
/// bridgeAsset + positive bridgeAdjustment). To realise the remainder, the strategist
170-
/// can `requestSettlement()` (folding bridgeAdjustment into remoteStrategyBalance)
171-
/// and/or use the locally-held bridgeAsset, then withdraw.
167+
/// Drawable bound is `_drawableRemoteBalance()` = `remoteStrategyBalance +
168+
/// min(0, bridgeAdjustment)`, NOT `remoteStrategyBalance` alone:
169+
/// - a NEGATIVE `bridgeAdjustment` (net BRIDGE_OUT) is folded in, lowering the cap so we
170+
/// never request more shares than Remote can actually unwrap (else it NACKs on Remote);
171+
/// - a POSITIVE `bridgeAdjustment` is excluded here even though `checkBalance` counts it
172+
/// (that + local bridgeAsset can report more). To realise it, `requestSettlement()`
173+
/// first (folds bridgeAdjustment into remoteStrategyBalance) and/or use the local
174+
/// bridgeAsset, then withdraw.
172175
function withdraw(
173176
address _recipient,
174177
address _asset,
@@ -197,8 +200,8 @@ contract MasterWOTokenStrategy is AbstractWOTokenStrategy {
197200
) {
198201
return;
199202
}
200-
// Best-effort: a mid-migration cleared inbound adapter must no-op the sweep, not
201-
// revert it (honors the "best-effort no-op" contract).
203+
// Best-effort no-op if the inbound adapter isn't wired yet (pre-configuration only —
204+
// setInboundAdapter now rejects zero, so it can't be cleared mid-migration).
202205
address inbound = inboundAdapter;
203206
if (inbound == address(0)) return;
204207
// remoteStrategyBalance is OToken (18dp); withdraw amounts are bridgeAsset units.

contracts/contracts/strategies/crosschainV3/RemoteWOTokenStrategy.sol

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,8 @@ contract RemoteWOTokenStrategy is AbstractWOTokenStrategy {
295295
internal
296296
{
297297
uint256 amount = CrossChainV3Helper.decodeUint256(payload);
298+
// Defensive / unreachable in normal flow: Master validates amount > 0 before shipping
299+
// WITHDRAW_REQUEST — this only guards a malformed/forged inbound message.
298300
require(amount > 0, "Remote: zero withdraw");
299301
require(
300302
outstandingRequestId == REQUEST_ID_EMPTY,
@@ -635,12 +637,15 @@ contract RemoteWOTokenStrategy is AbstractWOTokenStrategy {
635637
/// `oTokenAmount` is in OToken (18dp) units, matching `_viewCheckBalance`;
636638
/// `_yieldOnlyBaseline()` is the `oTokenAmount == 0` case.
637639
/// @dev Clamps to 0 rather than reverting on a negative. `_viewCheckBalance - bridgeAdjustment`
638-
/// is principal + yield + retained fees and is never *economically* negative, but the
639-
/// wOToken ERC-4626 rounds against the strategy by ~1 wei on each BRIDGE_IN (floor) /
640-
/// BRIDGE_OUT (ceil), so once a `withdrawAll` drains this near 0 a later bridge op can push
641-
/// it a few wei negative. Reverting there would freeze the whole serialized yield channel on
642-
/// dust; clamping reports a dust-accurate 0 instead (and matches the project-wide
643-
/// checkBalance-never-reverts convention). The accumulated drift is economically nil.
640+
/// is principal + yield + retained fees and is never *economically* negative **while
641+
/// (w)OTokens are up-only** — the only thing that pushes it a few wei negative is the
642+
/// wOToken 4626 rounding against the strategy (~1 wei per BRIDGE_IN floor / BRIDGE_OUT
643+
/// ceil) once a `withdrawAll` drains it near 0. Reverting on that dust would freeze the
644+
/// serialized yield channel, so we clamp (matches checkBalance-never-reverts).
645+
/// CAVEAT: a real negative rebase (loss/slashing) would make this genuinely negative; the
646+
/// clamp then masks it, and because Master re-adds its OWN `bridgeAdjustment`,
647+
/// `checkBalance` OVER-reports by ~`bridgeAdjustment` (not 0). Out of scope while
648+
/// (w)OTokens never negative-rebase; revisit (signed baseline) if that changes. See DESIGN §3.10.
644649
function _yieldOnlyBaselineAfter(uint256 oTokenAmount)
645650
internal
646651
view

0 commit comments

Comments
 (0)