| title | Language Basics |
|---|
EVMcrispr uses a custom DSL (domain-specific language) designed for writing EVM transaction scripts. This guide covers the core syntax.
Lines starting with # are comments:
# This is a comment
set $x 42 # Inline comments work too
Commands are the primary building blocks. They produce transactions or control program flow. Each command starts with its name followed by arguments:
exec 0xAbC... "transfer(address,uint256)" @me 100e18
set $greeting "hello"
print "The value is" $x
Commands from non-default modules use a prefix:
load aragonos
aragonos:connect my-dao.aragonid.eth (
aragonos:grant @me voting CREATE_VOTES_ROLE
)
Helpers are expressions that produce values. They are prefixed with @:
@me # No arguments
@token(DAI) # Single argument
@get(0xAbC... "balanceOf(address)(uint256)" @me) # Multiple arguments
Helpers can be nested and used as arguments to commands:
exec @token(DAI) "transfer(address,uint256)" @me @token.amount(DAI 100)
Use set to assign values and $name to reference them:
set $recipient 0x1234...
set $amount @token.amount(DAI 1000)
exec @token(DAI) "transfer(address,uint256)" $recipient $amount
The DSL supports these value types:
| Type | Example | Description |
|---|---|---|
address |
0xAbCd...1234 |
20-byte Ethereum address |
number |
42, 100e18, 1.5e6 |
Integer or scientific notation |
string |
"hello", 'it\'s fine' |
Quoted string (single or double); supports \', \", \\, \n, \r, \t, and \u{HHHH} escapes (any other \X is left literal); may span multiple lines |
bool |
true, false |
Boolean |
bytes |
0xdeadbeef |
Hex-encoded bytes |
bytes32 |
0x00...001 |
32-byte value |
array |
[1 2 3] |
Ordered collection |
Numbers support scientific notation with e: 100e18 means 100 * 10^18.
This is useful for token amounts with 18 decimals.
Some commands accept a block of sub-commands in parentheses:
batch (
exec 0xA... "foo()"
exec 0xB... "bar()"
)
for $i in @range(0 5) (
print $i
)
if @bool($x > 0) (
print "positive"
) else (
print "non-positive"
)
Contract function signatures follow Solidity syntax:
# Write functions (for exec)
"transfer(address,uint256)"
"approve(address,uint256)"
# Read functions (for @get) — include (returnType) after inputs
"balanceOf(address)(uint256)"
"name()(string)"
"getReserves()(uint112,uint112,uint32)"
The std module is loaded by default. Load additional modules with:
load aragonos
load sim
load ens
load http
After loading, use their commands and helpers with the module prefix:
load sim
sim:fork 1 (
sim:set-balance @me 100e18
exec 0xAbC... "foo()"
)
Some commands accept options with --name value:
exec 0xAbC... "foo()" --value 1e18
exec 0xAbC... "foo()" --from 0x1234...
load aragonos --as dao
The exec command can capture events emitted by the transaction:
exec 0xAbC... "createPool(address,uint24)" @token(DAI) 3000 -> Transfer [_ $pool]
Use -!> to catch and decode transaction reverts. After the error name you
can add a destructure ([...]), a boolean variable ($var), or nothing:
# Assert a specific error (no data captured)
exec $c "deny()" -!> Unauthorized()
# Destructure error arguments into variables
exec $c "withdraw(uint256)" 200 -!> InsufficientBalance(uint256,uint256) [$balance $required]
# Catch a require/revert reason
exec $c "transfer(address,uint256)" @me 100e18 -!> Error(string) [$reason]
# Boolean variable — $e is "true" if error matched
exec $c "deny()" -!> Unauthorized() $e
# Generic catch-all (no error name)
exec $c "doSomething()" -!> [$reason]
exec $c "doSomething()" -!> $e
Use -?!> if the error is optional (transaction may or may not revert).
With a boolean variable, $e is "true" on match, "false" on success or
mismatched error:
exec $c "maybeRevert()" -?!> Error(string) [$reason]
exec $c "maybeRevert()" -?!> Unauthorized() $e
Supported error types:
- Custom named errors:
revert CustomError(arg1, arg2) - Error(string):
require(cond, "msg")/revert("msg") - Panic(uint256):
assert(cond)failures - Empty reverts: pre-0.4.22
revert()with no data
Use @num() for arithmetic and @bool() for boolean logic:
set $total @num($a + $b * 2)
set $isValid @bool($x > 0 and $y < 100)
# Conditional
if @bool($balance > 0) (
print "Has balance"
)
# Loop over array
for $item in @range(0 10) (
print $item
)
# While loop
set $i 0
while @bool($i < 5) (
print $i
set $i @num($i + 1)
)
Use def to define reusable commands and helpers:
# Define a helper
def @double "$x: number -> number" (
@num($x * 2)
)
# Define a command
def transfer($token $to $amount) (
exec @token($token) "transfer(address,uint256)" $to @token.amount($token $amount)
)
# Use them
set $result @double(21)
transfer DAI 0x1234... 100