Skip to main content

Dividends, Burn & Share Math

Dividend accrual โ€” Synthetix StakingRewards patternโ€‹

Each vault keeps a cumulative index rewardPerTokenStored. Every tax event bumps it:

rewardPerTokenStored += (dividendPortion ร— 1e18) / totalSupply
pending[user] = balance[user] ร— (index_now โˆ’ index_paid[user]) / 1e18
  • Accrual is lazy and exact: balances are settled automatically on every share change (deposits, exits, even vault-share transfers settle both parties).
  • Claims pay in the underlying token โ€” never in shares โ€” via claimDividend(minAmountOut) with a slippage guard against front-running.
  • Unclaimed dividends are reserved liabilities (totalUnclaimed), excluded from share backing. The vault is solvent for every claim by construction โ€” issue #26 closed the "burn outruns backing" freeze class.

Lock-in-vault burnโ€‹

Burned tokens never leave the vault (USDT/USDC blacklist burn addresses) but are subtracted from totalAssets():

totalAssets = balance โˆ’ burnedBalance โˆ’ totalUnclaimed โˆ’ totalStuckRevenue (reverts loudly if violated)

Effect: share price rises for everyone as taxes accumulate, and the backing invariant is machine-checked on every pricing call. A token balance that drops below its liabilities (e.g. a hostile negative rebase) fails loudly, never silently.

Share accounting โ€” ERC-4626 semantics, hardenedโ€‹

PropertyMechanism
Share pricetotalAssets / totalSupply, monotonically non-decreasing through normal operation (taxes only remove backing from sellers' side)
Preview paritypreviewDeposit/Mint/Withdraw/Redeem read pre-transaction state โ€” execution matches preview exactly (fuzz-verified; a 56ร— under-credit divergence was found and fixed by the fuzz harness)
First-deposit guard1.0-token minimum (10^decimals) + mint() cannot seed an empty vault โ€” donation/squat inflation attacks are closed at all decimal scales
Anti-fee-on-transferStrict mode verifies the vault's balance delta equals the transferred amount on both sides of every operation โ€” FOT, rebase and hook tokens revert on contact
Stuck partner revenuePartner payouts that fail (blacklists, reverting receivers) never revert the user's tx and never get stolen: booked as named liabilities, excluded from backing, claimable permissionlessly only by the entitled partner (claimStuck). No admin path exists.
ReentrancyEvery state-mutating entry point carries nonReentrant (transient-storage guard); the asset-token โ‰  share-token split makes cross-contract reentry inert by design
Permissive mode for hook tokens

A vault can be created with acceptFeesFromTransfer: true in its TaxConfig, which bypasses the anti-FOT check. This is opt-in per vault for known hook tokens (rebasing, marketing-fee, gas-burn tokens). Default is strict, and the flag is immutable post-initialize().

Integrator note

DHP vaults implement the ERC-4626 surface but intentionally do not inherit OpenZeppelin's ERC4626 (it fixes the asset at deploy time, incompatible with per-token clones). Every divergence is enumerated in ERC-4626 Notes.