fix: support capital-preserving opposite-side bets - #122
Conversation
…tals, and refunds The two-sided BetEntry model (net/gross + opposite_net/opposite_gross) was introduced to allow hedging, but the switch logic didn't actually move funds between buckets, pool totals weren't adjusted on switch, and cancel_refund only knew about a single legacy bucket. - place_bet: fix side-switch to move the full active balance into the new active bucket (net/gross/claimed, keyed by is_yes) and clear the opposite bucket, instead of discarding the computed old-side values - place_bet: restore the missing match existing assignment - market settlement: adjust total_yes/total_no by the switched amount so pool totals stay accurate across repeated switches - claim: remove duplicate is_winner/real_win bindings where the second silently overrode the first (is_winner was being hardcoded to true) - cancel_refund: check cancellation status and claimed/opposite_claimed independently, refund both eligible gross buckets, mark both claimed - leaderboard/referral: fix stale reward calls that depended on the old single-bucket net value - storage: reduce legacy bettor page size to 97 entries to stay under Soroban's 100-entry footprint limit (BetEntry grew with the new opposite_* fields) Tests: switch, switch-back, cancellation-after-switch, MAX_BETS_PER_USER cap under switching, and a no-switch control case asserting payout math is unchanged. cargo test -p prediction_market: 65 passed, 0 failed
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
7 similar comments
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Hey @MJ-RWA, This PR cannot be reviewed automatically because it does not reference the issue it solves. Add |
|
Needs review Linked to The pull request introduces a new Reviewed commit: |
Muyideen-js
left a comment
There was a problem hiding this comment.
@MJ-RWA The PR does not correctly solve the issue. The side-switching logic in place_bet moves the entire existing position to the new side and adds the new bet, which increases the total pool on the new side by the old net plus the new net, effectively double-counting the user's capital. For example, a user bets 100 on YES, then 50 on NO: the code sets net = 100 + 50 = 150 on NO, and total_no increases by old_net + net = 100 + 150 = 250, but the user only deposited 150. This breaks the payout math and allows users to inflate the pool. The correct approach is to keep the old position on its original side and track the new bet on the opposite side, or to implement a proper position model with separate balances. Additionally, the PR includes unrelated changes (removed event, leaderboard ABI change, token mint, new referral_registry contract, MAX_BETTORS_PER_PAGE change) that should be in separate PRs. The tests do not cover the critical scenario of switching back to the original side after a switch, and the logic resets opposite fields to zero, losing the original position. Please revise the implementation to preserve capital and maintain accounting invariants, and remove unrelated changes. Also, ensure CI is run and passes.
Summary
Fixes the
OppositeSideBetlimitation that permanently locks users to the side of a prediction market they initially selected.The implementation extends the existing position model to support opposite-side position changes while preserving the contract's existing economic and accounting invariants.
Problem
Previously,
place_betrejected any attempt to bet on the opposite side of an existing position:This prevented users from correcting mistakes, rebalancing exposure, or switching sides before market resolution.
Simply removing this validation would be unsafe because the existing payout model assumes each user has a single-sided position.
Solution
This change introduces side-aware position tracking and treats opposite-side operations as capital-preserving position switches.
Key changes
BetEntryto track exposure on both sides.Economic Safety
The implementation preserves the following invariants:
total_yesandtotal_noremain synchronized with active positions.Testing
Added/updated coverage for:
Verification
The implementation was validated using the repository's applicable:
The final Git diff was also reviewed to ensure the changes remain limited to the scope of this issue.
Scope
This PR is intentionally focused on resolving the irreversible one-sided position limitation. It does not introduce unrelated trading, order-book, leverage, or position-management functionality.
Closes #59