Dynamic Function Calls in Clarity Using Traits

Table of contents
Hey builders! 👋🏽
If you’ve been writing smart contracts in Clarity, chances are you’ve heard of traits
. But if you’re like me when I started out, you probably thought:
“Okay cool, traits are like interfaces. But why do I actually need them?”
In this article, I want to show you with a real example how traits can power dynamic function calls and contract composability, using a marketplace + NFT example built with the SIP-009 NFT standard.
Let’s dive in! 👇
🔍 What are Traits in Clarity?
A trait
in Clarity is basically a contract interface as it defines a set of functions that another contract must implement if it wants to be compatible.
It’s Clarity’s way of saying:
“I don’t care how you implement the logic as long as you expose these exact function signatures, we’re good to go.”
Why Should You Use Traits?
Let’s say you’re building a marketplace where users can list NFTs for sale. But here's the thing:
NFTs can be from any contract, as long as they follow a known standard (like SIP-009).
You want your marketplace to interact with these NFTs without hardcoding their contract logic.
Enter traits.
With traits, your marketplace contract can accept any NFT contract that implements the SIP-009 trait meaning, you can interact with it in a dynamic way without coupling your logic.
Real-World Example: NFT Marketplace
Here’s how I used traits in my own project, a simple NFT marketplace.
1. The Trait (SIP-009)
This is the standard trait definition for NFTs:
;; sip-trait.clar
(define-trait sip-trait
(
(get-last-token-id () (response uint uint))
(get-token-uri (uint) (response (optional (string-ascii 256)) uint))
(get-owner (uint) (response (optional principal) uint))
(transfer (uint principal principal) (response bool uint))
)
)
Any NFT contract that implements this trait can now be plugged into our marketplace.
2. How to Implement a Trait in Your Contract (using impl-trait
)
Now let’s say you’re writing a contract and you want it to conform to the above trait. For example, so it can be used in a marketplace.
Here’s how you do it.
Implement the Trait
(impl-trait .sip-009.sip-trait)
This tells the Clarity compiler:
“Hey, I’m implementing this trait, check that I have all the required functions.”
If you miss anything or get the types wrong, Clarity will throw an error. Also, notice how I used .sip-009.sip-trait? I used the shorthand contract identifier because the trait contract is in my local environment. If the trait I’m trying to implement is already deployed I’d have to use the full contract identifier such as:
(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
3. Define the Required Functions
Next you define the required functions in the trait in your contract to make it compliant to that trait
(define-public (get-owner (token-id uint))
(ok (nft-get-owner? my-nft token-id))
)
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
(nft-transfer? my-nft token-id sender recipient)
)
(define-public (get-last-token-id)
(ok u100) ;; just a dummy value
)
(define-public (get-token-uri (token-id uint))
(ok (some "ipfs://some-uri"))
)
Boom 💥 Now your contract is compliant with SIP-009.
4. Use Traits for Dynamic Calls
Next thing to do is to import the trait and use it to make dynamic calls to any contract that implements it. Here's how:
(use-trait nft .sip-009.sip-trait)
That nft
alias can now be used like this:
(define-public (buy-item (nft-collection <nft>) (nft-id uint))
(let (
(owner (unwrap! (contract-call? nft-collection get-owner nft-id) (err "not-found")))
)
;; do something with the owner
(ok owner)
)
)
Notice the <nft>
type that enforces that the contract passed in must implement the sip-trait
.
This is super powerful for creating generic marketplaces, governance systems, or anything that works across different contracts.
Real-World Use Cases
Marketplaces: Accept any NFT contract that implements SIP-009
Governance: Accept multiple token contracts that follow SIP-010
Upgradable Logic: Swap in/out logic contracts as long as they follow the same trait
Tooling: Build frontend tools and SDKs that rely on predictable interfaces
Final Thoughts
Traits are one of the most underrated features in Clarity and they're a huge reason why Stacks contracts feel clean, modular, and safe.
If you’re building composable protocols or dApps that need to work with others in the ecosystem, traits are a must.
So next time you’re designing your contract architecture, ask yourself:
“Can I define a trait for this?”
Your future self will thank you.
Happy building ✨
Subscribe to my newsletter
Read articles from Muritadhor Arowolo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
