Using Macros
Scalus provides a set of macros that can be used to simplify your code.
Inlining constants
inline
Scalus can inline constants in your scripts, which is a powerful optimization technique. When you mark a parameter as inline
, its value is directly embedded in the compiled code rather than being passed as a parameter at runtime. This reduces script size and execution costs, which is particularly valuable for on-chain code where every byte counts.
For example, when you inline a ByteString constant like a public key hash, it appears directly in the compiled SIR as a literal value (#deadbeef
) rather than being computed or passed as a parameter.
import scalus.*, Compiler.*, builtin.{Data, Builtins, ByteString, given}, Builtins.*, ByteString.given
inline def validator(inline pubKeyHash: ByteString)(datum: Data, redeemer: Data, ctxData: Data) =
verifyEd25519Signature(pubKeyHash, datum.toByteString, redeemer.toByteString)
val script = compile:
validator(hex"deadbeef")
… generates the following SIR code, where you can see the inlined constant #deadbeef
directly in the compiled output:
{λ datum redeemer ctxData ->
verifyEd25519Signature(#deadbeef, unBData(datum), unBData(redeemer))
}
Conditional code generation
TODO: Check using
The using
macro allows you to conditionally include or exclude code at compile time based on configuration parameters. This is particularly useful for creating separate debug and production versions of your validators.
dbg
- Trace calls generation
import scalus.*, Compiler.*, builtin.Data, builtin.Builtins
// the `dbg` macro will generate `trace` calls only if the `debug` flag is set to `true`
inline def dbg[A](msg: String)(a: A)(using debug: Boolean): A =
inline if debug then Builtins.trace(msg)(a) else a
inline def validator(using debug: Boolean)(datum: Data, redeemer: Data, ctxData: Data) =
dbg("datum")(datum)
val releaseScript = compile(validator(using false))
// {λ datum redeemer ctxData -> datum }
val debugScript = compile(validator(using true))
// {λ datum redeemer ctxData -> trace("datum", datum) }
Here, the releaseScript
will not contain any trace
calls, while the debugScript
will contain them. This demonstrates how Scalus can conditionally include or exclude code at compile time based on configuration parameters.
The conditional code is evaluated during compilation, so there’s zero runtime overhead for disabled features.