Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support state transitions for dependent contracts during invariant testing #97

Open
BowTiedRadone opened this issue Jan 21, 2025 · 4 comments
Labels
question Further information is requested

Comments

@BowTiedRadone
Copy link
Collaborator

When a smart contract depends on another contract (e.g., a function in the target contract calls another contract), include state transitions for all contracts in the dependency chain during invariant testing. This means:

  • Identify all contracts directly or indirectly involved in the dependency tree of the target contract.
  • Enable calling random public functions with randomly generated arguments for each of these contracts.

This approach ensures state transitions across the entire dependency chain, improving the accuracy of invariant testing.

@moodmosaic moodmosaic added the enhancement New feature or request label Jan 21, 2025
@BowTiedRadone
Copy link
Collaborator Author

Thanks to @BowTiedBlox for pointing out this idea!

@moodmosaic
Copy link
Member

moodmosaic commented Jan 26, 2025

It's an interesting idea but tricky in practice.

Covering every contract in the dependency chain risks combinatorial explosion, as the state space grows rapidly. Mocks and stubs (and Test Doubles in general) may actually help here.

Imagine that the main contract (SUT) manages positions for a CLMM/DEX pool (e.g., a vault). It interacts with the pool's range, tick, and math functions, plus other libraries. Targeting the entire dependency chain (pool code, price oracles, etc.) makes the complexity unmanageable.

We likely only care about how the vault updates user liquidity or handles rebalances. Test Doubles help simulate relevant pool state changes (e.g., tick moves, fees, liquidity updates), helping detect the unexpected without integrating the full DEX code.

This questioning also applies to example/unit-based and property-based testing: in the context of testing the SUT, is there a use case where touching all its dependencies uncovered a bug missed otherwise?

@moodmosaic moodmosaic added question Further information is requested and removed enhancement New feature or request labels Jan 26, 2025
@BowTiedRadone
Copy link
Collaborator Author

@moodmosaic Thank you for the detailed and well-documented comment, as always! Based on the Test Double approach, here’s how a stacking pool contract developer can cause state transitions in the dependent contract:

For example, if a stacking pool contract depends on pox-4 and is the target contract of a Rendezvous fuzzing session, the solution would be to temporarily move the dependent functions into the SUT (System Under Test). This effectively makes them test doubles:

(contract-call? .pox-4 function-name function-args)  
;; becomes  
(function-name (function-args))  
;; where function-name is temporarily moved to the SUT for fuzz testing purposes  

However, we can still cause state transitions in the boot contracts (including pox-4), as they are responsible for the chain functionality and advancement.

@moodmosaic
Copy link
Member

The solution would be to temporarily move the dependent functions into the SUT

Yes, if xyz.clar is the SUT and xyz.tests.clar is the rendezvous test file, Mocks, Stubs, and other Test Doubles should reside in the test file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants