Allocate / Deallocate Flow

How the Curator moves assets between FundVault and a strategy

Pre-condition (from deposit flow):  After users deposit, USDC sits idle in FundVault.  balanceOf(fundVault) = 1,000 USDC |  PPS = 1.0 |  totalSupply = 1,000 shares
① Allocate — FundVault → Strategy
ROLE: CURATOR FundVault.allocate() SHARED · FUND VAULT FundVault balanceOf = 1,000 USDC ↓ STRATEGY HaForDefiStrategy totalAssets() = 0 → 800 ↑ USDC transferred safeTransfer(strategy, 800) 1
Step Action Effect
Curator calls FundVault.allocate(strategy, amount) Entry point, checks cap & idle balance
IERC20.safeTransfer(strategy, amount) balanceOf(fundVault) ↓
Tokens arrive at strategy contract strategy.totalAssets() ↑
totalAllocated[strategy] += amount Cumulative audit counter updated

Examples

Starting: 1,000 USDC idle in FundVault, strategy empty.

Event balanceOf
(fundVault)
strategy
.totalAssets()
totalAllocated
(cumul.)
idle + deployed
Initial state 1,000001,000
allocate(strategy, 800) 2008008001,000
allocate(strategy, 200)
deploy remaining idle
01,0001,0001,000
Yield accrues (+50 USDC)
operator updates forDefiValue
01,0501,0001,050
Note: Total assets in the system (idle + deployed) stays constant during allocate. The Curator simply moves USDC from FundVault into the strategy — no value is created or destroyed.
⚠ Warning — FundNavFeed must be updated after allocate
The NAV formula reads FundNavFeed (an off-chain value) — not strategy.totalAssets() directly. After allocating, the idle balance of FundVault drops but FundNavFeed is not yet updated. If the operator calls updateNav before syncing FundNavFeed to reflect the deployed amount, the NAV will be understated and PPS will drop, falsely signalling a loss.

Rule: always set FundNavFeed ≥ strategy.totalAssets() before or atomically with the next updateNav call.
② Deallocate — Strategy → FundVault
ROLE: CURATOR FundVault.deallocate() SHARED · FUND VAULT FundVault balanceOf = 200 → 1,050 ↑ STRATEGY HaForDefiStrategy totalAssets() = 850 → 0 ↓ USDC returned strategy.withdraw(850) → FundVault 2
Step Action Effect
Curator calls FundVault.deallocate(strategy, amount) Entry point
IStrategy(strategy).withdraw(amount) called internally Triggers strategy's transfer logic
Strategy calls safeTransfer(fundVault, amount) strategy.totalAssets() ↓
Tokens arrive back in FundVault balanceOf(fundVault) ↑
totalDeallocated[strategy] += amount Cumulative audit counter updated

Examples

Starting: 200 USDC idle in FundVault, strategy holds 850 USDC (800 deployed + 50 yield).

Event balanceOf
(fundVault)
strategy
.totalAssets()
totalDeallocated
(cumul.)
idle + deployed
Before deallocate
after yield accrued
20085001,050
deallocate(strategy, 850)
full withdrawal
1,05008501,050
deallocate(strategy, 400)
partial withdrawal only
6004504001,050
Strategy suffers loss (−50)
then deallocate(strategy, 750)
9500750950 (loss realized)
Note: Total assets after deallocate equals whatever strategy.withdraw() actually returns — which may be more (yield) or less (loss) than the original allocated amount. totalDeallocated records what was returned, not what was originally deployed.