Skip to content

kitWarse/Stellar-Royalty-Splitter

 
 

Repository files navigation

Stellar Royalty Splitter

On-chain royalty distribution for NFT collaborators on Stellar.

A Soroban smart contract that automatically splits NFT sale proceeds
among multiple collaborators based on predefined percentage allocations —
instantly, on-chain, with no intermediaries.

License: MIT Soroban Rust Stellar Networks


Overview

Stellar Royalty Splitter solves the coordination problem in multi-collaborator NFT projects. Instead of relying on a central party to manually divide and send proceeds, the contract enforces the agreed split at the protocol level. Shares are defined once at initialization and cannot be altered — every distribution is deterministic, transparent, and verifiable on-chain.

The contract supports both primary sales and secondary market royalties, with rounding handled explicitly so the full amount is always distributed.


Table of Contents


How It Works

Deploy contract
      │
      ▼
initialize(collaborators, shares)   ← one-time setup, basis points sum to 10,000
      │
      ▼
NFT sale occurs → funds sent to contract address
      │
      ▼
distribute(token, amount)           ← splits and transfers proportionally, on-chain
      │
      ▼
Each collaborator receives their share instantly

Shares are expressed in basis points (1 bp = 0.01%). They must sum to 10,000 (100%).


Prerequisites

Tool Install
Rust https://rustup.rs
wasm32 target rustup target add wasm32-unknown-unknown
Stellar CLI cargo install --locked stellar-cli

Build

cargo build --target wasm32-unknown-unknown --release

Test

cargo test

Deploy

chmod +x scripts/deploy.sh
./scripts/deploy.sh

The deploy script targets Stellar Testnet by default. See Environment Variables to switch to Mainnet.


Contract API

initialize(collaborators: Vec<Address>, shares: Vec<u32>)

Sets up the revenue split. Can only be called once. Subsequent calls will be rejected.

Parameter Description
collaborators List of recipient wallet addresses
shares Basis-point allocation per collaborator (must sum to 10,000)

distribute(token: Address, amount: i128)

Transfers amount of token from the contract address to all collaborators proportionally. Any rounding dust is assigned to the last collaborator — see Rounding.

record_secondary_sale(nft_id, previous_owner, new_owner, sale_price, sale_token)

Records a secondary market resale and accumulates the royalty amount owed to collaborators.

distribute_secondary_royalties(token: Address)

Distributes all pending secondary royalties accumulated via record_secondary_sale.

get_collaborators() → Vec<Address>

Returns all registered collaborator addresses.

get_share(collaborator: Address) → u32

Returns the basis-point share for a given collaborator address.

update_wasm(wasm_hash: BytesN<32>)

Replaces the contract's executable WASM while preserving all instance storage (admin, collaborators, shares, balances, etc.). Requires admin authorization. The replacement Wasm must be uploaded to the network first via stellar contract upload; use the returned hash as wasm_hash.


Usage Examples

Initialize a 3-way split

# 50% artist / 30% musician / 20% animator
stellar contract invoke \
  --id <CONTRACT_ID> \
  --source deployer \
  --network testnet \
  -- initialize \
  --collaborators '["GARTIST...","GMUSICIAN...","GANIMATOR..."]' \
  --shares '[5000,3000,2000]'

Distribute primary sale proceeds

# Distribute 1,000 XLM from a sale (amounts in stroops: 1 XLM = 10,000,000 stroops)
stellar contract invoke \
  --id <CONTRACT_ID> \
  --source seller \
  --network testnet \
  -- distribute \
  --token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
  --amount 10000000000

Check a collaborator's share

stellar contract invoke \
  --id <CONTRACT_ID> \
  --source anyone \
  --network testnet \
  -- get_share \
  --collaborator GARTIST...

Upgrade contract WASM (admin only)

# 1. Build and upload the new contract Wasm
cargo build --target wasm32-unknown-unknown --release
stellar contract upload \
  --source deployer \
  --wasm target/wasm32-unknown-unknown/release/stellar_royalty_splitter.wasm \
  --network testnet
