From 27add6899f8c04b5e4f51b37979aaf04977ba484 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Thu, 11 Sep 2025 22:57:50 +0700 Subject: [PATCH 1/3] format Signed-off-by: Vu Vo --- kiosk/sources/extensions/personal_kiosk.move | 261 ++++++++++--------- kiosk/sources/rules/floor_price_rule.move | 72 +++-- kiosk/sources/rules/kiosk_lock_rule.move | 47 ++-- kiosk/sources/rules/personal_kiosk_rule.move | 46 ++-- kiosk/sources/rules/royalty_rule.move | 121 +++++---- kiosk/sources/rules/witness_rule.move | 53 ++-- kiosk/tests/rules/kiosk_lock_rule_tests.move | 89 ++++--- kiosk/tests/rules/royalty_rule_tests.move | 235 +++++++++-------- 8 files changed, 446 insertions(+), 478 deletions(-) diff --git a/kiosk/sources/extensions/personal_kiosk.move b/kiosk/sources/extensions/personal_kiosk.move index 4a7135e..56e6ed1 100644 --- a/kiosk/sources/extensions/personal_kiosk.move +++ b/kiosk/sources/extensions/personal_kiosk.move @@ -5,153 +5,156 @@ /// This module provides a wrapper for the KioskOwnerCap that makes the Kiosk /// non-transferable and "owned". /// -module kiosk::personal_kiosk { - use std::option::{Self, Option}; - use sui::transfer; - use sui::kiosk::{Self, Kiosk, KioskOwnerCap}; - use sui::object::{Self, ID, UID}; - use sui::tx_context::{sender, TxContext}; - use sui::dynamic_field as df; - - /// Trying to return the Cap / Borrow to a wrong PersonalKioskCap object. - const EIncorrectCapObject: u64 = 0; - /// Trying to return the Cap / Borrow to a wrong PersonalKioskCap object. - const EIncorrectOwnedObject: u64 = 1; - /// Trying to get the owner of a non-personal Kiosk. - const EKioskNotOwned: u64 = 2; - /// Trying to make a someone else's Kiosk "personal". - const EWrongKiosk: u64 = 3; - - /// A key-only wrapper for the KioskOwnerCap. Makes sure that the Kiosk can - /// not be traded altogether with its contents. - struct PersonalKioskCap has key { - id: UID, - cap: Option - } +module kiosk::personal_kiosk; + +use std::option::{Self, Option}; +use sui::dynamic_field as df; +use sui::kiosk::{Self, Kiosk, KioskOwnerCap}; +use sui::object::{Self, ID, UID}; +use sui::transfer; +use sui::tx_context::{sender, TxContext}; + +/// Trying to return the Cap / Borrow to a wrong PersonalKioskCap object. +const EIncorrectCapObject: u64 = 0; +/// Trying to return the Cap / Borrow to a wrong PersonalKioskCap object. +const EIncorrectOwnedObject: u64 = 1; +/// Trying to get the owner of a non-personal Kiosk. +const EKioskNotOwned: u64 = 2; +/// Trying to make a someone else's Kiosk "personal". +const EWrongKiosk: u64 = 3; + +/// A key-only wrapper for the KioskOwnerCap. Makes sure that the Kiosk can +/// not be traded altogether with its contents. +struct PersonalKioskCap has key { + id: UID, + cap: Option, +} - /// The hot potato making sure the KioskOwnerCap is returned after borrowing. - struct Borrow { cap_id: ID, owned_id: ID } +/// The hot potato making sure the KioskOwnerCap is returned after borrowing. +struct Borrow { cap_id: ID, owned_id: ID } - /// The dynamic field to mark the Kiosk as owned (to allow guaranteed owner - /// checks through the Kiosk). - struct OwnerMarker has copy, store, drop {} +/// The dynamic field to mark the Kiosk as owned (to allow guaranteed owner +/// checks through the Kiosk). +struct OwnerMarker has copy, drop, store {} - /// Event that is emitted when a new PersonalKioskCap is created. The sender - /// of the transaction is always the new Kiosk Owner. - struct NewPersonalKiosk has copy, drop { kiosk_id: ID } +/// Event that is emitted when a new PersonalKioskCap is created. The sender +/// of the transaction is always the new Kiosk Owner. +struct NewPersonalKiosk has copy, drop { kiosk_id: ID } - /// The default setup for the PersonalKioskCap. - entry fun default(kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext) { - transfer_to_sender(new(kiosk, cap, ctx), ctx); - } +/// The default setup for the PersonalKioskCap. +entry fun default(kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext) { + transfer_to_sender(new(kiosk, cap, ctx), ctx); +} - /// Wrap the KioskOwnerCap making the Kiosk "owned" and non-transferable. - /// The `PersonalKioskCap` is returned to allow chaining within a PTB, but - /// the value must be consumed by the `transfer_to_sender` call in any case. - public fun new( - kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext - ): PersonalKioskCap { - create(kiosk, cap, sender(ctx), ctx) - } +/// Wrap the KioskOwnerCap making the Kiosk "owned" and non-transferable. +/// The `PersonalKioskCap` is returned to allow chaining within a PTB, but +/// the value must be consumed by the `transfer_to_sender` call in any case. +public fun new(kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext): PersonalKioskCap { + create(kiosk, cap, sender(ctx), ctx) +} - /// Create a `PersonalKiosk` for `recipient`. - /// This is useful when (e.g.) an admin account wants to mint an asset with - /// royalty enforcement on behalf of a user. - public fun create_for( - kiosk: &mut Kiosk, cap: KioskOwnerCap, recipient: address, ctx: &mut TxContext - ) { - let personal_owner_cap = create(kiosk, cap, recipient, ctx); - transfer::transfer(personal_owner_cap, recipient) - } +/// Create a `PersonalKiosk` for `recipient`. +/// This is useful when (e.g.) an admin account wants to mint an asset with +/// royalty enforcement on behalf of a user. +public fun create_for( + kiosk: &mut Kiosk, + cap: KioskOwnerCap, + recipient: address, + ctx: &mut TxContext, +) { + let personal_owner_cap = create(kiosk, cap, recipient, ctx); + transfer::transfer(personal_owner_cap, recipient) +} - /// Wrap the KioskOwnerCap making the Kiosk "owned" and non-transferable. - /// The `PersonalKioskCap` is returned to allow chaining within a PTB, but - /// the value must be consumed by the `transfer_to_sender` call in any case. - fun create( - kiosk: &mut Kiosk, cap: KioskOwnerCap, owner: address, ctx: &mut TxContext - ): PersonalKioskCap { - assert!(kiosk::has_access(kiosk, &cap), EWrongKiosk); - - // set the owner property of the Kiosk - kiosk::set_owner_custom(kiosk, &cap, owner); - - // add the owner marker to the Kiosk; uses `_as_owner` to always pass, - // even if Kiosk "allow_extensions" is set to false - df::add( - kiosk::uid_mut_as_owner(kiosk, &cap), - OwnerMarker {}, - owner - ); - - sui::event::emit(NewPersonalKiosk { - kiosk_id: object::id(kiosk) - }); - - // wrap the Cap in the `PersonalKioskCap` - PersonalKioskCap { - id: object::new(ctx), - cap: option::some(cap) - } +/// Wrap the KioskOwnerCap making the Kiosk "owned" and non-transferable. +/// The `PersonalKioskCap` is returned to allow chaining within a PTB, but +/// the value must be consumed by the `transfer_to_sender` call in any case. +fun create( + kiosk: &mut Kiosk, + cap: KioskOwnerCap, + owner: address, + ctx: &mut TxContext, +): PersonalKioskCap { + assert!(kiosk::has_access(kiosk, &cap), EWrongKiosk); + + // set the owner property of the Kiosk + kiosk::set_owner_custom(kiosk, &cap, owner); + + // add the owner marker to the Kiosk; uses `_as_owner` to always pass, + // even if Kiosk "allow_extensions" is set to false + df::add( + kiosk::uid_mut_as_owner(kiosk, &cap), + OwnerMarker {}, + owner, + ); + + sui::event::emit(NewPersonalKiosk { + kiosk_id: object::id(kiosk), + }); + + // wrap the Cap in the `PersonalKioskCap` + PersonalKioskCap { + id: object::new(ctx), + cap: option::some(cap), } +} - /// Borrow the `KioskOwnerCap` from the `PersonalKioskCap` object. - public fun borrow(self: &PersonalKioskCap): &KioskOwnerCap { - option::borrow(&self.cap) - } +/// Borrow the `KioskOwnerCap` from the `PersonalKioskCap` object. +public fun borrow(self: &PersonalKioskCap): &KioskOwnerCap { + option::borrow(&self.cap) +} - /// Mutably borrow the `KioskOwnerCap` from the `PersonalKioskCap` object. - public fun borrow_mut(self: &mut PersonalKioskCap): &mut KioskOwnerCap { - option::borrow_mut(&mut self.cap) - } +/// Mutably borrow the `KioskOwnerCap` from the `PersonalKioskCap` object. +public fun borrow_mut(self: &mut PersonalKioskCap): &mut KioskOwnerCap { + option::borrow_mut(&mut self.cap) +} - /// Borrow the `KioskOwnerCap` from the `PersonalKioskCap` object; `Borrow` - /// hot-potato makes sure that the Cap is returned via `return_val` call. - public fun borrow_val( - self: &mut PersonalKioskCap - ): (KioskOwnerCap, Borrow) { - let cap = option::extract(&mut self.cap); - let id = object::id(&cap); +/// Borrow the `KioskOwnerCap` from the `PersonalKioskCap` object; `Borrow` +/// hot-potato makes sure that the Cap is returned via `return_val` call. +public fun borrow_val(self: &mut PersonalKioskCap): (KioskOwnerCap, Borrow) { + let cap = option::extract(&mut self.cap); + let id = object::id(&cap); - (cap, Borrow { + ( + cap, + Borrow { owned_id: object::id(self), - cap_id: id - }) - } + cap_id: id, + }, + ) +} - /// Return the Cap to the PersonalKioskCap object. - public fun return_val( - self: &mut PersonalKioskCap, cap: KioskOwnerCap, borrow: Borrow - ) { - let Borrow { owned_id, cap_id } = borrow; - assert!(object::id(self) == owned_id, EIncorrectOwnedObject); - assert!(object::id(&cap) == cap_id, EIncorrectCapObject); +/// Return the Cap to the PersonalKioskCap object. +public fun return_val(self: &mut PersonalKioskCap, cap: KioskOwnerCap, borrow: Borrow) { + let Borrow { owned_id, cap_id } = borrow; + assert!(object::id(self) == owned_id, EIncorrectOwnedObject); + assert!(object::id(&cap) == cap_id, EIncorrectCapObject); - option::fill(&mut self.cap, cap) - } + option::fill(&mut self.cap, cap) +} - /// Check if the Kiosk is "personal". - public fun is_personal(kiosk: &Kiosk): bool { - df::exists_(kiosk::uid(kiosk), OwnerMarker {}) - } +/// Check if the Kiosk is "personal". +public fun is_personal(kiosk: &Kiosk): bool { + df::exists_(kiosk::uid(kiosk), OwnerMarker {}) +} - /// Get the owner of the Kiosk if the Kiosk is "personal". Aborts otherwise. - public fun owner(kiosk: &Kiosk): address { - assert!(is_personal(kiosk), EKioskNotOwned); - *df::borrow(kiosk::uid(kiosk), OwnerMarker {}) - } +/// Get the owner of the Kiosk if the Kiosk is "personal". Aborts otherwise. +public fun owner(kiosk: &Kiosk): address { + assert!(is_personal(kiosk), EKioskNotOwned); + *df::borrow(kiosk::uid(kiosk), OwnerMarker {}) +} - /// Try to get the owner of the Kiosk if the Kiosk is "personal". Returns - /// None otherwise. - public fun try_owner(kiosk: &Kiosk): Option
{ - if (is_personal(kiosk)) { - option::some(owner(kiosk)) - } else { - option::none() - } +/// Try to get the owner of the Kiosk if the Kiosk is "personal". Returns +/// None otherwise. +public fun try_owner(kiosk: &Kiosk): Option
{ + if (is_personal(kiosk)) { + option::some(owner(kiosk)) + } else { + option::none() } +} - /// Transfer the `PersonalKioskCap` to the transaction sender. - public fun transfer_to_sender(self: PersonalKioskCap, ctx: &mut TxContext) { - transfer::transfer(self, sender(ctx)); - } +/// Transfer the `PersonalKioskCap` to the transaction sender. +public fun transfer_to_sender(self: PersonalKioskCap, ctx: &mut TxContext) { + transfer::transfer(self, sender(ctx)); } diff --git a/kiosk/sources/rules/floor_price_rule.move b/kiosk/sources/rules/floor_price_rule.move index 7ce376a..4cd1d42 100644 --- a/kiosk/sources/rules/floor_price_rule.move +++ b/kiosk/sources/rules/floor_price_rule.move @@ -11,46 +11,34 @@ /// - Defining a floor price for all trades of type T. /// - Prevent trading of locked items with low amounts (e.g. by using purchase_cap). /// -module kiosk::floor_price_rule { - use sui::transfer_policy::{ - Self as policy, - TransferPolicy, - TransferPolicyCap, - TransferRequest - }; - - /// The price was lower than the floor price. - const EPriceTooSmall: u64 = 0; - - /// The "Rule" witness to authorize the policy. - struct Rule has drop {} - - /// Configuration for the `Floor Price Rule`. - /// It holds the minimum price that an item can be sold at. - /// There can't be any sales with a price < than the floor_price. - struct Config has store, drop { - floor_price: u64 - } - - /// Creator action: Add the Floor Price Rule for the `T`. - /// Pass in the `TransferPolicy`, `TransferPolicyCap` and `floor_price`. - public fun add( - policy: &mut TransferPolicy, - cap: &TransferPolicyCap, - floor_price: u64 - ) { - policy::add_rule(Rule {}, policy, cap, Config { floor_price }) - } - - /// Buyer action: Prove that the amount is higher or equal to the floor_price. - public fun prove( - policy: &mut TransferPolicy, - request: &mut TransferRequest - ) { - let config: &Config = policy::get_rule(Rule {}, policy); - - assert!(policy::paid(request) >= config.floor_price, EPriceTooSmall); - - policy::add_receipt(Rule {}, request) - } +module kiosk::floor_price_rule; + +use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; + +/// The price was lower than the floor price. +const EPriceTooSmall: u64 = 0; + +/// The "Rule" witness to authorize the policy. +struct Rule has drop {} + +/// Configuration for the `Floor Price Rule`. +/// It holds the minimum price that an item can be sold at. +/// There can't be any sales with a price < than the floor_price. +struct Config has drop, store { + floor_price: u64, +} + +/// Creator action: Add the Floor Price Rule for the `T`. +/// Pass in the `TransferPolicy`, `TransferPolicyCap` and `floor_price`. +public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap, floor_price: u64) { + policy::add_rule(Rule {}, policy, cap, Config { floor_price }) +} + +/// Buyer action: Prove that the amount is higher or equal to the floor_price. +public fun prove(policy: &mut TransferPolicy, request: &mut TransferRequest) { + let config: &Config = policy::get_rule(Rule {}, policy); + + assert!(policy::paid(request) >= config.floor_price, EPriceTooSmall); + + policy::add_receipt(Rule {}, request) } diff --git a/kiosk/sources/rules/kiosk_lock_rule.move b/kiosk/sources/rules/kiosk_lock_rule.move index 623bdf4..1897c3e 100644 --- a/kiosk/sources/rules/kiosk_lock_rule.move +++ b/kiosk/sources/rules/kiosk_lock_rule.move @@ -19,35 +19,30 @@ /// the owner to use `list` or `list_with_purchase_cap` methods if they /// wish to move the item somewhere else. /// -module kiosk::kiosk_lock_rule { - use sui::kiosk::{Self, Kiosk}; - use sui::transfer_policy::{ - Self as policy, - TransferPolicy, - TransferPolicyCap, - TransferRequest - }; +module kiosk::kiosk_lock_rule; - /// Item is not in the `Kiosk`. - const ENotInKiosk: u64 = 0; +use sui::kiosk::{Self, Kiosk}; +use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; - /// The type identifier for the Rule. - struct Rule has drop {} +/// Item is not in the `Kiosk`. +const ENotInKiosk: u64 = 0; - /// An empty configuration for the Rule. - struct Config has store, drop {} +/// The type identifier for the Rule. +struct Rule has drop {} - /// Creator: Adds a `kiosk_lock_rule` Rule to the `TransferPolicy` forcing - /// buyers to lock the item in a Kiosk on purchase. - public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { - policy::add_rule(Rule {}, policy, cap, Config {}) - } +/// An empty configuration for the Rule. +struct Config has drop, store {} - /// Buyer: Prove the item was locked in the Kiosk to get the receipt and - /// unblock the transfer request confirmation. - public fun prove(request: &mut TransferRequest, kiosk: &Kiosk) { - let item = policy::item(request); - assert!(kiosk::has_item(kiosk, item) && kiosk::is_locked(kiosk, item), ENotInKiosk); - policy::add_receipt(Rule {}, request) - } +/// Creator: Adds a `kiosk_lock_rule` Rule to the `TransferPolicy` forcing +/// buyers to lock the item in a Kiosk on purchase. +public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { + policy::add_rule(Rule {}, policy, cap, Config {}) +} + +/// Buyer: Prove the item was locked in the Kiosk to get the receipt and +/// unblock the transfer request confirmation. +public fun prove(request: &mut TransferRequest, kiosk: &Kiosk) { + let item = policy::item(request); + assert!(kiosk::has_item(kiosk, item) && kiosk::is_locked(kiosk, item), ENotInKiosk); + policy::add_receipt(Rule {}, request) } diff --git a/kiosk/sources/rules/personal_kiosk_rule.move b/kiosk/sources/rules/personal_kiosk_rule.move index 94285d9..f941cc9 100644 --- a/kiosk/sources/rules/personal_kiosk_rule.move +++ b/kiosk/sources/rules/personal_kiosk_rule.move @@ -18,36 +18,30 @@ /// enforce policies on every trade (item can be transferred only through a /// trade + Kiosk is fixed to the owner). /// -module kiosk::personal_kiosk_rule { - use sui::kiosk::{Self, Kiosk}; - use sui::transfer_policy::{ - Self as policy, - TransferPolicy, - TransferPolicyCap, - TransferRequest - }; +module kiosk::personal_kiosk_rule; - use kiosk::personal_kiosk; +use kiosk::personal_kiosk; +use sui::kiosk::{Self, Kiosk}; +use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; - /// An item hasn't been placed into the Kiosk before the call. - const EItemNotInKiosk: u64 = 0; - /// The Kiosk is not owned; the OwnerMarker is not present. - const EKioskNotOwned: u64 = 1; +/// An item hasn't been placed into the Kiosk before the call. +const EItemNotInKiosk: u64 = 0; +/// The Kiosk is not owned; the OwnerMarker is not present. +const EKioskNotOwned: u64 = 1; - /// The Rule checking that the Kiosk is an owned one. - struct Rule has drop {} +/// The Rule checking that the Kiosk is an owned one. +struct Rule has drop {} - /// Add the "owned" rule to the KioskOwnerCap. - public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { - policy::add_rule(Rule {}, policy, cap, true) - } +/// Add the "owned" rule to the KioskOwnerCap. +public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { + policy::add_rule(Rule {}, policy, cap, true) +} - /// Make sure that the destination Kiosk has the Owner key. Item is already - /// placed by the time this check is performed - otherwise fails. - public fun prove(kiosk: &Kiosk, request: &mut TransferRequest) { - assert!(kiosk::has_item(kiosk, policy::item(request)), EItemNotInKiosk); - assert!(personal_kiosk::is_personal(kiosk), EKioskNotOwned); +/// Make sure that the destination Kiosk has the Owner key. Item is already +/// placed by the time this check is performed - otherwise fails. +public fun prove(kiosk: &Kiosk, request: &mut TransferRequest) { + assert!(kiosk::has_item(kiosk, policy::item(request)), EItemNotInKiosk); + assert!(personal_kiosk::is_personal(kiosk), EKioskNotOwned); - policy::add_receipt(Rule {}, request) - } + policy::add_receipt(Rule {}, request) } diff --git a/kiosk/sources/rules/royalty_rule.move b/kiosk/sources/rules/royalty_rule.move index 39c54cd..533f421 100644 --- a/kiosk/sources/rules/royalty_rule.move +++ b/kiosk/sources/rules/royalty_rule.move @@ -27,78 +27,73 @@ /// acceptable and the `amount_bp` to the percentage of the purchase price. /// The higher of the two will be used. /// -module kiosk::royalty_rule { - use sui::sui::SUI; - use sui::coin::{Self, Coin}; - use sui::transfer_policy::{ - Self as policy, - TransferPolicy, - TransferPolicyCap, - TransferRequest - }; +module kiosk::royalty_rule; - /// The `amount_bp` passed is more than 100%. - const EIncorrectArgument: u64 = 0; - /// The `Coin` used for payment is not enough to cover the fee. - const EInsufficientAmount: u64 = 1; +use sui::coin::{Self, Coin}; +use sui::sui::SUI; +use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; - /// Max value for the `amount_bp`. - const MAX_BPS: u16 = 10_000; +/// The `amount_bp` passed is more than 100%. +const EIncorrectArgument: u64 = 0; +/// The `Coin` used for payment is not enough to cover the fee. +const EInsufficientAmount: u64 = 1; - /// The "Rule" witness to authorize the policy. - struct Rule has drop {} +/// Max value for the `amount_bp`. +const MAX_BPS: u16 = 10_000; - /// Configuration for the Rule. The `amount_bp` is the percentage - /// of the transfer amount to be paid as a royalty fee. The `min_amount` - /// is the minimum amount to be paid if the percentage based fee is - /// lower than the `min_amount` setting. - /// - /// Adding a mininum amount is useful to enforce a fixed fee even if - /// the transfer amount is very small or 0. - struct Config has store, drop { - amount_bp: u16, - min_amount: u64 - } +/// The "Rule" witness to authorize the policy. +struct Rule has drop {} - /// Creator action: Add the Royalty Rule for the `T`. - /// Pass in the `TransferPolicy`, `TransferPolicyCap` and the configuration - /// for the policy: `amount_bp` and `min_amount`. - public fun add( - policy: &mut TransferPolicy, - cap: &TransferPolicyCap, - amount_bp: u16, - min_amount: u64 - ) { - assert!(amount_bp <= MAX_BPS, EIncorrectArgument); - policy::add_rule(Rule {}, policy, cap, Config { amount_bp, min_amount }) - } +/// Configuration for the Rule. The `amount_bp` is the percentage +/// of the transfer amount to be paid as a royalty fee. The `min_amount` +/// is the minimum amount to be paid if the percentage based fee is +/// lower than the `min_amount` setting. +/// +/// Adding a mininum amount is useful to enforce a fixed fee even if +/// the transfer amount is very small or 0. +struct Config has drop, store { + amount_bp: u16, + min_amount: u64, +} - /// Buyer action: Pay the royalty fee for the transfer. - public fun pay( - policy: &mut TransferPolicy, - request: &mut TransferRequest, - payment: Coin - ) { - let paid = policy::paid(request); - let amount = fee_amount(policy, paid); +/// Creator action: Add the Royalty Rule for the `T`. +/// Pass in the `TransferPolicy`, `TransferPolicyCap` and the configuration +/// for the policy: `amount_bp` and `min_amount`. +public fun add( + policy: &mut TransferPolicy, + cap: &TransferPolicyCap, + amount_bp: u16, + min_amount: u64, +) { + assert!(amount_bp <= MAX_BPS, EIncorrectArgument); + policy::add_rule(Rule {}, policy, cap, Config { amount_bp, min_amount }) +} - assert!(coin::value(&payment) == amount, EInsufficientAmount); +/// Buyer action: Pay the royalty fee for the transfer. +public fun pay( + policy: &mut TransferPolicy, + request: &mut TransferRequest, + payment: Coin, +) { + let paid = policy::paid(request); + let amount = fee_amount(policy, paid); - policy::add_to_balance(Rule {}, policy, payment); - policy::add_receipt(Rule {}, request) - } + assert!(coin::value(&payment) == amount, EInsufficientAmount); - /// Helper function to calculate the amount to be paid for the transfer. - /// Can be used dry-runned to estimate the fee amount based on the Kiosk listing price. - public fun fee_amount(policy: &TransferPolicy, paid: u64): u64 { - let config: &Config = policy::get_rule(Rule {}, policy); - let amount = (((paid as u128) * (config.amount_bp as u128) / 10_000) as u64); + policy::add_to_balance(Rule {}, policy, payment); + policy::add_receipt(Rule {}, request) +} + +/// Helper function to calculate the amount to be paid for the transfer. +/// Can be used dry-runned to estimate the fee amount based on the Kiosk listing price. +public fun fee_amount(policy: &TransferPolicy, paid: u64): u64 { + let config: &Config = policy::get_rule(Rule {}, policy); + let amount = (((paid as u128) * (config.amount_bp as u128) / 10_000) as u64); - // If the amount is less than the minimum, use the minimum - if (amount < config.min_amount) { - amount = config.min_amount - }; + // If the amount is less than the minimum, use the minimum + if (amount < config.min_amount) { + amount = config.min_amount + }; - amount - } + amount } diff --git a/kiosk/sources/rules/witness_rule.move b/kiosk/sources/rules/witness_rule.move index e5e9168..20def28 100644 --- a/kiosk/sources/rules/witness_rule.move +++ b/kiosk/sources/rules/witness_rule.move @@ -15,37 +15,32 @@ /// - Require a confirmation in a third party module /// - Implement a custom requirement on the creator side an link the logic. /// -module kiosk::witness_rule { - use sui::transfer_policy::{ - Self as policy, - TransferPolicy, - TransferPolicyCap, - TransferRequest - }; +module kiosk::witness_rule; - /// When a Proof does not find its Rule. - const ERuleNotFound: u64 = 0; +use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; - /// Custom witness-key for the "proof policy". - struct Rule has drop {} +/// When a Proof does not find its Rule. +const ERuleNotFound: u64 = 0; - /// Creator action: adds the Rule. - /// Requires a "Proof" witness confirmation on every transfer. - public fun add( - policy: &mut TransferPolicy, - cap: &TransferPolicyCap - ) { - policy::add_rule(Rule {}, policy, cap, true); - } +/// Custom witness-key for the "proof policy". +struct Rule has drop {} - /// Buyer action: follow the policy. - /// Present the required "Proof" instance to get a receipt. - public fun prove( - _proof: Proof, - policy: &TransferPolicy, - request: &mut TransferRequest - ) { - assert!(policy::has_rule>(policy), ERuleNotFound); - policy::add_receipt(Rule {}, request) - } +/// Creator action: adds the Rule. +/// Requires a "Proof" witness confirmation on every transfer. +public fun add( + policy: &mut TransferPolicy, + cap: &TransferPolicyCap, +) { + policy::add_rule(Rule {}, policy, cap, true); +} + +/// Buyer action: follow the policy. +/// Present the required "Proof" instance to get a receipt. +public fun prove( + _proof: Proof, + policy: &TransferPolicy, + request: &mut TransferRequest, +) { + assert!(policy::has_rule>(policy), ERuleNotFound); + policy::add_receipt(Rule {}, request) } diff --git a/kiosk/tests/rules/kiosk_lock_rule_tests.move b/kiosk/tests/rules/kiosk_lock_rule_tests.move index 4bbec5e..9fd5d5b 100644 --- a/kiosk/tests/rules/kiosk_lock_rule_tests.move +++ b/kiosk/tests/rules/kiosk_lock_rule_tests.move @@ -3,49 +3,48 @@ #[test_only] /// Test illustrating how an asset can be forever locked in the Kiosk. -module kiosk::kiosk_lock_rule_tests { - use sui::kiosk; - use sui::kiosk_test_utils::{Self as test, Asset}; - use sui::transfer_policy as policy; - use sui::transfer; - - use kiosk::kiosk_lock_rule as kiosk_lock; - - #[test] - fun test_item_always_locked() { - let ctx = &mut test::ctx(); - let (_, _, carl) = test::folks(); - let (policy, policy_cap) = test::get_policy(ctx); - let (kiosk, kiosk_cap) = test::get_kiosk(ctx); - let (item, item_id) = test::get_asset(ctx); - let payment = test::get_sui(1000, ctx); - - // Alice the Creator - // - disallow taking from the Kiosk - // - require "PlacedWitness" on purchase - // - place an asset into the Kiosk so it can only be sold - kiosk_lock::add(&mut policy, &policy_cap); - kiosk::lock(&mut kiosk, &kiosk_cap, &policy, item); - kiosk::list(&mut kiosk, &kiosk_cap, item_id, 1000); - - // Bob the Buyer - // - places the item into his Kiosk and gets the proof - // - prove placing and confirm request - let (bob_kiosk, bob_kiosk_cap) = test::get_kiosk(ctx); - let (item, request) = kiosk::purchase(&mut kiosk, item_id, payment); - kiosk::lock(&mut bob_kiosk, &bob_kiosk_cap, &policy, item); - - // The difference! - kiosk_lock::prove(&mut request, &mut bob_kiosk); - policy::confirm_request(&policy, request); - - // Carl the Cleaner; - // - cleans up and transfer Kiosk to himself - // - as we can't take an item due to the policy setting (and kiosk must be empty) - test::return_policy(policy, policy_cap, ctx); - test::return_kiosk(kiosk, kiosk_cap, ctx); - - transfer::public_transfer(bob_kiosk, carl); - transfer::public_transfer(bob_kiosk_cap, carl); - } +module kiosk::kiosk_lock_rule_tests; + +use kiosk::kiosk_lock_rule as kiosk_lock; +use sui::kiosk; +use sui::kiosk_test_utils::{Self as test, Asset}; +use sui::transfer; +use sui::transfer_policy as policy; + +#[test] +fun test_item_always_locked() { + let ctx = &mut test::ctx(); + let (_, _, carl) = test::folks(); + let (policy, policy_cap) = test::get_policy(ctx); + let (kiosk, kiosk_cap) = test::get_kiosk(ctx); + let (item, item_id) = test::get_asset(ctx); + let payment = test::get_sui(1000, ctx); + + // Alice the Creator + // - disallow taking from the Kiosk + // - require "PlacedWitness" on purchase + // - place an asset into the Kiosk so it can only be sold + kiosk_lock::add(&mut policy, &policy_cap); + kiosk::lock(&mut kiosk, &kiosk_cap, &policy, item); + kiosk::list(&mut kiosk, &kiosk_cap, item_id, 1000); + + // Bob the Buyer + // - places the item into his Kiosk and gets the proof + // - prove placing and confirm request + let (bob_kiosk, bob_kiosk_cap) = test::get_kiosk(ctx); + let (item, request) = kiosk::purchase(&mut kiosk, item_id, payment); + kiosk::lock(&mut bob_kiosk, &bob_kiosk_cap, &policy, item); + + // The difference! + kiosk_lock::prove(&mut request, &mut bob_kiosk); + policy::confirm_request(&policy, request); + + // Carl the Cleaner; + // - cleans up and transfer Kiosk to himself + // - as we can't take an item due to the policy setting (and kiosk must be empty) + test::return_policy(policy, policy_cap, ctx); + test::return_kiosk(kiosk, kiosk_cap, ctx); + + transfer::public_transfer(bob_kiosk, carl); + transfer::public_transfer(bob_kiosk_cap, carl); } diff --git a/kiosk/tests/rules/royalty_rule_tests.move b/kiosk/tests/rules/royalty_rule_tests.move index fbf81bf..fd10f95 100644 --- a/kiosk/tests/rules/royalty_rule_tests.move +++ b/kiosk/tests/rules/royalty_rule_tests.move @@ -2,161 +2,160 @@ // SPDX-License-Identifier: Apache-2.0 #[test_only] -module kiosk::royalty_rule_tests { - use sui::coin; - use sui::sui::SUI; - use sui::tx_context::dummy as ctx; - use sui::transfer_policy as policy; - use sui::transfer_policy_tests as test; +module kiosk::royalty_rule_tests; - use kiosk::royalty_rule; +use kiosk::royalty_rule; +use sui::coin; +use sui::sui::SUI; +use sui::transfer_policy as policy; +use sui::transfer_policy_tests as test; +use sui::tx_context::dummy as ctx; - #[test] - fun test_default_flow_0() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +fun test_default_flow_0() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 0% royalty; min 0 MIST - royalty_rule::add(&mut policy, &cap, 0, 0); + // 0% royalty; min 0 MIST + royalty_rule::add(&mut policy, &cap, 0, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(0, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(0, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); - let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 0, 1); - } + let profits = test::wrapup(policy, cap, ctx); + assert!(profits == 0, 1); +} - #[test] - #[expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] - fun test_default_flow_0_invalid_amount_fail() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +#[expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] +fun test_default_flow_0_invalid_amount_fail() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 0% royalty; min 0 MIST - royalty_rule::add(&mut policy, &cap, 0, 0); + // 0% royalty; min 0 MIST + royalty_rule::add(&mut policy, &cap, 0, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(10, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(10, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); - let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 0, 1); - } + let profits = test::wrapup(policy, cap, ctx); + assert!(profits == 0, 1); +} - #[test] - fun test_default_flow_1() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +fun test_default_flow_1() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 1% royalty; min 0 MIST - royalty_rule::add(&mut policy, &cap, 100, 0); + // 1% royalty; min 0 MIST + royalty_rule::add(&mut policy, &cap, 100, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(1000, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(1000, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); - let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 1000, 1); - } + let profits = test::wrapup(policy, cap, ctx); + assert!(profits == 1000, 1); +} - #[test] - fun test_default_flow_100() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +fun test_default_flow_100() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 100% royalty; min 0 MIST - royalty_rule::add(&mut policy, &cap, 10_000, 0); + // 100% royalty; min 0 MIST + royalty_rule::add(&mut policy, &cap, 10_000, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(100_000, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(100_000, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); - let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 100_000, 1); - } + let profits = test::wrapup(policy, cap, ctx); + assert!(profits == 100_000, 1); +} - #[test] - fun test_default_flow_1_min_10_000() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +fun test_default_flow_1_min_10_000() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 1% royalty; min 10_000 MIST - royalty_rule::add(&mut policy, &cap, 100, 10_000); + // 1% royalty; min 10_000 MIST + royalty_rule::add(&mut policy, &cap, 100, 10_000); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(10_000, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(10_000, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - assert!(test::wrapup(policy, cap, ctx) == 10_000, 1); - } + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); + assert!(test::wrapup(policy, cap, ctx) == 10_000, 1); +} - #[test] - fun test_default_flow_10_min_10_000() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +fun test_default_flow_10_min_10_000() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 10% royalty; min 10_000 MIST - royalty_rule::add(&mut policy, &cap, 1000, 10_000); + // 10% royalty; min 10_000 MIST + royalty_rule::add(&mut policy, &cap, 1000, 10_000); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(10_000, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(10_000, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - assert!(test::wrapup(policy, cap, ctx) == 10_000, 1); - } + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); + assert!(test::wrapup(policy, cap, ctx) == 10_000, 1); +} - #[test] - fun test_default_flow_20_min_10_000() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +fun test_default_flow_20_min_10_000() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 20% royalty; min 10_000 MIST - royalty_rule::add(&mut policy, &cap, 20_00, 10_000); + // 20% royalty; min 10_000 MIST + royalty_rule::add(&mut policy, &cap, 20_00, 10_000); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(20_000, ctx); + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(20_000, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - assert!(test::wrapup(policy, cap, ctx) == 20_000, 1); - } + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); + assert!(test::wrapup(policy, cap, ctx) == 20_000, 1); +} - #[test] - #[expected_failure(abort_code = kiosk::royalty_rule::EIncorrectArgument)] - fun test_incorrect_config() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +#[expected_failure(abort_code = kiosk::royalty_rule::EIncorrectArgument)] +fun test_incorrect_config() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - royalty_rule::add(&mut policy, &cap, 11_000, 0); - test::wrapup(policy, cap, ctx); - } + royalty_rule::add(&mut policy, &cap, 11_000, 0); + test::wrapup(policy, cap, ctx); +} - #[test] - #[expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] - fun test_insufficient_amount() { - let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); +#[test] +#[expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] +fun test_insufficient_amount() { + let ctx = &mut ctx(); + let (policy, cap) = test::prepare(ctx); - // 1% royalty - royalty_rule::add(&mut policy, &cap, 100, 0); + // 1% royalty + royalty_rule::add(&mut policy, &cap, 100, 0); - // Requires 1_000 MIST, coin has only 999 - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); - let payment = coin::mint_for_testing(999, ctx); + // Requires 1_000 MIST, coin has only 999 + let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let payment = coin::mint_for_testing(999, ctx); - royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - test::wrapup(policy, cap, ctx); - } + royalty_rule::pay(&mut policy, &mut request, payment); + policy::confirm_request(&mut policy, request); + test::wrapup(policy, cap, ctx); } From b38aaf6b27c8440ce32afd41b999e6f654ad9b10 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Thu, 11 Sep 2025 23:06:52 +0700 Subject: [PATCH 2/3] update to public struct Signed-off-by: Vu Vo --- kiosk/Move.lock | 6 +++--- kiosk/Move.toml | 1 + kiosk/sources/extensions/personal_kiosk.move | 8 ++++---- kiosk/sources/rules/floor_price_rule.move | 4 ++-- kiosk/sources/rules/kiosk_lock_rule.move | 4 ++-- kiosk/sources/rules/personal_kiosk_rule.move | 2 +- kiosk/sources/rules/royalty_rule.move | 8 ++++---- kiosk/sources/rules/witness_rule.move | 2 +- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/kiosk/Move.lock b/kiosk/Move.lock index 3f93432..058ee26 100644 --- a/kiosk/Move.lock +++ b/kiosk/Move.lock @@ -2,7 +2,7 @@ [move] version = 3 -manifest_digest = "E5E2B73D4A2E45CD2F3F1BBF8D2EDEB17C41E4A6CF5685C7049EE342D5245487" +manifest_digest = "955C4B6EC9AB5C70702544008200CFFADC7FBACC70C9868A8020680FCE4959E8" deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082" dependencies = [ @@ -22,8 +22,8 @@ dependencies = [ ] [move.toolchain-version] -compiler-version = "1.45.2" -edition = "2024" +compiler-version = "1.54.2" +edition = "2024.beta" flavor = "sui" [env] diff --git a/kiosk/Move.toml b/kiosk/Move.toml index 2b502d2..246c0de 100644 --- a/kiosk/Move.toml +++ b/kiosk/Move.toml @@ -1,5 +1,6 @@ [package] name = "Kiosk" +edition = "2024.beta" # or (just) "2024" version = "0.0.2" [dependencies] diff --git a/kiosk/sources/extensions/personal_kiosk.move b/kiosk/sources/extensions/personal_kiosk.move index 56e6ed1..7fd36e9 100644 --- a/kiosk/sources/extensions/personal_kiosk.move +++ b/kiosk/sources/extensions/personal_kiosk.move @@ -25,21 +25,21 @@ const EWrongKiosk: u64 = 3; /// A key-only wrapper for the KioskOwnerCap. Makes sure that the Kiosk can /// not be traded altogether with its contents. -struct PersonalKioskCap has key { +public struct PersonalKioskCap has key { id: UID, cap: Option, } /// The hot potato making sure the KioskOwnerCap is returned after borrowing. -struct Borrow { cap_id: ID, owned_id: ID } +public struct Borrow { cap_id: ID, owned_id: ID } /// The dynamic field to mark the Kiosk as owned (to allow guaranteed owner /// checks through the Kiosk). -struct OwnerMarker has copy, drop, store {} +public struct OwnerMarker has copy, drop, store {} /// Event that is emitted when a new PersonalKioskCap is created. The sender /// of the transaction is always the new Kiosk Owner. -struct NewPersonalKiosk has copy, drop { kiosk_id: ID } +public struct NewPersonalKiosk has copy, drop { kiosk_id: ID } /// The default setup for the PersonalKioskCap. entry fun default(kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext) { diff --git a/kiosk/sources/rules/floor_price_rule.move b/kiosk/sources/rules/floor_price_rule.move index 4cd1d42..07e2d0e 100644 --- a/kiosk/sources/rules/floor_price_rule.move +++ b/kiosk/sources/rules/floor_price_rule.move @@ -19,12 +19,12 @@ use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, Tr const EPriceTooSmall: u64 = 0; /// The "Rule" witness to authorize the policy. -struct Rule has drop {} +public struct Rule has drop {} /// Configuration for the `Floor Price Rule`. /// It holds the minimum price that an item can be sold at. /// There can't be any sales with a price < than the floor_price. -struct Config has drop, store { +public struct Config has drop, store { floor_price: u64, } diff --git a/kiosk/sources/rules/kiosk_lock_rule.move b/kiosk/sources/rules/kiosk_lock_rule.move index 1897c3e..447bd4f 100644 --- a/kiosk/sources/rules/kiosk_lock_rule.move +++ b/kiosk/sources/rules/kiosk_lock_rule.move @@ -28,10 +28,10 @@ use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, Tr const ENotInKiosk: u64 = 0; /// The type identifier for the Rule. -struct Rule has drop {} +public struct Rule has drop {} /// An empty configuration for the Rule. -struct Config has drop, store {} +public struct Config has drop, store {} /// Creator: Adds a `kiosk_lock_rule` Rule to the `TransferPolicy` forcing /// buyers to lock the item in a Kiosk on purchase. diff --git a/kiosk/sources/rules/personal_kiosk_rule.move b/kiosk/sources/rules/personal_kiosk_rule.move index f941cc9..766bd65 100644 --- a/kiosk/sources/rules/personal_kiosk_rule.move +++ b/kiosk/sources/rules/personal_kiosk_rule.move @@ -30,7 +30,7 @@ const EItemNotInKiosk: u64 = 0; const EKioskNotOwned: u64 = 1; /// The Rule checking that the Kiosk is an owned one. -struct Rule has drop {} +public struct Rule has drop {} /// Add the "owned" rule to the KioskOwnerCap. public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { diff --git a/kiosk/sources/rules/royalty_rule.move b/kiosk/sources/rules/royalty_rule.move index 533f421..a961eea 100644 --- a/kiosk/sources/rules/royalty_rule.move +++ b/kiosk/sources/rules/royalty_rule.move @@ -42,7 +42,7 @@ const EInsufficientAmount: u64 = 1; const MAX_BPS: u16 = 10_000; /// The "Rule" witness to authorize the policy. -struct Rule has drop {} +public struct Rule has drop {} /// Configuration for the Rule. The `amount_bp` is the percentage /// of the transfer amount to be paid as a royalty fee. The `min_amount` @@ -51,7 +51,7 @@ struct Rule has drop {} /// /// Adding a mininum amount is useful to enforce a fixed fee even if /// the transfer amount is very small or 0. -struct Config has drop, store { +public struct Config has drop, store { amount_bp: u16, min_amount: u64, } @@ -78,7 +78,7 @@ public fun pay( let paid = policy::paid(request); let amount = fee_amount(policy, paid); - assert!(coin::value(&payment) == amount, EInsufficientAmount); + assert!(payment.value() == amount, EInsufficientAmount); policy::add_to_balance(Rule {}, policy, payment); policy::add_receipt(Rule {}, request) @@ -88,7 +88,7 @@ public fun pay( /// Can be used dry-runned to estimate the fee amount based on the Kiosk listing price. public fun fee_amount(policy: &TransferPolicy, paid: u64): u64 { let config: &Config = policy::get_rule(Rule {}, policy); - let amount = (((paid as u128) * (config.amount_bp as u128) / 10_000) as u64); + let mut amount = (((paid as u128) * (config.amount_bp as u128) / 10_000) as u64); // If the amount is less than the minimum, use the minimum if (amount < config.min_amount) { diff --git a/kiosk/sources/rules/witness_rule.move b/kiosk/sources/rules/witness_rule.move index 20def28..08931db 100644 --- a/kiosk/sources/rules/witness_rule.move +++ b/kiosk/sources/rules/witness_rule.move @@ -23,7 +23,7 @@ use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, Tr const ERuleNotFound: u64 = 0; /// Custom witness-key for the "proof policy". -struct Rule has drop {} +public struct Rule has drop {} /// Creator action: adds the Rule. /// Requires a "Proof" witness confirmation on every transfer. From d473e488320821933e2de161d247d53d9dd50c1e Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Fri, 12 Sep 2025 00:11:29 +0700 Subject: [PATCH 3/3] use struct method; assert_eq in test Signed-off-by: Vu Vo --- kiosk/Move.toml | 1 - kiosk/sources/extensions/personal_kiosk.move | 12 ++-- kiosk/sources/rules/floor_price_rule.move | 2 +- kiosk/sources/rules/kiosk_lock_rule.move | 6 +- kiosk/sources/rules/personal_kiosk_rule.move | 5 +- kiosk/sources/rules/royalty_rule.move | 4 +- kiosk/sources/rules/witness_rule.move | 2 +- kiosk/tests/rules/kiosk_lock_rule_tests.move | 11 ++- kiosk/tests/rules/royalty_rule_tests.move | 74 ++++++++++---------- 9 files changed, 55 insertions(+), 62 deletions(-) diff --git a/kiosk/Move.toml b/kiosk/Move.toml index 246c0de..eeee84f 100644 --- a/kiosk/Move.toml +++ b/kiosk/Move.toml @@ -4,7 +4,6 @@ edition = "2024.beta" # or (just) "2024" version = "0.0.2" [dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } [addresses] kiosk = "0x0" diff --git a/kiosk/sources/extensions/personal_kiosk.move b/kiosk/sources/extensions/personal_kiosk.move index 7fd36e9..298ddbd 100644 --- a/kiosk/sources/extensions/personal_kiosk.move +++ b/kiosk/sources/extensions/personal_kiosk.move @@ -7,12 +7,8 @@ /// module kiosk::personal_kiosk; -use std::option::{Self, Option}; use sui::dynamic_field as df; use sui::kiosk::{Self, Kiosk, KioskOwnerCap}; -use sui::object::{Self, ID, UID}; -use sui::transfer; -use sui::tx_context::{sender, TxContext}; /// Trying to return the Cap / Borrow to a wrong PersonalKioskCap object. const EIncorrectCapObject: u64 = 0; @@ -50,7 +46,7 @@ entry fun default(kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext) { /// The `PersonalKioskCap` is returned to allow chaining within a PTB, but /// the value must be consumed by the `transfer_to_sender` call in any case. public fun new(kiosk: &mut Kiosk, cap: KioskOwnerCap, ctx: &mut TxContext): PersonalKioskCap { - create(kiosk, cap, sender(ctx), ctx) + create(kiosk, cap, ctx.sender(), ctx) } /// Create a `PersonalKiosk` for `recipient`. @@ -75,10 +71,10 @@ fun create( owner: address, ctx: &mut TxContext, ): PersonalKioskCap { - assert!(kiosk::has_access(kiosk, &cap), EWrongKiosk); + assert!(kiosk.has_access(&cap), EWrongKiosk); // set the owner property of the Kiosk - kiosk::set_owner_custom(kiosk, &cap, owner); + kiosk.set_owner_custom(&cap, owner); // add the owner marker to the Kiosk; uses `_as_owner` to always pass, // even if Kiosk "allow_extensions" is set to false @@ -156,5 +152,5 @@ public fun try_owner(kiosk: &Kiosk): Option
{ /// Transfer the `PersonalKioskCap` to the transaction sender. public fun transfer_to_sender(self: PersonalKioskCap, ctx: &mut TxContext) { - transfer::transfer(self, sender(ctx)); + transfer::transfer(self, ctx.sender()); } diff --git a/kiosk/sources/rules/floor_price_rule.move b/kiosk/sources/rules/floor_price_rule.move index 07e2d0e..917225b 100644 --- a/kiosk/sources/rules/floor_price_rule.move +++ b/kiosk/sources/rules/floor_price_rule.move @@ -38,7 +38,7 @@ public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap, fl public fun prove(policy: &mut TransferPolicy, request: &mut TransferRequest) { let config: &Config = policy::get_rule(Rule {}, policy); - assert!(policy::paid(request) >= config.floor_price, EPriceTooSmall); + assert!(request.paid() >= config.floor_price, EPriceTooSmall); policy::add_receipt(Rule {}, request) } diff --git a/kiosk/sources/rules/kiosk_lock_rule.move b/kiosk/sources/rules/kiosk_lock_rule.move index 447bd4f..bf35473 100644 --- a/kiosk/sources/rules/kiosk_lock_rule.move +++ b/kiosk/sources/rules/kiosk_lock_rule.move @@ -21,7 +21,7 @@ /// module kiosk::kiosk_lock_rule; -use sui::kiosk::{Self, Kiosk}; +use sui::kiosk::Kiosk; use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; /// Item is not in the `Kiosk`. @@ -42,7 +42,7 @@ public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { /// Buyer: Prove the item was locked in the Kiosk to get the receipt and /// unblock the transfer request confirmation. public fun prove(request: &mut TransferRequest, kiosk: &Kiosk) { - let item = policy::item(request); - assert!(kiosk::has_item(kiosk, item) && kiosk::is_locked(kiosk, item), ENotInKiosk); + let item = request.item(); + assert!(kiosk.has_item(item) && kiosk.is_locked(item), ENotInKiosk); policy::add_receipt(Rule {}, request) } diff --git a/kiosk/sources/rules/personal_kiosk_rule.move b/kiosk/sources/rules/personal_kiosk_rule.move index 766bd65..ac97ab6 100644 --- a/kiosk/sources/rules/personal_kiosk_rule.move +++ b/kiosk/sources/rules/personal_kiosk_rule.move @@ -21,7 +21,7 @@ module kiosk::personal_kiosk_rule; use kiosk::personal_kiosk; -use sui::kiosk::{Self, Kiosk}; +use sui::kiosk::Kiosk; use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; /// An item hasn't been placed into the Kiosk before the call. @@ -40,7 +40,8 @@ public fun add(policy: &mut TransferPolicy, cap: &TransferPolicyCap) { /// Make sure that the destination Kiosk has the Owner key. Item is already /// placed by the time this check is performed - otherwise fails. public fun prove(kiosk: &Kiosk, request: &mut TransferRequest) { - assert!(kiosk::has_item(kiosk, policy::item(request)), EItemNotInKiosk); + let item = request.item(); + assert!(kiosk.has_item(item), EItemNotInKiosk); assert!(personal_kiosk::is_personal(kiosk), EKioskNotOwned); policy::add_receipt(Rule {}, request) diff --git a/kiosk/sources/rules/royalty_rule.move b/kiosk/sources/rules/royalty_rule.move index a961eea..d4dde6c 100644 --- a/kiosk/sources/rules/royalty_rule.move +++ b/kiosk/sources/rules/royalty_rule.move @@ -29,7 +29,7 @@ /// module kiosk::royalty_rule; -use sui::coin::{Self, Coin}; +use sui::coin::Coin; use sui::sui::SUI; use sui::transfer_policy::{Self as policy, TransferPolicy, TransferPolicyCap, TransferRequest}; @@ -75,7 +75,7 @@ public fun pay( request: &mut TransferRequest, payment: Coin, ) { - let paid = policy::paid(request); + let paid = request.paid(); let amount = fee_amount(policy, paid); assert!(payment.value() == amount, EInsufficientAmount); diff --git a/kiosk/sources/rules/witness_rule.move b/kiosk/sources/rules/witness_rule.move index 08931db..1846990 100644 --- a/kiosk/sources/rules/witness_rule.move +++ b/kiosk/sources/rules/witness_rule.move @@ -41,6 +41,6 @@ public fun prove( policy: &TransferPolicy, request: &mut TransferRequest, ) { - assert!(policy::has_rule>(policy), ERuleNotFound); + assert!(policy.has_rule>(), ERuleNotFound); policy::add_receipt(Rule {}, request) } diff --git a/kiosk/tests/rules/kiosk_lock_rule_tests.move b/kiosk/tests/rules/kiosk_lock_rule_tests.move index 9fd5d5b..2693308 100644 --- a/kiosk/tests/rules/kiosk_lock_rule_tests.move +++ b/kiosk/tests/rules/kiosk_lock_rule_tests.move @@ -8,15 +8,14 @@ module kiosk::kiosk_lock_rule_tests; use kiosk::kiosk_lock_rule as kiosk_lock; use sui::kiosk; use sui::kiosk_test_utils::{Self as test, Asset}; -use sui::transfer; use sui::transfer_policy as policy; #[test] fun test_item_always_locked() { let ctx = &mut test::ctx(); let (_, _, carl) = test::folks(); - let (policy, policy_cap) = test::get_policy(ctx); - let (kiosk, kiosk_cap) = test::get_kiosk(ctx); + let (mut policy, policy_cap) = test::get_policy(ctx); + let (mut kiosk, kiosk_cap) = test::get_kiosk(ctx); let (item, item_id) = test::get_asset(ctx); let payment = test::get_sui(1000, ctx); @@ -31,12 +30,12 @@ fun test_item_always_locked() { // Bob the Buyer // - places the item into his Kiosk and gets the proof // - prove placing and confirm request - let (bob_kiosk, bob_kiosk_cap) = test::get_kiosk(ctx); - let (item, request) = kiosk::purchase(&mut kiosk, item_id, payment); + let (mut bob_kiosk, bob_kiosk_cap) = test::get_kiosk(ctx); + let (item, mut request) = kiosk::purchase(&mut kiosk, item_id, payment); kiosk::lock(&mut bob_kiosk, &bob_kiosk_cap, &policy, item); // The difference! - kiosk_lock::prove(&mut request, &mut bob_kiosk); + kiosk_lock::prove(&mut request, &bob_kiosk); policy::confirm_request(&policy, request); // Carl the Cleaner; diff --git a/kiosk/tests/rules/royalty_rule_tests.move b/kiosk/tests/rules/royalty_rule_tests.move index fd10f95..4923841 100644 --- a/kiosk/tests/rules/royalty_rule_tests.move +++ b/kiosk/tests/rules/royalty_rule_tests.move @@ -10,152 +10,150 @@ use sui::sui::SUI; use sui::transfer_policy as policy; use sui::transfer_policy_tests as test; use sui::tx_context::dummy as ctx; +use std::unit_test::assert_eq; #[test] fun test_default_flow_0() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 0% royalty; min 0 MIST royalty_rule::add(&mut policy, &cap, 0, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(0, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + policy::confirm_request(&policy, request); let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 0, 1); + assert_eq!(profits, 0); } -#[test] -#[expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] +#[test, expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] fun test_default_flow_0_invalid_amount_fail() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 0% royalty; min 0 MIST royalty_rule::add(&mut policy, &cap, 0, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(10, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + policy::confirm_request(&policy, request); let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 0, 1); + assert_eq!(profits, 0); } #[test] fun test_default_flow_1() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 1% royalty; min 0 MIST royalty_rule::add(&mut policy, &cap, 100, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(1000, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + policy::confirm_request(&policy, request); let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 1000, 1); + assert_eq!(profits, 1000); } #[test] fun test_default_flow_100() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 100% royalty; min 0 MIST royalty_rule::add(&mut policy, &cap, 10_000, 0); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(100_000, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + policy::confirm_request(&policy, request); let profits = test::wrapup(policy, cap, ctx); - assert!(profits == 100_000, 1); + assert_eq!(profits, 100_000); } #[test] fun test_default_flow_1_min_10_000() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 1% royalty; min 10_000 MIST royalty_rule::add(&mut policy, &cap, 100, 10_000); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(10_000, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - assert!(test::wrapup(policy, cap, ctx) == 10_000, 1); + policy::confirm_request(&policy, request); + assert_eq!(test::wrapup(policy, cap, ctx), 10_000); } #[test] fun test_default_flow_10_min_10_000() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 10% royalty; min 10_000 MIST royalty_rule::add(&mut policy, &cap, 1000, 10_000); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(10_000, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - assert!(test::wrapup(policy, cap, ctx) == 10_000, 1); + policy::confirm_request(&policy, request); + assert_eq!(test::wrapup(policy, cap, ctx), 10_000); } #[test] fun test_default_flow_20_min_10_000() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 20% royalty; min 10_000 MIST royalty_rule::add(&mut policy, &cap, 20_00, 10_000); - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(20_000, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); - assert!(test::wrapup(policy, cap, ctx) == 20_000, 1); + policy::confirm_request(&policy, request); + assert_eq!(test::wrapup(policy, cap, ctx), 20_000); } -#[test] -#[expected_failure(abort_code = kiosk::royalty_rule::EIncorrectArgument)] +#[test, expected_failure(abort_code = kiosk::royalty_rule::EIncorrectArgument)] fun test_incorrect_config() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); royalty_rule::add(&mut policy, &cap, 11_000, 0); test::wrapup(policy, cap, ctx); } -#[test] -#[expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] +#[test, expected_failure(abort_code = kiosk::royalty_rule::EInsufficientAmount)] fun test_insufficient_amount() { let ctx = &mut ctx(); - let (policy, cap) = test::prepare(ctx); + let (mut policy, cap) = test::prepare(ctx); // 1% royalty royalty_rule::add(&mut policy, &cap, 100, 0); // Requires 1_000 MIST, coin has only 999 - let request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); + let mut request = policy::new_request(test::fresh_id(ctx), 100_000, test::fresh_id(ctx)); let payment = coin::mint_for_testing(999, ctx); royalty_rule::pay(&mut policy, &mut request, payment); - policy::confirm_request(&mut policy, request); + policy::confirm_request(&policy, request); test::wrapup(policy, cap, ctx); }