Conditionally deploy code in Simnet
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.
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