clarinet icon indicating copy to clipboard operation
clarinet copied to clipboard

Conditionally deploy code in Simnet

Open hugo-stacks opened this issue 2 months ago • 2 comments

Description

Using annotations, Clarinet should let developers specify that a particular function or expression should be deployed only in Simnet.

By implementing this, we would not only enable developers to use it directly but also improve their experience with testing frameworks like clarunit and rendezvous. Making it easier to write tests in Clarity.

Clarinet should check that the contract remains valid both with and without the conditionally deployed code.

Technical Details

Comment annotation

The annotation could be ;; #[env(simnet)], similar to existing annotations that we have in the linter (eg, ;; #[allow(unchecked_data)]) — and inspired by Rust conditional compilation flags.

;; #[env(simnet)]
(define-private (faucet (recipient principal))
  (ft-mint? SuperFT u1000 recipient)
)

Validating code

One of the benefits of this feature, compared to the workaround that third-party tools had to find, is that the testing code can be considered as first-class Clarity code, and therefore benefits from all of the tools that exist in the lsp (check, linter, documentation, autocompletion, etc).

It means that, when a Clarinet project contains such annotations, clarinet check and the LSP diagnostics should make two check passes with and without the simnet-only code.

So that this contract would be invalid:

;; clarinet check should throw something like
;; `use of unresolved function 'another-func', only available on simnet`
(define-public (my-func)
  (another-func u"hello")
)

;; #[env(simnet)]
(define-public (another-func (msg (string-utf8 128)))
  (print u"hello")
)

Use cases

Testing function

Other tools had to implement this logic. See how clarunit did it, or how rendezvous did it. It comes with major caveats:

  • Not standardized across the ecosystem
  • Hard to implement for test framework developers
    • In rendezvous, the testing code is injected into the regular smart contracts, and rendezvous handles a custom deployment with the new contracts
  • Test code doesn't benefit from the LSP (auto completion, documentation, linter, etc)

This feature would overcome all of these caveats. Here is an example from the rendezvous documentation. Instead of putting this code in a separate file counter.test.clar, developers could put it directly in counter.clar and have the code checked/linted.

;; counter.clar

(define-public (increment)
  (ok (var-set counter (+ (var-get counter) u1)))
)

;; #[env(simnet)]
(define-public (test-increment)
  (let ((counter-before (get-counter)))
    (unwrap-panic (increment))
    (asserts! (is-eq (get-counter) (+ counter-before u1)) (err u404))
    (ok true)
  )
)

Admin function

Here, we want the (faucet) function to be deployed in simnet. In this example, not that private function can be called like public functions in simnet, so the testing code can be private functions

(define-fungible-token SuperFT)

;; #[env(simnet)]
(define-private (faucet (recipient principal))
  (ft-mint? SuperFT u1000 recipient)
)

Other use cases

Any expression could be annotated to be deployed only in simnet. In the example below, add a print only in simnet (eg: for testing purpose).

(define-public (set-admin (new-admin principal))
  (begin
    (try! (is-admin))
    ;; #[env(simnet)]
    (print "updating ata admin")
    (ok (var-set admin new-admin))
  )
)

What this is NOT

This annotation for conditional deployment will be super powerful, but also somehow dangerous for developers who misuse it (with great powers [...]).

So we want to:

  • keep it simple
  • easy to use
  • hard to misuse

While Rust's conditional compilation slightly inspires it, we don't want something as complex (nor as powerful). It should only work Simnet, and not for other env: eg, [env(testnet)], which I think would be a big footgun. Similarly, we don't want to have complex conditions such as [env(and(...))] or [env(or(...))]. Having this only for simnet is already super powerful and makes a lot of sense.

If developers need to implement different behaviors in their contracts for testnet/mainnet, they can already use features like is-in-mainnet.

hugo-stacks avatar Oct 16 '25 09:10 hugo-stacks

CNET-154

linear[bot] avatar Oct 16 '25 09:10 linear[bot]

I like this idea and I agree with your "with great power..." comment. A couple of thoughts:

  • Make sure the syntax highlighter makes these expressions clearly differentiated somehow
  • Provide some way to see the version to be published

brice-stacks avatar Oct 22 '25 20:10 brice-stacks