Earn vault math
Total assets with pending interest, share conversions and their rounding, withdrawal limits, allocation, the high-water-mark fee, and the net rate.
The narrative is on Earn vault. The vault is an OpenZeppelin
ERC4626Upgradeable (v5.4.0) with Paboxo's own totalAssets, limits and hooks.
Total assets
Where:
- — the vault's own USDG balance (idle).
- — the stored
totalSupplyAssetsof each market in the withdraw or supply queue. - — that market's pending supply interest: what its
accrueInterest()would add to if it ran now.pendingInterest(pool)returns it.
is the supply share of the interest from one accrual, computed with the market's own rate model:
and when , or , as in the router. Here
is the seconds since the market's lastAccrued, its current borrow rate and
its effective reserve factor.
Source: PaboxoEarnVault · 0xaF5a…c634, PaboxoEarnVault · 0xaF5a…c634.
It is an exact figure, not an estimate. Every input is what the real accrual reads, and accruing
one market does not change another market's figure, so the sum is what deposit and the other
entry points see once they have accrued every market.
Mainnet, block 93419456 (20 October 2026, 11:33:27 UTC):
| NVDA/USDG | 0.000001 | 0 |
| SPY/USDG | 0.000001 | 0 |
| WETH/USDG | 50.000757 | 0.000003 |
| Idle | 2,161.320702 |
2211321464, 2,211.321464 USDG. Only WETH/USDG had debt; its next accrual would book 3
base units for the vault (pendingInterest(pool) returned 3, and 0 for the other two). Share
supply 2211320691380952698037, 2,211.320691380952698037 peUSDG.
Shares and assets
Where is the decimals offset (PaboxoEarnVault · 0xaF5a…c634): shares carry
12 more decimals than USDG, and the virtual shares plus 1 virtual asset are the vault's
guard against the first-depositor inflation attack. is the share supply after the
performance fee the next accrual would mint ( while the fee is 0, as
it is now). Source: PaboxoEarnVault · 0xaF5a…c634. Each conversion rounds
toward the vault:
| Function | Rounds | Mainnet, block 93419456: 1 unit in |
|---|---|---|
previewDeposit, deposit | down | 1 USDG → 0.999999650607539640 peUSDG |
previewMint, mint | up | 1 peUSDG → 1.000001 USDG |
previewWithdraw, withdraw | up | 1 USDG → 0.999999650607539641 peUSDG |
previewRedeem, redeem | down | 1 peUSDG → 1.000000 USDG |
Share price at the read block: about 1.00000035 USDG per peUSDG, from the interest WETH/USDG's
borrow has paid. The price no longer divides evenly, so each rounding moves a unit toward the
vault: a deposit's shares and a withdrawal's shares differ by one, and minting 1 peUSDG costs
1.000001 USDG while redeeming it pays 1.000000.
Worked, previewDeposit(1000000):
As interest accrues, grows faster than and a USDG buys fewer peUSDG.
Previews equal execution
deposit, mint, withdraw and redeem accrue every market and take the fee before pricing
(PaboxoEarnVault · 0xaF5a…c634). The preview* and convertTo* functions
price against the same figures without writing anything:
already includes every , and already includes the fee shares. So in the same
block a preview returns exactly what the call will do. That is stronger than ERC-4626 requires,
which only asks that a preview not be more favourable than execution.
assetsIn(pool) still returns the stored . For what the vault holds in a market right now,
use assetsIn(pool) + pendingInterest(pool).
Limits
Where is liquidAssets(): idle USDG plus the cash each market in the withdraw queue can spare
without breaking its rule. The vault's claim on a market includes its pending
interest ; the market's spare cash does not need it, because an accrual adds the same
interest to as to , so does not move. Source: PaboxoEarnVault · 0xaF5a…c634,
PaboxoEarnVault · 0xaF5a…c634, PaboxoEarnVault · 0xaF5a…c634.
A deposit, or the assets a mint would take, below minDeposit reverts DepositBelowMinimum, per
call: , with = 100000, 0.1 USDG on mainnet (read at block
99393260). maxDeposit does not include it, and withdrawals are never held to it. The protocol
sets it (setMinDeposit, the factory's vaultAdmin), not the curator. Source:
PaboxoEarnVault · 0xaF5a…c634.
Mainnet, block 93419456: deposit cap 0 (uncapped, so maxDeposit is );
= 2161320704, 2,161.320704 USDG: the idle balance plus the NVDA/USDG and SPY/USDG seeds.
WETH/USDG adds nothing: its (50000757 + 83) equals its (50000840), so all
of the vault's 50.000757 USDG in it is lent out.
Allocation
| Event | What moves |
|---|---|
| Deposit | After minting, _supplyIdle pushes idle USDG into the markets in supply-queue order, each up to its cap. A market that refuses is skipped. With an empty supply queue it does nothing (PaboxoEarnVault · 0xaF5a…c634) |
| Borrow | If the market holds less cash than the borrow, it pulls the shortfall from idle USDG through pullTo, never past its cap (LendingPool, PaboxoEarnVault · 0xaF5a…c634) |
| Repay | The market sends cash above its reserves back to the vault (LendingPool) |
| Liquidation | Since 26 October 2026, the same as a repay: what the liquidator pays above the market's reserves goes back to the vault (LendingPool) |
| Withdraw | Idle first, then each market in withdraw-queue order, up to what it can spare (PaboxoEarnVault · 0xaF5a…c634) |
Every vault action accrues every market first and stores the new
vault-wide utilisation last, and the pools call the
vault's accrueInterestAll() before and after every borrow, repay and liquidation. A pull moves
USDG from idle into a market's supply, so it leaves , and the rate, unchanged; the borrow it
funds raises .
Live: pull only. The supply queue is empty (supplyQueueLength() = 0),
the withdraw queue lists all ten markets and the two LP markets, and each market's cap is
type(uint256).max (block 113204091). So
_supplyIdle iterates nothing and a deposit stays idle; a market draws on idle through pullTo when
a borrow needs it, and with unlimited caps any market can draw on all of it:
Withdrawals pay from idle first. The total interest earned does not depend on where idle sits, since only borrowed USDG earns. The push path still runs if the curator fills the supply queue again. At block 93419456, 2,161.320702 of 's 2,211.321464 USDG was idle; the rest was the two seeds, what WETH/USDG pulled for its borrow, and its pending interest.
Performance fee
On each deposit, mint, withdraw and redeem, on the curator's supplyToMarket and
withdrawFromMarket, when a market is listed (seedMarket) and when the fee is changed, after
accruing the markets, the vault charges a fee on the part of the share price above a high-water mark:
Where:
- — the share price in USDG base units per share, on ERC-4626's virtual totals, scaled by
(
HWM_SCALE). - —
feeHighWaterMark, the highest price the fee has been settled at. is what the vault would hold if the price sat exactly on the mark, rounded up so a gain has to be real before anything is charged. - —
performanceFee, in WAD: 10% is . - — the fee in USDG; — the
peUSDGminted to the fee recipient, priced so their claim is worth afterwards.
After minting, the mark becomes : it only rises,
and it follows the price up even while the fee is 0, so a fee switched on later does not charge the
past. A vault with seeds it at its first accrual from , the
price at lastTotalAssets. is capped at
MAX_PERFORMANCE_FEE = 0.5e18 (50%). Source: PaboxoEarnVault · 0xaF5a…c634,
PaboxoEarnVault · 0xaF5a…c634.
Measuring per share separates yield from money moving in and out. Deposits and withdrawals happen at the current price, so they move but not ; interest raises and bad debt lowers it. After a loss no fee is taken and the mark stays where it was, so the recovery back up to the mark is not charged. Only growth above the previous high is.
A simpler fee, with reset to the lower figure after a loss, would charge the recovery again. One trade-off remains by design: someone who deposits while the price is below the mark gets the recovery up to the mark without a fee.
Mainnet, block 93419456: the fee is 0% and feeHighWaterMark is
1000000050476191755697597 (1.00000005 USDG per peUSDG), set at the vault's last settlement. The
price at the read block, = 1000000349392582434561216, is above it: the mark follows
the price up at each settlement even with no fee.
Illustrative, at a 10% fee () on the vault as it stood at block 92690105
(19 October 2026), when the price sat exactly on a mark of : = 1916370704 and
= 1916370704000000000000, no debt. The markets then earn 10 USDG for the vault
( = 1926370704). The mark sits at , so
= 1916370705 and 1000000 (1 USDG, 10% of the 10 earned), and
995325575497420897 (0.9953256 peUSDG). Suppose the vault then took 1,000 USDG of bad
debt and later earned its way back to 10 USDG above where it was. This fee charges 1 USDG, 10% of
the 10 above the mark. The simpler fee would have charged 101 USDG, 10% of the whole 1,010.
Net rate
What the vault earns per year, as a rate on its assets:
with from supply rate. Idle USDG earns nothing and dilutes it. This is the vault's rate as an APR. Interest compounds at each booking, so the app shows it compounded, , as the vault's APY: the continuous-compounding ceiling (see APR and APY).
Mainnet, block 93419456: with the fee at 0, : WETH/USDG's 50 USDG paid 0.7120% and the other 2,161 USDG of sat idle, earning nothing.
Bad debt
Bad debt lowers a market's and so . Every peUSDG is worth less, whichever market it
came from; what bounds the loss from one market is the vault's cap on it. The fee's high-water
mark does not follow the price down, so the recovery from a loss is not charged the fee. See
liquidation math for when it happens.
Liquidation math
When a position can be liquidated, how much one liquidation may repay, how much is seized, how the bonus is split, and exactly where bad debt starts.
Oracles and market hours
The order the adapter checks things in, the freshness rules, the calendar arithmetic, and what a weekend does to a position.