Skip to content

fix(prediction_market): validate referrer registration before paying … - #179

Open
EneGab wants to merge 2 commits into
SPulse-Org:mainfrom
EneGab:CRITICAL]-register_referral-doesn't-validate-that-the-referrer-is-registered-unregistered-referrers-get-paid
Open

fix(prediction_market): validate referrer registration before paying …#179
EneGab wants to merge 2 commits into
SPulse-Org:mainfrom
EneGab:CRITICAL]-register_referral-doesn't-validate-that-the-referrer-is-registered-unregistered-referrers-get-paid

Conversation

@EneGab

@EneGab EneGab commented Aug 23, 2026

Copy link
Copy Markdown

Summary

place_bet paid out the 0.5% referral fee whenever the bettor's profile
named a referrer — without ever verifying that the referrer is actually
registered
. The XLM was transferred to the referral registry before
credit() was invoked, and credit() silently returned false when the
referrer's profile could not be found. Result: the fee left the market,
was never credited to the referrer's earnings, and became permanently
stranded inside the registry contract.

This branch closes that hole with pre-validation and adds defense in depth
so unattributable fees are never shipped out of the market contract.

Root cause

In prediction_market/src/lib.rs (place_bet):

  1. referral_registry.get_referrer(user) returns Some(addr) from the
    bettor's stored profile.
  2. The contract immediately transferred referral_fee to the registry and
    called credit(...).
  3. Nothing checked whether addr still resolves to a live registration.
    A missing/archived/wiped referrer profile made credit() drop the
    funds on the floor.

Realistic triggers: referrer profile TTL archival, a bad migration or
legacy-key wipe leaving the bettor's profile pointing at an address whose
registration no longer exists.

Changes

referral_registry/src/lib.rs

prediction_market/src/lib.rs

leaderboard/src/lib.rs

  • Restored truncated record_bet body and removed duplicated legacy
    point/bonus block (merge corruption repair required for the workspace
    to build).

Behavior change (intentional)

Bets placed by users without a registered referrer now retain their
full 2% fee in the market's AccumulatedFees (previously only the 1.5%
platform share landed there). Consequences:

Scenario Before After
Registered referrer 1.5% kept, 0.5% paid to referrer unchanged
No/unregistered referrer 1.5% kept, 0.5% stranded 2.0% kept as revenue
cancel_market reclaim capped at platform-derived share unchanged cap — retained referral slices survive cancellation as withdrawable revenue

Tests

New regression tests (prediction_market/src/tests.rs):

  • test_unregistered_referrer_fee_routed_to_market — wipes the
    referrer's registration post-registration (simulating archive/wipe),
    bets, asserts full 2% stays in AccumulatedFees/market ledger and the
    referrer receives nothing.
  • test_unregistered_referrer_cached_false_across_bets — second bet
    takes the cached HasReferrer = false path with identical outcome.

Updated assertions (~12 sites) where behavior legitimately changed:
no-referrer fee totals, per-market ledgers, cancel-reclaim leftovers,
empty-side resolution withdrawals, migration/cap/two-step-withdraw tests,
and e2e flow.

Point-claim alignment: bonus points are queued by design; inserted
claim_pending_rewards(...) before point assertions in market, e2e, and
registry tests, and corrected corrupt-era expected values (e.g. referrer
earns 3 pts/referred-bet; welcome 5 pts go to the registrant).

Corruption repairs in test suites (required to compile at all):
duplicate test function, two missing braces, double registrations that
panicked, moved-value borrow, and sdk-26 ContractEvents API misuse
(3 sites across leaderboard/referral_registry/prediction_market).

Verification

…referral fee

place_bet transferred the 0.5% referral fee to the registry and called
credit() without checking that the stored referrer is still registered.
If the referrer's profile was missing (archived TTL, bad migration,
legacy wipe), credit() silently dropped the already-transferred XLM.

- place_bet now resolves get_referrer() and verifies it via the new
  referral_registry.is_registered_referrer() view before transferring;
  unattributable fees stay with the market (AccumulatedFees) instead
- add is_registered_referrer(address) view to referral_registry
- regression tests: unregistered referrer never paid; retained path
  also used via cached HasReferrer=false on repeat bets
- update affected market tests for retained no-referrer referral slices
  and queued bonus-point claims
- restore referral_registry sources wiped by merge corruption (from
  1ed3350); repair corrupt-era damage in leaderboard/prediction_market
  tests (missing braces, duplicate fns, sdk-26 ContractEvents API)
@Muyideen-js

Copy link
Copy Markdown
Contributor

Hey @EneGab,

This PR cannot be reviewed automatically because it does not reference the issue it solves. Add Closes #<issue_number>, Fixes #<issue_number>, or Resolves #<issue_number> to the description.

…-that-the-referrer-is-registered-unregistered-referrers-get-paid
@Muyideen-js

Copy link
Copy Markdown
Contributor

Hey @EneGab,

This PR cannot be reviewed automatically because it does not reference the issue it solves. Add Closes #<issue_number>, Fixes #<issue_number>, or Resolves #<issue_number> to the description.

@Muyideen-js Muyideen-js left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EneGab This PR addresses the core issue by adding is_registered_referrer and routing fees to the market when the referrer is unregistered. However, it introduces severe regressions and lacks CI verification. The record_bet function in leaderboard is now a no-op, breaking stats tracking. The removal of legacy add_pts and add_bonus_pts breaks backward compatibility. Tests contain errors: duplicate registrations, incorrect expected values (e.g., referrer points should be 8 not 3), and the test suite cannot be run on the dev machine. CI status is 'none', so we cannot approve. Please fix the leaderboard regression, restore legacy functions or provide migration, correct the tests, and run the full test suite in CI. Also, ensure the PR is rebased on the latest main to avoid merge corruption.

@EneGab

EneGab commented Aug 23, 2026

Copy link
Copy Markdown
Author

Noted!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CRITICAL] register_referral doesn't validate that the referrer is registered — unregistered referrers get paid

2 participants