Validating your contracts

Clarinet provides powerful tools for validating, analyzing, and debugging your smart contracts. From static type checking to real-time cost analysis, you can ensure your contracts are correct and efficient before deployment.

Contract validation in Clarity development encompasses static analysis, runtime debugging, and cost optimization. Each approach serves different purposes in ensuring contract correctness and efficiency.

Understanding contract validation

Static Analysis vs Runtime Debugging

Static AnalysisRuntime Debugging
Catches errors before deploymentReveals behavior during execution
Type mismatches, syntax errorsActual execution costs
Trait compliance violationsState changes and side effects
Undefined variable usageTransaction flow and results
Function signature issuesPerformance bottlenecks

Static analysis

The clarinet check command performs comprehensive validation of your contracts without executing them:

Terminal
$
clarinet check
3 contracts checked

When validation fails, Clarinet provides detailed diagnostics:

Terminal
$
clarinet check
1 error detected
Error in contracts/token.clar:15:10
|
15| (ok (+ balance amount))
| ^^^^^^^
|
= Type error: expected uint, found (response uint uint)

Validation scope

Clarinet validates multiple aspects of your contracts:

Validation TypeWhat It Checks
Type safetyFunction parameters, return values, variable types
Trait complianceImplementation matches trait definitions
Response consistencyOk/err branches return same types
Variable scopeAll variables defined before use
Function visibilityPublic/private/read-only modifiers

Checking specific contracts

Validate individual contracts during focused development:

Terminal
$
clarinet check contracts/nft.clar
contracts/nft.clar Syntax of contract successfully checked

Integration with CI/CD

Automate validation in your continuous integration pipeline:

.github/workflows/validate.yml
name: Contract Validation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Clarinet
run: curl -L https://install.hiro.so/clarinet | sh
- name: Validate contracts
run: clarinet check

Runtime analysis

The Clarinet console provides powerful runtime analysis tools that let you inspect contract behavior during execution.

Cost analysis with ::toggle_costs

Enable automatic cost display after every expression:

Terminal
$
clarinet console
$
::toggle_costs
Always show costs: true
$
(contract-call? .counter count-up)
+----------------------+----------+------------+------------+
| | Consumed | Limit | Percentage |
|----------------------+----------+------------+------------|
| Runtime | 4775 | 5000000000 | 0.00 % |
|----------------------+----------+------------+------------|
| Read count | 5 | 15000 | 0.03 % |
|----------------------+----------+------------+------------|
| Read length (bytes) | 268 | 100000000 | 0.00 % |
|----------------------+----------+------------+------------|
| Write count | 1 | 15000 | 0.01 % |
|----------------------+----------+------------+------------|
| Write length (bytes) | 41 | 15000000 | 0.00 % |
+----------------------+----------+------------+------------+
(ok true)

Execution tracing with ::trace

Trace function calls to understand execution flow:

Terminal
$
::trace (contract-call? .defi-pool swap u100 'token-a 'token-b)
(contract-call? .defi-pool swap u100 'token-a 'token-b) <console>
( get-pool-balance 'token-a ) defi-pool:15:8
↳ args: 'token-a
u50000
( get-pool-balance 'token-b ) defi-pool:16:8
↳ args: 'token-b
u75000
( calculate-output u100 u50000 u75000 ) defi-pool:18:12
↳ args: u100, u50000, u75000
u149
(ok u149)

Interactive debugging with ::debug

Set breakpoints and step through execution:

Terminal
$
::debug (contract-call? .complex-contract process-batch)
$
break validate-input
Breakpoint set at validate-input
$
continue
Hit breakpoint at validate-input:23

Debug navigation commands:

  • step or s - Step into sub-expressions
  • finish or f - Complete current expression
  • next or n - Step over sub-expressions
  • continue or c - Continue to next breakpoint

Using ::get_costs for analyzing specific function costs

Terminal
$
::get_costs (contract-call? .defi-pool add-liquidity u1000 u1000)
+----------------------+----------+------------+------------+
| | Consumed | Limit | Percentage |
|----------------------+----------+------------+------------|
| Runtime | 12250 | 5000000000 | 0.00 % |
|----------------------+----------+------------+------------|
| Read count | 6 | 15000 | 0.04 % |
|----------------------+----------+------------+------------|
| Read length (bytes) | 192 | 100000000 | 0.00 % |
|----------------------+----------+------------+------------|
| Write count | 3 | 15000 | 0.02 % |
|----------------------+----------+------------+------------|
| Write length (bytes) | 96 | 15000000 | 0.00 % |
+----------------------+----------+------------+------------+
(ok {lp-tokens: u1000})

Identifying and optimizing costly operations using ::trace

Terminal
$
::trace (contract-call? .complex-algo process-large-dataset)

Look for:

  • Loops with high iteration counts
  • Nested map/filter operations
  • Repeated contract calls
  • Large data structure manipulations

Debugging workflows

Master interactive debugging to quickly identify issues by starting a debugging session:

Terminal
$
clarinet console
$
::debug (contract-call? .counter count-up)
ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter:9:3
6
7 ;; Increment the count for the caller
8 (define-public (count-up)
-> 9 (ok (map-set Counters tx-sender (+ (get-count tx-sender) u1)))
^
10 )
11
12 ;; Get the current count for a user
(debug)
$
continue

Analyzing failed transactions using ::trace

Terminal
$
::trace (contract-call? .marketplace purchase u999)
(contract-call? .marketplace purchase u999) <console>
( get-listing u999 ) marketplace:45:12
↳ args: u999
none
(err u404) # Listing not found

Using ::encode and ::decode for data inspection

Debug complex data structures:

Terminal
$
::encode { id: u1, active: true }
0c0000000206616374697665030269640100000000000000000000000000000001
$
::decode 0d0000000b48656c6c6f20776f726c64
"Hello world"

Testing time-dependent logic

Terminal
$
::get_block_height
Current block height: 4
$
::advance_chain_tip 100
new burn height: 3
new stacks height: 104
$
(contract-call? .vesting claim)
(ok {claimed: u2500, remaining: u7500})

Next steps