Paboxo

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

A=Ivault+∑m(Sm+Pm)A = I_{\text{vault}} + \sum_{m} \big(S_m + P_m\big)

Where:

  • IvaultI_{\text{vault}} — the vault's own USDG balance (idle).
  • SmS_m — the stored totalSupplyAssets of each market in the withdraw or supply queue.
  • PmP_m — that market's pending supply interest: what its accrueInterest() would add to SmS_m if it ran now. pendingInterest(pool) returns it.

PmP_m is the supply share of the interest from one accrual, computed with the market's own rate model:

im=⌊Bm⋅rm⋅Δtm1018⋅Y⌋i_m = \left\lfloor \frac{B_m \cdot r_m \cdot \Delta t_m}{10^{18} \cdot Y} \right\rfloor Pm=im−⌊im⋅Rf,m1018⌋P_m = i_m - \left\lfloor \frac{i_m \cdot R_{f,m}}{10^{18}} \right\rfloor

and Pm=0P_m = 0 when Δtm=0\Delta t_m = 0, Bm=0B_m = 0 or im=0i_m = 0, as in the router. Here Δtm\Delta t_m is the seconds since the market's lastAccrued, rmr_m its current borrow rate and Rf,mR_{f,m} 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):

SmS_mPmP_m
NVDA/USDG0.0000010
SPY/USDG0.0000010
WETH/USDG50.0007570.000003
Idle IvaultI_{\text{vault}}2,161.320702

A=A = 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 T=T = 2211320691380952698037, 2,211.320691380952698037 peUSDG.

Shares and assets

shares(a)=a (T′+10o)A+1,assets(s)=s (A+1)T′+10o,o=18−6=12\text{shares}(a) = \frac{a\,(T' + 10^{o})}{A + 1}, \qquad \text{assets}(s) = \frac{s\,(A + 1)}{T' + 10^{o}}, \qquad o = 18 - 6 = 12

Where oo is the decimals offset (PaboxoEarnVault · 0xaF5a…c634): shares carry 12 more decimals than USDG, and the 101210^{12} virtual shares plus 1 virtual asset are the vault's guard against the first-depositor inflation attack. T′=T+sFT' = T + s_F is the share supply after the performance fee the next accrual would mint (sF=0s_F = 0 while the fee is 0, as it is now). Source: PaboxoEarnVault · 0xaF5a…c634. Each conversion rounds toward the vault:

FunctionRoundsMainnet, block 93419456: 1 unit in
previewDeposit, depositdown1 USDG → 0.999999650607539640 peUSDG
previewMint, mintup1 peUSDG → 1.000001 USDG
previewWithdraw, withdrawup1 USDG → 0.999999650607539641 peUSDG
previewRedeem, redeemdown1 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):

⌊1000000×(2211320691380952698037+1012)2211321464+1⌋=999999650607539640\left\lfloor \frac{1000000 \times (2211320691380952698037 + 10^{12})}{2211321464 + 1} \right\rfloor = 999999650607539640

As interest accrues, AA grows faster than TT 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: AA already includes every PmP_m, and T′T' 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 SmS_m. For what the vault holds in a market right now, use assetsIn(pool) + pendingInterest(pool).

Limits

maxDeposit={0paused2256−1depositCap=0max⁡(depositCap−A, 0)otherwise\text{maxDeposit} = \begin{cases} 0 & \text{paused} \\ 2^{256} - 1 & \text{depositCap} = 0 \\ \max(\text{depositCap} - A,\ 0) & \text{otherwise} \end{cases} maxWithdraw=min⁡(assets↓(balance), L),L=Ivault+∑m∈WQ{0m pausedmin⁡(Sm+Pm, max⁡(Sm+Rm−Bm, 0))otherwise\text{maxWithdraw} = \min\big(\text{assets}_{\downarrow}(\text{balance}),\ L\big), \qquad L = I_{\text{vault}} + \sum_{m \in WQ} \begin{cases} 0 & m \text{ paused} \\ \min\big(S_m + P_m,\ \max(S_m + R_m - B_m,\ 0)\big) & \text{otherwise} \end{cases}

Where LL is liquidAssets(): idle USDG plus the cash each market in the withdraw queue can spare without breaking its S+R≥BS + R \ge B rule. The vault's claim on a market includes its pending interest PmP_m; the market's spare cash does not need it, because an accrual adds the same interest to BB as to S+RS + R, so S+R−BS + R - B 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: a≥amin⁡a \ge a_{\min}, with amin⁡a_{\min} = 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 2256−12^{256} - 1); LL = 2161320704, 2,161.320704 USDG: the idle balance plus the NVDA/USDG and SPY/USDG seeds. WETH/USDG adds nothing: its S+RS + R (50000757 + 83) equals its BB (50000840), so all of the vault's 50.000757 USDG in it is lent out.

Allocation

EventWhat moves
DepositAfter 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)
BorrowIf 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)
RepayThe market sends cash above its reserves back to the vault (LendingPool)
LiquidationSince 26 October 2026, the same as a repay: what the liquidator pays above the market's reserves goes back to the vault (LendingPool)
WithdrawIdle 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 AA, and the rate, unchanged; the borrow it funds raises BB.

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:

availableFor(m)=min⁡(capm−heldm, Ivault)=Ivault\text{availableFor}(m) = \min\big(\text{cap}_m - \text{held}_m,\ I_{\text{vault}}\big) = I_{\text{vault}}

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 AA'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:

p(A,T)=⌊(A+1)⋅1036T+10o⌋p(A, T) = \left\lfloor \frac{(A + 1)\cdot 10^{36}}{T + 10^{o}} \right\rfloor AM=⌈M (T+10o)1036⌉A_M = \left\lceil \frac{M\,(T + 10^{o})}{10^{36}} \right\rceil F=⌊max⁡(A+1−AM, 0)⋅ϕv1018⌋F = \left\lfloor \frac{\max(A + 1 - A_M,\ 0)\cdot \phi_v}{10^{18}} \right\rfloor sF=⌊F (T+10o)A−F+1⌋s_F = \left\lfloor \frac{F\,(T + 10^{o})}{A - F + 1} \right\rfloor

Where:

  • pp — the share price in USDG base units per share, on ERC-4626's virtual totals, scaled by 103610^{36} (HWM_SCALE).
  • MM — feeHighWaterMark, the highest price the fee has been settled at. AMA_M 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.
  • ϕv\phi_v — performanceFee, in WAD: 10% is 0.1×1018=10170.1 \times 10^{18} = 10^{17}.
  • FF — the fee in USDG; sFs_F — the peUSDG minted to the fee recipient, priced so their claim is worth FF afterwards.

After minting, the mark becomes M←max⁡(M, p(A, T+sF))M \leftarrow \max\big(M,\ p(A,\ T + s_F)\big): 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 M=0M = 0 seeds it at its first accrual from p(Alast,T)p(A_{\text{last}}, T), the price at lastTotalAssets. ϕv\phi_v 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 AA but not pp; interest raises pp 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, ⌊(Anew−Alast)⋅ϕv/1018⌋\lfloor (A_{\text{new}} - A_{\text{last}}) \cdot \phi_v / 10^{18} \rfloor with AlastA_{\text{last}} 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, p(A,T)p(A, T) = 1000000349392582434561216, is above it: the mark follows the price up at each settlement even with no fee.

Illustrative, at a 10% fee (ϕv=1017\phi_v = 10^{17}) on the vault as it stood at block 92690105 (19 October 2026), when the price sat exactly on a mark of 102410^{24}: AA = 1916370704 and TT = 1916370704000000000000, no debt. The markets then earn 10 USDG for the vault (AA = 1926370704). The mark sits at M=1024M = 10^{24}, so AMA_M = 1916370705 and F=F = 1000000 (1 USDG, 10% of the 10 earned), and sF=s_F = 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:

rvault=∑mSm rmsupA⋅(1−ϕv1018)r_{\text{vault}} = \frac{\sum_m S_m\, r^{\text{sup}}_m}{A} \cdot \left(1 - \frac{\phi_v}{10^{18}}\right)

with rmsupr^{\text{sup}}_m 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, ervault−1e^{r_{\text{vault}}} - 1, as the vault's APY: the continuous-compounding ceiling (see APR and APY).

Mainnet, block 93419456: with the fee at 0, rvault=0.7120%×2.2611%×0.9=0.0145%r_{\text{vault}} = 0.7120\% \times 2.2611\% \times 0.9 = 0.0145\%: WETH/USDG's 50 USDG paid 0.7120% and the other 2,161 USDG of AA sat idle, earning nothing.

Bad debt

Bad debt lowers a market's SmS_m and so AA. 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.

On this page