Documentation

Solanalib.Finance.WithdrawalCap

WithdrawalCap: a stateful sliding-window rate limiter #

The first stateful primitive in Solanalib. Where WindowedDecay / GrowthCurve / MonotoneSequence describe pure functions of time, WithdrawalCap describes a resource that mutates: an accumulator tracking how much has been withdrawn in the current time window, plus the window-start timestamp that resets on interval elapse.

This is the per-reserve last line of defence against drain attacks in lending protocols.

Bug classes the theorems catch #

TheoremBug class
remaining_le_capacitySaturating-arithmetic underflow on the remaining calculation
tryAdd_preserves_invariantcurrent escaping above capacity after a successful add
tryAdd_rejects_over_capOff-by-one on the boundary check
interval_reset_idempotentFirst add of a new interval forgetting to zero the accumulator

Design notes #

A per-reserve withdrawal cap. intervalSeconds = 0 disables the cap.

  • capacity : Timestamp

    Maximum lamports addable per window.

  • intervalSeconds : Timestamp

    Window length in seconds. 0 disables the cap.

  • current : Timestamp

    Lamports added so far in the current window.

  • windowStart : Timestamp

    Wall-clock timestamp when the current window started.

Instances For
    Equations
    • One or more equations did not get rendered due to their size.
    Instances For
      Equations
      • One or more equations did not get rendered due to their size.
      Instances For

        The invariant a well-formed WithdrawalCap should satisfy.

        Equations
        Instances For

          Has the current window elapsed at time now? Computed at the .toNat level so the comparison cannot underflow.

          Equations
          Instances For

            The maximum amount that can still be added in the current window.

            When disabled, returns the largest representable Lamports (so callers treating remaining as a hint won't refuse a valid add). When the window has elapsed, returns the full capacity. Otherwise returns capacitycurrent.

            Equations
            Instances For

              The result of attempting to add to the cap.

              • ok (c' : WithdrawalCap) : TryAdd

                Success: the post-operation cap state.

              • capReached : TryAdd

                The add would have exceeded the cap; no state change.

              Instances For
                Equations
                • One or more equations did not get rendered due to their size.
                Instances For

                  Try to add amount lamports to the cap at time now.

                  Three branches:

                  1. Disabled (intervalSeconds = 0): no-op, return ok c.
                  2. Window elapsed: reset to a fresh window and check whether amount alone exceeds capacity.
                  3. Mid-window: check whether current + amount exceeds capacity.
                  Equations
                  • One or more equations did not get rendered due to their size.
                  Instances For

                    Theorems #

                    P1 — Bounded remaining. When the cap is enabled and the invariant holds, remaining never exceeds capacity.

                    theorem Solanalib.Finance.WithdrawalCap.tryAdd_preserves_invariant (c : WithdrawalCap) (amount now : Timestamp) (h_inv : c.Invariant) {c' : WithdrawalCap} (h_ok : c.tryAdd amount now = TryAdd.ok c') :

                    P2 — tryAdd preserves the invariant.

                    theorem Solanalib.Finance.WithdrawalCap.tryAdd_rejects_over_cap_midwindow (c : WithdrawalCap) (amount now : Timestamp) (h_enabled : c.intervalSeconds.toNat 0) (h_not_elapsed : ¬c.IsElapsed now) (h_over : c.current.toNat + amount.toNat > c.capacity.toNat) :

                    P3 — tryAdd rejects over-cap requests in mid-window.

                    theorem Solanalib.Finance.WithdrawalCap.tryAdd_rejects_over_cap_at_reset (c : WithdrawalCap) (amount now : Timestamp) (h_enabled : c.intervalSeconds.toNat 0) (h_elapsed : c.IsElapsed now) (h_over : amount.toNat > c.capacity.toNat) :

                    P3' — tryAdd rejects over-cap requests at interval reset. After a window elapses, an add still rejects if amount alone exceeds capacity.

                    theorem Solanalib.Finance.WithdrawalCap.interval_reset_idempotent (c : WithdrawalCap) (amount now : Timestamp) (h_enabled : c.intervalSeconds.toNat 0) (h_elapsed : c.IsElapsed now) (h_fits : amount.toNat c.capacity.toNat) {c' : WithdrawalCap} (h_ok : c.tryAdd amount now = TryAdd.ok c') :
                    c'.current = amount c'.windowStart = now

                    P4 — Interval-reset idempotence. After the window elapses, a successful add zeroes the previous accumulator and starts a fresh window: c'.current = amount and c'.windowStart = now.