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 Analysis | Runtime Debugging |
---|---|
Catches errors before deployment | Reveals behavior during execution |
Type mismatches, syntax errors | Actual execution costs |
Trait compliance violations | State changes and side effects |
Undefined variable usage | Transaction flow and results |
Function signature issues | Performance bottlenecks |
Static analysis
The clarinet check
command performs comprehensive validation of your contracts without executing them:
$clarinet check✔ 3 contracts checked
When validation fails, Clarinet provides detailed diagnostics:
$clarinet check✖ 1 error detectedError 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 Type | What It Checks |
---|---|
Type safety | Function parameters, return values, variable types |
Trait compliance | Implementation matches trait definitions |
Response consistency | Ok/err branches return same types |
Variable scope | All variables defined before use |
Function visibility | Public/private/read-only modifiers |
Checking specific contracts
Validate individual contracts during focused development:
$clarinet check contracts/nft.clar✔ contracts/nft.clar Syntax of contract successfully checked
Integration with CI/CD
Automate validation in your continuous integration pipeline:
name: Contract Validationon: [push, pull_request]jobs:validate:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- name: Install Clarinetrun: curl -L https://install.hiro.so/clarinet | sh- name: Validate contractsrun: 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:
$clarinet console$::toggle_costsAlways 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:
$::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-au50000( get-pool-balance 'token-b ) defi-pool:16:8↳ args: 'token-bu75000( calculate-output u100 u50000 u75000 ) defi-pool:18:12↳ args: u100, u50000, u75000u149(ok u149)
Interactive debugging with ::debug
Set breakpoints and step through execution:
$::debug (contract-call? .complex-contract process-batch)$break validate-inputBreakpoint set at validate-input$continueHit breakpoint at validate-input:23
Debug navigation commands:
step
ors
- Step into sub-expressionsfinish
orf
- Complete current expressionnext
orn
- Step over sub-expressionscontinue
orc
- Continue to next breakpoint
Using ::get_costs
for analyzing specific function costs
$::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
$::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:
$clarinet console$::debug (contract-call? .counter count-up)ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter:9:367 ;; Increment the count for the caller8 (define-public (count-up)-> 9 (ok (map-set Counters tx-sender (+ (get-count tx-sender) u1)))^10 )1112 ;; Get the current count for a user(debug)$continue
Analyzing failed transactions using ::trace
$::trace (contract-call? .marketplace purchase u999)(contract-call? .marketplace purchase u999) <console>( get-listing u999 ) marketplace:45:12↳ args: u999none(err u404) # Listing not found
Using ::encode
and ::decode
for data inspection
Debug complex data structures:
$::encode { id: u1, active: true }0c0000000206616374697665030269640100000000000000000000000000000001$::decode 0d0000000b48656c6c6f20776f726c64"Hello world"
Testing time-dependent logic
$::get_block_heightCurrent block height: 4$::advance_chain_tip 100new burn height: 3new stacks height: 104$(contract-call? .vesting claim)(ok {claimed: u2500, remaining: u7500})