diff --git a/doc/src/build/move.md b/doc/src/build/move.md index 4ae4d312ef7e7..3f3d6f455d82f 100644 --- a/doc/src/build/move.md +++ b/doc/src/build/move.md @@ -125,7 +125,7 @@ package in Sui is also a Sui object and must have a unique numeric ID in addition to a unique name, which is assigned in the manifest file: -``` +``` [addresses] Sui = "0x2" @@ -277,7 +277,7 @@ value, and has three parameters: - `_ctx` - a mutable reference to an instance of the `TxContext` struct (in this particular case, this parameter is not actually used in the function's body as indicated by its name starting with `_`) - + You can see how the `transfer` function is called from a sample Sui wallet in [Calling Move code](wallet.md#calling-move-code). @@ -442,17 +442,17 @@ file: #[test] public fun test_sword_create() { use Sui::TxContext; - + // create a dummy TxContext for testing let ctx = TxContext::dummy(); - + // create a sword let sword = Sword { id: TxContext::new_id(&mut ctx), magic: 42, - strength: 7, + strength: 7, }; - + // check if accessor functions return correct values assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); } @@ -479,16 +479,16 @@ get a compilation error: ``` shell error[E06001]: unused value without 'drop' ┌─ ./sources/M1.move:34:65 - │ + │ 4 │ struct Sword has key, store { │ ----- To satisfy the constraint, the 'drop' ability would need to be added here - · + · 27 │ let sword = Sword { │ ----- The local variable 'sword' still contains a value. The value does not have the 'drop' ability and must be consumed before the function returns │ ╭─────────────────────' 28 │ │ id: TxContext::new_id(&mut ctx), 29 │ │ magic: 42, -30 │ │ strength: 7, +30 │ │ strength: 7, 31 │ │ }; │ ╰─────────' The type 'MyMovePackage::M1::Sword' does not have the ability 'drop' · │ @@ -532,7 +532,7 @@ the end of our test function: ``` rust // create a dummy address and transfer the sword - let dummy_address = @0xCAFE; + let dummy_address = @0xCAFE; Transfer::transfer(sword, dummy_address); ``` @@ -712,6 +712,25 @@ Running Move unit tests Test result: OK. Total tests: 2; passed: 2; failed: 0 ``` +## Debugging a package +At the moment there isn't a yet debugger for Move. To help with debugging, however, you could use `Std::Debug` module to print out arbitrary value. To do so, first import the `Debug` module: +``` +use Std::Debug; +``` +Then in places where you want to print out a value `v`, regardless of its type, simply do: +``` +Debug::print(&v); +``` +or the following if v is already a reference: +``` +Debug::print(v); +``` +`Debug` module also provides a function to print out the current stacktrace: +``` +Debug::print_stack_trace(); +``` +Alternatively, any call to `abort` or assertion failure will also print the stacktrace at the point of failure. + ## Publishing a package For functions in a Move package to actually be callable from Sui @@ -763,7 +782,7 @@ number of created swords as follows and put into the `M1.move` file: id: VersionedID, swords_created: u64, } - + public fun swords_created(self: &Forge): u64 { self.swords_created } @@ -810,7 +829,7 @@ We can now create a function to test the module initialization: let admin = @0xABBA; // first transaction to emulate module initialization - let scenario = &mut TestScenario::begin(&admin); + let scenario = &mut TestScenario::begin(&admin); { init(TestScenario::ctx(scenario)); }; @@ -842,6 +861,104 @@ entire source code for the package we have developed (with all the tests properly adjusted) can be found [here](../../sui_programmability/tutorial/sources/M1.move). +## Sui Move Library +Sui provides a list of Move library functions that allows us to manipulate objects in Sui. + +### Object Ownership +Objects in Sui can have different ownership types. Specifically, they are: +- Exclusively owned by an account address. +- Exclusively owned by another object. +- Shared and immutable. +- Shared and mutable (work-in-progress). + +**Transfer to Address** +The [`Transfer`](../../sui_programmability/framework/sources/Transfer.move) module provides all the APIs needed to manipuate the ownership of objects. + +The most common case is to transfer an object to an account address. For example, when a new object is created, it is typically transferred to an account address so that the address owns the object. To transfer an object `obj` to an account address `recipient`: +``` +use Sui::Transfer; + +Transfer::transfer(obj, recipient); +``` +This call will fully consume the object, making it no longer accessible in the current transaction. +Once an account address owns an object, for any future use (either read or write) of this object, the signer of the transaction must be the owner of the object. + +**Transfer to Object** +We can also transfer an object to be owned by another object. Note that the ownership is only tracked in Sui. From Move's perspective, these two objects are still more or less independent, in that the child object isn't part of the parent object in terms of data store. +Once an object is owned by another object, it is required that for any such object referenced in the entry function, its owner must also be one of the argument objects. For instance, if we have a chain of ownership: account address `Addr` owns object `a`, object `a` owns object `b`, and `b` owns object `c`, in order to use object `c` in a Move call, the entry function must also include both `b` and `a`, and the signer of the transaction must be `Addr1`, like this: +``` +// signer of ctx is Addr1. +public fun entry_function(a: &A, b: &B, c: &mut C, ctx: &mut TxContext); +``` + +A common pattern of object owning another object is to have a field in the parent object to track the ID of the child object. It is important to ensure that we keep such a field's value consistent with the actual ownership relationship. For example, we do not end up in a situation where the parent's child field contains an ID pointing to object A, while in fact the parent owns object B. To ensure the consistency, we defined a custom type called `ChildRef` to represent object ownership. Whenever an object is transferred to another object, a `ChildRef` instance is created to uniquely identify the ownership. The library implementation ensures that the `ChildRef` goes side-by-side with the child object so that we never lose track or mix up objects. +To transfer an object `obj` (whose owner is an account address) to another object `owner`: +``` +Transfer::transfer_to_object(obj, &mut owner); +``` +This function returns a `ChildRef` instance that cannot be dropped arbitrarily. It can be stored in the parent as a field. +Sometimes we need to set the child field of a parent while constructing it. In this case, we don't yet have a parent object to transfer into. In this case, we can call the `transfer_to_object_id` API. Example: +``` +let parent_id = TxContext::new_id(ctx); +let child = Child { id: TxContext::new_id(ctx) }; +let (parent_id, child_ref) = Transfer::transfer_to_object_id(child, parent_id); +let parent = Parent { + id: parent_id, + child: child_ref, +}; +Transfer::transfer(parent, TxContext::sender(ctx)); +``` +To transfer an object `child` from one parent object to a new parent object `new_parent`, we can use the following API: +``` +Transfer::transfer_child_to_object(child, child_ref, &mut new_parent); +``` +Note that in this call, we must also have the `child_ref` to prove the original ownership. The call will return a new instance of `ChildRef` that the new parent can maintain. +To transfer an object `child` from an object to an account address `recipient`, we can use the following API: +``` +Transfer::transfer_child_to_address(child, child_ref, recipient); +``` +This call also requires to have the `child_ref` as proof of original ownership. +After this transfer, the object will be owned by `recipient`. + +More examples of how objects can be transferred and owned can be found [here](../../sui_core/src/unit_tests/data/object_owner/sources/ObjectOwner.move). + +**Freeze an object** +To make an object `obj` shared and immutable, one can call: +``` +Transfer::freeze_object(obj); +``` +After this call, `obj` becomes immutable which means it can never be mutated or deleted. This process is also irreversible: once an object is frozen, it will stay frozen forever. An shared immutable object can be used as reference by anyone in their Move call. + +**Share an object (experimental)** +This feature is still in development. It only works in Move for demo purpose, and doesn't yet work in Sui. + +To make an object `obj` shared and mutable, one can call: +``` +Transfer::share_object(obj); +``` +After this call, `obj` stays mutable, but becomes shared by everyone, i.e. anyone can send a transaction to mutate this object. However, such an object cannot be deleted, transferred or embedded in another object as a field. +Shared mutable object can be powerful in that it will make programming a lot simpler in many cases. However shared object is also more expensive to use: it requires a full sequencer (a.k.a. a consensus engine) to order the transactions that touch the shared object, which means longer latency/lower throughput and higher gas cost. One can see the difference of the two programming schemes between not using shared object vs using shared object by looking at the two different implementations of TicTacToe: [No Shared Object](../../sui_programmability/examples/games/sources/TicTacToe.move) vs [Shared Object](../../sui_programmability/examples/games/sources/TicTacToeV2.move). + +### Transaction Context +`TxContext` module provides a few important APIs that operate based on the current transaction context. + +To create a new ID for a new object: +``` +use Sui::TxContext; + +// assmue `ctx` has type `&mut TxContext`. +let id = TxContext::new_id(ctx); +``` + +To obtain the current transaction sender's account address: +``` +TxContext::sender(ctx) +``` + +### Collection + +### NFT + ## Next steps Now that you are familiar with the Move language, as well as with how @@ -851,4 +968,3 @@ playing with some larger programs, such as implementation of the tic-tac-toe game or a more fleshed out variant of a fantasy game similar to the one we have been developing during this tutorial. -