From 9ee971ad8e9ad14528204c88fb1763c9ec13518c Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Fri, 3 Jun 2022 15:31:33 -0600 Subject: [PATCH] [sui framework][verifier] Make Transfer functions restricted (#2396) * [sui framework][verifier] Make Transfer functions restricted - Transfer functions are now private to the module that defines the type - This restriction is lifted for types with store --- .../children/child_of_shared_object.move | 4 +- crates/sui-framework/sources/Bag.move | 24 ++- crates/sui-framework/sources/Balance.move | 1 + crates/sui-framework/sources/Coin.move | 4 +- crates/sui-framework/sources/Collection.move | 55 +++++-- crates/sui-framework/tests/BagTests.move | 10 +- .../sui-framework/tests/CollectionTests.move | 8 +- .../sui-transactional-test-runner/src/args.rs | 10 +- .../src/test_adapter.rs | 2 +- crates/sui-verifier/src/lib.rs | 1 + crates/sui-verifier/src/private_transfer.rs | 137 ++++++++++++++++++ crates/sui-verifier/src/verifier.rs | 3 +- .../examples/nfts/sources/Auction.move | 2 +- .../examples/nfts/sources/AuctionLib.move | 17 +++ .../examples/nfts/sources/Geniteam.move | 10 +- .../examples/nfts/sources/Marketplace.move | 2 +- .../examples/nfts/sources/SharedAuction.move | 3 +- 17 files changed, 244 insertions(+), 49 deletions(-) create mode 100644 crates/sui-verifier/src/private_transfer.rs diff --git a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move index 219f0ce9cd89d..6d1da13ef142b 100644 --- a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move +++ b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move @@ -10,7 +10,7 @@ module T3::O3 { use Sui::Transfer; use Sui::TxContext::{Self, TxContext}; - struct O3 has key { + struct O3 has key, store { id: VersionedID, } @@ -28,7 +28,7 @@ module T2::O2 { use Sui::TxContext::{Self, TxContext}; use T3::O3::O3; - struct O2 has key { + struct O2 has key, store { id: VersionedID, child: ChildRef, } diff --git a/crates/sui-framework/sources/Bag.move b/crates/sui-framework/sources/Bag.move index cd7b9a6a5d763..7ab0d26dd591b 100644 --- a/crates/sui-framework/sources/Bag.move +++ b/crates/sui-framework/sources/Bag.move @@ -74,7 +74,7 @@ module Sui::Bag { /// Abort if the object is already in the Bag. /// If the object was owned by another object, an `old_child_ref` would be around /// and need to be consumed as well. - fun add_impl(c: &mut Bag, object: T, old_child_ref: Option>) { + fun add_impl(c: &mut Bag, object: T, old_child_ref: Option>) { assert!( size(c) + 1 <= c.max_capacity, Errors::limit_exceeded(EMaxCapacityExceeded) @@ -89,14 +89,14 @@ module Sui::Bag { /// Add a new object to the Bag. /// Abort if the object is already in the Bag. - public fun add(c: &mut Bag, object: T) { + public fun add(c: &mut Bag, object: T) { add_impl(c, object, Option::none()) } /// Transfer a object that was owned by another object to the bag. /// Since the object is a child object of another object, an `old_child_ref` /// is around and needs to be consumed. - public fun add_child_object(c: &mut Bag, object: T, old_child_ref: ChildRef) { + public fun add_child_object(c: &mut Bag, object: T, old_child_ref: ChildRef) { add_impl(c, object, Option::some(old_child_ref)) } @@ -108,7 +108,7 @@ module Sui::Bag { /// Remove and return the object from the Bag. /// Abort if the object is not found. - public fun remove(c: &mut Bag, object: T): T { + public fun remove(c: &mut Bag, object: T): T { let idx = find(c, ID::id(&object)); if (Option::is_none(&idx)) { abort EObjectNotFound @@ -118,16 +118,28 @@ module Sui::Bag { } /// Remove the object from the Bag, and then transfer it to the signer. - public(script) fun remove_and_take(c: &mut Bag, object: T, ctx: &mut TxContext) { + public(script) fun remove_and_take(c: &mut Bag, object: T, ctx: &mut TxContext) { let object = remove(c, object); Transfer::transfer(object, TxContext::sender(ctx)); } /// Transfer the entire Bag to `recipient`. - public(script) fun transfer(c: Bag, recipient: address, _ctx: &mut TxContext) { + public(script) fun transfer_(c: Bag, recipient: address) { Transfer::transfer(c, recipient) } + /// Transfer the entire Bag to `recipient`. + public fun transfer(c: Bag, recipient: address) { + Transfer::transfer(c, recipient) + } + + public fun transfer_to_object_id( + obj: Bag, + owner_id: VersionedID, + ): (VersionedID, ChildRef) { + Transfer::transfer_to_object_id(obj, owner_id) + } + /// Look for the object identified by `id_bytes` in the Bag. /// Returns the index if found, none if not found. fun find(c: &Bag, id: &ID): Option { diff --git a/crates/sui-framework/sources/Balance.move b/crates/sui-framework/sources/Balance.move index 7d484b9bbfd34..ca880d968a876 100644 --- a/crates/sui-framework/sources/Balance.move +++ b/crates/sui-framework/sources/Balance.move @@ -17,6 +17,7 @@ module Sui::Balance { /// Storable balance - an inner struct of a Coin type. /// Can be used to store coins which don't need to have the /// key ability. + /// Helpful in representing a Coin without having to create a stand-alone object. struct Balance has store { value: u64 } diff --git a/crates/sui-framework/sources/Coin.move b/crates/sui-framework/sources/Coin.move index 412cd023126ad..562ba55bae371 100644 --- a/crates/sui-framework/sources/Coin.move +++ b/crates/sui-framework/sources/Coin.move @@ -8,8 +8,8 @@ module Sui::Coin { use Sui::TxContext::{Self, TxContext}; use Std::Vector; - /// A coin of type `T` worth `value`. Transferable but not storable - struct Coin has key { + /// A coin of type `T` worth `value`. Transferable and storable + struct Coin has key, store { id: VersionedID, balance: Balance } diff --git a/crates/sui-framework/sources/Collection.move b/crates/sui-framework/sources/Collection.move index e689944c5e27d..27a76e2de3417 100644 --- a/crates/sui-framework/sources/Collection.move +++ b/crates/sui-framework/sources/Collection.move @@ -41,19 +41,22 @@ module Sui::Collection { // we could use more efficient data structure such as set. const DEFAULT_MAX_CAPACITY: u64 = 65536; - struct Collection has key { + struct Collection has key { id: VersionedID, objects: vector>, max_capacity: u64, } /// Create a new Collection and return it. - public fun new(ctx: &mut TxContext): Collection { + public fun new(ctx: &mut TxContext): Collection { new_with_max_capacity(ctx, DEFAULT_MAX_CAPACITY) } /// Create a new Collection with custom size limit and return it. - public fun new_with_max_capacity(ctx: &mut TxContext, max_capacity: u64): Collection { + public fun new_with_max_capacity( + ctx: &mut TxContext, + max_capacity: u64, + ): Collection { assert!( max_capacity <= DEFAULT_MAX_CAPACITY && max_capacity > 0 , Errors::limit_exceeded(EInvalidMaxCapacity) @@ -66,12 +69,12 @@ module Sui::Collection { } /// Create a new Collection and transfer it to the signer. - public(script) fun create(ctx: &mut TxContext) { + public(script) fun create(ctx: &mut TxContext) { Transfer::transfer(new(ctx), TxContext::sender(ctx)) } /// Returns the size of the collection. - public fun size(c: &Collection): u64 { + public fun size(c: &Collection): u64 { Vector::length(&c.objects) } @@ -79,7 +82,11 @@ module Sui::Collection { /// If the object was owned by another object, an `old_child_ref` would be around /// and need to be consumed as well. /// Abort if the object is already in the collection. - fun add_impl(c: &mut Collection, object: T, old_child_ref: Option>) { + fun add_impl( + c: &mut Collection, + object: T, + old_child_ref: Option>, + ) { assert!( size(c) + 1 <= c.max_capacity, Errors::limit_exceeded(EMaxCapacityExceeded) @@ -98,26 +105,30 @@ module Sui::Collection { /// Add an object to the collection. /// Abort if the object is already in the collection. - public fun add(c: &mut Collection, object: T) { + public fun add(c: &mut Collection, object: T) { add_impl(c, object, Option::none()) } /// Transfer an object that was owned by another object to the collection. /// Since the object is a child object of another object, an `old_child_ref` /// is around and needs to be consumed. - public fun add_child_object(c: &mut Collection, object: T, old_child_ref: ChildRef) { + public fun add_child_object( + c: &mut Collection, + object: T, + old_child_ref: ChildRef, + ) { add_impl(c, object, Option::some(old_child_ref)) } /// Check whether the collection contains a specific object, /// identified by the object id in bytes. - public fun contains(c: &Collection, id: &ID): bool { + public fun contains(c: &Collection, id: &ID): bool { Option::is_some(&find(c, id)) } /// Remove and return the object from the collection. /// Abort if the object is not found. - public fun remove(c: &mut Collection, object: T): (T, ChildRef) { + public fun remove(c: &mut Collection, object: T): (T, ChildRef) { let idx = find(c, ID::id(&object)); assert!(Option::is_some(&idx), EObjectNotFound); let child_ref = Vector::remove(&mut c.objects, *Option::borrow(&idx)); @@ -125,19 +136,35 @@ module Sui::Collection { } /// Remove the object from the collection, and then transfer it to the signer. - public(script) fun remove_and_take(c: &mut Collection, object: T, ctx: &mut TxContext) { + public(script) fun remove_and_take( + c: &mut Collection, + object: T, + ctx: &mut TxContext, + ) { let (object, child_ref) = remove(c, object); Transfer::transfer_child_to_address(object, child_ref, TxContext::sender(ctx)); } /// Transfer the entire collection to `recipient`. - public(script) fun transfer(c: Collection, recipient: address, _ctx: &mut TxContext) { + public(script) fun transfer_(c: Collection, recipient: address) { Transfer::transfer(c, recipient) } + /// Transfer the entire collection to `recipient`. + public fun transfer(c: Collection, recipient: address) { + Transfer::transfer(c, recipient) + } + + public fun transfer_to_object_id( + obj: Collection, + owner_id: VersionedID, + ): (VersionedID, ChildRef>) { + Transfer::transfer_to_object_id(obj, owner_id) + } + /// Look for the object identified by `id_bytes` in the collection. /// Returns the index if found, none if not found. - fun find(c: &Collection, id: &ID): Option { + fun find(c: &Collection, id: &ID): Option { let i = 0; let len = size(c); while (i < len) { @@ -147,6 +174,6 @@ module Sui::Collection { }; i = i + 1; }; - return Option::none() + Option::none() } } diff --git a/crates/sui-framework/tests/BagTests.move b/crates/sui-framework/tests/BagTests.move index ab63142702568..b048f452f6d46 100644 --- a/crates/sui-framework/tests/BagTests.move +++ b/crates/sui-framework/tests/BagTests.move @@ -11,11 +11,11 @@ module Sui::BagTests { const EBAG_SIZE_MISMATCH: u64 = 0; const EOBJECT_NOT_FOUND: u64 = 1; - struct Object1 has key { + struct Object1 has key, store { id: VersionedID, } - struct Object2 has key { + struct Object2 has key, store { id: VersionedID, } @@ -60,7 +60,7 @@ module Sui::BagTests { // Sui::Bag::DEFAULT_MAX_CAPACITY is not readable outside the module let max_capacity = 65536; let bag = Bag::new_with_max_capacity(&mut ctx, max_capacity + 1); - Bag::transfer(bag, TxContext::sender(&ctx), &mut ctx); + Bag::transfer_(bag, TxContext::sender(&ctx)); } #[test] @@ -68,7 +68,7 @@ module Sui::BagTests { public(script) fun test_init_with_zero() { let ctx = TxContext::dummy(); let bag = Bag::new_with_max_capacity(&mut ctx, 0); - Bag::transfer(bag, TxContext::sender(&ctx), &mut ctx); + Bag::transfer_(bag, TxContext::sender(&ctx)); } #[test] @@ -81,6 +81,6 @@ module Sui::BagTests { Bag::add(&mut bag, obj1); let obj2 = Object2 { id: TxContext::new_id(&mut ctx) }; Bag::add(&mut bag, obj2); - Bag::transfer(bag, TxContext::sender(&ctx), &mut ctx); + Bag::transfer_(bag, TxContext::sender(&ctx)); } } diff --git a/crates/sui-framework/tests/CollectionTests.move b/crates/sui-framework/tests/CollectionTests.move index ceb4f216b7add..c1fd724436a14 100644 --- a/crates/sui-framework/tests/CollectionTests.move +++ b/crates/sui-framework/tests/CollectionTests.move @@ -9,7 +9,7 @@ module Sui::CollectionTests { use Sui::TestScenario; use Sui::TxContext; - struct Object has key { + struct Object has key, store { id: VersionedID, } @@ -114,7 +114,7 @@ module Sui::CollectionTests { // Sui::Collection::DEFAULT_MAX_CAPACITY is not readable outside the module let max_capacity = 65536; let collection = Collection::new_with_max_capacity(&mut ctx, max_capacity + 1); - Collection::transfer(collection, TxContext::sender(&ctx), &mut ctx); + Collection::transfer_(collection, TxContext::sender(&ctx)); } #[test] @@ -122,7 +122,7 @@ module Sui::CollectionTests { public(script) fun test_init_with_zero() { let ctx = TxContext::dummy(); let collection = Collection::new_with_max_capacity(&mut ctx, 0); - Collection::transfer(collection, TxContext::sender(&ctx), &mut ctx); + Collection::transfer_(collection, TxContext::sender(&ctx)); } #[test] @@ -135,6 +135,6 @@ module Sui::CollectionTests { Collection::add(&mut collection, obj1); let obj2 = Object { id: TxContext::new_id(&mut ctx) }; Collection::add(&mut collection, obj2); - Collection::transfer(collection, TxContext::sender(&ctx), &mut ctx); + Collection::transfer_(collection, TxContext::sender(&ctx)); } } diff --git a/crates/sui-transactional-test-runner/src/args.rs b/crates/sui-transactional-test-runner/src/args.rs index a44ae9b1531ea..6ad646b3daedf 100644 --- a/crates/sui-transactional-test-runner/src/args.rs +++ b/crates/sui-transactional-test-runner/src/args.rs @@ -80,16 +80,16 @@ impl SuiValue { } } - pub(crate) fn into_call_args(self, test_adapter: &SuiTestAdapter) -> CallArg { - match self { + pub(crate) fn into_call_args(self, test_adapter: &SuiTestAdapter) -> anyhow::Result { + Ok(match self { SuiValue::Object(fake_id) => { let id = match test_adapter.fake_to_real_object_id(fake_id) { Some(id) => id, - None => panic!("Unknown object, object({})", fake_id), + None => bail!("INVALID TEST. Unknown object, object({})", fake_id), }; let obj = match test_adapter.storage.get_object(&id) { Some(obj) => obj, - None => panic!("Could not load object argument {}", id), + None => bail!("INVALID TEST. Could not load object argument {}", id), }; if obj.is_shared() { CallArg::SharedObject(id) @@ -99,7 +99,7 @@ impl SuiValue { } } SuiValue::MoveValue(v) => CallArg::Pure(v.simple_serialize().unwrap()), - } + }) } } diff --git a/crates/sui-transactional-test-runner/src/test_adapter.rs b/crates/sui-transactional-test-runner/src/test_adapter.rs index 3e951e52e919b..89fb56153678d 100644 --- a/crates/sui-transactional-test-runner/src/test_adapter.rs +++ b/crates/sui-transactional-test-runner/src/test_adapter.rs @@ -279,7 +279,7 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> { let arguments = args .into_iter() .map(|arg| arg.into_call_args(self)) - .collect(); + .collect::>()?; let package_id = ObjectID::from(*module_id.address()); let package_ref = match self.storage.get_object(&package_id) { Some(obj) => obj.compute_object_reference(), diff --git a/crates/sui-verifier/src/lib.rs b/crates/sui-verifier/src/lib.rs index 35d107c593be0..b29472a049630 100644 --- a/crates/sui-verifier/src/lib.rs +++ b/crates/sui-verifier/src/lib.rs @@ -7,6 +7,7 @@ pub mod entry_points_verifier; pub mod global_storage_access_verifier; pub mod id_immutable_verifier; pub mod id_leak_verifier; +pub mod private_transfer; pub mod struct_with_key_verifier; use move_binary_format::{ diff --git a/crates/sui-verifier/src/private_transfer.rs b/crates/sui-verifier/src/private_transfer.rs new file mode 100644 index 0000000000000..b50be214f7a8b --- /dev/null +++ b/crates/sui-verifier/src/private_transfer.rs @@ -0,0 +1,137 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use move_binary_format::{ + access::ModuleAccess, + binary_views::BinaryIndexedView, + file_format::{ + Bytecode, FunctionDefinition, FunctionInstantiation, ModuleHandle, SignatureToken, + }, + CompiledModule, +}; +use sui_types::{ + error::{SuiError, SuiResult}, + SUI_FRAMEWORK_ADDRESS, +}; + +use crate::format_signature_token; + +/// All transfer functions (the functions in `Sui::Transfer`) are "private" in that they are +/// restricted to the module. +/// For example, with `Transfer::transfer(...)`, either: +/// - `T` must be a type declared in the current module or +/// - `T` must have `store` +pub fn verify_module(module: &CompiledModule) -> SuiResult { + let view = &BinaryIndexedView::Module(module); + // do not need to check the Sui::Transfer module itself + if is_transfer_module(view, module.self_handle()) { + return Ok(()); + } + for func_def in &module.function_defs { + verify_function(view, func_def).map_err(|error| SuiError::ModuleVerificationFailure { + error: format!( + "{}::{}. {}", + module.self_id(), + module.identifier_at(module.function_handle_at(func_def.function).name), + error + ), + })?; + } + Ok(()) +} + +fn verify_function(view: &BinaryIndexedView, fdef: &FunctionDefinition) -> Result<(), String> { + let code = match &fdef.code { + None => return Ok(()), + Some(code) => code, + }; + let function_type_parameters = &view.function_handle_at(fdef.function).type_parameters; + for instr in &code.code { + if let Bytecode::CallGeneric(finst_idx) = instr { + let FunctionInstantiation { + handle, + type_parameters, + } = view.function_instantiation_at(*finst_idx); + + let fhandle = view.function_handle_at(*handle); + let mhandle = view.module_handle_at(fhandle.module); + + let type_arguments = &view.signature_at(*type_parameters).0; + if !is_transfer_module(view, mhandle) { + continue; + } + let fident = view.identifier_at(fhandle.name); + match fident.as_str() { + // transfer functions + "transfer" + | "transfer_to_object_id" + | "freeze_object" + | "share_object" + | "transfer_to_object" + | "transfer_to_object_unsafe" + | "transfer_child_to_object" + | "transfer_child_to_address" => (), + // these functions operate over ChildRef + "is_child" | "is_child_unsafe" | "delete_child_object" => { + continue; + } + // should be unreachable + // these are private and the module itself is skipped + "transfer_internal" | "delete_child_object_internal" => { + debug_assert!(false); + continue; + } + // unknown function, so a bug in the implementation here + s => { + debug_assert!(false, "unknown transfer function {}", s); + continue; + } + }; + for type_arg in type_arguments { + let has_store = view + .abilities(type_arg, function_type_parameters) + .map_err(|vm_err| vm_err.to_string())? + .has_store(); + let is_defined_in_current_module = match type_arg { + SignatureToken::TypeParameter(_) => false, + SignatureToken::Struct(idx) | SignatureToken::StructInstantiation(idx, _) => { + let shandle = view.struct_handle_at(*idx); + view.self_handle_idx() == Some(shandle.module) + } + // do not have key or cannot instantiate generics + // should be caught already by Move's bytecode verifier + SignatureToken::Bool + | SignatureToken::U8 + | SignatureToken::U64 + | SignatureToken::U128 + | SignatureToken::Address + | SignatureToken::Signer + | SignatureToken::Vector(_) + | SignatureToken::Reference(_) + | SignatureToken::MutableReference(_) => { + debug_assert!(false); + false + } + }; + if !has_store && !is_defined_in_current_module { + return Err(format!( + "Invalid call to '{}::Transfer::{}'. \ + Invalid transfer of object of type '{}'. \ + The transferred object's type must be defined in the current module, \ + or must have the 'store' type ability", + SUI_FRAMEWORK_ADDRESS, + fident, + format_signature_token(view, type_arg), + )); + } + } + } + } + Ok(()) +} + +fn is_transfer_module(view: &BinaryIndexedView, mhandle: &ModuleHandle) -> bool { + let maddr = view.address_identifier_at(mhandle.address); + let mident = view.identifier_at(mhandle.name); + maddr == &SUI_FRAMEWORK_ADDRESS && mident.as_str() == "Transfer" +} diff --git a/crates/sui-verifier/src/verifier.rs b/crates/sui-verifier/src/verifier.rs index 51362012f2eb8..374f95b85a033 100644 --- a/crates/sui-verifier/src/verifier.rs +++ b/crates/sui-verifier/src/verifier.rs @@ -8,7 +8,7 @@ use sui_types::error::SuiResult; use crate::{ entry_points_verifier, global_storage_access_verifier, id_immutable_verifier, id_leak_verifier, - struct_with_key_verifier, + private_transfer, struct_with_key_verifier, }; /// Helper for a "canonical" verification of a module. @@ -17,5 +17,6 @@ pub fn verify_module(module: &CompiledModule) -> SuiResult { global_storage_access_verifier::verify_module(module)?; id_immutable_verifier::verify_module(module)?; id_leak_verifier::verify_module(module)?; + private_transfer::verify_module(module)?; entry_points_verifier::verify_module(module) } diff --git a/sui_programmability/examples/nfts/sources/Auction.move b/sui_programmability/examples/nfts/sources/Auction.move index 4254d5c874fe4..7bb2e773ed887 100644 --- a/sui_programmability/examples/nfts/sources/Auction.move +++ b/sui_programmability/examples/nfts/sources/Auction.move @@ -69,7 +69,7 @@ module NFTs::Auction { to_sell: T, id: VersionedID, auctioneer: address, ctx: &mut TxContext ) { let auction = AuctionLib::create_auction(id, to_sell, ctx); - Transfer::transfer(auction, auctioneer); + AuctionLib::transfer(auction, auctioneer); } /// Creates a bid a and send it to the auctioneer along with the diff --git a/sui_programmability/examples/nfts/sources/AuctionLib.move b/sui_programmability/examples/nfts/sources/AuctionLib.move index ed3d03e426498..f4e52c7698817 100644 --- a/sui_programmability/examples/nfts/sources/AuctionLib.move +++ b/sui_programmability/examples/nfts/sources/AuctionLib.move @@ -158,4 +158,21 @@ module NFTs::AuctionLib { fun send_balance(balance: Balance, to: address, ctx: &mut TxContext) { Transfer::transfer(Coin::from_balance(balance, ctx), to) } + + /// exposes Transfer::transfer + public fun transfer(obj: Auction, recipient: address) { + Transfer::transfer(obj, recipient) + } + + /// exposes Transfer::transfer_to_object_id + public fun transfer_to_object_id( + obj: Auction, + owner_id: VersionedID, + ): (VersionedID, Transfer::ChildRef>) { + Transfer::transfer_to_object_id(obj, owner_id) + } + + public fun share_object(obj: Auction) { + Transfer::share_object(obj) + } } diff --git a/sui_programmability/examples/nfts/sources/Geniteam.move b/sui_programmability/examples/nfts/sources/Geniteam.move index 11534d1775dc5..7490ad3f7ef19 100644 --- a/sui_programmability/examples/nfts/sources/Geniteam.move +++ b/sui_programmability/examples/nfts/sources/Geniteam.move @@ -58,7 +58,7 @@ module NFTs::Geniteam { applied_farm_cosmetic_1: Option>, } - struct Monster has key { + struct Monster has key, store { id: VersionedID, monster_name: String, monster_img_index: u64, @@ -79,13 +79,13 @@ module NFTs::Geniteam { } - struct FarmCosmetic has key { + struct FarmCosmetic has key, store{ id: VersionedID, cosmetic_type: u8, display: String, } - struct MonsterCosmetic has key { + struct MonsterCosmetic has key, store { id: VersionedID, cosmetic_type: u8, display: String, @@ -299,7 +299,7 @@ module NFTs::Geniteam { let inventory = Bag::new(ctx); // Transfer ownership of inventory to player. - let (id, child_ref) = Transfer::transfer_to_object_id(inventory, id); + let (id, child_ref) = Bag::transfer_to_object_id(inventory, id); let player = Player { id, @@ -327,7 +327,7 @@ module NFTs::Geniteam { let pet_monsters = Collection::new(ctx); // Transfer ownership of pet monsters to farm. - let (id, child_ref) = Transfer::transfer_to_object_id(pet_monsters, id); + let (id, child_ref) = Collection::transfer_to_object_id(pet_monsters, id); let farm = Farm { diff --git a/sui_programmability/examples/nfts/sources/Marketplace.move b/sui_programmability/examples/nfts/sources/Marketplace.move index a5819bf32dcd0..89c3b7d58c052 100644 --- a/sui_programmability/examples/nfts/sources/Marketplace.move +++ b/sui_programmability/examples/nfts/sources/Marketplace.move @@ -31,7 +31,7 @@ module NFTs::Marketplace { public(script) fun create(ctx: &mut TxContext) { let id = TxContext::new_id(ctx); let objects = Bag::new(ctx); - let (id, objects) = Transfer::transfer_to_object_id(objects, id); + let (id, objects) = Bag::transfer_to_object_id(objects, id); let market_place = Marketplace { id, objects, diff --git a/sui_programmability/examples/nfts/sources/SharedAuction.move b/sui_programmability/examples/nfts/sources/SharedAuction.move index 13cad5cce4d37..faa521b89ab49 100644 --- a/sui_programmability/examples/nfts/sources/SharedAuction.move +++ b/sui_programmability/examples/nfts/sources/SharedAuction.move @@ -28,7 +28,6 @@ module NFTs::SharedAuction { use Sui::Coin::{Self, Coin}; use Sui::SUI::SUI; - use Sui::Transfer; use Sui::TxContext::{Self,TxContext}; use NFTs::AuctionLib::{Self, Auction}; @@ -44,7 +43,7 @@ module NFTs::SharedAuction { /// to be auctioned. public(script) fun create_auction(to_sell: T, ctx: &mut TxContext) { let auction = AuctionLib::create_auction(TxContext::new_id(ctx), to_sell, ctx); - Transfer::share_object(auction); + AuctionLib::share_object(auction); } /// Sends a bid to the auction. The result is either successful