This document serves as a formal audit checklist for the Checkmate-Escrow smart contract, ensuring comprehensive security review and test coverage for all critical functions and edge cases.
-
Initialize Function
- Only callable once (checked:
AlreadyInitializederror) - Oracle address cannot be the contract itself
- Admin address is properly set
- Oracle role is properly set
- Only callable once (checked:
-
Admin Functions (pause, unpause, token management)
- Only admin can pause/unpause
- Only admin can add/remove tokens from allowlist
- Admin authentication is enforced via
require_auth()
-
Player Functions (create_match, deposit, cancel_match)
- Only relevant players can initiate actions
- Player authentication is enforced via
require_auth() - Invalid player addresses are rejected
-
Oracle Functions (submit_result)
- Only oracle can submit results
- Oracle authentication is enforced
- Oracle record integration maintains audit trail
-
Match States
- Transitions: Pending → Active → Completed
- Invalid state transitions are prevented
- Match completion is final (immutable)
-
Deposit Handling
- Players must deposit before match activates
- Double deposits are prevented (
AlreadyFundederror) - Match activates only when both players deposit
- Refunds occur on cancellation
-
Payout Distribution
- Correct payout to winner (2x stake)
- Correct refund split on draw
- All tokens are transferred or refunded
-
Game ID Validation
- Game ID length must be > 0 and ≤ 64 bytes
- Duplicate game IDs are rejected
- Matches are properly indexed by game ID
-
Stake Amount Validation
- Stake amount must be > 0
- Zero and negative amounts are rejected
- Arithmetic overflow is prevented (checked_mul, checked_add)
-
Player Validation
- Player1 ≠ Player2
- Neither player can be the contract itself
- Both player addresses are properly stored and matched
-
Match Count & ID Management
- Match IDs are monotonically increasing
- u64 overflow is prevented in match ID generation
- Match records are properly stored and retrievable
-
Token Allowlist
- Allowlist can be enabled/disabled
- Tokens can be added to allowlist
- Tokens can be removed from allowlist
- Token presence is correctly checked
- Match creation respects allowlist enforcement
-
Token Operations
- Token transfers succeed with valid tokens
- Token transfers fail with non-allowed tokens (when enforced)
- Token addresses are properly validated
-
Pause/Unpause
- Paused contract blocks: create_match, deposit, submit_result
- Paused contract allows: query functions
- Pause/unpause events are published
-
TTL (Time-To-Live) Management
- Instance storage TTL is extended on each invocation
- Match storage TTL is properly set
- TTL values follow constants (MATCH_TTL_LEDGERS)
-
Event Publishing
- Initialize event includes oracle and admin
- Pause/unpause events are published
- Token addition/removal events are published
- Match creation events include ID, players, and amount
- Deposit events track progress
- Match activation events are published
- Match completion events include winner
- Cancel events are published
-
Oracle Record
- Oracle records are stored with match results
- Game ID is recorded for audit trail
- Records are retrievable by match ID
- Positive and negative amounts
- Zero amounts
- Very large amounts (near i128::MAX)
- Arithmetic overflow scenarios
- Sequential ID generation
- u64 overflow prevention
- ID collision detection
- Empty strings
- Exact length boundary (64 bytes)
- Over-length strings
- Special characters and Unicode
- Wrong caller authorization
- Unauthorized state transitions
- Re-authorization attempts
- Double-deposit attacks
- Double-spend attempts
- Unauthorized payout attempts
- Invalid state transition attacks
- Allowlist bypass attempts
- Contract pause bypass attempts
-
Invariant 1: Total token balance is preserved
- Both players deposit, one receives payout (or both on draw)
-
Invariant 2: Match can only complete once
- Completed matches cannot be re-submitted or cancelled
-
Invariant 3: Players cannot be identical
- Prevents self-play and self-transfer scenarios
-
Invariant 4: All deposits are eventually refunded or transferred
- No tokens remain stuck in contracts
-
Invariant 5: Admin/Oracle cannot arbitrarily transfer tokens
- Only payout or refund through proper match lifecycle
The following code paths are intentionally not covered by automated tests:
-
Soroban SDK Built-ins: Functions provided by
soroban-sdkare tested by Stellar and assumed to work correctly.- Example:
env.register_contract(),env.mock_all_auths()
- Example:
-
Token Contract Interface: Token operations (
token::Client::transfer) are mocked in tests and assumed to work correctly.- These are Stellar's standard token interface.
-
Event Publishing Failures: Edge cases in event publishing are not tested (e.g., storage full).
- These are environmental issues outside the contract's control.
-
Storage TTL Management: TTL extension edge cases are not explicitly tested.
- Soroban's TTL system is tested by Stellar; we verify our TTL constants are correct.
To run the security test suite:
cd contracts/escrow
cargo test --lib security --test-threads=1To generate coverage reports:
cargo tarpaulin --out Html --output-dir coverageMinimum coverage requirements:
- Line coverage: 95%+
- Branch coverage: 90%+
| Role | Name | Date | Status |
|---|---|---|---|
| Developer | Implementation | Pending | |
| Security Reviewer | Review | Pending | |
| Audit Team | Formal Audit | Pending |