diff --git a/contracts/kiosk/sources/kiosk/ob_kiosk.move b/contracts/kiosk/sources/kiosk/ob_kiosk.move index 8e4e3e83..e849fea0 100644 --- a/contracts/kiosk/sources/kiosk/ob_kiosk.move +++ b/contracts/kiosk/sources/kiosk/ob_kiosk.move @@ -29,7 +29,7 @@ /// - Permissionless `Kiosk` needs to signer, apps don't have to wrap both /// the `KioskOwnerCap` and the `Kiosk` in a smart contract. module ob_kiosk::ob_kiosk { - use std::option::Option; + use std::option::{Self, Option}; use std::string::utf8; use std::vector; use std::type_name::{Self, TypeName}; @@ -39,7 +39,7 @@ module ob_kiosk::ob_kiosk { use sui::dynamic_field::{Self as df}; use sui::kiosk::{Self, Kiosk, KioskOwnerCap, uid, uid_mut as ext}; use sui::object::{Self, ID, UID, uid_to_address}; - use sui::coin; + use sui::coin::{Self, Coin}; use sui::table::{Self, Table}; use sui::transfer::{transfer, public_share_object, public_transfer}; use sui::tx_context::{Self, TxContext, sender}; @@ -55,6 +55,7 @@ module ob_kiosk::ob_kiosk { // Track the current version of the module const VERSION: u64 = 3; + const EDeprecatedApi: u64 = 998; const ENotUpgraded: u64 = 999; const EWrongVersion: u64 = 1000; @@ -328,8 +329,11 @@ module ob_kiosk::ob_kiosk { assert_version_and_upgrade(ext(self)); assert_can_deposit(self, ctx); + let nft_id = object::id(&nft); + let cap = pop_cap(self); - deposit_(self, &cap, nft); + kiosk::place(self, &cap, nft); + register_nft_(self, nft_id); set_cap(self, cap); } @@ -353,29 +357,41 @@ module ob_kiosk::ob_kiosk { let cap = pop_cap(self); while (!vector::is_empty(&nfts)) { let nft = vector::pop_back(&mut nfts); - deposit_(self, &cap, nft); + let nft_id = object::id(&nft); + kiosk::place(self, &cap, nft); + register_nft_(self, nft_id); }; vector::destroy_empty(nfts); set_cap(self, cap); } - /// Deposits NFT into `Kiosk` and handles `NftRef` accounting - fun deposit_( + /// Deposit an NFT and lock it within the `Kiosk` + /// + /// NFTs deposited using `deposit_locked` must use `transfer_locked_nft` to + /// transfer the NFT. + /// + /// Useful for interacting with non-OB collections. + /// + /// #### Panics + /// + /// Panics if transaction sender is not owner or `Kiosk` is not + /// permissionless. + public fun deposit_locked( self: &mut Kiosk, - cap: &KioskOwnerCap, + policy: &sui::transfer_policy::TransferPolicy, nft: T, + ctx: &mut TxContext, ) { - let nft_id = object::id(&nft); + assert_version_and_upgrade(ext(self)); + assert_can_deposit(self, ctx); - let refs = nft_refs_mut(self); - table::add(refs, nft_id, NftRef { - auths: vec_set::empty(), - is_exclusively_listed: false, - }); + let nft_id = object::id(&nft); - // place underlying NFT to kiosk - kiosk::place(self, cap, nft); + let cap = pop_cap(self); + kiosk::lock(self, &cap, policy, nft); + register_nft_(self, nft_id); + set_cap(self, cap); } // === Withdraw from the Kiosk === @@ -478,8 +494,7 @@ module ob_kiosk::ob_kiosk { assert_version_and_upgrade(ext(source)); assert_permission(source, ctx); - let refs = nft_refs_mut(source); - let ref = table::remove(refs, nft_id); + let ref = deregister_nft_(source, nft_id); assert_ref_not_exclusively_listed(&ref); let cap = pop_cap(source); @@ -520,32 +535,104 @@ module ob_kiosk::ob_kiosk { (target_kiosk_id, target_token) } + /// Deprecated, use `transfer_delegated_unlocked` + public fun transfer_delegated( + _source: &mut Kiosk, + _target: &mut Kiosk, + _nft_id: ID, + _entity_id: &UID, + _price: u64, + _ctx: &mut TxContext, + ): TransferRequest { + abort(EDeprecatedApi) + } + /// Transfer NFT out of Kiosk that has been previously delegated /// + /// Handles the case that NFT could be locked or not in the source `Kiosk`. + /// NFT will be locked in the target `Kiosk`. + /// /// Requires that address of sender was previously passed to /// `auth_transfer`. /// + /// `paid` argument is used to pass the amount on which royalties must be + /// paid on the base Sui transfer request, whilst, `price` is used on the + /// OB request. Will panic if `paid` is provided for a non-locked NFT. + /// /// #### Panics /// /// - Entity `UID` was not previously authorized for transfer /// - NFT does not exist /// - Target `Kiosk` deposit conditions were not met, see `deposit` method /// - Source or target `Kiosk` are not OriginByte kiosks - public fun transfer_delegated( + /// - Panics if `Coin` is provided for an NFT that is not + /// locked + public fun transfer_delegated_unlocked( source: &mut Kiosk, target: &mut Kiosk, nft_id: ID, entity_id: &UID, price: u64, + paid: Option>, ctx: &mut TxContext, ): TransferRequest { assert_version_and_upgrade(ext(source)); - let (nft, req) = transfer_nft_(source, nft_id, uid_to_address(entity_id), price, ctx); + let (nft, req) = transfer_nft_(source, nft_id, uid_to_address(entity_id), price, paid, ctx); deposit(target, nft, ctx); req } + /// Transfer NFT out of Kiosk that has been previously delegated + /// + /// NFT will be locked in the target `Kiosk`. + /// + /// Handles the case that NFT could be locked or not in the source `Kiosk`. + /// NFT will be locked in the target `Kiosk`. + /// + /// Requires that address of sender was previously passed to + /// `auth_transfer`. + /// + /// `paid` argument is used to pass the amount on which royalties must be + /// paid on the base Sui transfer request, whilst, `price` is used on the + /// OB request. Will panic if `paid` is provided for a non-locked NFT. + /// + /// #### Panics + /// + /// - Entity `UID` was not previously authorized for transfer + /// - NFT does not exist + /// - Target `Kiosk` deposit conditions were not met, see `deposit` method + /// - Source or target `Kiosk` are not OriginByte kiosks + /// - Panics if `Coin` is provided for an NFT that is not + /// locked + public fun transfer_delegated_locked( + source: &mut Kiosk, + target: &mut Kiosk, + nft_id: ID, + entity_id: &UID, + price: u64, + paid: Option>, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext, + ): TransferRequest { + assert_version_and_upgrade(ext(source)); + + let (nft, req) = transfer_nft_(source, nft_id, uid_to_address(entity_id), price, paid, ctx); + deposit_locked(target, transfer_policy, nft, ctx); + req + } + + /// Deprecated, use `transfer_signed_unlocked` + public fun transfer_signed( + _source: &mut Kiosk, + _target: &mut Kiosk, + _nft_id: ID, + _price: u64, + _ctx: &mut TxContext, + ): TransferRequest { + abort(EDeprecatedApi) + } + /// Transfer NFT out of Kiosk that has been previously delegated /// /// Requires that address of sender was previously passed to @@ -559,11 +646,14 @@ module ob_kiosk::ob_kiosk { /// - NFT does not exist /// - Target `Kiosk` deposit conditions were not met, see `deposit` method /// - Source or target `Kiosk` are not OriginByte kiosks - public fun transfer_signed( + /// - Panics if `Coin` is provided for an NFT that is not + /// locked + public fun transfer_signed_unlocked( source: &mut Kiosk, target: &mut Kiosk, nft_id: ID, price: u64, + paid: Option>, ctx: &mut TxContext, ): TransferRequest { assert_version_and_upgrade(ext(source)); @@ -571,16 +661,17 @@ module ob_kiosk::ob_kiosk { // otherwise it's possible to create dangling locks assert_not_exclusively_listed(source, nft_id); - let (nft, req) = transfer_nft_(source, nft_id, sender(ctx), price, ctx); + let (nft, req) = transfer_nft_(source, nft_id, sender(ctx), price, paid, ctx); deposit(target, nft, ctx); req } - /// Transfer NFT out of Kiosk that has been previously delegated to a base - /// Sui `Kiosk` + /// Transfer NFT out of Kiosk that has been previously delegated /// - /// Requires that `UID` of sender was previously passed to either - /// `auth_transfer` or `auth_exclusive_transfer`. + /// NFT will be locked in the target `Kiosk` + /// + /// Requires that address of sender was previously passed to + /// `auth_transfer` or transaction sender is `Kiosk` owner. /// /// Will always work if transaction sender is the `Kiosk` owner. /// @@ -588,30 +679,39 @@ module ob_kiosk::ob_kiosk { /// /// - Sender was not previously authorized for transfer or is not owner /// - NFT does not exist - /// - Source is not an OriginByte `Kiosk` - public fun transfer_locked_nft( + /// - Target `Kiosk` deposit conditions were not met, see `deposit` method + /// - Source or target `Kiosk` are not OriginByte kiosks + /// - Panics if `Coin` is provided for an NFT that is not + /// locked + public fun transfer_signed_locked( source: &mut Kiosk, target: &mut Kiosk, nft_id: ID, - entity_id: &UID, + price: u64, + paid: Option>, ctx: &mut TxContext, ): TransferRequest { assert_version_and_upgrade(ext(source)); + // Exclusive transfers need to be settled via `transfer_delegated` + // otherwise it's possible to create dangling locks + assert_not_exclusively_listed(source, nft_id); - check_entity_and_pop_ref(source, uid_to_address(entity_id), nft_id, ctx); - - let cap = pop_cap(source); - kiosk::list(source, &cap, nft_id, 0); - set_cap(source, cap); - - let (nft, req) = kiosk::purchase(source, nft_id, coin::zero(ctx)); + let (nft, req) = transfer_nft_(source, nft_id, sender(ctx), price, paid, ctx); deposit(target, nft, ctx); - - let req = transfer_request::from_sui(req, nft_id, uid_to_address(entity_id), ctx); - req } + /// Deprecated, use `transfer_delegated` instead + public fun transfer_locked_nft( + _source: &mut Kiosk, + _target: &mut Kiosk, + _nft_id: ID, + _entity_id: &UID, + _ctx: &mut TxContext, + ): TransferRequest { + abort(EDeprecatedApi) + } + /// Withdraw NFT from `Kiosk` without returning it /// /// Requires that `UID` of sender was previously passed to either @@ -730,8 +830,7 @@ module ob_kiosk::ob_kiosk { assert!(kiosk::owner(source) != PermissionlessAddr, ENotAuthorized); assert!(kiosk::owner(source) == kiosk::owner(target), ENotOwner); - let refs = nft_refs_mut(source); - let ref = table::remove(refs, nft_id); + let ref = deregister_nft_(source, nft_id); assert_ref_not_exclusively_listed(&ref); let cap = pop_cap(source); @@ -819,6 +918,8 @@ module ob_kiosk::ob_kiosk { assert!(kiosk::has_access(self, &kiosk_cap), ENotOwner); assert!(!is_ob_kiosk(self), EKioskOriginByteVersion); + // Ensure that `uid_mut` will work + kiosk::set_allow_extensions(self, &kiosk_cap, true); let kiosk_ext = ext(self); df::add(kiosk_ext, VersionDfKey {}, VERSION); @@ -849,12 +950,21 @@ module ob_kiosk::ob_kiosk { assert_version_and_upgrade(ext(self)); assert_permission(self, ctx); - // Assert that Kiosk has NFT + register_nft_(self, nft_id); + } + + /// Create an `NftRef` entry for the NFT + /// + /// #### Panics + /// + /// Panics if `NftRef` already exists + fun register_nft_( + self: &mut Kiosk, + nft_id: ID, + ) { assert_has_nft(self, nft_id); assert!(!kiosk::is_listed(self, nft_id), ENftIsListedInBaseKiosk); - // Assert that Kiosk has no NftRef, which means the NFT was - // placed in the Kiosk before installing the OB extension let refs = nft_refs_mut(self); assert_missing_ref(refs, nft_id); @@ -864,6 +974,20 @@ module ob_kiosk::ob_kiosk { }); } + /// Pop `NftRef` entry for the NFT + /// + /// #### Panics + /// + /// Panics if `NftRef` does not exist + fun deregister_nft_( + self: &mut Kiosk, + nft_id: ID, + ): NftRef { + let refs = nft_refs_mut(self); + assert!(table::contains(refs, nft_id), EMissingNft); + table::remove(refs, nft_id) + } + // === Private Functions === /// Initializes a transfer transaction @@ -873,15 +997,33 @@ module ob_kiosk::ob_kiosk { /// - Originator is not authorized to withdraw and transaction sender is /// not owner. /// - NFT does not exist + /// - Panics if `Coin` is provided for an NFT that is not + /// locked fun transfer_nft_( self: &mut Kiosk, nft_id: ID, originator: address, price: u64, + paid: Option>, ctx: &mut TxContext, ): (T, TransferRequest) { - let nft = remove_nft(self, nft_id, originator, ctx); - (nft, transfer_request::new(nft_id, originator, object::id(self), price, ctx)) + if (kiosk::is_locked(self, nft_id)) { + let paid = if (option::is_some(&paid)) { + option::destroy_some(paid) + } else { + option::destroy_none(paid); + coin::zero(ctx) + }; + + let (nft, req) = + remove_locked_nft(self, nft_id, originator, paid, ctx); + (nft, transfer_request::from_sui(req, nft_id, originator, ctx)) + } else { + option::destroy_none(paid); + + let nft = remove_nft(self, nft_id, originator, ctx); + (nft, transfer_request::new(nft_id, originator, object::id(self), price, ctx)) + } } /// Initializes a withdrawal transaction @@ -891,6 +1033,7 @@ module ob_kiosk::ob_kiosk { /// - Originator is not authorized to withdraw and transaction sender is /// not owner. /// - NFT does not exist + /// - NFT is locked fun withdraw_nft_( self: &mut Kiosk, nft_id: ID, @@ -901,20 +1044,23 @@ module ob_kiosk::ob_kiosk { (nft, withdraw_request::new(originator, ctx)) } - /// Checks that originator is authorized to withdraw NFT + /// Checks that originator is authorized to withdraw NFT and returns the + /// NFT /// /// #### Panics /// /// - Originator is not authorized to withdraw and transaction sender is /// not owner. /// - NFT does not exist + /// - NFT is locked fun remove_nft( self: &mut Kiosk, nft_id: ID, originator: address, ctx: &mut TxContext, ): T { - check_entity_and_pop_ref(self, originator, nft_id, ctx); + assert_can_transfer(self, nft_id, originator, ctx); + deregister_nft_(self, nft_id); let cap = pop_cap(self); let nft = kiosk::take(self, &cap, nft_id); @@ -923,6 +1069,32 @@ module ob_kiosk::ob_kiosk { nft } + /// Checks that originator is authorized to withdraw NFT and returns the + /// NFT + /// + /// #### Panics + /// + /// - Originator is not authorized to withdraw and transaction sender is + /// not owner. + /// - NFT does not exist + /// - NFT is not locked + fun remove_locked_nft( + self: &mut Kiosk, + nft_id: ID, + originator: address, + paid: Coin, + ctx: &mut TxContext + ): (T, sui::transfer_policy::TransferRequest) { + assert_can_transfer(self, nft_id, originator, ctx); + deregister_nft_(self, nft_id); + + let cap = pop_cap(self); + kiosk::list(self, &cap, nft_id, coin::value(&paid)); + set_cap(self, cap); + + kiosk::purchase(self, nft_id, paid) + } + // === Request Auth === /// Proves access to given type `Auth`. @@ -1164,17 +1336,27 @@ module ob_kiosk::ob_kiosk { df::exists_(uid(self), NftRefsDfKey {}) } + /// Returns whether the current transaction sender can deposit into `Kiosk` + /// /// Either sender is owner or permissionless deposits of `T` enabled. public fun can_deposit(self: &mut Kiosk, ctx: &mut TxContext): bool { sender(ctx) == kiosk::owner(self) || can_deposit_permissionlessly(self) } + /// Returns whether `DepositSettings` allow for permissionless deposits. + /// + /// Either `Kiosk` is permissionless, any deposits are allowed, or `T` was + /// explicitly whitelisted. + /// + /// If `Kiosk` is not an OriginByte `Kiosk` then we assume that + /// permissionless deposits are allowed and trust that the base `Kiosk` + /// manages this itself. public fun can_deposit_permissionlessly(self: &mut Kiosk): bool { - if (is_permissionless(self)) { + if (!is_ob_kiosk(self) || is_permissionless(self)) { return true }; - let settings = deposit_setting_mut(self); + let settings = deposit_setting(self); settings.enable_any_deposit || vec_set::contains( &settings.collections_with_enabled_deposits, @@ -1189,7 +1371,6 @@ module ob_kiosk::ob_kiosk { /// Panics if `Kiosk` is not OriginByte `Kiosk` // // TODO: Replace with immutable API - // TODO: Consider removing test_only public fun nft_refs(self: &Kiosk): &Table { is_ob_kiosk_imut(self); df::borrow(uid(self), NftRefsDfKey {}) @@ -1201,7 +1382,6 @@ module ob_kiosk::ob_kiosk { /// /// Panics if `Kiosk` is not OriginByte `Kiosk` or if NFT does not exist. // - // TODO: Replace with immutable API // TODO: Consider making it public fun nft_ref(self: &Kiosk, nft_id: ID): &NftRef { let refs = nft_refs(self); @@ -1221,6 +1401,11 @@ module ob_kiosk::ob_kiosk { assert!(is_permissionless(self), EKioskNotPermissionless); } + /// Asserts that the transaction sender may deposit into `Kiosk` + /// + /// #### Panics + /// + /// Panics if sender is not owner or `Kiosk` is not permissionless. public fun assert_can_deposit(self: &mut Kiosk, ctx: &mut TxContext) { assert!(can_deposit(self, ctx), ECannotDeposit); } @@ -1229,6 +1414,20 @@ module ob_kiosk::ob_kiosk { assert!(can_deposit_permissionlessly(self), EPermissionlessDepositsDisabled); } + /// Asserts that current transaction sender may transfer an NFT out of `Kiosk` + fun assert_can_transfer( + self: &Kiosk, + nft_id: ID, + entity: address, + ctx: &mut TxContext + ) { + let ref = nft_ref(self, nft_id); + assert!( + is_owner(self, tx_context::sender(ctx)) || vec_set::contains(&ref.auths, &entity), + ENotAuthorized, + ); + } + /// Asserts that owner is provided address /// /// #### Panics @@ -1296,28 +1495,6 @@ module ob_kiosk::ob_kiosk { assert!(vec_set::size(&ref.auths) == 0, ENftAlreadyListed); } - /// Check whether NFT can be transferred by given authority - /// - /// #### Panics - /// - /// Panics if `address` was not authorized to transfer and transaction - /// sender is not the `Kiosk` owner. - fun check_entity_and_pop_ref( - self: &mut Kiosk, - entity: address, - nft_id: ID, - ctx: &mut TxContext, - ) { - let refs = nft_refs_mut(self); - // NFT is being transferred - destroy the ref - let ref: NftRef = table::remove(refs, nft_id); - // Sender is owner or entity is an authority - assert!( - is_owner(self, sender(ctx)) || vec_set::contains(&ref.auths, &entity), - ENotAuthorized, - ); - } - /// Borrow `DepositSetting` field /// /// #### Panics diff --git a/contracts/liquidity_layer_v1/sources/trading/orderbook.move b/contracts/liquidity_layer_v1/sources/trading/orderbook.move index d85fc72a..ae6a7bb1 100644 --- a/contracts/liquidity_layer_v1/sources/trading/orderbook.move +++ b/contracts/liquidity_layer_v1/sources/trading/orderbook.move @@ -29,6 +29,7 @@ module liquidity_layer_v1::orderbook { use std::type_name; use std::vector; + use sui::sui::SUI; use sui::event; use sui::package::{Self, Publisher}; use sui::transfer_policy::TransferPolicy; @@ -88,9 +89,6 @@ module liquidity_layer_v1::orderbook { /// No order matches the given price level or ownership level const EOrderDoesNotExist: u64 = 6; - /// Market orders fail with this error if they cannot be filled - const EMarketOrderNotFilled: u64 = 6; - /// Trying to create an orderbook via a witness protected endpoint /// without TransferPolicy being registered with OriginByte const ENotOriginBytePolicy: u64 = 7; @@ -126,6 +124,15 @@ module liquidity_layer_v1::orderbook { /// Tried to call administrator protected endpoint while not administrator const ENotAdministrator: u64 = 16; + /// Market orders fail with this error if they cannot be filled + const EMarketOrderNotFilled: u64 = 17; + + /// Tried to resolve a `TradeIntermediate` field when one did not exist + const EUndefinedTradeIntermediate: u64 = 18; + + /// Tried to resolve `SUI` trade using generic endpoint + const EIncorrectEndpoint: u64 = 19; + // === Structs === /// Add this witness type to allowlists via @@ -728,20 +735,25 @@ module liquidity_layer_v1::orderbook { // === Finish trade === - /// When a bid is created and there's an ask with a lower price, then the - /// trade cannot be resolved immediately. - /// That's because we don't know the `Kiosk` ID up front in OB. - /// Conversely, when an ask is created, we don't know the `Kiosk` ID of the - /// buyer as the best bid can change at any time. + /// Executes a trade after orders have been matched + /// + /// NFT will be deposited in target `Kiosk` without locking it. /// - /// Therefore, orderbook creates [`TradeIntermediate`] which then has to be - /// permissionlessly resolved via this endpoint. + /// A separate trade execution step is necessary as we don't know the + /// target `Kiosk` upfront as the best bid or ask can change at any time. + /// + /// To resolve this, `Orderbook` creates a `TradeIntermediate` dynamic + /// field which can be permissionlessly resolved via this endpoint. /// /// See the documentation for `nft_protocol::transfer_request` to understand /// how to deal with the returned [`TransferRequest`] type. /// - /// * the buyer's kiosk must allow permissionless deposits of `T` unless - /// buyer is the signer + /// #### Panics + /// + /// - Tried to trade locked NFT with SUI + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. public fun finish_trade( book: &mut Orderbook, trade_id: ID, @@ -749,10 +761,39 @@ module liquidity_layer_v1::orderbook { buyer_kiosk: &mut Kiosk, ctx: &mut TxContext, ): TransferRequest { - // Version is being checked in function call `finish_trade_` - finish_trade_(book, trade_id, seller_kiosk, buyer_kiosk, ctx) + assert_version_and_upgrade(book); + + let trade = finalize_seller_side(book, trade_id, seller_kiosk); + let price = balance::value(&trade.paid); + let nft_id = trade.nft_id; + + // Do not allow trading `SUI` using generic function + assert!( + kiosk::is_locked(seller_kiosk, nft_id) && + std::type_name::get() == std::type_name::get(), + EIncorrectEndpoint + ); + + let transfer_req = ob_kiosk::transfer_delegated_unlocked( + seller_kiosk, buyer_kiosk, nft_id, &book.id, price, option::none(), ctx, + ); + + finalize_buyer_side(&mut transfer_req, trade, buyer_kiosk, ctx); + + transfer_req } + /// Optimistic equivalent of `finish_trade` + /// + /// Executes a trade after orders have been matched but will not panic if + /// `Kiosks` do not match the trade ID. + /// + /// #### Panics + /// + /// - Tried to trade locked NFT with SUI + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. public fun finish_trade_if_kiosks_match( book: &mut Orderbook, trade_id: ID, @@ -760,18 +801,574 @@ module liquidity_layer_v1::orderbook { buyer_kiosk: &mut Kiosk, ctx: &mut TxContext ): Option> { - // Version is being checked in function call `finish_trade_` + // Version asserted by `finish_trade` + + let trade = trade(book, trade_id); + let kiosks_match = &trade.seller_kiosk == &object::id(seller_kiosk) + && &trade.buyer_kiosk == &object::id(buyer_kiosk); + + if (kiosks_match) { + option::some( + finish_trade(book, trade_id, seller_kiosk, buyer_kiosk, ctx) + ) + } else { + option::none() + } + } + + /// Executes a trade after orders have been matched + /// + /// NFT will be deposited in target `Kiosk` and immediately locked. + /// + /// A separate trade execution step is necessary as we don't know the + /// target `Kiosk` upfront as the best bid or ask can change at any time. + /// + /// To resolve this, `Orderbook` creates a `TradeIntermediate` dynamic + /// field which can be permissionlessly resolved via this endpoint. + /// + /// See the documentation for `nft_protocol::transfer_request` to understand + /// how to deal with the returned [`TransferRequest`] type. + /// + /// #### Panics + /// + /// - Tried to trade locked NFT with SUI + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_trade_locked( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext, + ): TransferRequest { + assert_version_and_upgrade(book); + + let trade = finalize_seller_side(book, trade_id, seller_kiosk); + let price = balance::value(&trade.paid); + let nft_id = trade.nft_id; + + // Do not allow trading `SUI` using generic function + assert!( + kiosk::is_locked(seller_kiosk, nft_id) && + std::type_name::get() == std::type_name::get(), + EIncorrectEndpoint + ); + + let transfer_req = ob_kiosk::transfer_delegated_locked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + price, + option::none(), + transfer_policy, + ctx, + ); + + finalize_buyer_side(&mut transfer_req, trade, buyer_kiosk, ctx); + + transfer_req + } + + /// Optimistic equivalent of `finish_trade_locked` + /// + /// Executes a trade after orders have been matched but will not panic if + /// `Kiosks` do not match the trade ID. + /// + /// #### Panics + /// + /// - Tried to trade locked NFT with SUI + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_trade_locked_if_kiosks_match( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext + ): Option> { + // Version asserted by `finish_trade_locked` + + let trade = trade(book, trade_id); + let kiosks_match = &trade.seller_kiosk == &object::id(seller_kiosk) + && &trade.buyer_kiosk == &object::id(buyer_kiosk); + + if (kiosks_match) { + option::some( + finish_trade_locked( + book, + trade_id, + seller_kiosk, + buyer_kiosk, + transfer_policy, + ctx + ) + ) + } else { + option::none() + } + } + + /// Executes a trade after orders have been matched + /// + /// Inherits the NFTs locked state from the source `Kiosk` to the target. + /// - If NFT was locked in source it will be locked in target + /// - If NFT was unlocked in source it will be locked in target + /// + /// A separate trade execution step is necessary as we don't know the + /// target `Kiosk` upfront as the best bid or ask can change at any time. + /// + /// To resolve this, `Orderbook` creates a `TradeIntermediate` dynamic + /// field which can be permissionlessly resolved via this endpoint. + /// + /// See the documentation for `nft_protocol::transfer_request` to understand + /// how to deal with the returned [`TransferRequest`] type. + /// + /// #### Panics + /// + /// - Tried to trade locked NFT with SUI + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_trade_inherit( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext, + ): TransferRequest { + assert_version_and_upgrade(book); + + let trade = finalize_seller_side(book, trade_id, seller_kiosk); + let price = balance::value(&trade.paid); + let nft_id = trade.nft_id; + + // Do not allow trading `SUI` using generic function + assert!( + kiosk::is_locked(seller_kiosk, nft_id) && + std::type_name::get() == std::type_name::get(), + EIncorrectEndpoint + ); + + let transfer_req = if (kiosk::is_locked(seller_kiosk, nft_id)) { + ob_kiosk::transfer_delegated_locked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + price, + option::none(), + transfer_policy, + ctx, + ) + } else { + ob_kiosk::transfer_delegated_unlocked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + price, + option::none(), + ctx, + ) + }; + + finalize_buyer_side(&mut transfer_req, trade, buyer_kiosk, ctx); + + transfer_req + } + + /// Optimistic equivalent of `finish_trade_inherit` + /// + /// Executes a trade after orders have been matched but will not panic if + /// `Kiosks` do not match the trade ID. + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_trade_inherit_if_kiosks_match( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext + ): Option> { + // Version asserted by `finish_trade_inherit` + + let trade = trade(book, trade_id); + let kiosks_match = &trade.seller_kiosk == &object::id(seller_kiosk) + && &trade.buyer_kiosk == &object::id(buyer_kiosk); + + if (kiosks_match) { + option::some( + finish_trade_inherit( + book, + trade_id, + seller_kiosk, + buyer_kiosk, + transfer_policy, + ctx + ) + ) + } else { + option::none() + } + } + + /// Equivalent to `finish_trade` but respects SUI royalties + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_sui_trade( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + ctx: &mut TxContext, + ): TransferRequest { + assert_version_and_upgrade(book); + + let trade = finalize_seller_side(book, trade_id, seller_kiosk); + let price = balance::value(&trade.paid); + let nft_id = trade.nft_id; + + let transfer_req = if (kiosk::is_locked(seller_kiosk, nft_id)) { + let paid = coin::take(&mut trade.paid, price, ctx); + ob_kiosk::transfer_delegated_unlocked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + 0, + option::some(paid), + ctx, + ) + } else { + ob_kiosk::transfer_delegated_unlocked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + price, + option::none(), + ctx, + ) + }; + + finalize_buyer_side(&mut transfer_req, trade, buyer_kiosk, ctx); + + transfer_req + } + + /// Optimistic equivalent of `finish_sui_trade` + /// + /// Executes a trade after orders have been matched but will not panic if + /// `Kiosks` do not match the trade ID. + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_sui_trade_if_kiosks_match( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + ctx: &mut TxContext + ): Option> { + // Version asserted by `finish_trade` + + let trade = trade(book, trade_id); + let kiosks_match = &trade.seller_kiosk == &object::id(seller_kiosk) + && &trade.buyer_kiosk == &object::id(buyer_kiosk); + + if (kiosks_match) { + option::some( + finish_sui_trade(book, trade_id, seller_kiosk, buyer_kiosk, ctx) + ) + } else { + option::none() + } + } + + /// Executes a trade after orders have been matched + /// + /// NFT will be deposited in target `Kiosk` and immediately locked. + /// + /// A separate trade execution step is necessary as we don't know the + /// target `Kiosk` upfront as the best bid or ask can change at any time. + /// + /// To resolve this, `Orderbook` creates a `TradeIntermediate` dynamic + /// field which can be permissionlessly resolved via this endpoint. + /// + /// See the documentation for `nft_protocol::transfer_request` to understand + /// how to deal with the returned [`TransferRequest`] type. + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_sui_trade_locked( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext, + ): TransferRequest { + assert_version_and_upgrade(book); + + let trade = finalize_seller_side(book, trade_id, seller_kiosk); + let price = balance::value(&trade.paid); + let nft_id = trade.nft_id; + + let transfer_req = if (kiosk::is_locked(seller_kiosk, nft_id)) { + let paid = coin::take(&mut trade.paid, price, ctx); + ob_kiosk::transfer_delegated_locked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + 0, + option::some(paid), + transfer_policy, + ctx, + ) + } else { + ob_kiosk::transfer_delegated_locked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + price, + option::none(), + transfer_policy, + ctx, + ) + }; + + finalize_buyer_side(&mut transfer_req, trade, buyer_kiosk, ctx); + + transfer_req + } + + /// Optimistic equivalent of `finish_sui_trade_locked` + /// + /// Executes a trade after orders have been matched but will not panic if + /// `Kiosks` do not match the trade ID. + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_sui_trade_locked_if_kiosks_match( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext + ): Option> { + // Version asserted by `finish_trade_locked` - let t = trade(book, trade_id); - let kiosks_match = &t.seller_kiosk == &object::id(seller_kiosk) && &t.buyer_kiosk == &object::id(buyer_kiosk); + let trade = trade(book, trade_id); + let kiosks_match = &trade.seller_kiosk == &object::id(seller_kiosk) + && &trade.buyer_kiosk == &object::id(buyer_kiosk); if (kiosks_match) { - option::some(finish_trade(book, trade_id, seller_kiosk, buyer_kiosk, ctx)) + option::some( + finish_sui_trade_locked( + book, + trade_id, + seller_kiosk, + buyer_kiosk, + transfer_policy, + ctx + ) + ) } else { option::none() } } + /// Executes a trade after orders have been matched + /// + /// Inherits the NFTs locked state from the source `Kiosk` to the target. + /// - If NFT was locked in source it will be locked in target + /// - If NFT was unlocked in source it will be locked in target + /// + /// A separate trade execution step is necessary as we don't know the + /// target `Kiosk` upfront as the best bid or ask can change at any time. + /// + /// To resolve this, `Orderbook` creates a `TradeIntermediate` dynamic + /// field which can be permissionlessly resolved via this endpoint. + /// + /// See the documentation for `nft_protocol::transfer_request` to understand + /// how to deal with the returned [`TransferRequest`] type. + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_sui_trade_inherit( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext, + ): TransferRequest { + assert_version_and_upgrade(book); + + let trade = finalize_seller_side(book, trade_id, seller_kiosk); + let price = balance::value(&trade.paid); + let nft_id = trade.nft_id; + + let transfer_req = if (kiosk::is_locked(seller_kiosk, nft_id)) { + let paid = coin::take(&mut trade.paid, price, ctx); + ob_kiosk::transfer_delegated_locked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + 0, + option::some(paid), + transfer_policy, + ctx, + ) + } else { + ob_kiosk::transfer_delegated_unlocked( + seller_kiosk, + buyer_kiosk, + nft_id, + &book.id, + price, + option::none(), + ctx, + ) + }; + + finalize_buyer_side(&mut transfer_req, trade, buyer_kiosk, ctx); + + transfer_req + } + + /// Optimistic equivalent of `finish_sui_trade_inherit` + /// + /// Executes a trade after orders have been matched but will not panic if + /// `Kiosks` do not match the trade ID. + /// + /// #### Panics + /// + /// - Trade `ID` does not exist + /// - Buyer's `Kiosk` does not allow permissionless deposits of `T` unless + /// buyer is the transaction sender. + public fun finish_sui_trade_inherit_if_kiosks_match( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + transfer_policy: &sui::transfer_policy::TransferPolicy, + ctx: &mut TxContext + ): Option> { + // Version asserted by `finish_trade_inherit` + + let trade = trade(book, trade_id); + let kiosks_match = &trade.seller_kiosk == &object::id(seller_kiosk) + && &trade.buyer_kiosk == &object::id(buyer_kiosk); + + if (kiosks_match) { + option::some( + finish_sui_trade_inherit( + book, + trade_id, + seller_kiosk, + buyer_kiosk, + transfer_policy, + ctx + ) + ) + } else { + option::none() + } + } + + /// Finalizes trade on the seller side by resolving `TradeIntermediate` + /// + /// #### Panics + /// + /// - `TradeIntermediate` did not exist for given type and `ID` + /// - Seller `Kiosk` did not match trade + fun finalize_seller_side( + book: &mut Orderbook, + trade_id: ID, + seller_kiosk: &mut Kiosk, + ): TradeIntermediate { + let trade_key = TradeIntermediateDfKey { trade_id }; + let trade_opt = df::remove_if_exists(&mut book.id, trade_key); + assert!(option::is_some(&trade_opt), EUndefinedTradeIntermediate); + let trade: TradeIntermediate = option::destroy_some(trade_opt); + + // Assert we are resolving from the correct `Kiosk` + assert!( + trade.seller_kiosk == object::id(seller_kiosk), + EKioskIdMismatch, + ); + + trade + } + + /// Finalizes trade on the buyer side by consuming `TradeIntermediate` + /// + /// #### Panics + /// + /// - Buyer `Kiosk` did not match trade + fun finalize_buyer_side( + transfer_req: &mut TransferRequest, + trade: TradeIntermediate, + buyer_kiosk: &Kiosk, + ctx: &mut TxContext, + ) { + assert!( + trade.buyer_kiosk == object::id(buyer_kiosk), + EKioskIdMismatch, + ); + + let TradeIntermediate { + id, + nft_id: _, + seller_kiosk: _, + paid, + seller, + buyer: _, + buyer_kiosk: _, + commission: commission, + } = trade; + + object::delete(id); + + // Sell-side commission gets transferred to the sell-side intermediary + trading::transfer_ask_commission(&mut commission, &mut paid, ctx); + + transfer_request::set_paid(transfer_req, paid, seller); + ob_kiosk::set_transfer_request_auth(transfer_req, &Witness {}); + } + // === Create orderbook === /// NFTs of type `T` to be traded, and `F`ungible `T`oken to be @@ -1342,14 +1939,15 @@ module liquidity_layer_v1::orderbook { // === Priv fns === - /// * buyer kiosk must be in Originbyte ecosystem - /// * sender must be owner of buyer kiosk - /// * kiosk must allow permissionless deposits of `T` + /// Creates a bid and attempts to immediately execute it /// - /// Either `TradeIntermediate` is shared, or bid is added to the state. + /// Returns `TradeInfo` which can be used to to call `finish_trade` + /// endpoints. /// - /// Returns `Some` with amount if matched. - /// The amount is always equal or less than price. + /// #### Panics + /// + /// - Transaction sender is not owner of `Kiosk` + /// - `Kiosk` does not allow permissionless deposits of `T` fun create_bid_( book: &mut Orderbook, buyer_kiosk: &mut Kiosk, @@ -1360,7 +1958,6 @@ module liquidity_layer_v1::orderbook { ): Option { assert_version_and_upgrade(book); assert_tick_level(price, book.tick_size); - ob_kiosk::assert_is_ob_kiosk(buyer_kiosk); ob_kiosk::assert_permission(buyer_kiosk, ctx); ob_kiosk::assert_can_deposit_permissionlessly(buyer_kiosk); @@ -1374,7 +1971,6 @@ module liquidity_layer_v1::orderbook { (false, 0) } else { let lowest_ask_price = crit_bit::min_key(asks); - (lowest_ask_price <= price, lowest_ask_price) }; @@ -1819,12 +2415,13 @@ module liquidity_layer_v1::orderbook { ); option::destroy_none(maybe_commission); - let transfer_req = ob_kiosk::transfer_delegated( + let transfer_req = ob_kiosk::transfer_delegated_unlocked( seller_kiosk, buyer_kiosk, nft_id, &book.id, price, + option::none(), ctx, ); @@ -1834,68 +2431,6 @@ module liquidity_layer_v1::orderbook { transfer_req } - fun finish_trade_( - book: &mut Orderbook, - trade_id: ID, - seller_kiosk: &mut Kiosk, - buyer_kiosk: &mut Kiosk, - ctx: &mut TxContext, - ): TransferRequest { - assert_version_and_upgrade(book); - - let trade = df::remove( - &mut book.id, TradeIntermediateDfKey { trade_id } - ); - - let TradeIntermediate { - id, - nft_id, - seller_kiosk: _, - paid, - seller, - buyer: _, - buyer_kiosk: expected_buyer_kiosk_id, - commission: maybe_commission, - } = trade; - - object::delete(id); - - let price = balance::value(&paid); - - assert!( - expected_buyer_kiosk_id == object::id(buyer_kiosk), EKioskIdMismatch, - ); - - // Sell-side commission gets transferred to the sell-side intermediary - trading::transfer_ask_commission(&mut maybe_commission, &mut paid, ctx); - - let transfer_req = if (kiosk::is_locked(seller_kiosk, nft_id)) { - ob_kiosk::transfer_locked_nft( - seller_kiosk, - buyer_kiosk, - nft_id, - &book.id, - ctx, - ) - } else { - ob_kiosk::transfer_delegated( - seller_kiosk, - buyer_kiosk, - nft_id, - &book.id, - price, - ctx, - ) - }; - - transfer_request::set_paid( - &mut transfer_req, paid, seller, - ); - ob_kiosk::set_transfer_request_auth(&mut transfer_req, &Witness {}); - - transfer_req - } - /// Finds an ask of a given NFT advertized for the given price. Removes it /// from the asks vector preserving order and returns it. fun remove_ask(asks: &mut CBTree>, price: u64, nft_id: ID): Ask { diff --git a/contracts/liquidity_layer_v1/tests/locked.move b/contracts/liquidity_layer_v1/tests/locked.move new file mode 100644 index 00000000..27ba2567 --- /dev/null +++ b/contracts/liquidity_layer_v1/tests/locked.move @@ -0,0 +1,648 @@ +#[test_only] +/// Tests trading compatibility with locked NFTs +module liquidity_layer_v1::test_orderbook_locked { + use std::option; + + use sui::package; + use sui::coin; + use sui::object::{Self, ID, UID}; + use sui::transfer; + use sui::sui::SUI; + use sui::kiosk::{Self, Kiosk}; + use sui::test_scenario::{Self, Scenario, ctx}; + use sui::transfer_policy::TransferPolicy; + + use ob_permissions::witness; + use ob_kiosk::ob_kiosk; + + use liquidity_layer_v1::orderbook::{Self, TradeInfo, Orderbook}; + + const CREATOR: address = @0xA1C03; + const BUYER: address = @0xA1C04; + const SELLER: address = @0xA1C05; + + struct Foo has key, store { + id: UID + } + + struct Witness has drop {} + struct TEST_ORDERBOOK_LOCKED has drop {} + + #[test_only] + /// Initializes `Orderbook` and `TransferPolicy` + fun init_ob(scenario: &mut Scenario) { + let publisher = package::test_claim(TEST_ORDERBOOK_LOCKED {}, ctx(scenario)); + let (tx_policy, policy_cap) = ob_request::transfer_request::init_policy(&publisher, ctx(scenario)); + + let delegated_witness = witness::from_witness(Witness {}); + + let ob = orderbook::new_unprotected(delegated_witness, &tx_policy, ctx(scenario)); + orderbook::share(ob); + + transfer::public_share_object(tx_policy); + transfer::public_transfer(publisher, CREATOR); + transfer::public_transfer(policy_cap, CREATOR); + + test_scenario::next_tx(scenario, CREATOR); + } + + #[test_only] + /// Initializes non-OB `Orderbook` and `TransferPolicy` + fun init_non_ob(scenario: &mut Scenario) { + let publisher = package::test_claim(TEST_ORDERBOOK_LOCKED {}, ctx(scenario)); + let (tx_policy, policy_cap) = sui::transfer_policy::new(&publisher, ctx(scenario)); + + orderbook::create_for_external(&tx_policy, ctx(scenario)); + + transfer::public_share_object(tx_policy); + transfer::public_transfer(publisher, CREATOR); + transfer::public_transfer(policy_cap, CREATOR); + + test_scenario::next_tx(scenario, CREATOR); + } + + #[test_only] + /// Generates a trade on the `Orderbook` that has to be finished + fun init_trade( + orderbook: &mut Orderbook, + seller_kiosk: &mut Kiosk, + buyer_kiosk: &mut Kiosk, + nft_id: ID, + scenario: &mut Scenario, + ): TradeInfo { + test_scenario::next_tx(scenario, SELLER); + + orderbook::create_ask( + orderbook, + seller_kiosk, + 1_000_000, + nft_id, + ctx(scenario), + ); + + test_scenario::next_tx(scenario, BUYER); + + let coin = coin::mint_for_testing(1_000_000, ctx(scenario)); + let trade_opt = orderbook::create_bid( + orderbook, + buyer_kiosk, + 1_000_000, + &mut coin, + ctx(scenario), + ); + let trade = option::destroy_some(trade_opt); + coin::burn_for_testing(coin); + + test_scenario::next_tx(scenario, CREATOR); + + trade + } + + #[test] + fun test_transfer_locked_to_unlocked() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit_locked(&mut seller_kiosk, &tx_policy, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify unlocked + assert!(!kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_locked_to_unlocked_non_ob_policy() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit_locked(&mut seller_kiosk, &tx_policy, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify unlocked + assert!(!kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_locked_to_locked() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit_locked(&mut seller_kiosk, &tx_policy, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_locked( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_locked_to_locked_non_ob_policy() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit_locked(&mut seller_kiosk, &tx_policy, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_locked( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_unlocked_to_locked() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_sui_trade_locked( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_unlocked_to_locked_non_ob_policy() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_sui_trade_locked( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = liquidity_layer_v1::orderbook::EIncorrectEndpoint)] + fun test_transfer_unlocked_to_locked_generic() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_locked( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = liquidity_layer_v1::orderbook::EIncorrectEndpoint)] + fun test_transfer_unlocked_to_locked_non_ob_policy_generic() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_locked( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_locked_to_inherit() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit_locked(&mut seller_kiosk, &tx_policy, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_inherit( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_locked_to_inherit_non_ob_policy() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit_locked(&mut seller_kiosk, &tx_policy, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_inherit( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify locked + assert!(kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_unlocked_to_inherit() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_sui_trade_inherit( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify unlocked + assert!(!kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + fun test_transfer_unlocked_to_inherit_non_ob_policy() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_sui_trade_inherit( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify unlocked + assert!(!kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = liquidity_layer_v1::orderbook::EIncorrectEndpoint)] + fun test_transfer_unlocked_to_inherit_generic() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_inherit( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify unlocked + assert!(!kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = liquidity_layer_v1::orderbook::EIncorrectEndpoint)] + fun test_transfer_unlocked_to_inherit_non_ob_policy_generic() { + let scenario = test_scenario::begin(CREATOR); + + // 1. Create prerequisites + init_non_ob(&mut scenario); + let tx_policy = test_scenario::take_shared>(&mut scenario); + let orderbook = test_scenario::take_shared>(&scenario); + let (buyer_kiosk, _) = ob_kiosk::new_for_address(BUYER, ctx(&mut scenario)); + let (seller_kiosk, _) = ob_kiosk::new_for_address(SELLER, ctx(&mut scenario)); + + // 2. Add NFT to seller kiosk + let nft = Foo { id: object::new(ctx(&mut scenario)) }; + let nft_id = object::id(&nft); + ob_kiosk::deposit(&mut seller_kiosk, nft, ctx(&mut scenario)); + + // 3. Perform trade on NFT and finish + let trade = init_trade(&mut orderbook, &mut seller_kiosk, &mut buyer_kiosk, nft_id, &mut scenario); + + let request = orderbook::finish_trade_inherit( + &mut orderbook, + orderbook::trade_id(&trade), + &mut seller_kiosk, + &mut buyer_kiosk, + &tx_policy, + ctx(&mut scenario), + ); + ob_request::transfer_request::confirm(request, &tx_policy, ctx(&mut scenario)); + test_scenario::return_shared(tx_policy); + + // 4. Verify unlocked + assert!(!kiosk::is_locked(&buyer_kiosk, nft_id), 0); + + transfer::public_transfer(seller_kiosk, CREATOR); + transfer::public_transfer(buyer_kiosk, CREATOR); + test_scenario::return_shared(orderbook); + test_scenario::end(scenario); + } +} \ No newline at end of file diff --git a/contracts/tests/tests/kiosks/ob_kiosk.move b/contracts/tests/tests/kiosks/ob_kiosk.move index 33462eef..210e35c6 100644 --- a/contracts/tests/tests/kiosks/ob_kiosk.move +++ b/contracts/tests/tests/kiosks/ob_kiosk.move @@ -23,6 +23,7 @@ // borrow #[test_only] module ob_tests::test_ob_kiosk { + use std::option; use sui::test_scenario::{Self, ctx}; use sui::kiosk::{Self, Kiosk}; use sui::transfer; @@ -268,12 +269,13 @@ module ob_tests::test_ob_kiosk { // Init Buyer's Kiosk let (buyer_kiosk, _) = ob_kiosk::new(ctx(&mut scenario)); // Transfer NFT and get - let request = ob_kiosk::transfer_delegated( + let request = ob_kiosk::transfer_delegated_unlocked( &mut kiosk, &mut buyer_kiosk, nft_id, &rand_entity, 0, + option::none(), ctx(&mut scenario) ); @@ -330,12 +332,13 @@ module ob_tests::test_ob_kiosk { // Init Buyer's Kiosk let (buyer_kiosk, _) = ob_kiosk::new(ctx(&mut scenario)); // Transfer NFT and get - let request = ob_kiosk::transfer_delegated( + let request = ob_kiosk::transfer_delegated_unlocked( &mut kiosk, &mut buyer_kiosk, nft_id, &rand_entity, 0, + option::none(), ctx(&mut scenario) ); @@ -391,12 +394,13 @@ module ob_tests::test_ob_kiosk { test_scenario::next_tx(&mut scenario, fake_address()); - let request = ob_kiosk::transfer_delegated( + let request = ob_kiosk::transfer_delegated_unlocked( &mut kiosk, &mut buyer_kiosk, nft_id, &rand_entity, 0, + option::none(), ctx(&mut scenario) ); @@ -537,11 +541,12 @@ module ob_tests::test_ob_kiosk { let (buyer_kiosk, _) = ob_kiosk::new(ctx(&mut scenario)); // Transfer NFT and get - let request = ob_kiosk::transfer_signed( + let request = ob_kiosk::transfer_signed_unlocked( &mut kiosk, &mut buyer_kiosk, nft_id, 0, + option::none(), ctx(&mut scenario) ); @@ -602,11 +607,12 @@ module ob_tests::test_ob_kiosk { assert!(tx_context::sender(ctx(&mut scenario)) == authorised_address, 0); // Transfer NFT and get - let request = ob_kiosk::transfer_signed( + let request = ob_kiosk::transfer_signed_unlocked( &mut kiosk, &mut buyer_kiosk, nft_id, 0, + option::none(), ctx(&mut scenario) ); @@ -664,11 +670,12 @@ module ob_tests::test_ob_kiosk { assert!(tx_context::sender(ctx(&mut scenario)) == unauthorised_address, 0); // Transfer NFT and get - let request = ob_kiosk::transfer_signed( + let request = ob_kiosk::transfer_signed_unlocked( &mut kiosk, &mut buyer_kiosk, nft_id, 0, + option::none(), ctx(&mut scenario) ); diff --git a/contracts/tests/tests/liquidity_layer_v1/orderbook/orderbook.move b/contracts/tests/tests/liquidity_layer_v1/orderbook/orderbook.move index 373227e1..ff08fb12 100644 --- a/contracts/tests/tests/liquidity_layer_v1/orderbook/orderbook.move +++ b/contracts/tests/tests/liquidity_layer_v1/orderbook/orderbook.move @@ -148,14 +148,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -217,7 +215,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(tx_policy); test_scenario::return_shared(seller_kiosk); @@ -231,14 +228,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -336,7 +331,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(bid_commission); coin::burn_for_testing(ask_commission); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(tx_policy); test_scenario::return_shared(seller_kiosk); @@ -607,13 +601,11 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = transfer_policy::new(&publisher, ctx(&mut scenario)); test_utils::create_external_orderbook_v1(&tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -676,7 +668,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(tx_policy); test_scenario::return_shared(seller_kiosk); @@ -690,13 +681,11 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = transfer_policy::new(&publisher, ctx(&mut scenario)); test_utils::create_external_orderbook_v1(&tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -772,7 +761,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, buyer()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(tx_policy); test_scenario::return_shared(seller_kiosk); @@ -786,14 +774,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -879,7 +865,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); // test_scenario::return_shared(tx_policy); test_scenario::return_shared(seller_kiosk); @@ -893,14 +878,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -992,7 +975,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1005,15 +987,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1116,7 +1095,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1129,14 +1107,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1237,7 +1213,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1250,14 +1225,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1336,7 +1309,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1349,14 +1321,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1450,7 +1420,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1463,14 +1432,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1555,7 +1522,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1568,14 +1534,12 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); let dw = witness::test_dw(); test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1677,7 +1641,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(seller_kiosk); test_scenario::return_shared(buyer_kiosk); @@ -1690,7 +1653,6 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); @@ -1706,7 +1668,6 @@ module ob_tests::orderbook_v1 { orderbook::change_tick_size(dw, &mut ob, 1); orderbook::share(ob); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 2. Insert time lock @@ -1749,7 +1710,6 @@ module ob_tests::orderbook_v1 { clock::destroy_for_testing(clock); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(book); test_scenario::end(scenario); @@ -1761,7 +1721,6 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); @@ -1777,7 +1736,6 @@ module ob_tests::orderbook_v1 { orderbook::change_tick_size(dw, &mut ob, 1); orderbook::share(ob); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 2. Insert time lock @@ -1796,7 +1754,6 @@ module ob_tests::orderbook_v1 { clock::destroy_for_testing(clock); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(book); test_scenario::end(scenario); @@ -1808,7 +1765,6 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); @@ -1816,7 +1772,6 @@ module ob_tests::orderbook_v1 { test_utils::create_orderbook_v1(dw, &tx_policy, &mut scenario); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 3. Create Buyer Kiosk @@ -1883,7 +1838,6 @@ module ob_tests::orderbook_v1 { coin::burn_for_testing(coin); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(bid); test_scenario::return_shared(tx_policy); @@ -1899,7 +1853,6 @@ module ob_tests::orderbook_v1 { let scenario = test_scenario::begin(creator()); // 1. Create Collection, TransferPolicy and Orderbook - let (collection, mint_cap) = test_utils::init_collection_foo(ctx(&mut scenario)); let publisher = test_utils::get_publisher(ctx(&mut scenario)); let (tx_policy, policy_cap) = test_utils::init_transfer_policy(&publisher, ctx(&mut scenario)); @@ -1915,7 +1868,6 @@ module ob_tests::orderbook_v1 { orderbook::change_tick_size(dw, &mut ob, 1); orderbook::share(ob); - transfer::public_share_object(collection); transfer::public_share_object(tx_policy); // 2. Insert administrator @@ -1953,7 +1905,6 @@ module ob_tests::orderbook_v1 { orderbook::remove_start_time_as_administrator(&mut book, ctx(&mut scenario)); transfer::public_transfer(publisher, creator()); - transfer::public_transfer(mint_cap, creator()); transfer::public_transfer(policy_cap, creator()); test_scenario::return_shared(book);