Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kiosk-marketplace/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
36 changes: 36 additions & 0 deletions kiosk-marketplace/Move.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# @generated by Move, please check-in and do not edit manually.

[move]
version = 0
manifest_digest = "0B26186432FECAA89587FCB88D7D71F62A085F4EE2FE186CBB6DEE4752AF791B"
deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600"

dependencies = [
{ name = "Kiosk" },
{ name = "Sui" },
]

[[move.package]]
name = "Kiosk"
source = { local = "../kiosk" }

dependencies = [
{ name = "Sui" },
]

[[move.package]]
name = "MoveStdlib"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/testnet", subdir = "crates/sui-framework/packages/move-stdlib" }

[[move.package]]
name = "Sui"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/testnet", subdir = "crates/sui-framework/packages/sui-framework" }

dependencies = [
{ name = "MoveStdlib" },
]

[move.toolchain-version]
compiler-version = "1.21.0"
edition = "legacy"
flavor = "sui"
39 changes: 39 additions & 0 deletions kiosk-marketplace/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "KioskMarketplace"
edition = "2024.alpha"

# edition = "2024.alpha" # To use the Move 2024 edition, currently in alpha
# license = "" # e.g., "MIT", "GPL", "Apache 2.0"
# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"]

[dependencies]
Sui = { override = true, git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
Kiosk = { local = "../kiosk" }

# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`.
# Revision can be a branch, a tag, and a commit hash.
# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" }

# For local dependencies use `local = path`. Path is relative to the package root
# Local = { local = "../path/to" }

# To resolve a version conflict and force a specific version for dependency
# override use `override = true`
# Override = { local = "../conflicting/version", override = true }

[addresses]
mkt = "0x0"

# Named addresses will be accessible in Move as `@name`. They're also exported:
# for example, `std = "0x1"` is exported by the Standard Library.
# alice = "0xA11CE"

[dev-dependencies]
# The dev-dependencies section allows overriding dependencies for `--test` and
# `--dev` modes. You can introduce test-only dependencies here.
# Local = { local = "../path/to/dev-build" }

[dev-addresses]
# The dev-addresses section allows overwriting named addresses for the `--test`
# and `--dev` modes.
# alice = "0xB0B"
122 changes: 122 additions & 0 deletions kiosk-marketplace/sources/adapter.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// The best practical approach to trading on marketplaces and favoring their
/// fees and conditions is issuing an additional `TransferRequest` (eg `Market`).
/// To achieve that, the `adapter` module provides a wrapper around the `PurchaseCap`
/// which adds an extra `Market` type parameter and forces the trade transaction
/// sender to satisfy the `TransferPolicy<Market>` requirements.
///
/// Unlike `PurchaseCap` purpose of which is to be as compatible as possible,
/// `MarketPurchaseCap` - the wrapper - only comes with a `store` to reduce the
/// amount of scenarios when it is transferred by accident or sent to an address
/// or object.
///
/// Notes:
/// - The Adapter intentionally does not have any errors built-in and the error
/// handling needs to be implemented in the extension utilizing the Marketplace
/// Adapter.
module mkt::adapter {
use sui::transfer_policy::{Self as policy, TransferRequest};
use sui::kiosk::{Kiosk, KioskOwnerCap, PurchaseCap};
use sui::tx_context::TxContext;
use sui::object::ID;
use sui::coin::Coin;
use sui::sui::SUI;

friend mkt::collection_bidding;
friend mkt::fixed_trading;
friend mkt::single_bid;

/// The `NoMarket` type is used to provide a default `Market` type parameter
/// for a scenario when the `MarketplaceAdapter` is not used and extensions
/// maintain uniformity of emitted events. NoMarket = no marketplace.
public struct NoMarket {}

/// The `MarketPurchaseCap` wraps the `PurchaseCap` and forces the unlocking
/// party to satisfy the `TransferPolicy<Market>` requirements.
public struct MarketPurchaseCap<phantom Market, phantom T: key + store> has store {
purchase_cap: PurchaseCap<T>
}

/// Create a new `PurchaseCap` and wrap it into the `MarketPurchaseCap`.
public(friend) fun new<Market, T: key + store>(
kiosk: &mut Kiosk,
cap: &KioskOwnerCap,
item_id: ID,
min_price: u64,
ctx: &mut TxContext
): MarketPurchaseCap<Market, T> {
let purchase_cap = kiosk.list_with_purchase_cap(
cap, item_id, min_price, ctx
);

MarketPurchaseCap<Market, T> { purchase_cap }
}

/// Return the `MarketPurchaseCap` to the `Kiosk`. Similar to how the
/// `PurchaseCap` can be returned at any moment. But it can't be unwrapped
/// into the `PurchaseCap` because that would allow cheating on a `Market`.
public(friend) fun return_cap<Market, T: key + store>(
kiosk: &mut Kiosk,
cap: MarketPurchaseCap<Market, T>,
_ctx: &mut TxContext
) {
let MarketPurchaseCap { purchase_cap } = cap;
kiosk.return_purchase_cap(purchase_cap);
}

/// Use the `MarketPurchaseCap` to purchase an item from the `Kiosk`. Unlike
/// the default flow, this function adds a `TransferRequest<Market>` which
/// forces the unlocking party to satisfy the `TransferPolicy<Market>`
public(friend) fun purchase<Market, T: key + store>(
kiosk: &mut Kiosk,
cap: MarketPurchaseCap<Market, T>,
coin: Coin<SUI>,
_ctx: &mut TxContext
): (T, TransferRequest<T>, TransferRequest<Market>) {
let MarketPurchaseCap { purchase_cap } = cap;
let (item, request) = kiosk.purchase_with_cap(purchase_cap, coin);
let market_request = policy::new_request(
policy::item(&request),
policy::paid(&request),
policy::from(&request),
);

(item, request, market_request)
}

/// Purchase an item listed with "NoMarket" policy. This function ignores
/// the `Market` type parameter and returns only a `TransferRequest<T>`.
public(friend) fun purchase_no_market<T: key + store>(
kiosk: &mut Kiosk,
cap: MarketPurchaseCap<NoMarket, T>,
coin: Coin<SUI>,
_ctx: &mut TxContext
): (T, TransferRequest<T>) {
let MarketPurchaseCap { purchase_cap } = cap;
kiosk.purchase_with_cap(purchase_cap, coin)
}

// === Getters ===

/// Handy wrapper to read the `kiosk` field of the inner `PurchaseCap`
public(friend) fun kiosk<Market, T: key + store>(self: &MarketPurchaseCap<Market, T>): ID {
self.purchase_cap.purchase_cap_kiosk()
}

/// Handy wrapper to read the `item` field of the inner `PurchaseCap`
public(friend) fun item<Market, T: key + store>(self: &MarketPurchaseCap<Market, T>): ID {
self.purchase_cap.purchase_cap_item()
}

/// Handy wrapper to read the `min_price` field of the inner `PurchaseCap`
public(friend) fun min_price<Market, T: key + store>(self: &MarketPurchaseCap<Market, T>): u64 {
self.purchase_cap.purchase_cap_min_price()
}

// === Test ===

#[test_only] friend mkt::adapter_tests;
#[test_only] friend mkt::fixed_trading_tests;
}
Loading