Skip to content

feat: per-user max bet cap#884

Open
Danielobito009 wants to merge 1 commit into
Predictify-org:masterfrom
Danielobito009:feature/max-bet
Open

feat: per-user max bet cap#884
Danielobito009 wants to merge 1 commit into
Predictify-org:masterfrom
Danielobito009:feature/max-bet

Conversation

@Danielobito009

@Danielobito009 Danielobito009 commented Jul 23, 2026

Copy link
Copy Markdown

Per-User Max Bet Cap

Issue

Closes #842

Description

This PR implements per-user, per-market maximum bet cap enforcement for the Predictify Hybrid prediction market contract. Users are now prevented from placing bets that cumulatively exceed a configured cap on any given market, whether in a single bet or across multiple bets.

Changes

Core Features

  • Per-User Max Bet Cap: Enforce a global cap on cumulative user bets per market
  • Persistent Tracking: User stakes tracked in persistent storage (365-day TTL) across multiple bets
  • Fail-Fast Validation: Cap validation occurs BEFORE fund transfer to prevent partial state changes
  • Backward Compatible: No cap configured by default (uncapped mode preserves existing behavior)
  • Independent Tracking: Different users tracked independently; different markets tracked independently

Modified Files

1. contracts/predictify-hybrid/src/storage.rs

Added: 2 new DataKey variants for persistent storage

// Per-user cumulative stake on a specific market
UserStake(Symbol, Address) -> i128

// Global per-user max bet cap
MaxBetCap -> i128

2. contracts/predictify-hybrid/src/err.rs

Added: 3 new error codes

MaxBetCapExceeded = 528  // User's cumulative stake exceeds cap
InvalidCap = 529         // Cap value is zero or negative
Overflow = 530           // Arithmetic overflow on checked_add

3. contracts/predictify-hybrid/src/events.rs

Added:

  • New event type: MaxBetCapSetEvent with fields cap: i128, timestamp: u64
  • New emitter function: emit_max_bet_cap_set(env: &Env, cap: i128)

4. contracts/predictify-hybrid/src/bets.rs

Added: 5 new validator functions in BetValidator

pub fn get_max_bet_cap(env: &Env) -> Option<i128>
pub fn set_max_bet_cap(env: &Env, caller: &Address, cap: i128) -> Result<(), Error>
pub fn get_user_stake(env: &Env, market_id: &Symbol, user: &Address) -> i128
pub fn update_user_stake(env: &Env, market_id: &Symbol, user: &Address, amount: i128) -> Result<(), Error>
pub fn validate_user_stake_under_cap(env: &Env, market_id: &Symbol, user: &Address, amount: i128) -> Result<(), Error>

Modified: place_bet_inner() function

  • Added cap validation before fund transfer
  • Added user stake update after bet creation

5. contracts/predictify-hybrid/src/bet_tests.rs

Added: 15 comprehensive tests covering:

  • Cap configuration (set, validation, authorization)
  • User stake tracking (get, update, overflow detection)
  • Single bet scenarios (within cap, at boundary, over cap, uncapped)
  • Cumulative bets (multiple bets, overflow detection, exact boundary)
  • Independence tests (per-user on same market, per-market for same user)
  • Edge cases (zero amounts, uncapped mode)

Test Coverage:

  • Before: 52 tests
  • After: 67 tests (+15 new)

Implementation Details

Security Guarantees

Fail-Fast Validation - Cap check before any state mutation or fund transfer
Arithmetic Safety - All i128 operations use checked_add(), no panics on overflow
No Unsafe Patterns - No unwrap() or expect() in production paths
Admin Authentication - require_auth() on all state-changing operations
Persistent State - User stakes survive ledger expiry (365-day TTL matches market TTL)

Cap Semantics

  • Per-Market: Each market has independent cap tracking (user A's stake on market X doesn't affect market Y)
  • Per-User: Each user tracked independently (user A hitting cap doesn't affect user B)
  • Cumulative: Cap applies to total stake across all bets on a market
  • Optional: No cap set = uncapped behavior (backward compatible)
  • Global: Same cap value applies to all users and markets (can be extended per-market if needed)

Validation Flow

place_bet() called
  ↓
1. Authenticate user (require_auth)
2. Validate market state
3. Validate bet parameters (amount, outcome)
4. Validate fee slippage
5. Check existing bets on market
6. ✅ VALIDATE USER STAKE UNDER CAP (NEW)
7. Lock funds (transfer to contract)
8. Create bet
9. ✅ UPDATE USER STAKE (NEW)
10. Update market stats
11. Emit bet_placed event

Testing

Test Scenarios Covered

Configuration:

  • Setting valid cap
  • Rejecting invalid cap (zero, negative)
  • Authorization checks

Tracking:

  • Initial stake (0)
  • Stake updates
  • Overflow detection

Single Bets:

  • Bet within cap ✅
  • Bet exactly at cap (boundary) ✅
  • Bet one unit over cap ❌
  • Bet when uncapped ✅

Cumulative Bets:

  • Two bets within total cap ✅
  • Two bets exceeding total cap ❌
  • Two bets exactly at total cap ✅
  • Overflow on cumulative calculation ❌

Independence:

  • Multiple users on same market (independent caps)
  • Same user on different markets (independent caps)

Running Tests

cd contracts/predictify-hybrid
cargo test bet_tests -- --nocapture

Expected Result: All 67 tests pass (52 existing + 15 new)

Usage Examples

Setting a Cap (Admin Only)

// Admin sets max bet cap to 100 XLM per user per market
let cap = 100_000_000_000i128; // 100 XLM in stroops
BetValidator::set_max_bet_cap(&env, &admin_address, cap)?;
// Event: MaxBetCapSetEvent { cap: 100_000_000_000, timestamp: ... }

Checking Current Cap

if let Some(cap) = BetValidator::get_max_bet_cap(&env) {
    println!("Max cap: {} stroops", cap);
} else {
    println!("Uncapped");
}

User Bet Flow

// Before place_bet():
let current_stake = BetValidator::get_user_stake(&env, &market_id, &user);
// Returns cumulative amount user has bet on this market

// During place_bet():
// 1. Validates new bet won't exceed cap
BetValidator::validate_user_stake_under_cap(&env, &market_id, &user, bet_amount)?;

// 2. After bet created, updates cumulative stake
BetValidator::update_user_stake(&env, &market_id, &user, bet_amount)?;

Breaking Changes

None. This feature is backward compatible:

  • No cap configured by default
  • Existing bets unaffected
  • Existing API unchanged
  • Only adds new functions and storage keys

Deployment Considerations

Storage Impact

  • New persistent storage key per unique (market_id, user) pair when they bet
  • New persistent storage key for global cap (1 entry)
  • TTL: 365 days (matches market TTL for coherency)

Gas Impact

  • Minimal: One additional storage read/write per bet
  • Uses efficient persistent storage access patterns
  • No additional contract calls

Testing Before Mainnet

  1. ✅ Unit tests (15 new tests added)
  2. ⏳ Integration testing with full betting workflows
  3. ⏳ Testnet deployment and monitoring
  4. ⏳ Mainnet deployment

Files Changed Summary

 contracts/predictify-hybrid/src/bet_tests.rs | 317 +++++++++++++++++++++++++++
 contracts/predictify-hybrid/src/bets.rs      | 154 +++++++++++++
 contracts/predictify-hybrid/src/err.rs       |   6 +
 contracts/predictify-hybrid/src/events.rs    |  52 +++++
 contracts/predictify-hybrid/src/storage.rs   |   6 +
 5 files changed, 535 insertions(+)

Commit

feat: per-user max bet cap

- Add per-user, per-market cumulative bet cap enforcement
- New storage keys: UserStake(market_id, user) and MaxBetCap
- New error codes: MaxBetCapExceeded, InvalidCap, Overflow
- New validator functions: get/set_max_bet_cap, validate_user_stake_under_cap
- Cap check happens before fund transfer (fail-fast)
- All arithmetic uses checked_add (no panics on overflow)
- Event emission: MaxBetCapSetEvent on cap change
- Comprehensive test suite: 15 new tests covering all scenarios
- Backward compatible: no cap = uncapped (preserves existing behavior)

Checklist

  • Feature implemented per specification
  • Error handling with appropriate error codes
  • Event emission for cap changes
  • User stake tracking in persistent storage
  • Cap validation before fund transfer
  • No unsafe arithmetic (all checked operations)
  • No unwrap() or expect() in production code
  • Comprehensive test coverage (15 new tests)
  • Backward compatible (no breaking changes)
  • Rustdoc comments on all public functions
  • Independent per-user and per-market tracking
  • TTL management for storage entries

@drips-wave

drips-wave Bot commented Jul 23, 2026

Copy link
Copy Markdown

@Danielobito009 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

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.

Add per-user max bet cap

2 participants