Skip to content

Test/revenue pool invariant runner - #467

Merged
greatest0fallt1me merged 2 commits into
CalloraOrg:mainfrom
MerlinTheWhiz:test/revenue-pool-invariant-runner
Jun 26, 2026
Merged

Test/revenue pool invariant runner#467
greatest0fallt1me merged 2 commits into
CalloraOrg:mainfrom
MerlinTheWhiz:test/revenue-pool-invariant-runner

Conversation

@MerlinTheWhiz

Copy link
Copy Markdown
Contributor

Revenue Pool Invariant Test: balance >= scheduled per tick

Summary

Adds a Foundry-style stateful invariant test for the RevenuePool contract, verifying that the on-ledger USDC balance of the contract is always at least the sum of approved-but-not-yet-distributed payments at every step of a randomized trace.

Closes #446

Changes

New: contracts/revenue_pool/src/test_invariant.rs

A deterministic stateful invariant runner with:

  • 128 seeded traces (seeds 0..127) using a custom LCG SimpleRng
  • 75 actions per trace = 9,600 total stateful steps
  • 6 action types, randomly selected each step:
Action Weight Description
Fund 33% Mint USDC to pool (widens coverage gap)
Schedule & Fund 25% Mint + track virtual scheduled total
Distribute single 17% Call distribute, decrement scheduled on success
Batch distribute 8% Call batch_distribute with 1-5 legs
Pause/unpause toggle 8% Flip pause flag
Pause-then-distribute 8% Pause → attempt distribute (must revert) → unpause
  • Invariant check after every action: usdc.balance(pool) >= scheduled
  • Uses std::panic::catch_unwind for expected reverts so the trace continues after failures (e.g., distribute while paused, duplicate recipients, insufficient balance)

Fixed: contracts/revenue_pool/src/lib.rs

  • Added missing PAUSED_KEY and ERR_PAUSED constants — these were referenced by pause, unpause, and is_paused but not defined, preventing compilation
  • Added mod test_invariant; declaration

Updated: INVARIANTS.md

Added "Revenue Pool On-Ledger Coverage Invariant" section documenting the invariant, its pre/post-conditions, the functions that affect it, and how the test suite validates it.

Test Results

test test_invariant::invariant_pool_balance_ge_scheduled_128_traces ... ok

All 128 traces passed — the invariant held across all 9,600 steps.

Run with:

cargo test -p callora-revenue-pool test_invariant

Edge Cases Covered

  • Scheduled crosses available balance: Schedule up to the available pool balance; distribute naturally caps at the lesser of scheduled and available, and reverts cleanly if insufficient
  • Pause-then-distribute: The runner pauses the pool, attempts a distribute (which reverts with ERR_PAUSED), verifies scheduled is unchanged, then unpauses
  • Duplicate recipients in batch: Random address selection in batch distribute may occasionally pick the same developer twice, exercising the contract's duplicate detection
  • Overflow/underflow: Batch total is computed with scheduled - batch_total; cap amounts ensure no arithmetic edge cases

@drips-wave

drips-wave Bot commented Jun 25, 2026

Copy link
Copy Markdown

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

@MerlinTheWhiz

Copy link
Copy Markdown
Contributor Author

Hi @greatest0fallt1me Thanks for the opportunity to contribute. I applied for the remaining two issues on Creditra contract and Callora backend, please assign.

@greatest0fallt1me
greatest0fallt1me merged commit e2669a5 into CalloraOrg:main Jun 26, 2026
1 of 3 checks passed
@greatest0fallt1me

Copy link
Copy Markdown
Contributor

the revenue-pool invariant runner is great — codifying the conservation properties so they can't silently regress. merged 👍

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 Foundry-style invariant test for revenue_poolbalance(contract) >= sum(developer_pending_distributions) per tick

2 participants