Buys a listed (already-minted) token in a single invocation: payment
out, token in. payment_token selects which of the listing's prices
to pay — must be one the seller actually offered.
Only the buyer signs. The seller's consent was given when they created
the listing, and the token moves via [Consecutive::update] (the
low-level, no-auth path) rather than a full transfer, which would
demand the seller's signature at purchase time.
fn buy(
env: soroban_sdk::Env,
buyer: soroban_sdk::Address,
token_id: u32,
payment_token: soroban_sdk::Address,
inclusion_fee: i128,
network_fee: i128,
)
Lists the caller's token for sale in one or more currencies, same
shape as an edition's own price grid — a reseller isn't limited to
whichever currencies the creator originally offered. Listing does not
escrow the token — the owner keeps it and can still transfer it,
which is why buy re-checks ownership rather than trusting the
stored seller.
fn list(
env: soroban_sdk::Env,
seller: soroban_sdk::Address,
token_id: u32,
prices: soroban_sdk::Vec,
)
Returns the token collection name.
e - Access to the Soroban environment.fn name(env: soroban_sdk::Env) -> soroban_sdk::String
Emergency stop for buy_edition, list, buy, and
unlock_token_for. Transfers, approvals, and cancel_listing stay
open so holders can always exit a position while the platform is
halted.
fn pause(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Returns true if the contract is paused, and false otherwise.
e - Access to Soroban environment.fn paused(env: soroban_sdk::Env) -> bool
Returns the token collection symbol.
e - Access to the Soroban environment.fn symbol(env: soroban_sdk::Env) -> soroban_sdk::String
Gives permission to approved to transfer the token with token_id to
another account. The approval is cleared when the token is
transferred.
Only a single account can be approved at a time for a token_id.
To remove an approval, the approver can approve their own address,
effectively removing the previous approved address. Alternatively,
setting the live_until_ledger to 0 will also revoke the approval.
e - Access to Soroban environment.approver - The address of the approver (should be owner or
operator).approved - The address receiving the approval.token_id - Token ID as a number.live_until_ledger - The ledger number at which the allowance
expires. If live_until_ledger is 0, the approval is revoked.NonFungibleTokenError::NonExistentToken] - If the token does not
exist.NonFungibleTokenError::InvalidApprover] - If the owner address is
not the actual owner of the token.NonFungibleTokenError::InvalidLiveUntilLedger] - If the ledgefn approve(
env: soroban_sdk::Env,
approver: soroban_sdk::Address,
approved: soroban_sdk::Address,
token_id: u32,
live_until_ledger: u32,
)
Returns the number of tokens owned by account.
e - Access to the Soroban environment.account - The address for which the balance is being queried.fn balance(env: soroban_sdk::Env, account: soroban_sdk::Address) -> u32
fn listing(env: soroban_sdk::Env, token_id: u32) -> Option
fn unpause(env: soroban_sdk::Env, caller: soroban_sdk::Address)
Replaces this contract's executable code in place — same address,
same storage, so the platform can ship behavior changes (or fix a
bug) without a redeploy and without anyone needing to be pointed at a
new contract id. #[only_owner] ignores whatever address is passed
as _operator and enforces the real owner from storage instead — see
stellar-macros' docs on the macro.
fn upgrade(
env: soroban_sdk::Env,
new_wasm_hash: soroban_sdk::BytesN<32>,
operator: soroban_sdk::Address,
)
The contract build currently running on-chain — bump
CONTRACT_VERSION on every release that changes behavior so this
stays truthful after an upgrade.
fn version(env: soroban_sdk::Env) -> u32
Synthesizes a single token's metadata from the edition it was minted from — editions store their descriptive fields once, not once per copy, so this is an indirection rather than a direct read.
fn art_meta(env: soroban_sdk::Env, token_id: u32) -> Option
Returns the owner of the token with token_id.
e - Access to the Soroban environment.token_id - Token ID as a number.NonFungibleTokenError::NonExistentToken] - If the token does not
exist.fn owner_of(env: soroban_sdk::Env, token_id: u32) -> soroban_sdk::Address
Transfers the token with token_id from from to to.
WARNING: Confirmation that the recipient is capable of receiving the
Non-Fungible is the caller's responsibility; otherwise the NFT may be
permanently lost.
e - Access to the Soroban environment.from - Account of the sender.to - Account of the recipient.token_id - Token ID as a number.NonFungibleTokenError::IncorrectOwner] - If the current owner
(before calling this function) is not from.NonFungibleTokenError::NonExistentToken] - If the token does not
exist.["transfer", from: Address, to: Address][token_id: u32]fn transfer(
env: soroban_sdk::Env,
from: soroban_sdk::Address,
to: soroban_sdk::Address,
token_id: u32,
)
fn treasury(env: soroban_sdk::Env) -> Option
Buys several listed tokens at once, all paid in the same currency —
one signature instead of one buy call per token. The common case:
a buyer picking N copies pooled across one or more resale listings
for the same edition. Listings can belong to different sellers; each
token still settles (payment split, ownership transfer, Purchased
event) exactly as an individual buy would, just in one invocation.
inclusion_fee/network_fee are charged once for the whole batch
(there's only one real Soroban transaction underneath, regardless of
how many tokens it settles), not once per token — capped against the
sum of every token's own price, computed up front in a read-only
pass before any listing is touched.
fn buy_batch(
env: soroban_sdk::Env,
buyer: soroban_sdk::Address,
token_ids: soroban_sdk::Vec,
payment_token: soroban_sdk::Address,
inclusion_fee: i128,
network_fee: i128,
)
Returns Some(Address) if ownership is set, or None if ownership has
been renounced.
e - Access to the Soroban environment.fn get_owner(env: soroban_sdk::Env) -> Option
Returns the Uniform Resource Identifier (URI) for the token with
token_id.
e - Access to the Soroban environment.token_id - Token ID as a number.If the token does not exist, this function is expected to panic.
fn token_uri(env: soroban_sdk::Env, token_id: u32) -> soroban_sdk::String
fn keep_alive(
env: soroban_sdk::Env,
edition_ids: soroban_sdk::Vec,
edition_refs: soroban_sdk::Vec,
token_ids: soroban_sdk::Vec,
unlocked: soroban_sdk::Vec<(u32, u32)>,
)
Lists several of the caller's tokens at once, all at the same price
grid — one signature instead of one list call per token. The common
case: a seller holding a consecutive run from one buy_edition
purchase relists several of them together. Each token still gets its
own independent Listing entry (and its own Listed event, via
do_list) — this is purely a batching of the same per-token effect
list has, not a new pooled-listing concept.
fn list_batch(
env: soroban_sdk::Env,
seller: soroban_sdk::Address,
token_ids: soroban_sdk::Vec,
prices: soroban_sdk::Vec,
)
Buys quantity copies of an edition, minting them straight to
buyer in the same call that takes payment.
Only resolves edition_ref — never creates. Creation used to happen
here from a caller-supplied EditionInput, which let anyone front-run
an unsold item and define its terms; it now lives in
[Self::register_edition], behind the price authority.
purchase_ref is a fresh caller id per attempt, recorded so the minted
range can be read back with [Self::purchase_by_ref]: the pinned
stellar-sdk can't decode protocol-27 meta, so neither the return
value nor events survive a confirmed transaction, and re-deriving
"the last N minted" would race concurrent buyers. Reuse is rejected,
which also makes a retried submission safe.
fn buy_edition(
env: soroban_sdk::Env,
buyer: soroban_sdk::Address,
edition_ref: soroban_sdk::String,
purchase_ref: soroban_sdk::String,
payment_token: soroban_sdk::Address,
quantity: u32,
inclusion_fee: i128,
network_fee: i128,
) -> (u32, u32)
fn edition_meta(env: soroban_sdk::Env, edition_id: u32) -> Option
Returns the account approved for the token with token_id.
e - Access to the Soroban environment.token_id - Token ID as a number.NonFungibleTokenError::NonExistentToken] - If the token does not
exist.fn get_approved(env: soroban_sdk::Env, token_id: u32) -> Option
ERC2981-shaped royalty lookup, resolved from the token's edition
rather than the OZ royalties extension's own storage — see the doc
comment in buy_edition for why. A token with no edition (shouldn't
happen for anything this contract minted) reports no royalty rather
than panicking, matching the OZ default's own "nothing set" behavior.
fn royalty_info(
env: soroban_sdk::Env,
token_id: u32,
sale_price: i128,
) -> (soroban_sdk::Address, i128)
Runs exactly once, at deploy. Using a constructor rather than an
initialize entry point means there is no window in which an
uninitialized contract can be claimed by whoever calls first.
fn __constructor(
env: soroban_sdk::Env,
owner: soroban_sdk::Address,
treasury: soroban_sdk::Address,
platform_fee_bps: u32,
name: soroban_sdk::String,
symbol: soroban_sdk::String,
base_uri: soroban_sdk::String,
unlock_authority: soroban_sdk::Address,
price_authority: soroban_sdk::Address,
)
Transfers the token with token_id from from to to by using
spenders approval.
Unlike transfer(), which is used when the token owner initiates the
transfer, transfer_from() allows an approved third party
(spender) to transfer the token on behalf of the owner. This
function verifies that spender has the necessary approval.
WARNING: Confirmation that the recipient is capable of receiving the
Non-Fungible is the caller's responsibility; otherwise the NFT may be
permanently lost.
e - Access to the Soroban environment.spender - The address authorizing the transfer.from - Account of the sender.to - Account of the recipient.token_id - Token ID as a number.NonFungibleTokenError::IncorrectOwner] - If the current owner
(before calling this function) is not from.NonFungibleTokenError::InsufficientApproval] - If the spender does
not have a valid approval.NonFungibleTokenError::NonExistentToken] - If the token does not
exist.fn transfer_from(
env: soroban_sdk::Env,
spender: soroban_sdk::Address,
from: soroban_sdk::Address,
to: soroban_sdk::Address,
token_id: u32,
)
fn cancel_listing(env: soroban_sdk::Env, seller: soroban_sdk::Address, token_id: u32)
Resolves the caller's off-chain reference back to the registered edition id. This is how the backend confirms an edition exists on-chain and learns its id after the first purchase.
fn edition_by_ref(
env: soroban_sdk::Env,
edition_ref: soroban_sdk::String,
) -> Option
fn edition_prices(
env: soroban_sdk::Env,
edition_id: u32,
) -> soroban_sdk::Vec
Read-only preview of buy's payment split for one of the listing's
currencies, so the UI can show the buyer exactly where their money
goes before they sign.
fn sale_breakdown(
env: soroban_sdk::Env,
token_id: u32,
payment_token: soroban_sdk::Address,
) -> Option
Rewrites title/description/thumbnail/supply/prices.
media_url, media_type, creator and royalty_bps are not
parameters — carried over from the existing meta, so nothing can
alter them. supply may only move down to meta.minted: never
diluting existing holders, never letting minted copies exceed the cap.
fn update_edition(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
edition_id: u32,
title: soroban_sdk::String,
description: soroban_sdk::String,
thumbnail_url: soroban_sdk::String,
supply: u32,
prices: soroban_sdk::Vec,
)
Approve or remove operator as an operator for the owner.
Operators can call transfer_from() for any token held by owner,
and call approve() on behalf of owner.
e - Access to Soroban environment.owner - The address holding the tokens.operator - Account to add to the set of authorized operators.live_until_ledger - The ledger number at which the allowance
expires. If live_until_ledger is 0, the approval is revoked.NonFungibleTokenError::InvalidLiveUntilLedger] - If the ledger
number is less than the current ledger number.["approve_for_all", from: Address][operator: Address, live_until_ledger: u32]fn approve_for_all(
env: soroban_sdk::Env,
owner: soroban_sdk::Address,
operator: soroban_sdk::Address,
live_until_ledger: u32,
)
fn price_authority(env: soroban_sdk::Env) -> Option
Resolves what a specific purchase attempt actually minted — see
[Self::buy_edition]'s doc comment for why this, and not the
transaction's return value, is how a confirmation step learns the
assigned token range.
fn purchase_by_ref(
env: soroban_sdk::Env,
purchase_ref: soroban_sdk::String,
) -> Option
Called by the backend once it has independently verified (off-chain)
that this specific token's specific locked-content item had its
unlock rule completed. Idempotent — calling it again for an
already-unlocked (token, item) pair is a no-op, not an error, so a
retried backend call after a dropped response is safe. Keyed by
(token_id, media_index) alone, not edition or owner: each item's
rule applies to one specific minted copy, decided once, regardless
of who holds it later.
fn unlock_item_for(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
token_id: u32,
media_index: u32,
)
Accepts a pending ownership transfer.
e - Access to the Soroban environment.crate::role_transfer::RoleTransferError::NoPendingTransfer] - If
there is no pending transfer to accept.["ownership_transfer_completed"][new_owner: Address]fn accept_ownership(env: soroban_sdk::Env)
Public, permissionless read — anyone (the buyer, a marketplace UI, an auditor) can verify on-chain whether a given token's given locked-content item was unlocked, without trusting the backend's word for it.
fn is_item_unlocked(env: soroban_sdk::Env, token_id: u32, media_index: u32) -> bool
fn platform_fee_bps(env: soroban_sdk::Env) -> u32
Registers an edition ahead of its first sale, returning its id.
Gated to the price authority. This used to happen lazily inside
buy_edition, so the first caller for an edition_ref — the app's own
row id, visible in every listing URL — permanently set that edition's
creator, prices, royalty and supply. Anyone could front-run an unsold
item: name themselves creator to redirect payments, or set
supply: 1 and buy it to make the item unsellable.
Idempotent — re-registering an existing ref returns its id, so a retried call is safe.
fn register_edition(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
edition_ref: soroban_sdk::String,
edition: EditionInput,
) -> u32
fn remaining_supply(env: soroban_sdk::Env, edition_id: u32) -> u32
fn set_platform_fee(
env: soroban_sdk::Env,
fee_bps: u32,
treasury: soroban_sdk::Address,
)
Renews ownership, TokenEdition and any Listing for each token.
The lowest cap of the four: a token can touch four ledger entries where the other kinds touch one or two.
fn keep_tokens_alive(env: soroban_sdk::Env, token_ids: soroban_sdk::Vec)
Renounces ownership of the contract.
Permanently removes the owner, disabling all functions gated by
#[only_owner].
e - Access to the Soroban environment.OwnableError::TransferInProgress] - If there is a pending ownership
transfer.OwnableError::OwnerNotSet] - If the owner is not set.fn renounce_ownership(env: soroban_sdk::Env)
Initiates a 2-step ownership transfer to a new address.
Requires authorization from the current owner. The new owner must later
call accept_ownership() to complete the transfer.
e - Access to the Soroban environment.new_owner - The proposed new owner.live_until_ledger - Ledger number until which the new owner can
accept. A value of 0 cancels any pending transfer.OwnableError::OwnerNotSet] - If the owner is not set.crate::role_transfer::RoleTransferError::NoPendingTransfer] - If
trying to cancel a transfer that doesn't exist.crate::role_transfer::RoleTransferError::InvalidLiveUntilLedger] -
If the specified ledger is in the past.crate::role_transfer::RoleTransferError::InvalidPendingAccount] -
If the specified pending account is not the same as the provided new
address.fn transfer_ownership(
env: soroban_sdk::Env,
new_owner: soroban_sdk::Address,
live_until_ledger: u32,
)
Returns whether the operator is allowed to manage all the assets of
owner.
e - Access to the Soroban environment.owner - Account of the token's owner.operator - Account to be checked.fn is_approved_for_all(
env: soroban_sdk::Env,
owner: soroban_sdk::Address,
operator: soroban_sdk::Address,
) -> bool
Renews only this contract's own instance entry.
The instance holds Treasury, PriceAuthority, PlatformFeeBps,
NextEditionId and the wasm reference — lose it and nothing works, so
this is the one worth being able to run on its own. Every other
keep_*_alive renews it too; this is the no-argument case.
fn keep_contract_alive(env: soroban_sdk::Env)
Renews Edition and EditionPrices for each id.
One of the single-kind entry points, for an operator running a sweep by
hand. They exist alongside [Self::keep_alive] because mixing kinds in
one call is what makes a batch overflow the transaction footprint —
here that is not expressible, and each cap is sized for its own kind.
fn keep_editions_alive(env: soroban_sdk::Env, edition_ids: soroban_sdk::Vec)
Renews Unlocked(token_id, media_index) for each pair. Losing one
silently re-locks reward content a holder already earned.
fn keep_unlocked_alive(env: soroban_sdk::Env, unlocked: soroban_sdk::Vec<(u32, u32)>)
Rotates the price authority's hot key — same rationale as
set_unlock_authority: this key gets called by the backend on
every creator edit, so being able to swap it without touching the
owner's cold key matters.
fn set_price_authority(env: soroban_sdk::Env, new_authority: soroban_sdk::Address)
Rotates the unlock authority's hot key without a full upgrade — the backend process holding this key gets called automatically and often, so being able to swap it (e.g. after a suspected leak) without touching the owner's cold key matters more here than for most admin settings.
fn set_unlock_authority(env: soroban_sdk::Env, new_authority: soroban_sdk::Address)
Renews EditionByRef for each ref — the entry whose loss is worst,
since without it buy_edition cannot resolve a ref and
register_edition would create a duplicate edition instead of finding
the original.
fn keep_edition_refs_alive(
env: soroban_sdk::Env,
edition_refs: soroban_sdk::Vec,
)