Kiosk Module
The iota::kiosk module provides a primitive for building safe, decentralized, and trustless trading experiences. It allows storing and trading any types of assets with guarantees of “true ownership” and creator control through Transfer Policies.
Source: crates/iota-framework/packages/iota-framework/sources/kiosk/kiosk.move
Core Types
Kiosk
A container for storing and trading assets:
public struct Kiosk has key, store {
id: UID,
profits: Balance<IOTA>,
owner: address,
item_count: u32,
}
KioskOwnerCap
Capability granting rights to manage a Kiosk:
public struct KioskOwnerCap has key, store {
id: UID,
`for`: ID,
}
PurchaseCap
Capability to purchase a specific item:
public struct PurchaseCap<phantom T: key + store> has key, store {
id: UID,
kiosk_id: ID,
item_id: ID,
min_price: u64,
}
Creating a Kiosk
new
Create a new kiosk and owner capability:
public fun new(ctx: &mut TxContext): (Kiosk, KioskOwnerCap)
Example:
use iota::kiosk;
public fun create_my_kiosk(ctx: &mut TxContext) {
let (kiosk, cap) = kiosk::new(ctx);
transfer::public_share_object(kiosk);
transfer::public_transfer(cap, ctx.sender());
}
default
Convenience function to create and share a kiosk:
entry fun default(ctx: &mut TxContext)
Item States
Items in a Kiosk can be in different states:
- Placed: Freely tradeable and modifiable, can be taken out
- Locked: Cannot be taken, only traded or listed
- Listed: Available for purchase at a fixed price
- Listed Exclusively: Listed with a
PurchaseCap, cannot be delisted without the cap
Managing Items
place
Place an item into the kiosk:
Mutable reference to the kiosk
Item to place (must have key + store)
public fun place<T: key + store>(self: &mut Kiosk, cap: &KioskOwnerCap, item: T)
Example:
public struct NFT has key, store {
id: UID,
name: vector<u8>,
}
public fun place_nft(kiosk: &mut Kiosk, cap: &KioskOwnerCap, nft: NFT) {
kiosk::place(kiosk, cap, nft);
}
lock
Place and lock an item (requires TransferPolicy to exist):
Mutable reference to the kiosk
policy
&TransferPolicy<T>
required
Transfer policy (ensures item can be sold)
public fun lock<T: key + store>(
self: &mut Kiosk,
cap: &KioskOwnerCap,
_policy: &TransferPolicy<T>,
item: T,
)
Example:
public fun lock_nft(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
policy: &TransferPolicy<NFT>,
nft: NFT
) {
kiosk::lock(kiosk, cap, policy, nft);
}
take
Take an item out of the kiosk:
Mutable reference to the kiosk
public fun take<T: key + store>(self: &mut Kiosk, cap: &KioskOwnerCap, id: ID): T
Aborts if the item is locked or listed exclusively.
Example:
public fun take_nft(kiosk: &mut Kiosk, cap: &KioskOwnerCap, nft_id: ID): NFT {
kiosk::take(kiosk, cap, nft_id)
}
Listing and Purchasing
list
List an item for sale at a fixed price:
Mutable reference to the kiosk
public fun list<T: key + store>(self: &mut Kiosk, cap: &KioskOwnerCap, id: ID, price: u64)
Example:
public fun list_nft_for_sale(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
nft_id: ID,
price: u64
) {
kiosk::list<NFT>(kiosk, cap, nft_id, price);
}
place_and_list
Convenience function to place and list in one call:
Mutable reference to the kiosk
public fun place_and_list<T: key + store>(
self: &mut Kiosk,
cap: &KioskOwnerCap,
item: T,
price: u64,
)
delist
Remove a listing (item stays in kiosk):
Mutable reference to the kiosk
public fun delist<T: key + store>(self: &mut Kiosk, cap: &KioskOwnerCap, id: ID)
purchase
Purchase a listed item:
Mutable reference to the kiosk
ID of the item to purchase
Payment coin (must match exact price)
public fun purchase<T: key + store>(
self: &mut Kiosk,
id: ID,
payment: Coin<IOTA>,
): (T, TransferRequest<T>)
Returns the item and a TransferRequest that must be resolved with a TransferPolicy.
Example:
use iota::transfer_policy;
public fun buy_nft(
kiosk: &mut Kiosk,
nft_id: ID,
payment: Coin<IOTA>,
policy: &TransferPolicy<NFT>,
ctx: &mut TxContext
) {
let (nft, request) = kiosk::purchase<NFT>(kiosk, nft_id, payment);
// Confirm the transfer request with the policy
transfer_policy::confirm_request(policy, request);
// Transfer the NFT to the buyer
transfer::public_transfer(nft, ctx.sender());
}
Exclusive Listing with PurchaseCap
list_with_purchase_cap
List an item exclusively with a purchase capability:
Mutable reference to the kiosk
public fun list_with_purchase_cap<T: key + store>(
self: &mut Kiosk,
cap: &KioskOwnerCap,
id: ID,
min_price: u64,
ctx: &mut TxContext,
): PurchaseCap<T>
Example:
public fun create_exclusive_listing(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
nft_id: ID,
min_price: u64,
ctx: &mut TxContext
): PurchaseCap<NFT> {
kiosk::list_with_purchase_cap<NFT>(kiosk, cap, nft_id, min_price, ctx)
}
purchase_with_cap
Purchase using a purchase capability:
Mutable reference to the kiosk
Payment (must be >= min_price)
public fun purchase_with_cap<T: key + store>(
self: &mut Kiosk,
purchase_cap: PurchaseCap<T>,
payment: Coin<IOTA>,
): (T, TransferRequest<T>)
return_purchase_cap
Return a purchase capability and delist the item:
Mutable reference to the kiosk
Purchase capability to return
public fun return_purchase_cap<T: key + store>(self: &mut Kiosk, purchase_cap: PurchaseCap<T>)
Borrowing Items
borrow
Immutably borrow an item:
#[syntax(index)]
public fun borrow<T: key + store>(self: &Kiosk, cap: &KioskOwnerCap, id: ID): &T
borrow_mut
Mutably borrow an item (item must not be listed):
Mutable reference to the kiosk
#[syntax(index)]
public fun borrow_mut<T: key + store>(self: &mut Kiosk, cap: &KioskOwnerCap, id: ID): &mut T
borrow_val
Borrow an item by value with guarantee to return it:
Mutable reference to the kiosk
public fun borrow_val<T: key + store>(self: &mut Kiosk, cap: &KioskOwnerCap, id: ID): (T, Borrow)
Returns the item and a Borrow hot potato that enforces return.
return_val
Return a borrowed item:
Mutable reference to the kiosk
Borrow hot potato from borrow_val
public fun return_val<T: key + store>(self: &mut Kiosk, item: T, borrow: Borrow)
Profit Management
withdraw
Withdraw profits from the kiosk:
Mutable reference to the kiosk
Amount to withdraw (None = all)
public fun withdraw(
self: &mut Kiosk,
cap: &KioskOwnerCap,
amount: Option<u64>,
ctx: &mut TxContext,
): Coin<IOTA>
Example:
public fun withdraw_all_profits(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
ctx: &mut TxContext
): Coin<IOTA> {
kiosk::withdraw(kiosk, cap, option::none(), ctx)
}
public fun withdraw_partial(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
amount: u64,
ctx: &mut TxContext
): Coin<IOTA> {
kiosk::withdraw(kiosk, cap, option::some(amount), ctx)
}
Query Functions
has_item
Check if kiosk contains an item:
public fun has_item(self: &Kiosk, id: ID): bool
has_item_with_type
Check if kiosk contains an item of specific type:
public fun has_item_with_type<T: key + store>(self: &Kiosk, id: ID): bool
is_locked
Check if an item is locked:
public fun is_locked(self: &Kiosk, id: ID): bool
is_listed
Check if an item is listed:
public fun is_listed(self: &Kiosk, id: ID): bool
is_listed_exclusively
Check if an item has a PurchaseCap issued:
public fun is_listed_exclusively(self: &Kiosk, id: ID): bool
item_count
Get the number of items in the kiosk:
public fun item_count(self: &Kiosk): u32
profits_amount
Get the amount of profits:
public fun profits_amount(self: &Kiosk): u64
owner
Get the owner address:
public fun owner(self: &Kiosk): address
Complete Example: NFT Marketplace
module example::nft_marketplace {
use iota::kiosk::{Self, Kiosk, KioskOwnerCap};
use iota::transfer_policy::{Self, TransferPolicy, TransferRequest};
use iota::coin::Coin;
use iota::iota::IOTA;
use iota::object::{Self, ID, UID};
use iota::transfer;
use iota::tx_context::TxContext;
public struct ArtNFT has key, store {
id: UID,
title: vector<u8>,
artist: address,
url: vector<u8>,
}
public fun create_marketplace(ctx: &mut TxContext): KioskOwnerCap {
let (kiosk, cap) = kiosk::new(ctx);
transfer::public_share_object(kiosk);
cap
}
public fun mint_and_list(
title: vector<u8>,
url: vector<u8>,
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
price: u64,
ctx: &mut TxContext
) {
let nft = ArtNFT {
id: object::new(ctx),
title,
artist: ctx.sender(),
url,
};
kiosk::place_and_list(kiosk, cap, nft, price);
}
public fun buy_nft(
kiosk: &mut Kiosk,
nft_id: ID,
payment: Coin<IOTA>,
policy: &TransferPolicy<ArtNFT>,
ctx: &mut TxContext
) {
let (nft, request) = kiosk::purchase<ArtNFT>(kiosk, nft_id, payment);
transfer_policy::confirm_request(policy, request);
transfer::public_transfer(nft, ctx.sender());
}
public fun update_price(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
nft_id: ID,
new_price: u64
) {
kiosk::delist<ArtNFT>(kiosk, cap, nft_id);
kiosk::list<ArtNFT>(kiosk, cap, nft_id, new_price);
}
public fun collect_profits(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
ctx: &mut TxContext
): Coin<IOTA> {
kiosk::withdraw(kiosk, cap, option::none(), ctx)
}
}
Events
The module emits events for tracking:
public struct ItemListed<phantom T: key + store> has copy, drop {
kiosk: ID,
id: ID,
price: u64,
}
public struct ItemPurchased<phantom T: key + store> has copy, drop {
kiosk: ID,
id: ID,
price: u64,
}
public struct ItemDelisted<phantom T: key + store> has copy, drop {
kiosk: ID,
id: ID,
}
Best Practices
-
Always share kiosks: Kiosks should be shared objects for public access
-
Use Transfer Policies: Create Transfer Policies for your NFTs to enforce royalties
-
Lock valuable items: Use
lock for items that should never leave the kiosk
-
Be careful with PurchaseCap: Only use in trusted applications as losing it locks the item
-
Check item state: Use query functions before operations to avoid aborts
-
Handle TransferRequest: Always resolve the TransferRequest from purchases