Two New Tools for Testing Clarity Contracts

Two New Tools for Testing Clarity Contracts

Stacks Labs
June 2, 2026

Today we're announcing two new tools that improve testing for Clarity contracts: simnet-only Clarity code, and Rendezvous, the production-ready fuzzer for property-based and invariant testing.

Simnet-only Clarity code lets you add test helpers for contract states you'd never deploy on mainnet, opening the door to use cases like fuzz testing with Rendezvous. Here's how both tools sharpen the way you test Clarity contracts.

Native support for simnet only code

The preferred way to unit test Clarity smart contracts is the Clarinet SDK and Vitest combo. The Clarinet SDK runs the Clarity VM in Node.js and simulates a fast blockchain environment, so you can test contracts with the full JavaScript/TypeScript ecosystem instead of a bespoke Clarity test framework. Stacks app builders get one toolchain for their back-end, front-end, and on-chain logic.

The catch is that contract state is hard to set up and mock. Suppose you want to test what happens when a user already holds 1,000 tokens, or when a vault is partway through a lending cycle. From TypeScript, your only option is to reach that state through the contract's public functions. Anything the contract doesn't expose publicly, you can't reach at all.

Simnet-only Clarity code fixes this. With the ;; #[env(simnet)] annotation, you can write private helper functions that touch contract internals directly but only run in simnet, never on mainnet. Here's an FT smart contract to illustrate:

Here, test-mint is deployed only on simnet. It's never something you'd want on mainnet.

Clarinet validates the contract with and without this optional code, and gives it the full editor experience (linting, code completion, documentation). Keep simnet-only code to private functions, so an accidental mainnet deploy stays contained.

Learn more in the documentation. One of its biggest applications is fuzz testing, which is where Rendezvous comes in.

Rendezvous: fuzz testing for Clarity

Institutional-grade Bitcoin DeFi needs institutional-grade security practices, and fuzzing is one of them.

Rendezvous is a fuzzer for Clarity. Where an example-based unit test asks "does this work for input A? for input B?", a fuzzer asks the harder question: "does this always work, and can I find any input that breaks it?" It generates large volumes of valid, random Clarity values for any function signature, including recursive types, trait references, and custom principals.

It works in two complementary modes: property-based testing and invariant testing.

Property-based testing checks individual functions against properties you write. You add test- functions to your contract, each encoding and asserting a property: it passes on (ok true), is discarded on (ok false), and fails on any error or exception. Run rv <path-to-clarinet-project> <contract-name> test and Rendezvous pools every test- function, repeatedly draws one at random, generates fresh arguments, and calls it. State isn't reset between calls, so a run isn't a series of isolated checks but a random walk across your test functions over accumulating state.

That's what lets it surface order- and history-dependent bugs, not just bad inputs. The simplest example is a round-trip, where a list reversed twice should equal the original:

Rendezvous generates seq fresh on each draw, and when something breaks it shrinks the input down to the smallest case that still fails, so you debug a clean two-element list, not a random hundred-element one.

Invariant testing checks the whole system rather than one function. You write read-only invariant- functions describing truths that must always hold, and Rendezvous drives the contract by calling its public functions in random order with random arguments, re-checking invariants at random after each call. The difference from property mode is what sits in the pool and who checks: Rendezvous operates the real contract API while your invariants watch from the outside, rather than calling self-asserting test functions. Because invariants can read a context map of the call history, you can assert conditional truths like "if increment has run more often than decrement, the counter is positive."

Rendezvous was released over a year ago as a developer preview; today it's production-ready. The road to v1 was guided by Clarity devs, their requests, and their feedback.

Continuous verification: audits that keep running

A smart contract audit is usually a snapshot: an auditor reviews a specific commit and hands you a PDF. The moment you refactor or add a feature, that document describes code you're no longer running.

The solution is continuous verification, a standard practice across the largest smart contract ecosystems like Ethereum and Solana. It wasn't possible with Clarity before Rendezvous. Now you write the audit's expectations as invariants and property-based tests that live in the contract, and Rendezvous checks them on every run. It's the same fuzzing-in-CI model Foundry and Echidna brought to the EVM.

Take the bug at the top of the Rendezvous Trophy Case, our running log of real vulnerabilities rv has caught in production contracts. In Jing v3, canceling a cycle overwrote next-cycle totals that a deposit filter had rolled forward, silently wiping the accounting. The property it broke is one line: invariant-balance-eq-cycle-totals, meaning balances always equal the cycle totals. No auditor would catch that by reading the diff. The fuzzer found it by driving the contract until the books stopped balancing. Encode that invariant once, and it runs forever: every refactor, every PR, every CI build, the books have to balance or the build fails.

What v1.0 means

Beyond the native simnet-only support above, the release brings a few things worth calling out.

Fuzz against real mainnet state. Name mainnet addresses in rv.config.json and Rendezvous hydrates their real on-chain state into simnet. You can fuzz with wallets that actually hold positions, no private keys required. Real balances surface edge cases synthetic accounts never would.

Configs, not flags. Instead of chaining a long CLI string, point rv at a JSON config you can git-track. Each scenario (a fresh run, a regression sweep, a nightly CI pass) becomes a reviewable, diffable, rerunnable file in the repo.

Failures that stay caught. When Rendezvous finds a breaking seed, it saves the failure with the config that produced it. The regression corpus maintains itself: the new --regr flag replays those saved failures against a fresh simnet, so a bug you caught once can't quietly come back.

Tests can't leak to mainnet. This is the breaking change that earned the major version bump: the functions Rendezvous calls (properties and discards) are now define-private at the language level. Pre-1.0 they were public, and a missing annotation meant anyone could call them if they leaked into a mainnet-deployed contract. Now they're private by construction.

Rendezvous randomness exposed as a public library. strategyFor and getContractFunction hand you an fc.Arbitrary<ClarityValue[]> you can drop straight into fast-check to level up your clarinet-sdk example-based tests.

A public Trophy Case. New in v1.0, the Trophy Case is a public, running log of every real vulnerability rv has caught in production contracts, topped by the Jing v3 accounting bug. It's both a record of what fuzzing turns up in the wild and an open invitation: catch something with Rendezvous and submit it. The more bugs the case collects, the sharper the tool and the ecosystem get.

Try it

If you write Clarity, the next contract you test is the place to start.

Write your properties and invariants directly in your contract, and let the fuzzer look for the inputs and call sequences you'd never think to try.

Full documentation lives in the Rendezvous Book. When you catch something, add it to the Trophy Case. We'd love to see what you find.

Your contract's vulnerabilities are out there. Better to meet them now, in simnet, than later on mainnet.

Thanks to Nikos for your early work as co-contributor, Friedger for your thoughtful feedback throughout development, and Rafa for testing it out with Jing.

Previous Post
Next Post

Get more of Stacks

Get important updates about Stacks technology, projects, events, and more to your inbox.