Skip to content

Fix referral TTL squatting, tier downgrade reversion, and impact - #493

Merged
abayomicornelius merged 1 commit into
SO4-Markets:mainfrom
vjuliaife:fix/referral-ttl-tier-pricing-rounding-docs-445-444-455-460
Jul 25, 2026
Merged

Fix referral TTL squatting, tier downgrade reversion, and impact #493
abayomicornelius merged 1 commit into
SO4-Markets:mainfrom
vjuliaife:fix/referral-ttl-tier-pricing-rounding-docs-445-444-455-460

Conversation

@vjuliaife

Copy link
Copy Markdown
Contributor

Closes #445
Closes #444
Closes #455
Closes #460

Summary

Fixes three logic bugs and one stale-documentation issue in referral_storage
and pricing_utils.

#445 — referral code squatting after TTL expiry

register_code only guards against duplicate registration by checking
whether the CodeOwner persistent entry currently exists. That entry's TTL
is only bumped as a side effect of trader activity, and there was no way for
an owner to renew it directly. A code nobody trades through can therefore run
out its TTL; once archived, register_code reads it as "unregistered" and a
different address can claim the same code string, silently inheriting any
trader still linked to it via a live TraderCode entry.

Added a self-service renew_code(caller, code) entrypoint: the current owner
(verified via require_auth + an explicit ownership check) can extend the
CodeOwner TTL at any time, independent of trader activity.

#444 — tier auto-upgrade reverts manual admin downgrades

increment_referrer_volume recomputes a referrer's tier purely from lifetime
cumulative volume, which only ever grows. If an admin manually lowers a
referrer's tier via set_referrer_tier, the next trade re-evaluates the
unchanged, still-large volume against the thresholds and silently upgrades
the referrer right back — there was no way to make a downgrade durable.

Added an admin-only set_referrer_volume(admin, referrer, volume_usd) so a
manual tier downgrade can be paired with a volume reset, consistent with the
existing volume-driven auto-upgrade model.

#455 — negative price impact under-charged by floor rounding

apply_swap_impact_value, apply_position_impact_value, and the inline
impact conversion in get_swap_output_amount converted signed USD price
impact to token units with mul_div_wide, which truncates toward zero for
both signs. For a positive impact (a payout) that's correct — it should
floor. For a negative impact (a charge), truncating toward zero also rounds
the charge down, so the protocol collected strictly less than owed on
almost every negative-impact swap/position adjustment.

All three call sites now go through one convert_impact_usd_to_tokens
helper that floors payouts and takes the ceiling of the magnitude for
charges (mirroring how fee amounts already round up via mul_div_wide_up),
so the charge is never under-collected.

#460 — TTL strategy doc claimed persistent storage is never extended

docs/ttl-strategy.md claimed, in several places, that no contract in the
workspace ever calls extend_ttl on persistent storage. That's false:
order_handler, data_store's bytes32-set index helpers, and
referral_storage all do, on specific keys. Rewrote the current-state
warning, the persistent-tier table row, the bump-policy section, and the
"Current gaps" list to precisely state which keys in each of those three
contracts are covered and which related keys (e.g. the Position entry
itself, transfer_code_ownership's CodeOwner write, data_store's
AddrSet helpers and scalar accounting) are not — so the gap is precise
instead of blanket.

Changes

  • contracts/referral_storage/src/lib.rsrenew_code, set_referrer_volume, and tests
  • libs/pricing_utils/src/lib.rsconvert_impact_usd_to_tokens helper, applied at all three
    impact-conversion call sites
  • docs/ttl-strategy.md — corrected TTL coverage claims

Test plan

…ding

- referral_storage: add self-service renew_code so a code owner can keep
  their CodeOwner entry's TTL alive without depending on trader activity,
  closing the window where a dormant code's TTL lapses and a different
  address can register the same code string and inherit any trader still
  linked to it.
- referral_storage: add admin-only set_referrer_volume so a manual
  set_referrer_tier downgrade can be paired with a volume reset, since
  increment_referrer_volume's auto-upgrade otherwise re-evaluates the
  unchanged, still-large cumulative volume on the next trade and silently
  reverts the downgrade.
- pricing_utils: round the token-unit conversion of a negative price impact
  (a charge) up in magnitude instead of down, matching how fee amounts round
  up elsewhere so the protocol never under-collects. apply_swap_impact_value,
  apply_position_impact_value, and the inline conversion in
  get_swap_output_amount now share one conversion helper that floors payouts
  and ceils charges.
- docs/ttl-strategy.md: correct the blanket "persistent storage is never
  extend_ttl'd" claim — order_handler, data_store's bytes32-set helpers, and
  referral_storage all call it on specific keys — and precisely list which
  related keys in those same contracts still aren't covered.
@drips-wave

drips-wave Bot commented Jul 25, 2026

Copy link
Copy Markdown

@vjuliaife Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@abayomicornelius
abayomicornelius merged commit 13fffa6 into SO4-Markets:main Jul 25, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment