Hello Stacks - Your First Clarity 3.0 Smart Contract

Table of contents
- What You'll Learn Today
- Before We Start
- Understanding Clarity 3.0 Changes
- Why Clarity 3.0?
- Key Changes in Clarity 3.0
- Why This Matters:
- Setting Up Your Development Environment
- Writing Your First Clarity 3.0 Contract
- Deploying to Testnet
- Interacting with Your Contract
- Key Takeaways
- Tomorrow's Preview
- Join the Conversation
- Complete Implementation

Welcome to your 30-day journey mastering the Stacks ecosystem! Today, we're learning the fundamentals of Clarity 3.0 - the latest version of Stacks' smart contract language, and how it differs from previous versions.
What You'll Learn Today
By the end of this tutorial, you'll understand:
How to set up a modern Clarity 3.0 development environment
Key differences between Clarity 2.0 and 3.0 syntax
Why
stacks-block-height
replacedblock-height
Basic smart contract structure and best practices
How to test and deploy your first contract
Before We Start
This tutorial assumes basic programming knowledge in any language. We're using the absolute latest versions of all tools as of 2025, so everything will be modern and follow current best practices.
Understanding Clarity 3.0 Changes
Before diving into code, let's understand why Clarity 3.0 exists and what changed.
Why Clarity 3.0?
With the Nakamoto upgrade, Stacks blocks now occur much more frequently. Many existing contracts relied on block-height
to approximate time, assuming each block took ~10 minutes. Clarity 3.0 introduces new primitives to handle this change while preserving backward compatibility.
Key Changes in Clarity 3.0
Block Height Functions:
❌
block-height
(deprecated in Clarity 3.0)✅
stacks-block-height
(fast Stacks blocks)✅
tenure-height
(slower, ~10 minute intervals like old blocks)
Why This Matters:
;; Clarity 2.0 way (deprecated in 3.0)
(define-read-only (get-old-time)
(* block-height u600)) ;; Won't work in Clarity 3.0!
;; Clarity 3.0 way
(define-read-only (get-approximate-time)
(* tenure-height u600)) ;; Maintains ~10 min assumption
;; Or use fast blocks for precise block counting
(define-read-only (get-precise-block)
stacks-block-height)
Setting Up Your Development Environment
Step 1: Install Clarinet (Latest Version)
The modern way to install Clarinet varies by platform. Current Clarinet versions default to Clarity 3.0 and Epoch 3.0.
For macOS:
brew install clarinet
For Windows:
# Using Winget (recommended for 2025)
winget install clarinet
Install from a pre-built binary
# note: you can change v3.2.0 with the latest version available on the releases page.
wget -nv https://github.com/hirosystems/clarinet/releases/download/v3.2.0/clarinet-linux-x64-glibc.tar.gz -O clarinet-linux-x64.tar.gz
tar -xf clarinet-linux-x64.tar.gz
chmod +x ./clarinet
mv ./clarinet /usr/local/bin
If you have an issue at all, go to the clarity book
Read the clarity book installation guide: https://book.clarity-lang.org/ch01-01-installing-tools.html
Verify your installation:
clarinet --version
# Should show 3.0 or newer
Step 2: Understanding Project Structure
Create a new project to see the modern structure:
clarinet new hello-stacks-tutorial
cd hello-stacks-tutorial
What you get:
hello-stacks-tutorial/
├── Clarinet.toml # Project configuration
├── contracts/ # Your .clar smart contracts
├── tests/ # TypeScript test files
└── settings/ # Network configurations (testnet, mainnet)
Key Change: Tests are now in TypeScript and run with npm run test
, not clarinet test
.
Writing Your First Clarity 3.0 Contract
Step 1: Create the Contract
clarinet contract new hello-world
This creates contracts/hello-world.clar
and tests/hello-world_test.ts
.
Step 2: Understanding Modern Clarity Structure
Let's build a simple but educational contract that demonstrates Clarity 3.0 features:
;; Hello World - Clarity 3.0 Example
;; This contract demonstrates modern Clarity patterns
;; Storage - where our data lives on the blockchain
(define-data-var greeting (string-ascii 50) "Hello, Clarity 3.0!")
(define-data-var owner principal tx-sender)
;; Constants - error codes are a best practice
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-TOO-LONG (err u101))
;; Read-only function - doesn't cost gas to call
(define-read-only (get-greeting)
(var-get greeting)
)
;; Public function - costs gas and can modify state
(define-public (set-greeting (new-greeting (string-ascii 50)))
(begin
;; Check if caller is authorized
(asserts! (is-eq tx-sender (var-get owner)) ERR-NOT-AUTHORIZED)
;; Validate input length
(asserts! (<= (len new-greeting) u50) ERR-TOO-LONG)
;; Update the greeting
(var-set greeting new-greeting)
;; Return success with additional data
(ok {
message: "Greeting updated!",
block: stacks-block-height, ;; Clarity 3.0!
tenure: tenure-height ;; Clarity 3.0!
})
)
)
;; Demonstrating the difference between block types
(define-read-only (get-block-info)
{
stacks-blocks: stacks-block-height, ;; Fast blocks
tenure-blocks: tenure-height, ;; ~10 min intervals
estimated-time: (* tenure-height u600) ;; Approximate timestamp
}
)
Step 3: Key Concepts Explained
Data Variables:
Store mutable data on the blockchain
Type-safe: once you declare
(string-ascii 50)
, it can't be changedPersist between function calls
Constants:
Immutable values defined at contract deployment
Use for error codes, limits, addresses
Assertions:
asserts!
validates conditions and returns an error if falseEssential for security and user feedback
Return Values:
(ok ...)
for success(err ...)
for errorsFunctions must return consistent types
Testing Your Contract (Modern Way)
Step 1: Install Dependencies
In your project directory:
bash
npm install
This installs the testing framework that works with modern Clarinet.
Step 2: Understanding Test Structure
Look at tests/hello-world_test.ts
. Modern tests use TypeScript:
import { describe, it, expect } from 'vitest';
import { Cl } from '@stacks/transactions';
// Get test accounts from simnet
const accounts = simnet.getAccounts();
const deployer = accounts.get('deployer')!;
describe('Hello World Contract', () => {
// Test the default greeting message
it('returns the default greeting', () => {
const greeting = simnet.callReadOnlyFn(
'hello-world', // contract name
'get-greeting', // function to call
[], // no arguments
deployer // caller identity
);
// Check that the result matches the expected string
expect(greeting.result).toBeAscii('Hello, Clarity 3.0!');
});
// Test who the contract owner is
it('returns the contract owner', () => {
const owner = simnet.callReadOnlyFn(
'hello-world',
'get-owner',
[],
deployer
);
// Check that the owner is the deployer
expect(owner.result).toBePrincipal(deployer);
});
});
Step 3: Run Tests (Updated Command)
bash
# Modern way - NOT clarinet test
npm run test
Why the change?
Better integration with modern JavaScript tooling
Supports TypeScript out of the box
More familiar to web developers
Faster test execution
Deploying to Testnet
Step 1: Generate Configuration
bash
clarinet deployment generate --testnet
This creates deployment plans in your deployments/
folder.
Step 2: Get Test STX
Visit the Stacks Testnet Faucet and request test STX for your address.
Step 3: Deploy
bash
clarinet deployment apply --testnet
What happens:
Clarinet reads your deployment plan
Submits your contract to the testnet
Returns a contract address like
ST123...ABC.hello-world
Interacting with Your Contract
Using Clarinet Console
bash
clarinet console --testnet
Try these commands:
clarity
;; Read data (free)
(contract-call? .hello-world get-greeting)
;; See Clarity 3.0 block info
(contract-call? .hello-world get-block-info)
;; Try to update greeting (might fail if not owner)
(contract-call? .hello-world set-greeting "Hello, Testnet!")
Using Stacks Explorer
Go to explorer.stacks.co
Search for your contract address
Explore the functions and call them through the UI
Key Takeaways
What You Learned:
Clarity 3.0 uses
stacks-block-height
andtenure-height
instead ofblock-height
Modern testing uses
npm run test
, notclarinet test
Type safety is enforced throughout Clarity
Error handling uses constants and assertions
Smart contracts store data permanently on the blockchain
Modern Development Flow:
clarinet new
→ Create projectclarinet contract new
→ Add contractsnpm run test
→ Test contractsclarinet deployment generate --testnet
→ Prepare deploymentclarinet deployment apply --testnet
→ Deploy
Best Practices You Applied:
Used descriptive error constants
Validated inputs with assertions
Returned structured data from functions
Leveraged Clarity 3.0's new block height functions
Tomorrow's Preview
Ready to connect your smart contract to a beautiful web interface? Tomorrow we're building a modern React frontend using:
React 19 with latest hooks and features
Next.js 15 with App Router and TypeScript
Stacks.js 8.x with the new SIP-030 wallet connection standard
Tailwind CSS for modern, responsive styling
We'll create a web3 interface that lets users connect their wallets and interact with your Clarity 3.0 contract through an intuitive UI!
Join the Conversation
How did your first Clarity 3.0 contract deployment go? Share your contract address in the comments! If you encountered any issues with the updated commands or Clarity 3.0 syntax, let us know - that's how we all learn together.
Complete Implementation
Remember, all the working code for today's concepts is available in our GitHub repository. The tutorial teaches you why and how - the repo shows you the complete implementation details.
Next up: [Day 2 - Building a Modern React Frontend for Your Smart Contract]
This is Day 1 of our 30-day Clarity & Stacks.js tutorial series. Follow along daily as we progress from basics to building sophisticated decentralized applications.
Essential Links:
Subscribe to my newsletter
Read articles from Gbolahan Akande directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
