In this tutorial we are going to dive into the new NFT token extension for royalties on Stellar.

NFT royalties provide a mechanism for ensuring that creators continue to benefit from secondary sales of their work. With the latest release of OpenZeppelin’s NFT extensions for Soroban smart contracts on Stellar, developers can now seamlessly integrate royalty logic directly into their NFT contracts.
OpenZeppelin’s Stellar contracts now support royalties through a dedicated extension available at: OpenZeppelin Royalties Library
This module introduces native support for:
- Default collection-wide royalties
- Per-token royalty overrides
- Royalty information queries based on token ID and sale price
Full code for the example below is available here:
https://github.com/jamesbachini/NFT-Royalties-Example
You can use this with the Soroban playground at https://soropg.com
#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String};
use stellar_access_control::{self as access_control, AccessControl};
use stellar_access_control_macros::{has_role};
use stellar_default_impl_macro::default_impl;
use stellar_non_fungible::{royalties::NonFungibleRoyalties, Base, NonFungibleToken};
#[contract]
pub struct RoyaltyExampleContract;
#[contractimpl]
impl RoyaltyExampleContract {
pub fn __constructor(e: &Env, admin: Address, manager: Address) {
Base::set_metadata(
e,
String::from_str(e, "https://example.com/nft/"),
String::from_str(e, "Royalty NFT"),
String::from_str(e, "RNFT"),
);
// Set default royalty for the entire collection (10%)
Base::set_default_royalty(e, &admin, 100);
access_control::set_admin(e, &admin);
// create a role "manager" and grant it to `manager`
access_control::grant_role_no_auth(e, &admin, &manager, &symbol_short!("manager"));
}
pub fn mint(e: &Env, to: Address) -> u32 {
Base::sequential_mint(e, &to)
}
pub fn get_royalty_info(e: &Env, token_id: u32, sale_price: u32) -> (Address, u32) {
Base::royalty_info(e, token_id, sale_price)
}
}
#[default_impl]
#[contractimpl]
impl NonFungibleToken for RoyaltyExampleContract {
type ContractType = Base;
}
#[contractimpl]
impl NonFungibleRoyalties for RoyaltyExampleContract {
#[has_role(operator, "manager")]
fn set_default_royalty(e: &Env, receiver: Address, basis_points: u32, operator: Address) {
Base::set_default_royalty(e, &receiver, basis_points);
}
#[has_role(operator, "manager")]
fn set_token_royalty(
e: &Env,
token_id: u32,
receiver: Address,
basis_points: u32,
operator: Address,
) {
Base::set_token_royalty(e, token_id, &receiver, basis_points);
}
fn royalty_info(e: &Env, token_id: u32, sale_price: u32) -> (Address, u32) {
Base::royalty_info(e, token_id, sale_price)
}
}
#[default_impl]
#[contractimpl]
impl AccessControl for RoyaltyExampleContract {}
Understanding Royalties and Basis Points
The contract uses basis points (bps) to define royalty percentages:
1 basis point
= 0.01%100 basis points
= 1%1000 basis points
= 10%
In this example, we configure a default royalty of 1%, assigned to the admin address. This applies to all tokens in the collection unless a specific royalty is set per token.
Internally, the NonFungibleRoyalties
trait works as follows:
- Default royalties are stored and returned unless a token-specific royalty is set.
- Overrides take precedence when defined per token.
- The
royalty_info
function performs the logic to return the right configuration.
Developers don’t need to reimplement royalty logic—the base class handles the complexity.
Marketplace Integration
Note that royalties are not enforced automatically.
Smart contracts only expose royalty info, they do not collect or distribute royalties by default. It’s up to NFT marketplaces to query and enforce these settings during sales.
If an NFT is transferred peer-to-peer (wallet-to-wallet), no royalties are applied. Royalty enforcement only occurs if marketplaces voluntarily support and honor the standard.
Marketplace Adoption on Stellar
As of now, royalty enforcement is not yet common across Stellar NFT marketplaces. However:
- 🔍 Litemint has indicated in their documentation that they are working on a royalty system.
- 🚀 Wider adoption is expected as the OpenZeppelin library becomes the standard.
Even if royalties aren’t enforced today, implementing them prepares your contracts for future integrations.
Royalties introduce a sustainable revenue model for creators:
- Artists and developers earn ongoing revenue as their NFTs are traded.
- Incentivizes quality content and long-term community engagement.
- Aligns interest between creators, collectors, and platforms.
By adopting the royalty standard early, your NFT contracts are future-proofed for when marketplaces begin enforcement.