diff --git a/docs/examples/move/nft_marketplace/README.md b/docs/examples/move/nft_marketplace/README.md index 2a9b4748dfc..8078004157c 100644 --- a/docs/examples/move/nft_marketplace/README.md +++ b/docs/examples/move/nft_marketplace/README.md @@ -22,28 +22,47 @@ iota client call \ --function default ``` -### 3. Publish `item_for_market.move` +### 3. Publish `nft_marketplace` package + +#### 3.1(Optional) Publish Kiosk rules modules if these are not present in the network you are using + +Publish Kiosk rules modules(package): ```bash -iota client publish +iota client publish iota/kiosk ``` -### 4. Create an Item +After publishing, export the following variable: + +- `RULES_PACKAGE_ID`: The ID of rules package. + +#### 3.2 Publish marketplace extension + +Publish the nft_marketplace.move module: + +```bash +iota client publish iota/docs/examples/move/nft_marketplace +``` + +After publishing, export the following variables: + +- `MARKETPLACE_PACKAGE_ID`: The ID of whole marketplace package. +- `MARKETPLACE_PUBLISHER_ID`: The ID of the publisher object created during marketplace package publishing." + +### 4. Create an Clothing Store Item Create an item, for instance: ```bash iota client call \ - --package $M_ITEMS_ID \ - --module market_items \ + --package $MARKETPLACE_PACKAGE_ID \ + --module clothing_store \ --function new_jeans ``` -After creation, export the following variables: +After creation, export the following variable: -- `PACKAGE_ID`: The ID of the `market_items` package. -- `ITEM_ID`: The ID of the published item (in this case, Jeans). -- `PUBLISHER_ID`: The ID of the publisher object created during package publishing." +- `CLOTHING_STORE_ITEM_ID`: The ID of the published item (in this case, Jeans). ### 5. Create a Transfer Policy @@ -58,25 +77,16 @@ iota client call \ --module transfer_policy \ --function default \ --gas-budget 10000000 \ - --args $PUBLISHER_ID \ - --type-args "$ITEM_FOR_MARKET_PACKAGE_ID::market_items::Jeans" + --args $MARKETPLACE_PUBLISHER_ID \ + --type-args "$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans" ``` -### 6. Publish rules and marketplace extension - -Publish Kiosk rules modules: +After publishing, export the following variables: -```bash -iota client publish iota/kiosk/Move.toml -``` +- `ITEM_TRANS_POLICY`: The ID of the item transfer policy object. +- `ITEM_TRANS_POLICY_CAP`: The ID of the item transfer policy object owner capability" -Publish the nft_marketplace.move module: - -```bash -iota client publish iota/docs/examples/move/nft_marketplace/sources/nft_marketplace.move` -``` - -### 7. Install the Extension on the Kiosk +### 6. Install the Extension on the Kiosk The install function enables installation of the Marketplace extension in a kiosk. Under the hood it invokes `kiosk_extension::add` that adds extension to the Kiosk via dynamic field. @@ -84,26 +94,26 @@ Install the marketplace extension on the created kiosk using the command: ```bash iota client call \ - --package $MARKETPLACE_ID \ + --package $MARKETPLACE_PACKAGE_ID \ --module nft_marketplace \ --function install \ --args $KIOSK_ID $KIOSK_CAP_ID ``` -### 8. Set a Price for the Item +### 7. Set a Price for the Item Set the price for the item: ```bash iota client call \ - --package $MARKETPLACE_ID \ + --package $MARKETPLACE_PACKAGE_ID \ --module nft_marketplace \ --function set_price \ - --args $KIOSK_ID $KIOSK_CAP_ID $ITEM_ID 50000 \ - --type-args "&ITEM_FOR_MARKET_PACKAGE_ID::market_items::Jeans" + --args $KIOSK_ID $KIOSK_CAP_ID $CLOTHING_STORE_ITEM_ID 50000 \ + --type-args "$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans" ``` -### 9.(Optional) Set Royalties +### 8.(Optional) Set Royalties Royalties are a percentage of the item's price or revenue paid to the owner for the use or sale of their asset. @@ -111,24 +121,59 @@ Set royalties for the item: ```bash iota client call \ - --package $MARKETPLACE \ + --package $MARKETPLACE_PACKAGE_ID \ --module nft_marketplace \ --function setup_royalties \ - --args $ITEM_TRANS_POLICY_ID $ITEM_TRANS_POLICY_CAP_ID 5000 2000 \ - --type-args "ITEM_FOR_MARKET_PACKAGE_ID::market_items::Jeans" + --args $ITEM_TRANS_POLICY $ITEM_TRANS_POLICY_CAP 5000 2000 \ + --type-args "$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans" ``` -### 10. Buy an Item: +### 9. Buy an Item: -Here, when we buy an item, we pay the owner the item's price. If the royalty rule is enabled, an additional royalty fee, calculated as a percentage of the initial item price, is also paid. Once both payments are completed, the item is transferred to the buyer. +#### 9.1 Get the Item Price: + +```bash +iota client ptb \ +--move-call $MARKETPLACE_PACKAGE_ID::nft_marketplace::get_item_price "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" @$KIOSK_ID @$CLOTHING_STORE_ITEM_ID --assign item_price \ +``` + +#### 9.2(Optional) Calculate rolyalties of the Item: + +```bash +--move-call $RULES_PACKAGE_ID::royalty_rule::fee_amount "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" @$ITEM_TRANS_POLICY item_price --assign royalties_amount \ +``` + +#### 9.3 Create a payment coin with a specific amount (price + optional royalties): + +```bash +--split-coins gas "[item_price, royalties_amount]" --assign payment_coins \ +--merge-coins payment_coins.0 "[payment_coins.1]" \ +``` + +#### 9.4 Buy an Item using `payment_coins.0`: + +Here, when we buy an item, we pay the owner the item's price. If the royalty rule is enabled, an additional royalty fee, calculated as a percentage of the initial item price, is also paid. Once both payments are completed, the item is ready for transferring to the buyer. To purchase the item: ```bash -iota client call \ - --package $MARKETPLACE \ - --module nft_marketplace \ - --function buy_item \ - --args $KIOSK_ID $ITEM_TRANS_POLICY_ID &ITEM_ID $COIN_ID \ - --type-args "ITEM_FOR_MARKET_PACKAGE_ID::market_items::Jeans" +--move-call $MARKETPLACE_PACKAGE_ID::nft_marketplace::buy_item "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" @$KIOSK_ID @$ITEM_TRANS_POLICY @$CLOTHING_STORE_ITEM_ID payment_coins.0 --assign purchased_item +``` + +#### 9.5 Transfer an Item to the buyer: + +```bash +--move-call 0x2::transfer::public_transfer "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" purchased_item @ \ +``` + +The final purchase PTB request, including royalties, should look like this: + +```bash +iota client ptb \ +--move-call $MARKETPLACE_PACKAGE_ID::nft_marketplace::get_item_price "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" @$KIOSK_ID @$CLOTHING_STORE_ITEM_ID --assign item_price \ +--move-call $RULES_PACKAGE_ID::royalty_rule::fee_amount "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" @$ITEM_TRANS_POLICY item_price --assign royalties_amount \ +--split-coins gas "[item_price, royalties_amount]" --assign payment_coins \ +--merge-coins payment_coins.0 "[payment_coins.1]" \ +--move-call $MARKETPLACE_PACKAGE_ID::nft_marketplace::buy_item "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" @$KIOSK_ID @$ITEM_TRANS_POLICY @$CLOTHING_STORE_ITEM_ID payment_coins.0 --assign purchased_item \ +--move-call 0x2::transfer::public_transfer "<$MARKETPLACE_PACKAGE_ID::clothing_store::Jeans>" purchased_item @ ``` diff --git a/docs/examples/move/nft_marketplace/sources/item_for_market.move b/docs/examples/move/nft_marketplace/sources/clothing_store.move similarity index 89% rename from docs/examples/move/nft_marketplace/sources/item_for_market.move rename to docs/examples/move/nft_marketplace/sources/clothing_store.move index 2a11e131717..55294e84d75 100644 --- a/docs/examples/move/nft_marketplace/sources/item_for_market.move +++ b/docs/examples/move/nft_marketplace/sources/clothing_store.move @@ -1,11 +1,11 @@ /// Module provides `mock` items for using them in nft_marketplace and rental extensions. -module nft_marketplace::market_items { +module nft_marketplace::clothing_store { use iota::package; /// One Time Witness. - public struct MARKET_ITEMS has drop {} + public struct CLOTHING_STORE has drop {} - fun init(otw: MARKET_ITEMS, ctx: &mut TxContext) { + fun init(otw: CLOTHING_STORE, ctx: &mut TxContext) { package::claim_and_keep(otw, ctx) } diff --git a/docs/examples/move/nft_marketplace/sources/nft_marketplace.move b/docs/examples/move/nft_marketplace/sources/nft_marketplace.move index 571302f2eec..f34e6c36b66 100644 --- a/docs/examples/move/nft_marketplace/sources/nft_marketplace.move +++ b/docs/examples/move/nft_marketplace/sources/nft_marketplace.move @@ -10,16 +10,13 @@ module nft_marketplace::nft_marketplace { }; // rules imports - // use kiosk::floor_price_rule::Rule as FloorPriceRule; - // use kiosk::personal_kiosk_rule::Rule as PersonalRule; use kiosk::royalty_rule::Rule as RoyaltyRule; use kiosk::royalty_rule; - // use kiosk::witness_rule::Rule as WitnessRule; // === Errors === const EExtensionNotInstalled: u64 = 0; - const EObjectNotExist: u64 = 1; + const EWrongPaymentRoyalties: u64 = 1; // === Constants === const ALLOW_PLACE_AND_LOCK: u128 = 11; @@ -30,7 +27,7 @@ module nft_marketplace::nft_marketplace { /// Used as a key for the item that has been up for sale that's placed in the Extension's Bag. public struct Listed has store, copy, drop { id: ID } - public struct ItemPrice has store { + public struct ItemPrice has store { /// Total amount of time offered for renting in days. price: u64, } @@ -62,21 +59,23 @@ module nft_marketplace::nft_marketplace { } /// Buy listed item with the indicated price and pay royalties if needed - public fun buy_item(kiosk: &mut Kiosk, policy: &mut TransferPolicy, item_id: object::ID, mut payment: Coin, ctx: &mut TxContext) { + public fun buy_item(kiosk: &mut Kiosk, policy: &mut TransferPolicy, item_id: object::ID, mut payment: Coin, ctx: &mut TxContext): T { assert!(kiosk_extension::is_installed(kiosk), EExtensionNotInstalled); let ItemPrice { price } = take_from_bag(kiosk, Listed { id: item_id }); - let payment_amount = payment.split(price, ctx); - let payment_amount_value = payment_amount.value(); - let (item, mut transfer_request) = purchase(kiosk, item_id, payment_amount); + + let payment_amount_value = payment.value(); + let coin_price = payment.split(price, ctx); + + let (item, mut transfer_request) = purchase(kiosk, item_id, coin_price); if (policy.has_rule()) { - let royalties_value = royalty_rule::fee_amount(policy, payment_amount_value); - let royalties_coin = payment.split(royalties_value, ctx); - royalty_rule::pay(policy, &mut transfer_request, royalties_coin); + let royalties_value = royalty_rule::fee_amount(policy, price); + assert!(payment_amount_value == price + royalties_value, EWrongPaymentRoyalties); + royalty_rule::pay(policy, &mut transfer_request, payment); + } else { + payment.destroy_zero(); }; transfer_policy::confirm_request(policy, transfer_request); - transfer::public_transfer(item, ctx.sender()); - // Send a leftover back to buyer - transfer::public_transfer(payment, ctx.sender()); + item } @@ -98,6 +97,20 @@ module nft_marketplace::nft_marketplace { } + public fun get_item_price( + kiosk: &Kiosk, + item_id: ID, + ) : u64 { + let storage_ref = kiosk_extension::storage(Marketplace {}, kiosk); + let ItemPrice { price } = bag::borrow>( + storage_ref, + Listed { id: item_id }, + ); + + *price + } + + // === Private Functions === fun take_from_bag( diff --git a/docs/examples/move/nft_marketplace/sources/rental_extension.move b/docs/examples/move/nft_marketplace/sources/rental_extension.move index 592beb2049e..f8502c11dc6 100644 --- a/docs/examples/move/nft_marketplace/sources/rental_extension.move +++ b/docs/examples/move/nft_marketplace/sources/rental_extension.move @@ -30,7 +30,6 @@ module nft_marketplace::rental_extension { const ENotEnoughCoins: u64 = 2; const EInvalidKiosk: u64 = 3; const ERentingPeriodNotOver: u64 = 4; - const EObjectNotExist: u64 = 5; const ETotalPriceOverflow: u64 = 6; // === Constants ===