Idempotency ensures operations can be performed multiple times without changing the result beyond the first application. This is critical for financial operations.
Implementation Example: Claim Idempotency
- Problem: Users may retry failed transactions, leading to double payouts
- Solution: Track claim status with immutable records (ClaimInfo struct)
- Properties:
- One claim per user per market (enforced by claimed map)
- Immutable record (append-only, no modifications)
- Timestamp tracking (audit trail)
- Payout verification (exact amount recorded)
- Benefits:
- Safe retry mechanisms for users
- Complete audit trail for compliance
- Prevention of double-spend attacks
- Front-running resistance
See Claim Idempotency Guide for detailed implementation.
- Use OWASP Application Security Verification Standard(ASVS) for the verification of security controls
- Implement servers and frameworks are running on latest versions.
- Encrypt highly sensitive information(authentication verification data)
- Monitor networks and update software and hardware regularly
- Use Web Application Firewall(WAF) that monitors HTTP traffic across Internet and blocks vulnerabilities.
- Perform regular updates for libraries
- Use auto-scanning tools like Synk
- Principle of Least Priviledge(PoLP) ensures authorized users can execute jobs within the system.
- Roles based access towards some operations.
- Implementing strong password policies with rotation
- Implementing Multi-Factor Authentication(MFA)
- User tokens implemented during login form
Platform fees for any given market are collected at most once. The
fee_collected: bool field on the Market struct is the single source of
truth.
FeeManager::collect_fees checks market.fee_collected before any state
mutation:
- If
true→ returnsOk(0)immediately (idempotent no-op). - If
false→ validates, collects, setsfee_collected = true, persists.
FeeValidator::validate_market_for_fee_collection independently returns
Error::FeeAlreadyCollected for callers that use the validator directly.
| Threat | Mitigation |
|---|---|
| Retry on network failure double-charges users | fee_collected flag checked before any state write |
Admin calls collect_fees twice maliciously |
Second call returns Ok(0), no tokens move |
| Flag reset attack | Flag is stored in persistent ledger state; only mark_fees_collected sets it and no pclears it post-collection |
| Reentrancy via token callback | Fee vault accumulates internally; transfer to admin only happens via time-locked withdraw_fees |
-
market.fee_collectedisfalseinMarket::new(seetypes.rs) -
collect_feesreturnsOk(0)on retry, neverErr -
FeeValidatorreturnsError::FeeAlreadyCollected, notInvalidFeeConfig -
mark_fees_collectedis the only write path for the flag