diff --git a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp index 4f675ae8ed3e7..b32b5013808b0 100644 --- a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp +++ b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp @@ -4,25 +4,25 @@ task 1 'publish'. lines 6-21: created: object(103) written: object(102) -task 2 'publish'. lines 23-53: +task 2 'publish'. lines 23-52: created: object(105) written: object(104) -task 3 'publish'. lines 56-84: +task 3 'publish'. lines 55-82: created: object(107) written: object(106) -task 4 'run'. lines 86-86: +task 4 'run'. lines 84-84: created: object(109) written: object(108) -task 5 'run'. lines 88-90: +task 5 'run'. lines 86-88: created: object(111) written: object(109), object(110) -task 6 'run'. lines 91-91: +task 6 'run'. lines 89-89: Error: Transaction Effects Status: Invalid Shared Child Object Usage. When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109), whose ancestor fake(111) is a shared object, and neither are defined in this module.. Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InvalidSharedChildUse(InvalidSharedChildUse { child: fake(109), ancestor: fake(111) }), source: Some("When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109) (defined in module 'T3::O3'), whose ancestor fake(111) is a shared object (defined in module 'T2::O2'), and neither are defined in this module 'T1::O1'") } } -task 7 'run'. lines 93-93: +task 7 'run'. lines 91-91: written: object(109), object(111), object(113) 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 ec11b8fd38fb1..eca93ab9f548a 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 @@ -24,13 +24,12 @@ module T3::O3 { module T2::O2 { use sui::id::VersionedID; - use sui::transfer::{Self, ChildRef}; + use sui::transfer; use sui::tx_context::{Self, TxContext}; use T3::O3::O3; struct O2 has key, store { id: VersionedID, - child: ChildRef, } public entry fun create_shared(child: O3, ctx: &mut TxContext) { @@ -47,8 +46,8 @@ module T2::O2 { fun new(child: O3, ctx: &mut TxContext): O2 { let id = tx_context::new_id(ctx); - let (id, child) = transfer::transfer_to_object_id(child, id); - O2 { id, child } + transfer::transfer_to_object_id(child, &id); + O2 { id } } } @@ -57,14 +56,13 @@ module T2::O2 { module T1::O1 { use sui::id::VersionedID; - use sui::transfer::{Self, ChildRef}; + use sui::transfer; use sui::tx_context::{Self, TxContext}; use T2::O2::O2; use T3::O3::O3; struct O1 has key { id: VersionedID, - child: ChildRef, } public entry fun create_shared(child: O2, ctx: &mut TxContext) { @@ -78,8 +76,8 @@ module T1::O1 { fun new(child: O2, ctx: &mut TxContext): O1 { let id = tx_context::new_id(ctx); - let (id, child) = transfer::transfer_to_object_id(child, id); - O1 { id, child } + transfer::transfer_to_object_id(child, &id); + O1 { id } } } diff --git a/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.exp b/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.exp index 958873c7573b5..e133b78bb7ccf 100644 --- a/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.exp +++ b/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.exp @@ -3,26 +3,26 @@ processed 7 tasks init: A: object(100), B: object(101) -task 1 'publish'. lines 8-28: +task 1 'publish'. lines 8-27: created: object(105) written: object(104) -task 2 'run'. lines 30-30: +task 2 'run'. lines 29-29: created: object(107) written: object(106) -task 3 'run'. lines 32-32: +task 3 'run'. lines 31-31: created: object(109) written: object(107), object(108) -task 4 'view-object'. lines 34-34: +task 4 'view-object'. lines 33-33: Owner: Object ID: ( fake(107) ) Contents: test::m::Child {id: sui::id::VersionedID {id: sui::id::UniqueID {id: sui::id::ID {bytes: fake(109)}}, version: 1u64}} -task 5 'transfer-object'. lines 36-36: +task 5 'transfer-object'. lines 35-35: Error: Transaction Effects Status: Invalid Transfer Object Transaction. Possibly not address-owned or possibly does not have public transfer. Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InvalidTransferObject, source: None } } -task 6 'view-object'. lines 38-38: +task 6 'view-object'. lines 37-37: Owner: Object ID: ( fake(107) ) Contents: test::m::Child {id: sui::id::VersionedID {id: sui::id::UniqueID {id: sui::id::ID {bytes: fake(109)}}, version: 2u64}} diff --git a/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.move b/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.move index 0c31e147d0841..28899db430adc 100644 --- a/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.move +++ b/crates/sui-adapter-transactional-tests/tests/transfer_object/quasi_shared.move @@ -8,22 +8,21 @@ //# publish module test::m { - use sui::transfer::{Self, ChildRef}; + use sui::transfer; use sui::tx_context::{Self, TxContext}; use sui::id::VersionedID; - struct S has key { id: VersionedID, children: vector> } + struct S has key { id: VersionedID } struct Child has key { id: VersionedID } public entry fun mint_s(ctx: &mut TxContext) { let id = tx_context::new_id(ctx); - transfer::share_object(S { id, children: vector[] }) + transfer::share_object(S { id }) } public entry fun mint_child(s: &mut S, ctx: &mut TxContext) { let id = tx_context::new_id(ctx); - let child = transfer::transfer_to_object(Child { id }, s); - std::vector::push_back(&mut s.children, child) + transfer::transfer_to_object(Child { id }, s); } } diff --git a/crates/sui-adapter/src/adapter.rs b/crates/sui-adapter/src/adapter.rs index 41d1d7661aafc..22680d9c16e6a 100644 --- a/crates/sui-adapter/src/adapter.rs +++ b/crates/sui-adapter/src/adapter.rs @@ -484,15 +484,6 @@ fn process_successful_execution< // but only to be deleted. if !newly_generated_ids.contains(obj_id) { match by_value_objects.remove(id.object_id()) { - Some((Owner::ObjectOwner { .. }, _)) => { - // If an object is owned by another object, we are not allowed to directly delete the child - // object because this could lead to a dangling reference of the ownership. Such - // dangling reference can never be dropped. To delete this object, one must either first transfer - // the child object to an account address, or call through transfer::delete_child_object(), - // which would consume both the child object and the ChildRef ownership reference, - // and emit the DeleteChildObject event. These child objects can be safely deleted. - return Err(ExecutionErrorKind::DeleteObjectOwnedObject.into()); - } Some(_) => { state_view.log_event(Event::delete_object( module_id.address(), @@ -526,15 +517,6 @@ fn process_successful_execution< } Ok(()) } - EventType::DeleteChildObject => { - let id_bytes: AccountAddress = bcs::from_bytes(&event_bytes).unwrap(); - let obj_id: ObjectID = id_bytes.into(); - // unwrap safe since to delete a child object, this child object - // must be passed by value in the input. - let (_owner, version) = by_value_objects.remove(&obj_id).unwrap(); - state_view.delete_object(&obj_id, version, DeleteKind::Normal); - Ok(()) - } EventType::User => { match type_ { TypeTag::Struct(s) => state_view.log_event(Event::move_event( diff --git a/crates/sui-core/src/unit_tests/data/object_owner/sources/object_owner.move b/crates/sui-core/src/unit_tests/data/object_owner/sources/object_owner.move index 011f0099728e3..62aab0a2eec88 100644 --- a/crates/sui-core/src/unit_tests/data/object_owner/sources/object_owner.move +++ b/crates/sui-core/src/unit_tests/data/object_owner/sources/object_owner.move @@ -3,13 +3,13 @@ module object_owner::object_owner { use std::option::{Self, Option}; - use sui::id::{Self, VersionedID}; - use sui::transfer::{Self, ChildRef}; + use sui::id::{Self, ID, VersionedID}; + use sui::transfer; use sui::tx_context::{Self, TxContext}; struct Parent has key { id: VersionedID, - child: Option>, + child: Option, } struct Child has key { @@ -18,7 +18,7 @@ module object_owner::object_owner { struct AnotherParent has key { id: VersionedID, - child: ChildRef, + child: ID, } public entry fun create_child(ctx: &mut TxContext) { @@ -39,17 +39,19 @@ module object_owner::object_owner { public entry fun create_parent_and_child(ctx: &mut TxContext) { let parent_id = tx_context::new_id(ctx); let child = Child { id: tx_context::new_id(ctx) }; - let (parent_id, child_ref) = transfer::transfer_to_object_id(child, parent_id); + let child_id = *id::id(&child); + transfer::transfer_to_object_id(child, &parent_id); let parent = Parent { id: parent_id, - child: option::some(child_ref), + child: option::some(child_id), }; transfer::transfer(parent, tx_context::sender(ctx)); } public entry fun add_child(parent: &mut Parent, child: Child) { - let child_ref = transfer::transfer_to_object(child, parent); - option::fill(&mut parent.child, child_ref); + let child_id = *id::id(&child); + transfer::transfer_to_object(child, parent); + option::fill(&mut parent.child, child_id); } // Call to mutate_child will fail if its owned by a parent, @@ -60,14 +62,16 @@ module object_owner::object_owner { public entry fun mutate_child_with_parent(_child: &mut Child, _parent: &mut Parent) {} public entry fun transfer_child(parent: &mut Parent, child: Child, new_parent: &mut Parent) { - let child_ref = option::extract(&mut parent.child); - let new_child_ref = transfer::transfer_child_to_object(child, child_ref, new_parent); - option::fill(&mut new_parent.child, new_child_ref); + let child_id = option::extract(&mut parent.child); + assert!(id::id(&child) == &child_id, 0); + transfer::transfer_to_object(child, new_parent); + option::fill(&mut new_parent.child, child_id); } public entry fun remove_child(parent: &mut Parent, child: Child, ctx: &mut TxContext) { - let child_ref = option::extract(&mut parent.child); - transfer::transfer_child_to_address(child, child_ref, tx_context::sender(ctx)); + let child_id = option::extract(&mut parent.child); + assert!(id::id(&child) == &child_id, 0); + transfer::transfer(child, tx_context::sender(ctx)); } // Call to delete_child can fail if it's still owned by a parent. @@ -78,20 +82,21 @@ module object_owner::object_owner { public entry fun delete_parent_and_child(parent: Parent, child: Child) { let Parent { id: parent_id, child: child_ref_opt } = parent; - let child_ref = option::extract(&mut child_ref_opt); - option::destroy_none(child_ref_opt); + let child_id = option::extract(&mut child_ref_opt); + assert!(id::id(&child) == &child_id, 0); id::delete(parent_id); let Child { id: child_id } = child; - transfer::delete_child_object(child_id, child_ref); + id::delete(child_id); } public entry fun create_another_parent(child: Child, ctx: &mut TxContext) { let id = tx_context::new_id(ctx); - let (id, child_ref) = transfer::transfer_to_object_id(child, id); + let child_id = *id::id(&child); + transfer::transfer_to_object_id(child, &id); let parent = AnotherParent { id, - child: child_ref, + child: child_id, }; transfer::transfer(parent, tx_context::sender(ctx)); } diff --git a/crates/sui-core/src/unit_tests/move_integration_tests.rs b/crates/sui-core/src/unit_tests/move_integration_tests.rs index 4f872cfdda7e9..666793da304e6 100644 --- a/crates/sui-core/src/unit_tests/move_integration_tests.rs +++ b/crates/sui-core/src/unit_tests/move_integration_tests.rs @@ -399,57 +399,7 @@ async fn test_object_owning_another_object() { .await .unwrap(); // we expect this to be and error due to Deleting an Object Owned Object - assert!(matches!( - effects.status.unwrap_err(), - ExecutionFailureStatus::DeleteObjectOwnedObject, - )); - - // Remove the child from the parent. - let effects = call_move( - &authority, - &gas, - &sender, - &sender_key, - &package, - "object_owner", - "remove_child", - vec![], - vec![ - TestCallArg::Object(new_parent.0), - TestCallArg::Object(child.0), - ], - ) - .await - .unwrap(); assert!(effects.status.is_ok()); - // Removing child from parent results in a transfer event - assert_eq!(effects.events.len(), 1); - let event1 = &effects.events[0]; - assert_eq!(event1.event_type(), EventType::TransferObject); - assert_eq!(event1.object_id(), Some(child.0)); - - // Delete the child again. This time it will succeed because it's no longer owned by a parent. - let effects = call_move( - &authority, - &gas, - &sender, - &sender_key, - &package, - "object_owner", - "delete_child", - vec![], - vec![ - TestCallArg::Object(child.0), - TestCallArg::Object(new_parent.0), - ], - ) - .await - .unwrap(); - assert!(effects.status.is_ok()); - assert_eq!(effects.events.len(), 1); - let event1 = &effects.events[0]; - assert_eq!(event1.event_type(), EventType::DeleteObject); - assert_eq!(event1.object_id(), Some(child.0)); // Create a parent and a child together. This tests the // transfer::transfer_to_object_id() API. diff --git a/crates/sui-core/tests/staged/sui.yaml b/crates/sui-core/tests/staged/sui.yaml index 35e36d8903a0a..faaf2bc8c11f8 100644 --- a/crates/sui-core/tests/staged/sui.yaml +++ b/crates/sui-core/tests/staged/sui.yaml @@ -130,25 +130,23 @@ ExecutionFailureStatus: NEWTYPE: TYPENAME: InvalidSharedByValue 16: - DeleteObjectOwnedObject: UNIT - 17: PublishErrorEmptyPackage: UNIT - 18: + 17: PublishErrorNonZeroAddress: UNIT - 19: + 18: PublishErrorDuplicateModule: UNIT - 20: + 19: SuiMoveVerificationError: UNIT - 21: + 20: MovePrimitiveRuntimeError: UNIT - 22: + 21: MoveAbort: TUPLE: - TYPENAME: ModuleId - U64 - 23: + 22: VMVerificationOrDeserializationError: UNIT - 24: + 23: VMInvariantViolation: UNIT ExecutionStatus: ENUM: diff --git a/crates/sui-framework/sources/bag.move b/crates/sui-framework/sources/bag.move index d47239cf09705..9db035d02f005 100644 --- a/crates/sui-framework/sources/bag.move +++ b/crates/sui-framework/sources/bag.move @@ -10,13 +10,11 @@ /// Bag is different from the Collection type in that Collection /// only supports owning objects of the same type. module sui::bag { - use std::errors; - use std::option::{Self, Option}; use sui::id::{Self, ID, VersionedID}; - use sui::transfer::{Self, ChildRef}; + use sui::transfer; + use sui::typed_id::{Self, TypedID}; use sui::tx_context::{Self, TxContext}; - use sui::vec_set::VecSet; - use sui::vec_set; + use sui::vec_set::{Self, VecSet}; // Error codes /// Adding the same object to the collection twice is not allowed. @@ -39,6 +37,11 @@ module sui::bag { max_capacity: u64, } + struct Item has key { + id: VersionedID, + value: T, + } + /// Create a new Bag and return it. public fun new(ctx: &mut TxContext): Bag { new_with_max_capacity(ctx, DEFAULT_MAX_CAPACITY) @@ -46,10 +49,7 @@ module sui::bag { /// Create a new Bag with custom size limit and return it. public fun new_with_max_capacity(ctx: &mut TxContext, max_capacity: u64): Bag { - assert!( - max_capacity <= DEFAULT_MAX_CAPACITY && max_capacity > 0 , - errors::limit_exceeded(EInvalidMaxCapacity) - ); + assert!(max_capacity <= DEFAULT_MAX_CAPACITY && max_capacity > 0, EInvalidMaxCapacity); Bag { id: tx_context::new_id(ctx), objects: vec_set::empty(), @@ -67,37 +67,17 @@ module sui::bag { vec_set::size(&c.objects) } - /// Add an object to the 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>) { - assert!( - size(c) + 1 <= c.max_capacity, - errors::limit_exceeded(EMaxCapacityExceeded) - ); - let id = id::id(&object); - if (contains(c, id)) { - abort EObjectDoubleAdd - }; - vec_set::insert(&mut c.objects, *id); - transfer::transfer_to_object_unsafe(object, old_child_ref, c); - } - /// Add a new object to the Bag. - /// Abort if the object is already in the Bag. - 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) { - add_impl(c, object, option::some(old_child_ref)) + public fun add(c: &mut Bag, value: T, ctx: &mut TxContext): TypedID> { + assert!(size(c) + 1 <= c.max_capacity, EMaxCapacityExceeded); + let id = tx_context::new_id(ctx); + vec_set::insert(&mut c.objects, *id::inner(&id)); + let item = Item { id, value }; + let item_id = typed_id::new(&item); + transfer::transfer_to_object(item, c); + item_id } - /// Check whether the Bag contains a specific object, /// identified by the object id in bytes. public fun contains(c: &Bag, id: &ID): bool { vec_set::contains(&c.objects, id) @@ -105,14 +85,20 @@ 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 { - vec_set::remove(&mut c.objects, id::id(&object)); - object + public fun remove(c: &mut Bag, item: Item): T { + let Item { id, value } = item; + vec_set::remove(&mut c.objects, id::inner(&id)); + id::delete(id); + value } /// Remove the object from the Bag, and then transfer it to the signer. - public entry fun remove_and_take(c: &mut Bag, object: T, ctx: &mut TxContext) { - let object = remove(c, object); + public entry fun remove_and_take( + c: &mut Bag, + item: Item, + ctx: &mut TxContext, + ) { + let object = remove(c, item); transfer::transfer(object, tx_context::sender(ctx)); } @@ -123,8 +109,8 @@ module sui::bag { public fun transfer_to_object_id( obj: Bag, - owner_id: VersionedID, - ): (VersionedID, ChildRef) { + owner_id: &VersionedID, + ) { transfer::transfer_to_object_id(obj, owner_id) } } diff --git a/crates/sui-framework/sources/collection.move b/crates/sui-framework/sources/collection.move index 48878bb4e7b19..27d6f4588caeb 100644 --- a/crates/sui-framework/sources/collection.move +++ b/crates/sui-framework/sources/collection.move @@ -12,137 +12,105 @@ /// access and operate on each individual object. /// In contrast to `Bag`, `Collection` requires all objects have the same type. module sui::collection { - use std::errors; - use std::option::{Self, Option}; - use std::vector::Self; use sui::id::{Self, ID, VersionedID}; - use sui::transfer::{Self, ChildRef}; + use sui::transfer; + use sui::typed_id::{Self, TypedID}; use sui::tx_context::{Self, TxContext}; + use sui::vec_set::{Self, VecSet}; // Error codes - /// When removing an object from the collection, EObjectNotFound - /// will be triggered if the object is not owned by the collection. - const EObjectNotFound: u64 = 0; - - /// Adding the same object to the collection twice is not allowed. - const EObjectDoubleAdd: u64 = 1; /// The max capacity set for the collection cannot exceed the hard limit /// which is DEFAULT_MAX_CAPACITY. - const EInvalidMaxCapacity: u64 = 2; + const EInvalidMaxCapacity: u64 = 0; /// Trying to add object to the collection when the collection is /// already at its maximum capacity. - const EMaxCapacityExceeded: u64 = 3; + const EMaxCapacityExceeded: u64 = 1; // TODO: this is a placeholder number // We want to limit the capacity of collection because it requires O(N) // for search and removals. We could relax the capacity constraint once // we could use more efficient data structure such as set. - const DEFAULT_MAX_CAPACITY: u64 = 65536; + const DEFAULT_MAX_CAPACITY: u64 = 0x10000; - struct Collection has key { + struct Collection has key { id: VersionedID, - objects: vector>, + objects: VecSet, max_capacity: u64, } + struct Item has key { + id: VersionedID, + value: T, + } + /// 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( + 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) - ); + assert!(max_capacity <= DEFAULT_MAX_CAPACITY && max_capacity > 0, EInvalidMaxCapacity); Collection { id: tx_context::new_id(ctx), - objects: vector::empty(), + objects: vec_set::empty(), max_capacity, } } /// Create a new Collection and transfer it to the signer. - public entry fun create(ctx: &mut TxContext) { + public entry fun create(ctx: &mut TxContext) { transfer::transfer(new(ctx), tx_context::sender(ctx)) } /// Returns the size of the collection. - public fun size(c: &Collection): u64 { - vector::length(&c.objects) + public fun size(c: &Collection): u64 { + vec_set::size(&c.objects) } /// Add an object to the 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( + public fun add( c: &mut Collection, - object: T, - old_child_ref: Option>, - ) { - assert!( - size(c) + 1 <= c.max_capacity, - errors::limit_exceeded(EMaxCapacityExceeded) - ); - let id = id::id(&object); - assert!(!contains(c, id), EObjectDoubleAdd); - let child_ref = if (option::is_none(&old_child_ref)) { - transfer::transfer_to_object(object, c) - } else { - let old_child_ref = option::extract(&mut old_child_ref); - transfer::transfer_child_to_object(object, old_child_ref, c) - }; - vector::push_back(&mut c.objects, child_ref); - option::destroy_none(old_child_ref); - } - - /// Add an object to the collection. - /// Abort if the object is already in the collection. - 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, - ) { - add_impl(c, object, option::some(old_child_ref)) + value: T, + ctx: &mut TxContext, + ): TypedID> { + assert!(size(c) + 1 <= c.max_capacity, EMaxCapacityExceeded); + let id = tx_context::new_id(ctx); + vec_set::insert(&mut c.objects, *id::inner(&id)); + let item = Item { id, value }; + let item_id = typed_id::new(&item); + transfer::transfer_to_object(item, c); + item_id } /// Check whether the collection contains a specific object, /// identified by the object id in bytes. - public fun contains(c: &Collection, id: &ID): bool { - option::is_some(&find(c, id)) + public fun contains(c: &Collection, id: &ID): bool { + vec_set::contains(&c.objects, 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) { - let idx = find(c, id::id(&object)); - assert!(option::is_some(&idx), EObjectNotFound); - let child_ref = vector::remove(&mut c.objects, *option::borrow(&idx)); - (object, child_ref) + public fun remove(c: &mut Collection, item: Item): T { + let Item { id, value } = item; + vec_set::remove(&mut c.objects, id::inner(&id)); + id::delete(id); + value } /// Remove the object from the collection, and then transfer it to the signer. public entry fun remove_and_take( c: &mut Collection, - object: T, + item: Item, ctx: &mut TxContext, ) { - let (object, child_ref) = remove(c, object); - transfer::transfer_child_to_address(object, child_ref, tx_context::sender(ctx)); + let object = remove(c, item); + transfer::transfer(object, tx_context::sender(ctx)); } /// Transfer the entire collection to `recipient`. @@ -152,23 +120,9 @@ module sui::collection { public fun transfer_to_object_id( obj: Collection, - owner_id: VersionedID, - ): (VersionedID, ChildRef>) { + owner_id: &VersionedID, + ) { 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 { - let i = 0; - let len = size(c); - while (i < len) { - let child_ref = vector::borrow(&c.objects, i); - if (transfer::is_child_unsafe(child_ref, id)) { - return option::some(i) - }; - i = i + 1; - }; - option::none() - } } diff --git a/crates/sui-framework/sources/id.move b/crates/sui-framework/sources/id.move index 755b3af6fc925..aec66f54ed12e 100644 --- a/crates/sui-framework/sources/id.move +++ b/crates/sui-framework/sources/id.move @@ -37,7 +37,7 @@ module sui::id { /// An object ID. Unlike `UniqueID`, this is *not* guaranteed to be globally /// unique--anyone can create an `ID`, and ID's can be freely copied and dropped /// Useful for comparing with `UniqueID`'s. - struct ID has store, drop, copy { + struct ID has copy, drop, store { // We use `address` instead of `vector` here because `address` has a more // compact serialization. `address` is serialized as a BCS fixed-length sequence, // which saves us the length prefix we would pay for if this were `vector`. diff --git a/crates/sui-framework/sources/transfer.move b/crates/sui-framework/sources/transfer.move index 4898243370839..e97be977689c3 100644 --- a/crates/sui-framework/sources/transfer.move +++ b/crates/sui-framework/sources/transfer.move @@ -2,55 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 module sui::transfer { - use std::option::{Self, Option}; - use sui::id::{Self, ID, VersionedID}; - - // To allow access to transfer_to_object_unsafe. - friend sui::bag; - // To allow access to is_child_unsafe. - friend sui::collection; - - // When transferring a child object, this error is thrown if the child object - // doesn't match the ChildRef that represents the ownership. - const EChildIDMismatch: u64 = 0; - - /// Represents a reference to a child object, whose type is T. - /// This is used to track ownership between objects. - /// Whenever an object is transferred to another object (and hence owned by object), - /// a ChildRef is created. A ChildRef cannot be dropped. When a child object is - /// transferred to a new parent object, the original ChildRef is dropped but a new - /// one will be created. The only way to fully destroy a ChildRef is to transfer the - /// object to an account address. Because of this, an object cannot be deleted when - /// it's still owned by another object. - struct ChildRef has store { - child_id: ID, - } - - /// Check whether the `child` object is actually the child object - /// owned by the parent through the given `child_ref`. - public fun is_child(child_ref: &ChildRef, child: &T): bool { - &child_ref.child_id == id::id(child) - } - - /// Check whether the `child_ref`'s child_id is `id`. - /// This is less safe compared to `is_child` because we won't be able to check - /// whether the type of the child object is the same as what `ChildRef` represents. - /// We should always call `is_child` whenever we can. - /// This is currently only exposed to friend classes. If there turns out to be - /// general needs, we can open it up. - public(friend) fun is_child_unsafe(child_ref: &ChildRef, id: &ID): bool { - &child_ref.child_id == id - } - - /// Transfers are implemented by emitting a - /// special `TransferEvent` that the sui adapter - /// interprets differently than user events. - struct TransferEvent { - /// The object to be transferred - obj: T, - /// Address the object will be transferred to. - recipient: address, - } + use sui::id::{Self, VersionedID}; /// Transfer ownership of `obj` to `recipient`. `obj` must have the /// `key` attribute, which (in turn) ensures that `obj` has a globally @@ -61,74 +13,22 @@ module sui::transfer { } /// Transfer ownership of `obj` to another object `owner`. - /// Returns a non-droppable struct ChildRef that represents the ownership. - public fun transfer_to_object(obj: T, owner: &mut R): ChildRef { - let obj_id = *id::id(&obj); + public fun transfer_to_object(obj: T, owner: &mut R) { let owner_id = id::id_address(id::id(owner)); transfer_internal(obj, owner_id, true); - ChildRef { child_id: obj_id } } /// Similar to transfer_to_object where we want to transfer an object to another object. /// However, in the case when we haven't yet created the parent object (typically during /// parent object construction), and all we have is just a parent object ID, we could /// use this function to transfer an object to the parent object identified by its id. + /// Additionally, this API is useful for transfering to objects, outside of that object's + /// module. The object's module can expose a function that returns a reference to the object's + /// verssioned ID, `&VersionedID`. Which can then be used with this function. /// The child object is specified in `obj`, and the parent object id is specified in `owner_id`. - /// The function consumes `owner_id` to make sure that the caller actually owns the id. - /// The `owner_id` will be returned (so that it can be used to continue creating the parent object), - /// along returned is the ChildRef as a reference to the ownership. - public fun transfer_to_object_id(obj: T, owner_id: VersionedID): (VersionedID, ChildRef) { - let obj_id = *id::id(&obj); - let inner_owner_id = *id::inner(&owner_id); + public fun transfer_to_object_id(obj: T, owner_id: &VersionedID) { + let inner_owner_id = *id::inner(owner_id); transfer_internal(obj, id::id_address(&inner_owner_id), true); - let child_ref = ChildRef { child_id: obj_id }; - (owner_id, child_ref) - } - - /// Similar to transfer_to_object, to transfer an object to another object. - /// However it does not return the ChildRef. This can be unsafe to use since there is - /// no longer guarantee that the ID stored in the parent actually represent ownership. - /// If the object was owned by another object, an `old_child_ref` would be around - /// and need to be consumed as well. - public(friend) fun transfer_to_object_unsafe( - obj: T, - old_child_ref: Option>, - owner: &mut R, - ) { - let ChildRef { child_id: _ } = if (option::is_none(&old_child_ref)) { - transfer_to_object(obj, owner) - } else { - let child_ref = option::extract(&mut old_child_ref); - transfer_child_to_object(obj, child_ref, owner) - }; - option::destroy_none(old_child_ref); - } - - /// Transfer a child object to new owner. This is one of the two ways that can - /// consume a ChildRef. It will return a ChildRef that represents the new ownership. - public fun transfer_child_to_object(child: T, child_ref: ChildRef, owner: &mut R): ChildRef { - let ChildRef { child_id } = child_ref; - assert!(&child_id == id::id(&child), EChildIDMismatch); - transfer_to_object(child, owner) - } - - /// Transfer a child object to an account address. This is one of the two ways that can - /// consume a ChildRef. No new ChildRef will be created, as the object is no longer - /// owned by an object. - public fun transfer_child_to_address(child: T, child_ref: ChildRef, recipient: address) { - let ChildRef { child_id } = child_ref; - assert!(&child_id == id::id(&child), EChildIDMismatch); - transfer(child, recipient) - } - - /// Delete `child_ref`, which must point at `child_id`. - /// This is the second way to consume a `ChildRef`. - /// Passing ownership of `child_id` to this function implies that the child object - /// has been unpacked, so it is now safe to delete `child_ref`. - public fun delete_child_object(child_id: VersionedID, child_ref: ChildRef) { - let ChildRef { child_id: child_ref_id } = child_ref; - assert!(&child_ref_id == id::inner(&child_id), EChildIDMismatch); - delete_child_object_internal(id::id_address(&child_ref_id), child_id) } /// Freeze `obj`. After freezing `obj` becomes immutable and can no @@ -148,9 +48,6 @@ module sui::transfer { native fun transfer_internal(obj: T, recipient: address, to_object: bool); - // delete `child_id`, emit a system `DeleteChildObject(child)` event - native fun delete_child_object_internal(child: address, child_id: VersionedID); - // Cost calibration functions #[test_only] public fun calibrate_freeze_object(obj: T) { @@ -176,14 +73,4 @@ module sui::transfer { public fun calibrate_transfer_internal_nop(_obj: T, _recipient: address, _to_object: bool) { } - #[test_only] - public fun calibrate_delete_child_object_internal(child: address, child_id: VersionedID) { - delete_child_object_internal(child, child_id) - } - - // TBD - // #[test_only] - // public fun calibrate_delete_child_object_internal_nop(_child: address, _child_id: VersionedID) { - // } - } diff --git a/crates/sui-framework/sources/tx_context.move b/crates/sui-framework/sources/tx_context.move index b84bbbcb84c48..389fb96df0e79 100644 --- a/crates/sui-framework/sources/tx_context.move +++ b/crates/sui-framework/sources/tx_context.move @@ -5,8 +5,6 @@ module sui::tx_context { use std::signer; use sui::id::{Self, VersionedID}; - #[test_only] - use std::errors; #[test_only] use std::vector; #[test_only] @@ -74,10 +72,7 @@ module sui::tx_context { #[test_only] /// Create a `TxContext` for testing public fun new(addr: address, tx_hash: vector, epoch: u64, ids_created: u64): TxContext { - assert!( - vector::length(&tx_hash) == TX_HASH_LENGTH, - errors::invalid_argument(EBadTxHashLength) - ); + assert!(vector::length(&tx_hash) == TX_HASH_LENGTH, EBadTxHashLength); TxContext { signer: new_signer_from_address(addr), tx_hash, epoch, ids_created } } diff --git a/crates/sui-framework/sources/typed_id.move b/crates/sui-framework/sources/typed_id.move new file mode 100644 index 0000000000000..f58f3e2afd6ab --- /dev/null +++ b/crates/sui-framework/sources/typed_id.move @@ -0,0 +1,47 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Typed wrappers around Sui object IDs +/// While not always necessary, this is helpful for indicating the type of an object, particularly +/// when storing its ID in another object. +/// Additionally, it can be helpful for disambiguating between different IDs in an object. +/// For example +/// ``` +/// struct MyObject has key { +/// id: VersionedID, +/// child1: TypedID, +/// child2: TypedID, +/// } +/// ``` +/// We then know that `child1` is an ID for an object of type `A` and that `child2` is an `ID` +/// of an object of type `B` +module sui::typed_id { + use sui::id::{Self, ID}; + + /// An ID of an of type `T`. See `ID` for more details + /// By construction, it is guaranteed that the `ID` represents an object of type `T` + struct TypedID has copy, drop, store { + id: ID, + } + + /// Get the underlying `ID` of `obj`, and remember the type + public fun new(obj: &T): TypedID { + TypedID { id: *id::id(obj) } + } + + /// Borrow the inner `ID` of `typed_id` + public fun as_id(typed_id: &TypedID): &ID { + &typed_id.id + } + + /// Get the inner `ID` of `typed_id` + public fun to_id(typed_id: TypedID): ID { + let TypedID { id } = typed_id; + id + } + + /// Check that underlying `ID` in the `typed_id` equals the objects ID + public fun equals_object(typed_id: &TypedID, obj: &T): bool { + &typed_id.id == id::id(obj) + } +} diff --git a/crates/sui-framework/src/lib.rs b/crates/sui-framework/src/lib.rs index c037d3c2cca53..7e748a57948b7 100644 --- a/crates/sui-framework/src/lib.rs +++ b/crates/sui-framework/src/lib.rs @@ -67,8 +67,6 @@ pub enum EventType { /// deleted, the object ID must be deleted and this event will be /// emitted. DeleteObjectID, - /// System event: a child object is deleted along with a child ref. - DeleteChildObject, /// User-defined event User, } diff --git a/crates/sui-framework/src/natives/mod.rs b/crates/sui-framework/src/natives/mod.rs index 3ab6ac6a3fb7b..21b1d1011d1b1 100644 --- a/crates/sui-framework/src/natives/mod.rs +++ b/crates/sui-framework/src/natives/mod.rs @@ -52,11 +52,6 @@ pub fn all_natives( "update_object", test_scenario::update_object, ), - ( - "transfer", - "delete_child_object_internal", - transfer::delete_child_object_internal, - ), ("transfer", "transfer_internal", transfer::transfer_internal), ("transfer", "freeze_object", transfer::freeze_object), ("transfer", "share_object", transfer::share_object), diff --git a/crates/sui-framework/src/natives/test_scenario.rs b/crates/sui-framework/src/natives/test_scenario.rs index 365e3f8678ddd..78ac2602542fa 100644 --- a/crates/sui-framework/src/natives/test_scenario.rs +++ b/crates/sui-framework/src/natives/test_scenario.rs @@ -65,7 +65,6 @@ fn get_object_id_from_event(event_type_byte: u64, val: &Value) -> Option val, EventType::DeleteObjectID => get_nested_struct_field(val, &[0, 0, 0]).unwrap(), EventType::User => { return None; @@ -152,7 +151,7 @@ fn get_global_inventory(events: &[Event]) -> Result { }, ); } - EventType::DeleteObjectID | EventType::DeleteChildObject => { + EventType::DeleteObjectID => { // note: obj_id may or may not be present in `inventory`--a useer can create an ID and delete it without associating it with a transferred object inventory.remove(&obj_id); } diff --git a/crates/sui-framework/src/natives/transfer.rs b/crates/sui-framework/src/natives/transfer.rs index 2856b7910ea14..a31d689926176 100644 --- a/crates/sui-framework/src/natives/transfer.rs +++ b/crates/sui-framework/src/natives/transfer.rs @@ -92,26 +92,3 @@ pub fn share_object( Ok(NativeResult::err(cost, 0)) } } - -/// Implementation of Move native function -/// `delete_child_object_internal(child: T)` -pub fn delete_child_object_internal( - context: &mut NativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> PartialVMResult { - debug_assert!(ty_args.is_empty()); - // first args is an object ID that we will emit in a DeleteChildObject event - // second arg is VersionedID that we want to ignore - debug_assert!(args.len() == 2); - - let obj_id = args.pop_front().unwrap(); - let event_type = EventType::DeleteChildObject; - // TODO: Decide the cost. - let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 1); - if context.save_event(vec![], event_type as u64, Type::Address, obj_id)? { - Ok(NativeResult::ok(cost, smallvec![])) - } else { - Ok(NativeResult::err(cost, 0)) - } -} diff --git a/crates/sui-framework/tests/bag_tests.move b/crates/sui-framework/tests/bag_tests.move index 12c40ebc137ca..3cf2cffe29084 100644 --- a/crates/sui-framework/tests/bag_tests.move +++ b/crates/sui-framework/tests/bag_tests.move @@ -4,8 +4,9 @@ #[test_only] module sui::bag_tests { use sui::bag::{Self, Bag}; - use sui::id::{Self, VersionedID}; + use sui::id::VersionedID; use sui::test_scenario; + use sui::typed_id; use sui::tx_context; const EBAG_SIZE_MISMATCH: u64 = 0; @@ -37,16 +38,14 @@ module sui::bag_tests { assert!(bag::size(&bag) == 0, EBAG_SIZE_MISMATCH); let obj1 = Object1 { id: tx_context::new_id(test_scenario::ctx(scenario)) }; - let id1 = *id::id(&obj1); let obj2 = Object2 { id: tx_context::new_id(test_scenario::ctx(scenario)) }; - let id2 = *id::id(&obj2); - bag::add(&mut bag, obj1); - bag::add(&mut bag, obj2); + let item_id1 = bag::add(&mut bag, obj1, test_scenario::ctx(scenario)); + let item_id2 = bag::add(&mut bag, obj2, test_scenario::ctx(scenario)); assert!(bag::size(&bag) == 2, EBAG_SIZE_MISMATCH); - assert!(bag::contains(&bag, &id1), EOBJECT_NOT_FOUND); - assert!(bag::contains(&bag, &id2), EOBJECT_NOT_FOUND); + assert!(bag::contains(&bag, typed_id::as_id(&item_id1)), EOBJECT_NOT_FOUND); + assert!(bag::contains(&bag, typed_id::as_id(&item_id2)), EOBJECT_NOT_FOUND); test_scenario::return_owned(scenario, bag); }; @@ -54,7 +53,7 @@ module sui::bag_tests { } #[test] - #[expected_failure(abort_code = 264)] + #[expected_failure(abort_code = 1)] fun test_init_with_invalid_max_capacity() { let ctx = tx_context::dummy(); // Sui::bag::DEFAULT_MAX_CAPACITY is not readable outside the module @@ -64,7 +63,7 @@ module sui::bag_tests { } #[test] - #[expected_failure(abort_code = 264)] + #[expected_failure(abort_code = 1)] fun test_init_with_zero() { let ctx = tx_context::dummy(); let bag = bag::new_with_max_capacity(&mut ctx, 0); @@ -72,15 +71,15 @@ module sui::bag_tests { } #[test] - #[expected_failure(abort_code = 520)] + #[expected_failure(abort_code = 2)] fun test_exceed_max_capacity() { let ctx = tx_context::dummy(); let bag = bag::new_with_max_capacity(&mut ctx, 1); let obj1 = Object1 { id: tx_context::new_id(&mut ctx) }; - bag::add(&mut bag, obj1); + bag::add(&mut bag, obj1, &mut ctx); let obj2 = Object2 { id: tx_context::new_id(&mut ctx) }; - bag::add(&mut bag, obj2); + bag::add(&mut bag, obj2, &mut ctx); bag::transfer(bag, tx_context::sender(&ctx)); } } diff --git a/crates/sui-framework/tests/collection_tests.move b/crates/sui-framework/tests/collection_tests.move index e87502fdbc395..dde2e37be1864 100644 --- a/crates/sui-framework/tests/collection_tests.move +++ b/crates/sui-framework/tests/collection_tests.move @@ -5,8 +5,9 @@ module sui::collection_tests { use sui::bag::{Self, Bag}; use sui::collection::{Self, Collection}; - use sui::id::{Self, VersionedID}; + use sui::id::VersionedID; use sui::test_scenario; + use sui::typed_id; use sui::tx_context; struct Object has key, store { @@ -31,16 +32,14 @@ module sui::collection_tests { assert!(collection::size(&collection) == 0, 0); let obj1 = Object { id: tx_context::new_id(test_scenario::ctx(scenario)) }; - let id1 = *id::id(&obj1); let obj2 = Object { id: tx_context::new_id(test_scenario::ctx(scenario)) }; - let id2 = *id::id(&obj2); - collection::add(&mut collection, obj1); - collection::add(&mut collection, obj2); + let item_id1 = collection::add(&mut collection, obj1, test_scenario::ctx(scenario)); + let item_id2 = collection::add(&mut collection, obj2, test_scenario::ctx(scenario)); assert!(collection::size(&collection) == 2, 0); - assert!(collection::contains(&collection, &id1), 0); - assert!(collection::contains(&collection, &id2), 0); + assert!(collection::contains(&collection, typed_id::as_id(&item_id1)), 0); + assert!(collection::contains(&collection, typed_id::as_id(&item_id2)), 0); test_scenario::return_owned(scenario, collection); }; @@ -63,7 +62,7 @@ module sui::collection_tests { { let collection = test_scenario::take_owned>(scenario); let obj = Object { id: tx_context::new_id(test_scenario::ctx(scenario)) }; - collection::add(&mut collection, obj); + collection::add(&mut collection, obj, test_scenario::ctx(scenario)); test_scenario::return_owned(scenario, collection); }; @@ -72,15 +71,14 @@ module sui::collection_tests { { let collection = test_scenario::take_owned>(scenario); let bag = test_scenario::take_owned(scenario); - let obj = test_scenario::take_child_object, Object>(scenario, &collection); - let id = *id::id(&obj); + let obj = test_scenario::take_child_object, collection::Item>(scenario, &collection); - let (obj, child_ref) = collection::remove(&mut collection, obj); - bag::add_child_object(&mut bag, obj, child_ref); + let obj = collection::remove(&mut collection, obj); + let item_id = bag::add(&mut bag, obj, test_scenario::ctx(scenario)); assert!(collection::size(&collection) == 0, 0); assert!(bag::size(&bag) == 1, 0); - assert!(bag::contains(&bag, &id), 0); + assert!(bag::contains(&bag, typed_id::as_id(&item_id)), 0); test_scenario::return_owned(scenario, collection); test_scenario::return_owned(scenario, bag); @@ -91,15 +89,14 @@ module sui::collection_tests { { let collection = test_scenario::take_owned>(scenario); let bag = test_scenario::take_owned(scenario); - let obj = test_scenario::take_child_object(scenario, &bag); - let id = *id::id(&obj); + let obj = test_scenario::take_child_object>(scenario, &bag); let obj = bag::remove(&mut bag, obj); - collection::add(&mut collection, obj); + let item_id = collection::add(&mut collection, obj, test_scenario::ctx(scenario)); assert!(collection::size(&collection) == 1, 0); assert!(bag::size(&bag) == 0, 0); - assert!(collection::contains(&collection, &id), 0); + assert!(collection::contains(&collection, typed_id::as_id(&item_id)), 0); test_scenario::return_owned(scenario, collection); test_scenario::return_owned(scenario, bag); @@ -108,7 +105,7 @@ module sui::collection_tests { } #[test] - #[expected_failure(abort_code = 520)] + #[expected_failure(abort_code = 0)] fun test_init_with_invalid_max_capacity() { let ctx = tx_context::dummy(); // Sui::collection::DEFAULT_MAX_CAPACITY is not readable outside the module @@ -118,7 +115,7 @@ module sui::collection_tests { } #[test] - #[expected_failure(abort_code = 520)] + #[expected_failure(abort_code = 0)] fun test_init_with_zero() { let ctx = tx_context::dummy(); let collection = collection::new_with_max_capacity(&mut ctx, 0); @@ -126,15 +123,15 @@ module sui::collection_tests { } #[test] - #[expected_failure(abort_code = 776)] + #[expected_failure(abort_code = 1)] fun test_exceed_max_capacity() { let ctx = tx_context::dummy(); let collection = collection::new_with_max_capacity(&mut ctx, 1); let obj1 = Object { id: tx_context::new_id(&mut ctx) }; - collection::add(&mut collection, obj1); + collection::add(&mut collection, obj1, &mut ctx); let obj2 = Object { id: tx_context::new_id(&mut ctx) }; - collection::add(&mut collection, obj2); + collection::add(&mut collection, obj2, &mut ctx); collection::transfer(collection, tx_context::sender(&ctx)); } } diff --git a/crates/sui-framework/tests/test_scenario_tests.move b/crates/sui-framework/tests/test_scenario_tests.move index 8ae268a850f10..281241c6e9f07 100644 --- a/crates/sui-framework/tests/test_scenario_tests.move +++ b/crates/sui-framework/tests/test_scenario_tests.move @@ -5,7 +5,7 @@ module sui::test_scenarioTests { use sui::id; use sui::test_scenario::{Self, Scenario}; - use sui::transfer::{Self, ChildRef}; + use sui::transfer; use sui::tx_context; const ID_BYTES_MISMATCH: u64 = 0; @@ -24,13 +24,13 @@ module sui::test_scenarioTests { struct Parent has key { id: id::VersionedID, - child: ChildRef, + child: id::ID, } struct MultiChildParent has key { id: id::VersionedID, - child1: ChildRef, - child2: ChildRef, + child1: id::ID, + child2: id::ID, } #[test] @@ -331,13 +331,13 @@ module sui::test_scenarioTests { }; let child2_id = *id::id(&child2); let parent_id = test_scenario::new_id(&mut scenario); - let (parent_id, child1_ref) = transfer::transfer_to_object_id(child1, parent_id); - let (parent_id, child2_ref) = transfer::transfer_to_object_id(child2, parent_id); + transfer::transfer_to_object_id(child1, &parent_id); + transfer::transfer_to_object_id(child2, &parent_id); let parent = MultiChildParent { id: parent_id, - child1: child1_ref, - child2: child2_ref, + child1: child1_id, + child2: child2_id, }; transfer::transfer(parent, sender); @@ -362,10 +362,11 @@ module sui::test_scenarioTests { id: test_scenario::new_id(scenario), value: 10, }; - let (parent_id, child) = transfer::transfer_to_object_id(object, parent_id); + let child_id = *id::id(&object); + transfer::transfer_to_object_id(object, &parent_id); let parent = Parent { id: parent_id, - child, + child: child_id, }; transfer::transfer(parent, test_scenario::sender(scenario)); } diff --git a/crates/sui-transactional-test-runner/src/test_adapter.rs b/crates/sui-transactional-test-runner/src/test_adapter.rs index aabe25ff57670..f6195679e5ef7 100644 --- a/crates/sui-transactional-test-runner/src/test_adapter.rs +++ b/crates/sui-transactional-test-runner/src/test_adapter.rs @@ -351,9 +351,12 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> { macro_rules! get_obj { ($fake_id:ident) => {{ let id = match self.fake_to_real_object_id($fake_id) { - None => panic!( + None => bail!( "task {}, lines {}-{}. Unbound fake id {}", - number, start_line, command_lines_stop, $fake_id + number, + start_line, + command_lines_stop, + $fake_id ), Some(res) => res, }; diff --git a/crates/sui-types/src/messages.rs b/crates/sui-types/src/messages.rs index ad3f6dbd3c519..42e46ad1b409a 100644 --- a/crates/sui-types/src/messages.rs +++ b/crates/sui-types/src/messages.rs @@ -965,8 +965,6 @@ pub enum ExecutionFailureStatus { MissingObjectOwner(MissingObjectOwner), InvalidSharedChildUse(InvalidSharedChildUse), InvalidSharedByValue(InvalidSharedByValue), - // Likely going to be removed - DeleteObjectOwnedObject, // // MovePublish errors @@ -1100,10 +1098,6 @@ impl std::fmt::Display for ExecutionFailureStatus { ExecutionFailureStatus::InvalidSharedByValue(data) => { write!(f, "Invalid Shared Object By-Value Usage. {data}.") } - ExecutionFailureStatus::DeleteObjectOwnedObject => write!( - f, - "Invalid deletion of object owned object without a child ref." - ), ExecutionFailureStatus::PublishErrorEmptyPackage => write!( f, "Publish Error, Empty Package. A package must have at least one module." diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.exp b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.exp index fd3ff8357bef6..5d2af250d3db8 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.exp +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.exp @@ -1,4 +1,4 @@ -processed 11 tasks +processed 9 tasks task 1 'publish'. lines 8-11: created: object(103) @@ -28,14 +28,6 @@ task 7 'publish'. lines 53-59: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::t. Invalid call to 'sui::transfer::transfer_to_object' on an object of type 'a::m::S'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } -task 8 'publish'. lines 61-69: +task 8 'publish'. lines 61-67: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::transfer_child_to_object. Invalid call to 'sui::transfer::transfer_child_to_object' on an object of type 'a::m::S'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } - -task 9 'publish'. lines 71-79: -Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::transfer_child_to_object. Invalid call to 'sui::transfer::transfer_child_to_object' on an object of type 'a::m::S'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } - -task 10 'publish'. lines 81-89: -Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::transfer_child_to_object. Invalid call to 'sui::transfer::transfer_child_to_address' on an object of type 'a::m::S'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::t. Invalid call to 'sui::transfer::transfer_to_object_id' on an object of type 'a::m::S'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.move b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.move index 1c924fe06bceb..b128bec77625e 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.move +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer.move @@ -21,9 +21,9 @@ module test::m { module test::m { fun t( s: a::m::S, - owner_id: sui::id::VersionedID, + owner_id: &sui::id::VersionedID, ctx: &mut sui::tx_context::TxContext, - ): (sui::id::VersionedID, sui::transfer::ChildRef) { + ) { sui::transfer::transfer_to_object_id(s, owner_id) } } @@ -45,7 +45,7 @@ module test::m { //# publish module test::m { struct R has key { id: sui::id::VersionedID } - fun t(child: a::m::S, owner: &mut R): sui::transfer::ChildRef { + fun t(child: a::m::S, owner: &mut R) { sui::transfer::transfer_to_object(child, owner) } } @@ -53,37 +53,15 @@ module test::m { //# publish module test::m { struct R has key { id: sui::id::VersionedID } - fun t(child: R, owner: &mut a::m::S): sui::transfer::ChildRef { + fun t(child: R, owner: &mut a::m::S) { sui::transfer::transfer_to_object(child, owner) } } //# publish module test::m { - use sui::transfer::ChildRef; - use a::m::S; struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: S, c: ChildRef, owner: &mut R): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module test::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: R, c: ChildRef, owner: &mut S): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module test::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(s: S, c: ChildRef) { - sui::transfer::transfer_child_to_address(s, c, @0x100) + fun t(child: a::m::S, owner: &sui::id::VersionedID) { + sui::transfer::transfer_to_object_id(child, owner) } } diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.exp b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.exp index db1a23d335a97..be99ef11fb711 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.exp +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.exp @@ -1,4 +1,4 @@ -processed 10 tasks +processed 8 tasks task 1 'publish'. lines 10-15: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. @@ -24,14 +24,6 @@ task 6 'publish'. lines 50-56: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::t. Invalid call to 'sui::transfer::transfer_to_object' on an object of type 'T0'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } -task 7 'publish'. lines 58-65: +task 7 'publish'. lines 58-63: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::transfer_child_to_object. Invalid call to 'sui::transfer::transfer_child_to_object' on an object of type 'T0'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } - -task 8 'publish'. lines 67-74: -Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::transfer_child_to_object. Invalid call to 'sui::transfer::transfer_child_to_object' on an object of type 'T0'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } - -task 9 'publish'. lines 76-83: -Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::transfer_child_to_object. Invalid call to 'sui::transfer::transfer_child_to_address' on an object of type 'T0'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m::t. Invalid call to 'sui::transfer::transfer_to_object_id' on an object of type 'T0'. The transferred object's type must be defined in the current module, or must have the 'store' type ability") } } diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.move b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.move index fe26a3a59447c..08a657fc765d7 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.move +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/no_public_transfer_generic.move @@ -18,9 +18,9 @@ module test::m { module test::m { fun t( s: T, - owner_id: sui::id::VersionedID, + owner_id: &sui::id::VersionedID, ctx: &mut sui::tx_context::TxContext, - ): (sui::id::VersionedID, sui::transfer::ChildRef) { + ) { sui::transfer::transfer_to_object_id(s, owner_id) } } @@ -42,7 +42,7 @@ module test::m { //# publish module test::m { struct R has key { id: sui::id::VersionedID } - fun t(child: T, owner: &mut R): sui::transfer::ChildRef { + fun t(child: T, owner: &mut R) { sui::transfer::transfer_to_object(child, owner) } } @@ -50,34 +50,14 @@ module test::m { //# publish module test::m { struct R has key { id: sui::id::VersionedID } - fun t(child: R, owner: &mut T): sui::transfer::ChildRef { + fun t(child: R, owner: &mut T) { sui::transfer::transfer_to_object(child, owner) } } //# publish module test::m { - use sui::transfer::ChildRef; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: T, c: ChildRef, owner: &mut R): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module test::m { - use sui::transfer::ChildRef; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: R, c: ChildRef, owner: &mut T): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module test::m { - use sui::transfer::ChildRef; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(s: T, c: ChildRef) { - sui::transfer::transfer_child_to_address(s, c, @0x100) + fun t(child: T, owner: &sui::id::VersionedID) { + sui::transfer::transfer_to_object_id(child, owner) } } diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/public_child_ref_functions.exp b/crates/sui-verifier-transactional-tests/tests/private_generics/public_child_ref_functions.exp deleted file mode 100644 index f6a6e988e321c..0000000000000 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/public_child_ref_functions.exp +++ /dev/null @@ -1,13 +0,0 @@ -processed 4 tasks - -task 1 'publish'. lines 8-11: -created: object(103) -written: object(102) - -task 2 'publish'. lines 13-23: -created: object(105) -written: object(104) - -task 3 'publish'. lines 25-35: -created: object(107) -written: object(106) diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/public_child_ref_functions.move b/crates/sui-verifier-transactional-tests/tests/private_generics/public_child_ref_functions.move deleted file mode 100644 index f274d3d434543..0000000000000 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/public_child_ref_functions.move +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// tests modules can use child ref functions, even with a type that does not have store - -//# init --addresses a=0x0 t1=0x0 t2=0x0 - -//# publish -module a::m { - struct S has key { id: sui::id::VersionedID } -} - -//# publish -module t1::m { - use a::m::S; - use sui::transfer::ChildRef; - fun t(c: &ChildRef, child: &S): bool { - sui::transfer::is_child(c, child) - } - fun t_gen(c: &ChildRef, child: &T): bool { - sui::transfer::is_child(c, child) - } -} - -//# publish -module t2::m { - use a::m::S; - use sui::transfer::ChildRef; - fun t(id: sui::id::VersionedID, c: ChildRef) { - sui::transfer::delete_child_object(id, c) - } - fun t_gen(id: sui::id::VersionedID, c: ChildRef) { - sui::transfer::delete_child_object(id, c) - } -} diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.exp b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.exp index 0b786bd5d88bf..c395cd0733f54 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.exp +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.exp @@ -1,4 +1,4 @@ -processed 11 tasks +processed 9 tasks task 1 'publish'. lines 9-12: created: object(103) @@ -28,14 +28,6 @@ task 7 'publish'. lines 53-59: created: object(115) written: object(114) -task 8 'publish'. lines 61-69: +task 8 'publish'. lines 61-66: created: object(117) written: object(116) - -task 9 'publish'. lines 71-79: -created: object(119) -written: object(118) - -task 10 'publish'. lines 81-89: -created: object(121) -written: object(120) diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.move b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.move index d728c8dcba0c0..cb2ef19e2a542 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.move +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store.move @@ -22,8 +22,8 @@ module t1::m { module t2::m { fun t( s: a::m::S, - owner_id: sui::id::VersionedID, - ): (sui::id::VersionedID, sui::transfer::ChildRef) { + owner_id: &sui::id::VersionedID, + ) { sui::transfer::transfer_to_object_id(s, owner_id) } } @@ -45,7 +45,7 @@ module t4::m { //# publish module t5::m { struct R has key { id: sui::id::VersionedID } - fun t(child: a::m::S, owner: &mut R): sui::transfer::ChildRef { + fun t(child: a::m::S, owner: &mut R) { sui::transfer::transfer_to_object(child, owner) } } @@ -53,37 +53,14 @@ module t5::m { //# publish module t6::m { struct R has key { id: sui::id::VersionedID } - fun t(child: R, owner: &mut a::m::S): sui::transfer::ChildRef { + fun t(child: R, owner: &mut a::m::S) { sui::transfer::transfer_to_object(child, owner) } } //# publish module t7::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: S, c: ChildRef, owner: &mut R): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module t8::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: R, c: ChildRef, owner: &mut S): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module t9::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(s: S, c: ChildRef) { - sui::transfer::transfer_child_to_address(s, c, @0x100) + fun t(child: a::m::S, owner: &sui::id::VersionedID) { + sui::transfer::transfer_to_object_id(child, owner) } } diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.exp b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.exp index eee19758a0bb3..1eafb91b67d7a 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.exp +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.exp @@ -1,4 +1,4 @@ -processed 11 tasks +processed 9 tasks task 1 'publish'. lines 9-12: created: object(103) @@ -28,14 +28,6 @@ task 7 'publish'. lines 71-80: created: object(115) written: object(114) -task 8 'publish'. lines 82-101: +task 8 'publish'. lines 82-90: created: object(117) written: object(116) - -task 9 'publish'. lines 103-114: -created: object(119) -written: object(118) - -task 10 'publish'. lines 116-127: -created: object(121) -written: object(120) diff --git a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.move b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.move index 51957e43085ca..fecc536859830 100644 --- a/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.move +++ b/crates/sui-verifier-transactional-tests/tests/private_generics/public_transfer_with_store_generic.move @@ -25,14 +25,14 @@ module t1::m { module t2::m { fun t( s: a::m::S, - owner_id: sui::id::VersionedID, - ): (sui::id::VersionedID, sui::transfer::ChildRef>) { + owner_id: &sui::id::VersionedID, + ) { sui::transfer::transfer_to_object_id(s, owner_id) } fun t_gen( s: T, - owner_id: sui::id::VersionedID, - ): (sui::id::VersionedID, sui::transfer::ChildRef) { + owner_id: &sui::id::VersionedID, + ) { sui::transfer::transfer_to_object_id(s, owner_id) } } @@ -60,10 +60,10 @@ module t4::m { //# publish module t5::m { struct R has key { id: sui::id::VersionedID } - fun t(child: a::m::S, owner: &mut R): sui::transfer::ChildRef> { + fun t(child: a::m::S, owner: &mut R) { sui::transfer::transfer_to_object(child, owner) } - fun t_gen(child: T, owner: &mut R): sui::transfer::ChildRef { + fun t_gen(child: T, owner: &mut R) { sui::transfer::transfer_to_object(child, owner) } } @@ -71,57 +71,20 @@ module t5::m { //# publish module t6::m { struct R has key { id: sui::id::VersionedID } - fun t(child: R, owner: &mut a::m::S): sui::transfer::ChildRef { + fun t(child: R, owner: &mut a::m::S) { sui::transfer::transfer_to_object(child, owner) } - fun t_gen(child: R, owner: &mut T): sui::transfer::ChildRef { + fun t_gen(child: R, owner: &mut T) { sui::transfer::transfer_to_object(child, owner) } } //# publish module t7::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object( - child: S, - c: ChildRef>, - owner: &mut R, - ): ChildRef> { - sui::transfer::transfer_child_to_object(child, c, owner) - } - fun transfer_child_to_object_gen( - child: T, - c: ChildRef, - owner: &mut R, - ): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module t8::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(child: R, c: ChildRef, owner: &mut S): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } - fun transfer_child_to_object_gen(child: R, c: ChildRef, owner: &mut T): ChildRef { - sui::transfer::transfer_child_to_object(child, c, owner) - } -} - -//# publish -module t9::m { - use sui::transfer::ChildRef; - use a::m::S; - struct R has key { id: sui::id::VersionedID } - fun transfer_child_to_object(s: S, c: ChildRef>) { - sui::transfer::transfer_child_to_address(s, c, @0x100) + fun t(child: a::m::S, owner: &sui::id::VersionedID) { + sui::transfer::transfer_to_object_id(child, owner) } - fun transfer_child_to_object_gen(s: T, c: ChildRef) { - sui::transfer::transfer_child_to_address(s, c, @0x100) + fun t_gen(child: T, owner: &sui::id::VersionedID) { + sui::transfer::transfer_to_object_id(child, owner) } } diff --git a/crates/sui-verifier/src/private_generics.rs b/crates/sui-verifier/src/private_generics.rs index 94cbc963308d5..ceabb986f200d 100644 --- a/crates/sui-verifier/src/private_generics.rs +++ b/crates/sui-verifier/src/private_generics.rs @@ -89,20 +89,17 @@ fn verify_private_transfer( match fident.as_str() { // transfer functions "transfer" + | "transfer_to_object" | "transfer_to_object_id" | "freeze_object" - | "share_object" - | "transfer_to_object" - | "transfer_to_object_unsafe" - | "transfer_child_to_object" - | "transfer_child_to_address" => (), + | "share_object" => (), // these functions operate over ChildRef - "is_child" | "is_child_unsafe" | "delete_child_object" => { + "is_child" | "inner" => { return Ok(()); } // should be unreachable // these are private and the module itself is skipped - "transfer_internal" | "delete_child_object_internal" => { + "transfer_internal" => { debug_assert!(false, "internal error. Unexpected private function"); return Ok(()); } diff --git a/sui_programmability/examples/nfts/sources/auction_lib.move b/sui_programmability/examples/nfts/sources/auction_lib.move index 4fcbdf45f271f..0163ed64a9f71 100644 --- a/sui_programmability/examples/nfts/sources/auction_lib.move +++ b/sui_programmability/examples/nfts/sources/auction_lib.move @@ -167,8 +167,8 @@ module nfts::auction_lib { /// exposes transfer::transfer_to_object_id public fun transfer_to_object_id( obj: Auction, - owner_id: VersionedID, - ): (VersionedID, transfer::ChildRef>) { + owner_id: &VersionedID, + ) { transfer::transfer_to_object_id(obj, owner_id) } diff --git a/sui_programmability/examples/nfts/sources/geniteam.move b/sui_programmability/examples/nfts/sources/geniteam.move index d548b7b489173..0bc16bd053756 100644 --- a/sui_programmability/examples/nfts/sources/geniteam.move +++ b/sui_programmability/examples/nfts/sources/geniteam.move @@ -5,9 +5,10 @@ module nfts::geniteam { use sui::bag::{Self, Bag}; use sui::collection::{Self, Collection}; use sui::id::VersionedID; + use sui::typed_id::{Self, TypedID}; use sui::tx_context::{Self, TxContext}; use std::option::{Self, Option}; - use sui::transfer::{Self, ChildRef}; + use sui::transfer; use std::ascii::{Self, String}; use std::vector; @@ -32,12 +33,12 @@ module nfts::geniteam { earth_runes_count: u64, // Owned Farm - owned_farm: Option>, + owned_farm: Option>, // Inventory of unassigned cosmetics. // A cosmetic can be either a FarmCosmetic or a MonsterCosmetic. // Since they can be of different types, we use Bag instead of Collection. - inventory: ChildRef, + inventory: TypedID, } struct Farm has key { @@ -50,12 +51,12 @@ module nfts::geniteam { occupied_monster_slots: u64, // Collection of Pet monsters owned by this Farm - pet_monsters: ChildRef>, + pet_monsters: TypedID>, // Applied cosmetic at this slot - applied_farm_cosmetic_0: Option>, + applied_farm_cosmetic_0: Option>, // Applied cosmetic at this slot - applied_farm_cosmetic_1: Option>, + applied_farm_cosmetic_1: Option>, } struct Monster has key, store { @@ -73,9 +74,9 @@ module nfts::geniteam { display: String, // Applied cosmetic at this slot - applied_monster_cosmetic_0: Option>, + applied_monster_cosmetic_0: Option>, // Applied cosmetic at this slot - applied_monster_cosmetic_1: Option>, + applied_monster_cosmetic_1: Option>, } @@ -111,12 +112,13 @@ module nfts::geniteam { assert!(option::is_none(&player.owned_farm), ETooManyFarms); let farm = new_farm(farm_name, farm_img_index, total_monster_slots, ctx); + let farm_id = typed_id::new(&farm); // Transfer ownership of farm to player - let child_ref = transfer::transfer_to_object(farm, player); + transfer::transfer_to_object(farm, player); // Store the farm - option::fill(&mut player.owned_farm, child_ref) + option::fill(&mut player.owned_farm, farm_id) } /// Create a Monster and add it to the Farm's collection of Monsters, which @@ -144,13 +146,13 @@ module nfts::geniteam { // Check if this is the right collection assert!( - transfer::is_child(&farm.pet_monsters, pet_monsters), + typed_id::equals_object(&farm.pet_monsters, pet_monsters), EMonsterCollectionNotOwnedByFarm, ); // TODO: Decouple adding monster to farm from creating a monster. // Add it to the collection - collection::add(pet_monsters, monster); + collection::add(pet_monsters, monster, ctx); } /// Create Farm cosmetic owned by player and add to its inventory @@ -160,7 +162,7 @@ module nfts::geniteam { ) { // Check if this is the right collection assert!( - transfer::is_child(&player.inventory, inventory), + typed_id::equals_object(&player.inventory, inventory), EInventoryNotOwnedByPlayer, ); @@ -172,7 +174,7 @@ module nfts::geniteam { }; // Add it to the player's inventory - bag::add(inventory, farm_cosmetic); + bag::add(inventory, farm_cosmetic, ctx); } /// Create Monster cosmetic owned by player and add to its inventory @@ -182,7 +184,7 @@ module nfts::geniteam { ) { // Check if this is the right collection assert!( - transfer::is_child(&player.inventory, inventory), + typed_id::equals_object(&player.inventory, inventory), EInventoryNotOwnedByPlayer, ); @@ -194,7 +196,7 @@ module nfts::geniteam { }; // Add it to the player's inventory - bag::add(inventory, monster_cosmetic); + bag::add(inventory, monster_cosmetic, ctx); } /// Update the attributes of a player @@ -252,7 +254,8 @@ module nfts::geniteam { assert!(cosmetic_slot_id <= 1 , EInvalidCosmeticsSlot); // Transfer ownership of cosmetic to this farm - let child_ref = transfer::transfer_to_object(farm_cosmetic, farm); + let child_ref = typed_id::new(&farm_cosmetic); + transfer::transfer_to_object(farm_cosmetic, farm); // Assign by slot if (cosmetic_slot_id == 0) { @@ -274,7 +277,8 @@ module nfts::geniteam { assert!(cosmetic_slot_id <= 1 , EInvalidCosmeticsSlot); // Transfer ownership of cosmetic to this monster - let child_ref = transfer::transfer_to_object(monster_cosmetic, monster); + let child_ref = typed_id::new(&monster_cosmetic); + transfer::transfer_to_object(monster_cosmetic, monster); // Assign by slot if (cosmetic_slot_id == 0) { @@ -299,7 +303,8 @@ module nfts::geniteam { let inventory = bag::new(ctx); // Transfer ownership of inventory to player. - let (id, child_ref) = bag::transfer_to_object_id(inventory, id); + let inventory_id = typed_id::new(&inventory); + bag::transfer_to_object_id(inventory, &id); let player = Player { id, @@ -309,7 +314,7 @@ module nfts::geniteam { wind_runes_count: 0, earth_runes_count: 0, owned_farm: option::none(), - inventory: child_ref + inventory: inventory_id }; player @@ -327,7 +332,8 @@ module nfts::geniteam { let pet_monsters = collection::new(ctx); // Transfer ownership of pet monsters to farm. - let (id, child_ref) = collection::transfer_to_object_id(pet_monsters, id); + let pet_monsters_id = typed_id::new(&pet_monsters); + collection::transfer_to_object_id(pet_monsters, &id); let farm = Farm { @@ -338,7 +344,7 @@ module nfts::geniteam { level: 0, current_xp: 0, occupied_monster_slots: 0, - pet_monsters: child_ref, + pet_monsters: pet_monsters_id, applied_farm_cosmetic_0: option::none(), applied_farm_cosmetic_1: option::none(), }; diff --git a/sui_programmability/examples/nfts/sources/marketplace.move b/sui_programmability/examples/nfts/sources/marketplace.move index 0bc930064a818..cc5dccf169d3b 100644 --- a/sui_programmability/examples/nfts/sources/marketplace.move +++ b/sui_programmability/examples/nfts/sources/marketplace.move @@ -4,8 +4,9 @@ module nfts::marketplace { use sui::bag::{Self, Bag}; use sui::tx_context::{Self, TxContext}; - use sui::id::{Self, ID, VersionedID}; - use sui::transfer::{Self, ChildRef}; + use sui::id::{ID, VersionedID}; + use sui::typed_id::{Self, TypedID}; + use sui::transfer; use sui::coin::{Self, Coin}; // For when amount paid does not match the expected. @@ -16,12 +17,11 @@ module nfts::marketplace { struct Marketplace has key { id: VersionedID, - objects: ChildRef, + bag_id: TypedID, } /// A single listing which contains the listed item and its price in [`Coin`]. - struct Listing has key, store { - id: VersionedID, + struct Listing has store { item: T, ask: u64, // Coin owner: address, @@ -30,11 +30,12 @@ module nfts::marketplace { /// Create a new shared Marketplace. public entry fun create(ctx: &mut TxContext) { let id = tx_context::new_id(ctx); - let objects = bag::new(ctx); - let (id, objects) = bag::transfer_to_object_id(objects, id); + let bag = bag::new(ctx); + let bag_id = typed_id::new(&bag); + bag::transfer_to_object_id(bag, &id); let market_place = Marketplace { id, - objects, + bag_id, }; transfer::share_object(market_place); } @@ -50,25 +51,23 @@ module nfts::marketplace { let listing = Listing { item, ask, - id: tx_context::new_id(ctx), owner: tx_context::sender(ctx), }; - bag::add(objects, listing) + bag::add(objects, listing, ctx); } /// Remove listing and get an item back. Only owner can do that. public fun delist( _marketplace: &Marketplace, objects: &mut Bag, - listing: Listing, + listing: bag::Item>, ctx: &mut TxContext ): T { let listing = bag::remove(objects, listing); - let Listing { id, item, ask: _, owner } = listing; + let Listing { item, ask: _, owner } = listing; assert!(tx_context::sender(ctx) == owner, ENotOwner); - id::delete(id); item } @@ -76,10 +75,11 @@ module nfts::marketplace { public entry fun delist_and_take( _marketplace: &Marketplace, objects: &mut Bag, - listing: Listing, + listing: bag::Item>, ctx: &mut TxContext ) { - bag::remove_and_take(objects, listing, ctx) + let item = delist(_marketplace, objects, listing, ctx); + transfer::transfer(item, tx_context::sender(ctx)); } /// Purchase an item using a known Listing. Payment is done in Coin. @@ -87,23 +87,22 @@ module nfts::marketplace { /// owner of the item gets the payment and buyer receives their item. public fun buy( objects: &mut Bag, - listing: Listing, + listing: bag::Item>, paid: Coin, ): T { let listing = bag::remove(objects, listing); - let Listing { id, item, ask, owner } = listing; + let Listing { item, ask, owner } = listing; assert!(ask == coin::value(&paid), EAmountIncorrect); transfer::transfer(paid, owner); - id::delete(id); item } /// Call [`buy`] and transfer item to the sender. public entry fun buy_and_take( _marketplace: &Marketplace, - listing: Listing, + listing: bag::Item>, objects: &mut Bag, paid: Coin, ctx: &mut TxContext @@ -125,7 +124,7 @@ module nfts::marketplace { #[test_only] module nfts::marketplaceTests { use sui::id::{Self, VersionedID}; - use sui::bag::Bag; + use sui::bag::{Self, Bag}; use sui::transfer; use sui::coin::{Self, Coin}; use sui::sui::SUI; @@ -189,7 +188,7 @@ module nfts::marketplaceTests { let mkp_wrapper = test_scenario::take_shared(scenario); let mkp = test_scenario::borrow_mut(&mut mkp_wrapper); let bag = test_scenario::take_child_object(scenario, mkp); - let listing = test_scenario::take_child_object>(scenario, &bag); + let listing = test_scenario::take_child_object>>(scenario, &bag); // Do the delist operation on a Marketplace. let nft = marketplace::delist(mkp, &mut bag, listing, test_scenario::ctx(scenario)); @@ -218,7 +217,7 @@ module nfts::marketplaceTests { let mkp_wrapper = test_scenario::take_shared(scenario); let mkp = test_scenario::borrow_mut(&mut mkp_wrapper); let bag = test_scenario::take_child_object(scenario, mkp); - let listing = test_scenario::take_child_object>(scenario, &bag); + let listing = test_scenario::take_child_object>>(scenario, &bag); // Do the delist operation on a Marketplace. let nft = marketplace::delist(mkp, &mut bag, listing, test_scenario::ctx(scenario)); @@ -245,7 +244,7 @@ module nfts::marketplaceTests { let mkp_wrapper = test_scenario::take_shared(scenario); let mkp = test_scenario::borrow_mut(&mut mkp_wrapper); let bag = test_scenario::take_child_object(scenario, mkp); - let listing = test_scenario::take_child_object>(scenario, &bag); + let listing = test_scenario::take_child_object>>(scenario, &bag); let payment = coin::take(coin::balance_mut(&mut coin), 100, test_scenario::ctx(scenario)); // Do the buy call and expect successful purchase. @@ -277,7 +276,7 @@ module nfts::marketplaceTests { let mkp_wrapper = test_scenario::take_shared(scenario); let mkp = test_scenario::borrow_mut(&mut mkp_wrapper); let bag = test_scenario::take_child_object(scenario, mkp); - let listing = test_scenario::take_child_object>(scenario, &bag); + let listing = test_scenario::take_child_object>>(scenario, &bag); // AMOUNT here is 10 while expected is 100. let payment = coin::take(coin::balance_mut(&mut coin), 10, test_scenario::ctx(scenario));