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
| 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,000 | 0 | 0 | 1,000 |
| allocate(strategy, 800) | 200 | 800 | 800 | 1,000 |
|
allocate(strategy, 200) deploy remaining idle |
0 | 1,000 | 1,000 | 1,000 |
|
Yield accrues (+50 USDC) operator updates forDefiValue |
0 | 1,050 | 1,000 | 1,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
Rule: always set
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
| 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 |
200 | 850 | 0 | 1,050 |
|
deallocate(strategy, 850) full withdrawal |
1,050 | 0 | 850 | 1,050 |
|
deallocate(strategy, 400) partial withdrawal only |
600 | 450 | 400 | 1,050 |
|
Strategy suffers loss (−50) then deallocate(strategy, 750) |
950 | 0 | 750 | 950 (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.