# → aa24c81289997ad815489b29db337b53f284cca5aba86e9a8ae5cef7d31842c2

# 2. Invoke update_wasm with the uploaded hash (admin must sign)
stellar contract invoke \
  --id <CONTRACT_ID> \
  --source deployer \
  --network testnet \
  -- update_wasm \
  --wasm_hash aa24c81289997ad815489b29db337b53f284cca5aba86e9a8ae5cef7d31842c2

Record a secondary sale royalty

# Record a 5% royalty from a 500 XLM resale
stellar contract invoke \
  --id <CONTRACT_ID> \
  --source marketplace \
  --network testnet \
  -- record_secondary_sale \
  --nft_id "NFT_001" \
  --previous_owner GBUYER1... \
  --new_owner GBUYER2... \
  --sale_price 5000000000 \
  --sale_token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC

Distribute accumulated secondary royalties

stellar contract invoke \
  --id <CONTRACT_ID> \
  --source anyone \
  --network testnet \
  -- distribute_secondary_royalties \
  --token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC

Rounding

Payouts use integer division. Any rounding dust (typically 1–2 stroops) is assigned to the last collaborator in the list, ensuring the full distributed amount always equals the input — no funds are ever left in the contract.


Frontend & Backend

A React frontend and Express backend are included for interacting with the contract via a UI.

# Backend
cd backend
cp .env.example .env   # fill in your keys
npm install
npm run dev            # → http://localhost:3001

# Frontend (separate terminal)
cd frontend
npm install
npm run dev            # → http://localhost:5173

The frontend proxies /api/* to the backend automatically via the Vite config.

The backend builds unsigned transaction XDR and returns it to the frontend. Freighter signs and submits client-side — your private key never leaves the browser.


Environment Variables

Copy backend/.env.example to backend/.env:

cp backend/.env.example backend/.env
Variable Description
PORT Port the backend API listens on (default: 3001)
STELLAR_NETWORK testnet or mainnet
HORIZON_URL Horizon REST endpoint for the chosen network
SOROBAN_RPC_URL Soroban RPC endpoint for simulating and preparing transactions
SERVER_SECRET_KEY Server-side keypair used for read-only simulations only — never signs user transactions
SIGNING_KEY_FILE Optional secrets-manager file path; takes precedence over SERVER_SECRET_KEY on load
ADMIN_ROTATE_TOKEN Bearer token for POST /admin/rotate-key hot-reload without redeploy (#293)

Project Structure

Stellar-Royalty-Splitter/
├── src/
│   └── lib.rs                        # Soroban smart contract (Rust)
├── tests/
│   └── integration_test.rs
├── scripts/
│   └── deploy.sh
├── Cargo.toml
├── frontend/                         # React + Vite UI
│   └── src/
│       ├── App.tsx
│       ├── api.ts                    # Backend API client
│       └── components/
│           ├── WalletConnect.tsx     # Freighter wallet connection
│           ├── InitializeForm.tsx    # Collaborator setup
│           ├── DistributeForm.tsx    # Trigger distribution
│           └── CollaboratorTable.tsx # View current splits
└── backend/                          # Express API
    └── src/
        ├── index.js
        ├── stellar.js                # Soroban RPC helpers
        └── routes/
            ├── initialize.js
            ├── distribute.js
            └── collaborators.js

Roadmap

  • Primary sale distribution
  • Secondary market resale royalty hooks
  • Dashboard UI for earnings tracking
  • Admin authorization on set_royalty_rate and distribute
  • Dynamic royalty adjustments via governance
  • Role-based contributor management

Contributing

See CONTRIBUTING.md for setup instructions, branch naming conventions, and the PR checklist.


License

MIT

About

Stellar Royalty Splitter is a smart contract built on the Stellar network that automatically divides NFT sale proceeds among multiple collaborators based on predefined percentage allocations.

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • HTML 50.2%
  • JavaScript 15.4%
  • TypeScript 14.5%
  • Rust 11.4%
  • CSS 7.9%
  • Shell 0.4%
  • Makefile 0.2%