Skip to Content
LP Staking Vault

LP staking vault (LpStakingVault7D)

LpStakingVault7D lets users stake Aerodrome WETH/CLAIM LP tokens and harvest CLAIM rewards.

User lifecycle

Stake:

  • stake(amount)
  • updates reward accounting and increases stakedBalance

Unbond:

  • beginUnbond(amount) -> creates an unbond entry
  • UNBONDING_PERIOD = 7 days
  • MAX_UNBONDS_PER_USER = 25

Withdraw:

  • withdrawUnbond(unbondId)
  • only after unlockTime

Harvest (CLAIM rewards):

  • getReward()
  • UI verb: Harvest

Reward sources

Rewards distributed by the vault can come from:

  • Furnace LP top-up split (lpBonus)
  • Furnace LP overflow drip (protocol -> LP pot)
  • The vault’s own Aerodrome fee harvest (see below)

Reward accounting is standard “reward per token” style using ACC = 1e18.

Fee harvest: Aerodrome LP fees -> rewards

Method:

  • harvestFeesToRewards(deadline, minClaimOut)
  • permissionless

Flow:

  • calls Aerodrome pool claimFees() (WETH + CLAIM)
  • pays a WETH bounty (staleness-gated)
  • swaps remaining WETH -> CLAIM
  • credits total CLAIM (direct fee CLAIM + bought CLAIM) as new rewards

Bounty constants (v1.0.0):

  • STALE_BOUNTY_AFTER = 60 minutes
  • BOUNTY_BPS = 100 (1%)
  • MAX_BOUNTY_WETH = 0.01 ETH (in WETH)

Bounty rules:

  • if last harvest was within STALE_BOUNTY_AFTER: bounty = 0
  • if harvest is stale: bounty = min(MAX_BOUNTY_WETH, feeWeth * 1%)

Operational expectation:

  • the official keeper harvest cadence is < 60 minutes, so bounty is typically 0
  • the bounty is a liveness backstop, not a steady incentive

Auto-compound (vault rewards -> veCLAIM)

Users can opt in to auto-compound their vault $CLAIM rewards into the Furnace.

Key rules (v1.0.0):

  • Auto-compound is disabled by default (explicit user opt-in).
  • Auto-compound does not create new locks in v1.0.0:
    • a destination tokenId is required.
  • Execution is permissionless (official executor is an offchain maintainer bot).

Config per user (onchain):

  • enabled, paused
  • tokenId destination (must be owned by the user, not listed, not expired)
  • durationSeconds (target lock duration for extension-only compounding)

Execution:

  • compoundFor(user)
  • compoundForMany(users[], minVeOuts[], maxUsers) (bounded + best-effort per user)

Behavior:

  • Best-effort per user (batch does not revert for a single failure).
  • Skips if claimableRewards(user) == 0.
  • Computes effective duration as extension-only:
    • effectiveDurationSeconds = max(configDurationSeconds, remainingDurationSeconds(tokenId))
    • AutoMax forces MAX_LOCK_DURATION.
  • Pauses config if the destination lock becomes ineligible at execution time (listed/expired/not owned).
    • User must save a new config to resume.

UI/UX integration (recommended):

  • LP Vault rewards actions should mirror Royalties:
    • Primary (default): Harvest $CLAIM
    • Secondary: Harvest & Lock
    • Optional: “Remember my choice” toggle (local preference).
  • After a successful Harvest & Lock, show: “Enable Auto-compound?” (routes to config modal, prefilled).

notifyRewards allowlist

To prevent arbitrary reward injection, notifyRewards is allowlisted. Expected authorized sources:

  • Furnace
  • this vault itself (self-notify from harvestFeesToRewards)

Operational note:

  • Upstream funders may treat notifyRewards as best-effort (swallow reverts) to avoid DoS on unrelated flows (e.g. user locking).
  • This vault uses balance-delta accounting (amount is ignored), so any CLAIM transferred without a successful notify can still be accounted on a later successful notify (including notifyRewards(0)).

Integrator notes

  • If you display APR, use the canonical APR spec in docs/spec/apr-calculation-spec-v1.0.0.md.
  • Do not label CLAIM rewards as “claim” in UI. Use Harvest.