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

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 checks passed

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
$
(contract-call? .counter count-up)

Execution tracing with ::trace

Trace function calls to understand execution flow:

Terminal
$
::trace (contract-call? .defi-pool swap u100 'token-a 'token-b)

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

Cost optimization strategies

Understanding execution costs helps you write efficient contracts that minimize transaction fees for users.

Understanding cost metrics

Clarinet tracks five cost categories, each with block-level limits:

CategoryWhat It MeasuresOptimization Focus
RuntimeCode complexity and contract sizeSimplify logic, reduce contract size
Read countMemory/state access frequencyMinimize repeated reads
Read lengthData volume readUse efficient data structures
Write countState modification frequencyBatch updates when possible
Write lengthData volume writtenStore only essential data

Using ::get_costs for analyzing specific function costs:

Terminal
$
::get_costs (contract-call? .defi-pool add-liquidity u1000 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? .auction place-bid u1000)
$
continue

At each breakpoint:

  • Inspect variable values
  • Check contract state
  • Evaluate expressions
  • Modify execution flow

Analyzing failed transactions using ::trace:

Terminal
$
::trace (contract-call? .marketplace purchase u999)

Using ::encode and ::decode for data inspection

Debug complex data structures:

Terminal
$
::decode 0x0c00000002046e616d65020000000543686f636f62616c616e6365010000000000000000000000000000000064
{balance: u100, name: "Choco"}
$
::encode (list {id: u1, active: true} {id: u2, active: false})
0x0b000000020c00000002026964010000000000000000000000000000000001066163746976650301...

Testing time-dependent logic using ::get_block_height and ::advance_chain_tip:

Terminal
$
::get_block_height
$
::advance_chain_tip 100
$
(contract-call? .vesting claim)

Common issues

Next steps

Now that you understand contract validation and analysis: