Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions ECONOMIC_INVARIANTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,35 @@ More generally:

---

### I.4 sMax tracks the current leader
`sMax` is snapped to the largest active post's total via the top-3
leader tracker on every interaction. There is no slow decay during
normal operation: as soon as the previous leader withdraws below the
second-place total, `sMax` snaps down to the new leader. A
governance-configurable fallback exponential decay (currently 10% per
epoch capped at 30 epochs) only runs when no post has any stake, so
that a stale `sMax` cannot stay frozen forever after a complete
unwind. It is floored at the current leader's total
and raised immediately when a post exceeds it.
### I.4 sMax tracks the current leader (patch_prC_rulings)
`sMax` is a high-water mark over post totals with three movements and
no others: (1) it RAISES immediately to any tracked post total that
meets or exceeds it; (2) it DESCENDS only by the governance-configured
exponential decay (10%/epoch, capped at 30 epochs of catch-up),
floored at the tracked leader's total; (3) it never snaps down (S-03
layer i — the pre-PR-C immediate snap-down is gone).

The tracker is a TRACKED_POSTS(=10)-slot leader board. Because accrual
is lazy, a dormant post's stored total can sit above every tracked
total; while that holds, `sMax >= true leader` is not guaranteed by
the tracker alone. That deviation is CLOSEABLE BY ANYONE at any time
via the permissionless `refreshSMax(postId)` (S-03 layer ii), which
settles the post if an epoch has passed and feeds its true stored
total to the tracker; the ops worker pokes the largest known posts
each epoch. So I.4 is true-about-this-mechanism: `sMax >=` every
TRACKED total at all times after update, `sMax >=` any specific post's
total the moment anyone refreshes it, and participation factors are
clamped at 1.0 in all cases, bounding the worst pre-poke effect at the
rMax ceiling.

**Safety statement**
- Decay prevents historical peaks from permanently suppressing
participation factors on future posts.
- sMax >= leaderTotal at all times (after update), so participation
factors remain <= 1.0 in steady state.
participation factors on future posts; the tracked-leader floor
prevents decay from undershooting anything the tracker can see.
- Dust posts cannot drag `sMax` down (never-snap-down), and dormant
giants cannot be hidden from it for longer than one poke.
- S-04 ratification: participation coupling every post's rate to the
global leader via `sMax` is intended design, not a defect.

---

Expand Down
8 changes: 8 additions & 0 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ contract Deploy is Script {
address(stakeImpl), abi.encodeCall(StakeEngine.initialize, (gov, address(token), address(protocolPolicy)))
);
StakeEngine stake = StakeEngine(address(stakeProxy));
// patch_prC_rulings S-09: the decay backstop is 10%/day (9e17) BY DESIGN;
// the old docstring claiming 0.5%/day was the bug. Set it explicitly when
// the deployer holds governance (dev path), and pin it unconditionally so
// any drift fails the deploy loudly on every path.
if (stake.governance() == deployer) {
stake.setSMaxDecayRate(9e17);
}
require(stake.sMaxDecayRateRay() == 9e17, "Deploy: sMaxDecayRateRay != 9e17 (10%/day, S-09)");
// patch_stakeengine_exempt_precompute: fail loud if the nonce offset above ever drifts,
// rather than silently deploying VSPToken with a wrong exemption target.
require(
Expand Down
Loading