diff --git a/highlight.js b/highlight.js index 0c23b73a..a63c3615 100644 --- a/highlight.js +++ b/highlight.js @@ -441,7 +441,7 @@ hljs.registerLanguage('move', function(hljs) { // module definition scope: 'module', begin: /\bmodule\b/, - end: /\{/, + end: /[;{]/, keywords: 'module', contains: [ BLOCK_COMMENT, diff --git a/move-basics/comments.html b/move-basics/comments.html index fa938009..531d1e86 100644 --- a/move-basics/comments.html +++ b/move-basics/comments.html @@ -201,17 +201,36 @@
{{#include ../../../packages/samples/sources/move-basics/comments.move:line}}
-
You can use double slash //
to comment out the rest of the line. Everything after //
will be
ignored by the compiler.
{{#include ../../../packages/samples/sources/move-basics/comments.move:line_2}}
+module book::comments_line;
+
+// let's add a note to everything!
+fun some_function_with_numbers() {
+ let a = 10;
+ // let b = 10 this line is commented and won't be executed
+ let b = 5; // here comment is placed after code
+ a + b; // result is 15, not 10!
+}
Block comment
Block comments are used to comment out a block of code. They start with /*
and end with */
.
Everything between /*
and */
will be ignored by the compiler. You can use block comments to
comment out a single line or multiple lines. You can even use them to comment out a part of a line.
-{{#include ../../../packages/samples/sources/move-basics/comments.move:block}}
+module book::comments_block;
+
+fun /* you can comment everywhere */ go_wild() {
+ /* here
+ there
+ everywhere */ let a = 10;
+ let b = /* even here */ 10; /* and again */
+ a + b;
+}
+/* you can use it to remove certain expressions or definitions
+fun empty_commented_out() {
+
+}
+*/
This example is a bit extreme, but it shows how you can use block comments to comment out a part of
a line.
@@ -219,7 +238,21 @@ Doc comment
Documentation comments are special comments that are used to generate documentation for your code.
They are similar to block comments, but they start with three slashes ///
and are placed before
the definition of the item they document.
-{{#include ../../../packages/samples/sources/move-basics/comments.move:doc}}
+/// Module has documentation!
+module book::comments_doc;
+
+/// This is a 0x0 address constant!
+const AN_ADDRESS: address = @0x0;
+
+/// This is a struct!
+public struct AStruct {
+ /// This is a field of a struct!
+ a_field: u8,
+}
+
+/// This function does something!
+/// And it's documented!
+fun do_something() {}
diff --git a/move-basics/constants.html b/move-basics/constants.html
index 0fab8f18..be2d2cc9 100644
--- a/move-basics/constants.html
+++ b/move-basics/constants.html
@@ -205,32 +205,66 @@ Constants
give names to static values that are used throughout a module. For example, if there's a default
price for a product, you might define a constant for it. Constants are stored in the module's
bytecode, and each time they are used, the value is copied.
-
+module book::shop_price;
+
+use sui::{coin::Coin, sui::SUI};
+
+/// The price of an item in the shop.
+const ITEM_PRICE: u64 = 100;
+/// The owner of the shop, an address.
+const SHOP_OWNER: address = @0xa11ce;
+
+/// An item sold in the shop.
+public struct Item {}
+
+/// Purchase an item from the shop.
+public fun purchase(coin: Coin<SUI>): Item {
+ assert!(coin.value() == ITEM_PRICE, 0);
+
+ transfer::public_transfer(coin, SHOP_OWNER);
+
+ Item {}
+}
Naming Convention
Constants must start with a capital letter - this is enforced at the compiler level. For constants
used as a value, there's a convention to use uppercase letters and underscores to separate words.
It's a way to make constants stand out from other identifiers in the code. One exception is made for
error constants, which are written in ECamelCase.
-
+/// Price of the item used at the shop.
+const ITEM_PRICE: u64 = 100;
+
+/// Error constant.
+const EItemNotFound: u64 = 1;
Constants are Immutable
Constants can't be changed and assigned new values. They are part of the package bytecode, and
inherently immutable.
-module book::immutable_constants {
- const ITEM_PRICE: u64 = 100;
+module book::immutable_constants;
+
+const ITEM_PRICE: u64 = 100;
- // emits an error
- fun change_price() {
- ITEM_PRICE = 200;
- }
+// emits an error
+fun change_price() {
+ ITEM_PRICE = 200;
}
Using Config Pattern
A common use case for an application is to define a set of constants that are used throughout the
codebase. But due to constants being private to the module, they can't be accessed from other
modules. One way to solve this is to define a "config" module that exports the constants.
-
+module book::config;
+
+const ITEM_PRICE: u64 = 100;
+const TAX_RATE: u64 = 10;
+const SHIPPING_COST: u64 = 5;
+
+/// Returns the price of an item.
+public fun item_price(): u64 { ITEM_PRICE }
+/// Returns the tax rate.
+public fun tax_rate(): u64 { TAX_RATE }
+/// Returns the shipping cost.
+public fun shipping_cost(): u64 { SHIPPING_COST }
This way other modules can import and read the constants, and the update process is simplified. If
the constants need to be changed, only the config module needs to be updated during the package
diff --git a/move-basics/function.html b/move-basics/function.html
index 26a5258d..83de49ce 100644
--- a/move-basics/function.html
+++ b/move-basics/function.html
@@ -189,20 +189,7 @@
Function
code into reusable units. Functions can take arguments and return a value. They are declared with
the fun
keyword at the module level. Just like any other module member, by default they're private
and can only be accessed from within the module.
-module book::math;
-
-/// Function takes two arguments of type `u64` and returns their sum.
-/// The `public` visibility modifier makes the function accessible from
-/// outside the module.
-public fun add(a: u64, b: u64): u64 {
- a + b
-}
-
-#[test]
-fun test_add() {
- let sum = add(1, 2);
- assert!(sum == 3, 0);
-}
+{{#include ../../../packages/samples/sources/move-basics/function_math.move:math}}
In this example, we define a function add
that takes two arguments of type u64
and returns their
sum. The function is called from the test_add
function, which is a test function located in the
@@ -227,7 +214,14 @@
Acces
consists of the module path and the function name separated by ::
. For example, if you have a
function called add
in the math
module in the book
package, the path to it will be
book::math::add
, or, if the module is imported, math::add
.
-
+module book::use_math;
+
+use book::math;
+
+fun call_add() {
+ // function is called via the path
+ let sum = math::add(1, 2);
+}
Multiple return values
Move functions can return multiple values, which is useful when you need to return more than one
diff --git a/move-basics/importing-modules.html b/move-basics/importing-modules.html
index 6d6c4f7b..4bc61d5f 100644
--- a/move-basics/importing-modules.html
+++ b/move-basics/importing-modules.html
@@ -219,26 +219,50 @@
Importi
Another module defined in the same package can import the first module using the use
keyword.
// File: sources/module_two.move
+module book::module_two;
+use book::module_one; // importing module_one from the same package
+
+/// Calls the `new` function from the `module_one` module.
+public fun create_and_ignore() {
+ let _ = module_one::new();
+}
Importing Members
You can also import specific members from a module. This is useful when you only need a single
function or a single type from a module. The syntax is the same as for importing a module, but you
add the member name after the module path.
-
+{{#include ../../../packages/samples/sources/move-basics/importing-modules-members.move:members}}
Grouping Imports
Imports can be grouped into a single use
statement using the curly braces {}
. This is useful
when you need to import multiple members from the same module. Move allows grouping imports from the
same module and from the same package.
-
+module book::grouped_imports;
+
+// imports the `new` function and the `Character` struct from
+// the `module_one` module
+use book::module_one::{new, Character};
+
+/// Calls the `new` function from the `module_one` module.
+public fun create_character(): Character {
+ new()
+}
Single function imports are less common in Move, since the function names can overlap and cause
confusion. A recommended practice is to import the entire module and use the module path to access
the function. Types have unique names and should be imported individually.
To import members and the module itself in the group import, you can use the Self
keyword. The
Self
keyword refers to the module itself and can be used to import the module and its members.
-
+module book::self_imports;
+
+// imports the `Character` struct, and the `module_one` module
+use book::module_one::{Self, Character};
+
+/// Calls the `new` function from the `module_one` module.
+public fun create_character(): Character {
+ module_one::new()
+}
Resolving Name Conflicts
When importing multiple members from different modules, it is possible to have name conflicts. For
@@ -246,7 +270,15 @@
+module book::conflict_resolution;
+
+// `as` can be placed after any import, including group imports
+use book::module_one::{Self as mod, Character as Char};
+
+/// Calls the `new` function from the `module_one` module.
+public fun create(): Char {
+ mod::new()
+}
Adding an External Dependency
Every new package generated via the sui
binary features a Move.toml
file with a single
@@ -270,7 +302,10 @@
+module book::imports;
+
+use std::string; // std = 0x1, string is a module in the standard library
+use sui::coin; // sui = 0x2, coin is a module in the Sui Framework
diff --git a/move-basics/module.html b/move-basics/module.html
index 06b0d9f9..8994878a 100644
--- a/move-basics/module.html
+++ b/move-basics/module.html
@@ -199,14 +199,20 @@ Module
and all of the members of the module are private to the module by default. In this section you will
learn how to define a module, how to declare its members and how to access them from other modules.
Module declaration
-Modules are declared using the module
keyword followed by the package address, module name and the
-module body inside the curly braces {}
. The module name should be in snake_case
- all lowercase
-letters with underscores between words. Modules names must be unique in the package.
+Modules are declared using the module
keyword followed by the package address, module name,
+semicolon, and the module body. The module name should be in snake_case
- all lowercase letters
+with underscores between words. Modules names must be unique in the package.
Usually, a single file in the sources/
folder contains a single module. The file name should match
the module name - for example, a donut_shop
module should be stored in the donut_shop.move
file.
You can read more about coding conventions in the
Coding Conventions section.
-
+
+If you need to declare more than one module in a file, you must use Module Block.
+
+// Module label.
+module book::my_module;
+
+// module body
Structs, functions, constants and imports all part of the module:
@@ -232,6 +238,27 @@ Module members
Module members are declared inside the module body. To illustrate that, let's define a simple module
with a struct, a function and a constant:
+
module book::my_module_with_members;
+
+// import
+use book::my_module;
+
+// a constant
+const CONST: u8 = 0;
+
+// a struct
+public struct Struct {}
+
+// method alias
+public use fun function as Struct.struct_fun;
+
+// function
+fun function(_: &Struct) { /* function body */ }
+
+Module block
+Pre-2024 edition of Move required module block - the contents of the module need to be surrounded
+by curly braces {}
. The main reason to use block and not label is if you need to define more
+than one module in a file.
module book::my_block_module_with_members {
// import
use book::my_module;
diff --git a/move-basics/ownership-and-scope.html b/move-basics/ownership-and-scope.html
index 04a9db3a..c58a5858 100644
--- a/move-basics/ownership-and-scope.html
+++ b/move-basics/ownership-and-scope.html
@@ -199,21 +199,21 @@ Ownership
A variable defined in a function scope is owned by this scope. The runtime goes through the function
scope and executes every expression and statement. Once the function scope end, the variables
defined in it are dropped or deallocated.
-module book::ownership {
- public fun owner() {
- let a = 1; // a is owned by the `owner` function
- } // a is dropped here
-
- public fun other() {
- let b = 2; // b is owned by the `other` function
- } // b is dropped here
-
- #[test]
- fun test_owner() {
- owner();
- other();
- // a & b is not valid here
- }
+module book::ownership;
+
+public fun owner() {
+ let a = 1; // a is owned by the `owner` function
+} // a is dropped here
+
+public fun other() {
+ let b = 2; // b is owned by the `other` function
+} // b is dropped here
+
+#[test]
+fun test_owner() {
+ owner();
+ other();
+ // a & b is not valid here
}
In the example above, the variable a
is owned by the owner
function, and the variable b
is
@@ -222,70 +222,70 @@
Ownership
Returning a Value
If we changed the owner
function to return the variable a
, then the ownership of a
would be
transferred to the caller of the function.
-module book::ownership {
- public fun owner(): u8 {
- let a = 1; // a defined here
- a // scope ends, a is returned
- }
-
- #[test]
- fun test_owner() {
- let a = owner();
- // a is valid here
- } // a is dropped here
+module book::ownership;
+
+public fun owner(): u8 {
+ let a = 1; // a defined here
+ a // scope ends, a is returned
}
+
+#[test]
+fun test_owner() {
+ let a = owner();
+ // a is valid here
+} // a is dropped here
Passing by Value
Additionally, if we passed the variable a
to another function, the ownership of a
would be
transferred to this function. When performing this operation, we move the value from one scope to
another. This is also called move semantics.
-module book::ownership {
- public fun owner(): u8 {
- let a = 10;
- a
- } // a is returned
-
- public fun take_ownership(v: u8) {
- // v is owned by `take_ownership`
- } // v is dropped here
-
- #[test]
- fun test_owner() {
- let a = owner();
- take_ownership(a);
- // a is not valid here
- }
+module book::ownership;
+
+public fun owner(): u8 {
+ let a = 10;
+ a
+} // a is returned
+
+public fun take_ownership(v: u8) {
+ // v is owned by `take_ownership`
+} // v is dropped here
+
+#[test]
+fun test_owner() {
+ let a = owner();
+ take_ownership(a);
+ // a is not valid here
}
Scopes with Blocks
Each function has a main scope, and it can also have sub-scopes via the use of blocks. A block is a
sequence of statements and expressions, and it has its own scope. Variables defined in a block are
owned by this block, and when the block ends, the variables are dropped.
-module book::ownership {
- public fun owner() {
- let a = 1; // a is owned by the `owner` function's scope
+module book::ownership;
+
+public fun owner() {
+ let a = 1; // a is owned by the `owner` function's scope
+ {
+ let b = 2; // b is owned by the block
{
- let b = 2; // b is owned by the block
- {
- let c = 3; // c is owned by the block
- }; // c is dropped here
- }; // b is dropped here
- // a = b; // error: b is not valid here
- // a = c; // error: c is not valid here
- } // a is dropped here
-}
+ let c = 3; // c is owned by the block
+ }; // c is dropped here
+ }; // b is dropped here
+ // a = b; // error: b is not valid here
+ // a = c; // error: c is not valid here
+} // a is dropped here
However, shall we use the return value of a block, the ownership of the variable is transferred to
the caller of the block.
-module book::ownership {
- public fun owner(): u8 {
- let a = 1; // a is owned by the `owner` function's scope
- let b = {
- let c = 2; // c is owned by the block
- c // c is returned
- }; // c is dropped here
- a + b // both a and b are valid here
- }
+module book::ownership;
+
+public fun owner(): u8 {
+ let a = 1; // a is owned by the `owner` function's scope
+ let b = {
+ let c = 2; // c is owned by the block
+ c // c is returned
+ }; // c is dropped here
+ a + b // both a and b are valid here
}
Copyable Types
diff --git a/move-basics/references.html b/move-basics/references.html
index bc00afaf..237bde65 100644
--- a/move-basics/references.html
+++ b/move-basics/references.html
@@ -231,7 +231,6 @@ Layout
public fun purchase(/* pass a Coin */): Card {
Card { uses: USES }
}
-}
Reference
diff --git a/move-basics/struct-methods.html b/move-basics/struct-methods.html
index 54791542..46194472 100644
--- a/move-basics/struct-methods.html
+++ b/move-basics/struct-methods.html
@@ -244,7 +244,45 @@ Method Aliases<
and Villain
have similar field names and methods. And to avoid name conflicts, we prefixed methods
with hero_
and villain_
respectively. However, we can create aliases for these methods so that
they can be called on the instances of the structs without the prefix.
-
+module book::hero_and_villain;
+
+/// A struct representing a hero.
+public struct Hero has drop {
+ health: u8,
+}
+
+/// A struct representing a villain.
+public struct Villain has drop {
+ health: u8,
+}
+
+/// Create a new Hero.
+public fun new_hero(): Hero { Hero { health: 100 } }
+
+/// Create a new Villain.
+public fun new_villain(): Villain { Villain { health: 100 } }
+
+// Alias for the `hero_health` method. Will be imported automatically when
+// the module is imported.
+public use fun hero_health as Hero.health;
+
+public fun hero_health(hero: &Hero): u8 { hero.health }
+
+// Alias for the `villain_health` method. Will be imported automatically
+// when the module is imported.
+public use fun villain_health as Villain.health;
+
+public fun villain_health(villain: &Villain): u8 { villain.health }
+
+#[test]
+// Test the methods of the `Hero` and `Villain` structs.
+fun test_associated_methods() {
+ let hero = new_hero();
+ assert!(hero.health() == 100, 1);
+
+ let villain = new_villain();
+ assert!(villain.health() == 100, 3);
+}
As you can see, in the test function, we called the health
method on the instances of Hero
and
Villain
without the prefix. The compiler will automatically associate the methods with the
@@ -255,7 +293,29 @@
Standard Library and
associate it with the Hero
struct. It will allow serializing the Hero
struct to a vector of
bytes.
-
+// TODO: better example (external module...)
+module book::hero_to_bytes;
+
+// Alias for the `bcs::to_bytes` method. Imported aliases should be defined
+// in the top of the module.
+// public use fun bcs::to_bytes as Hero.to_bytes;
+
+/// A struct representing a hero.
+public struct Hero has drop {
+ health: u8,
+ mana: u8,
+}
+
+/// Create a new Hero.
+public fun new(): Hero { Hero { health: 100, mana: 100 } }
+
+#[test]
+// Test the methods of the `Hero` struct.
+fun test_hero_serialize() {
+ // let mut hero = new();
+ // let serialized = hero.to_bytes();
+ // assert!(serialized.length() == 3, 1);
+}
Further reading
diff --git a/move-basics/testing.html b/move-basics/testing.html
index 70c49429..c1150b38 100644
--- a/move-basics/testing.html
+++ b/move-basics/testing.html
@@ -192,21 +192,21 @@ The
-module book::testing {
- // Test attribute is placed before the `fun` keyword. Can be both above or
- // right before the `fun` keyword: `#[test] fun my_test() { ... }`
- // The name of the test would be `book::testing::simple_test`.
- #[test]
- fun simple_test() {
- let sum = 2 + 2;
- assert!(sum == 4, 1);
- }
-
- // The name of the test would be `book::testing::more_advanced_test`.
- #[test] fun more_advanced_test() {
- let sum = 2 + 2 + 2;
- assert!(sum == 4, 1);
- }
+module book::testing;
+
+// Test attribute is placed before the `fun` keyword. Can be both above or
+// right before the `fun` keyword: `#[test] fun my_test() { ... }`
+// The name of the test would be `book::testing::simple_test`.
+#[test]
+fun simple_test() {
+ let sum = 2 + 2;
+ assert!(sum == 4);
+}
+
+// The name of the test would be `book::testing::more_advanced_test`.
+#[test] fun more_advanced_test() {
+ let sum = 2 + 2 + 2;
+ assert!(sum == 4);
}
Running Tests
@@ -231,21 +231,20 @@ module book::testing_failure {
+module book::testing_failure;
- const EInvalidArgument: u64 = 1;
+const EInvalidArgument: u64 = 1;
- #[test]
- #[expected_failure(abort_code = 0)]
- fun test_fail() {
- abort 0 // aborts with 0
- }
+#[test]
+#[expected_failure(abort_code = 0)]
+fun test_fail() {
+ abort 0 // aborts with 0
+}
- // attributes can be grouped together
- #[test, expected_failure(abort_code = EInvalidArgument)]
- fun test_fail_1() {
- abort 1 // aborts with 1
- }
+// attributes can be grouped together
+#[test, expected_failure(abort_code = EInvalidArgument)]
+fun test_fail_1() {
+ abort 1 // aborts with 1
}
The abort_code
argument can use constants defined in the tests module as well as imported from
@@ -255,29 +254,29 @@
module book::testing {
- // Public function which uses the `secret` function.
- public fun multiply_by_secret(x: u64): u64 {
- x * secret()
- }
-
- /// Private function which is not available to the public.
- fun secret(): u64 { 100 }
-
- #[test_only]
- /// This function is only available for testing purposes in tests and other
- /// test-only functions. Mind the visibility - for `#[test_only]` it is
- /// common to use `public` visibility.
- public fun secret_for_testing(): u64 {
- secret()
- }
-
- #[test]
- // In the test environment we have access to the `secret_for_testing` function.
- fun test_multiply_by_secret() {
- let expected = secret_for_testing() * 2;
- assert!(multiply_by_secret(2) == expected, 1);
- }
+module book::testing;
+
+// Public function which uses the `secret` function.
+public fun multiply_by_secret(x: u64): u64 {
+ x * secret()
+}
+
+/// Private function which is not available to the public.
+fun secret(): u64 { 100 }
+
+#[test_only]
+/// This function is only available for testing purposes in tests and other
+/// test-only functions. Mind the visibility - for `#[test_only]` it is
+/// common to use `public` visibility.
+public fun secret_for_testing(): u64 {
+ secret()
+}
+
+#[test]
+// In the test environment we have access to the `secret_for_testing` function.
+fun test_multiply_by_secret() {
+ let expected = secret_for_testing() * 2;
+ assert!(multiply_by_secret(2) == expected);
}
Functions marked with the #[test_only]
will be available to the test environment, and to the other
diff --git a/move-basics/visibility.html b/move-basics/visibility.html
index 8bf9762f..aefd0a52 100644
--- a/move-basics/visibility.html
+++ b/move-basics/visibility.html
@@ -192,60 +192,60 @@
Vis
Internal Visibility
A function or a struct defined in a module which has no visibility modifier is private to the
module. It can't be called from other modules.
-module book::internal_visibility {
- // This function can be called from other functions in the same module
- fun internal() { /* ... */ }
-
- // Same module -> can call internal()
- fun call_internal() {
- internal();
- }
+module book::internal_visibility;
+
+// This function can be called from other functions in the same module
+fun internal() { /* ... */ }
+
+// Same module -> can call internal()
+fun call_internal() {
+ internal();
}
-module book::try_calling_internal {
- use book::internal_visibility;
+module book::try_calling_internal;
+
+use book::internal_visibility;
- // Different module -> can't call internal()
- fun try_calling_internal() {
- internal_visibility::internal();
- }
+// Different module -> can't call internal()
+fun try_calling_internal() {
+ internal_visibility::internal();
}
Public Visibility
A struct or a function can be made public by adding the public
keyword before the fun
or
struct
keyword.
-module book::public_visibility {
- // This function can be called from other modules
- public fun public() { /* ... */ }
-}
+module book::public_visibility;
+
+// This function can be called from other modules
+public fun public() { /* ... */ }
A public function can be imported and called from other modules. The following code will compile:
module book::try_calling_public {
- use book::public_visibility;
- // Different module -> can call public()
- fun try_calling_public() {
- public_visibility::public();
- }
+use book::public_visibility;
+
+// Different module -> can call public()
+fun try_calling_public() {
+ public_visibility::public();
}
Package Visibility
Move 2024 introduces the package visibility modifier. A function with package visibility can be
called from any module within the same package. It can't be called from other packages.
-module book::package_visibility {
- public(package) fun package_only() { /* ... */ }
-}
+module book::package_visibility;
+
+public(package) fun package_only() { /* ... */ }
A package function can be called from any module within the same package:
-module book::try_calling_package {
- use book::package_visibility;
+module book::try_calling_package;
+
+use book::package_visibility;
- // Same package `book` -> can call package_only()
- fun try_calling_package() {
- package_visibility::package_only();
- }
+// Same package `book` -> can call package_only()
+fun try_calling_package() {
+ package_visibility::package_only();
}
diff --git a/print.html b/print.html
index e384a338..04c2666c 100644
--- a/print.html
+++ b/print.html
@@ -378,9 +378,7 @@ Sources
hello_world.move and the Move CLI has already placed commented out code inside:
/*
/// Module: hello_world
-module hello_world::hello_world {
-
-}
+module hello_world::hello_world;
*/
@@ -404,21 +402,20 @@ Tests
The hello_world_tests.move file contains a commented out test module template:
/*
#[test_only]
-module hello_world::hello_world_tests {
- // uncomment this line to import the module
- // use hello_world::hello_world;
+module hello_world::hello_world_tests;
+// uncomment this line to import the module
+// use hello_world::hello_world;
- const ENotImplemented: u64 = 0;
+const ENotImplemented: u64 = 0;
- #[test]
- fun test_hello_world() {
- // pass
- }
+#[test]
+fun test_hello_world() {
+ // pass
+}
- #[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)]
- fun test_hello_world_fail() {
- abort ENotImplemented
- }
+#[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)]
+fun test_hello_world_fail() {
+ abort ENotImplemented
}
*/
@@ -435,14 +432,14 @@ C
with the following:
/// The module `hello_world` under named address `hello_world`.
/// The named address is set in the `Move.toml`.
-module hello_world::hello_world {
- // Imports the `String` type from the Standard Library
- use std::string::String;
+module hello_world::hello_world;
- /// Returns the "Hello, World!" as a `String`.
- public fun hello_world(): String {
- b"Hello, World!".to_string()
- }
+// Imports the `String` type from the Standard Library
+use std::string::String;
+
+/// Returns the "Hello, World!" as a `String`.
+public fun hello_world(): String {
+ b"Hello, World!".to_string()
}
During compilation, the code is built, but not run. A compiled package only includes functions that
@@ -473,13 +470,13 @@
Running Tests
the compiler. We explain tests in depth in the Testing section.
Replace the contents of the tests/hello_world_tests.move
with the following content:
#[test_only]
-module hello_world::hello_world_tests {
- use hello_world::hello_world;
+module hello_world::hello_world_tests;
- #[test]
- fun test_hello_world() {
- assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0);
- }
+use hello_world::hello_world;
+
+#[test]
+fun test_hello_world() {
+ assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0);
}
Here we import the hello_world
module, and call its hello_world
function to test that the output
@@ -1541,14 +1538,20 @@
T
and all of the members of the module are private to the module by default. In this section you will
learn how to define a module, how to declare its members and how to access them from other modules.
Module declaration
-Modules are declared using the module
keyword followed by the package address, module name and the
-module body inside the curly braces {}
. The module name should be in snake_case
- all lowercase
-letters with underscores between words. Modules names must be unique in the package.
+Modules are declared using the module
keyword followed by the package address, module name,
+semicolon, and the module body. The module name should be in snake_case
- all lowercase letters
+with underscores between words. Modules names must be unique in the package.
Usually, a single file in the sources/
folder contains a single module. The file name should match
the module name - for example, a donut_shop
module should be stored in the donut_shop.move
file.
You can read more about coding conventions in the
Coding Conventions section.
-
+
+If you need to declare more than one module in a file, you must use Module Block.
+
+// Module label.
+module book::my_module;
+
+// module body
Structs, functions, constants and imports all part of the module:
@@ -1574,6 +1577,27 @@ Module members
Module members are declared inside the module body. To illustrate that, let's define a simple module
with a struct, a function and a constant:
+
module book::my_module_with_members;
+
+// import
+use book::my_module;
+
+// a constant
+const CONST: u8 = 0;
+
+// a struct
+public struct Struct {}
+
+// method alias
+public use fun function as Struct.struct_fun;
+
+// function
+fun function(_: &Struct) { /* function body */ }
+
+Module block
+Pre-2024 edition of Move required module block - the contents of the module need to be surrounded
+by curly braces {}
. The main reason to use block and not label is if you need to define more
+than one module in a file.
module book::my_block_module_with_members {
// import
use book::my_module;
@@ -1619,17 +1643,36 @@ Further r
documentation. There are three types of comments in Move: line comment, block comment, and doc
comment.
Line comment
-{{#include ../../../packages/samples/sources/move-basics/comments.move:line}}
-
You can use double slash //
to comment out the rest of the line. Everything after //
will be
ignored by the compiler.
-{{#include ../../../packages/samples/sources/move-basics/comments.move:line_2}}
+module book::comments_line;
+
+// let's add a note to everything!
+fun some_function_with_numbers() {
+ let a = 10;
+ // let b = 10 this line is commented and won't be executed
+ let b = 5; // here comment is placed after code
+ a + b; // result is 15, not 10!
+}
Block comment
Block comments are used to comment out a block of code. They start with /*
and end with */
.
Everything between /*
and */
will be ignored by the compiler. You can use block comments to
comment out a single line or multiple lines. You can even use them to comment out a part of a line.
-{{#include ../../../packages/samples/sources/move-basics/comments.move:block}}
+module book::comments_block;
+
+fun /* you can comment everywhere */ go_wild() {
+ /* here
+ there
+ everywhere */ let a = 10;
+ let b = /* even here */ 10; /* and again */
+ a + b;
+}
+/* you can use it to remove certain expressions or definitions
+fun empty_commented_out() {
+
+}
+*/
This example is a bit extreme, but it shows how you can use block comments to comment out a part of
a line.
@@ -1637,7 +1680,21 @@ Doc comment
Documentation comments are special comments that are used to generate documentation for your code.
They are similar to block comments, but they start with three slashes ///
and are placed before
the definition of the item they document.
-{{#include ../../../packages/samples/sources/move-basics/comments.move:doc}}
+/// Module has documentation!
+module book::comments_doc;
+
+/// This is a 0x0 address constant!
+const AN_ADDRESS: address = @0x0;
+
+/// This is a struct!
+public struct AStruct {
+ /// This is a field of a struct!
+ a_field: u8,
+}
+
+/// This function does something!
+/// And it's documented!
+fun do_something() {}
Primitive Types
@@ -2183,26 +2240,50 @@ Importi
Another module defined in the same package can import the first module using the use
keyword.
// File: sources/module_two.move
+module book::module_two;
+
+use book::module_one; // importing module_one from the same package
+/// Calls the `new` function from the `module_one` module.
+public fun create_and_ignore() {
+ let _ = module_one::new();
+}
Importing Members
You can also import specific members from a module. This is useful when you only need a single
function or a single type from a module. The syntax is the same as for importing a module, but you
add the member name after the module path.
-
+{{#include ../../../packages/samples/sources/move-basics/importing-modules-members.move:members}}
Grouping Imports
Imports can be grouped into a single use
statement using the curly braces {}
. This is useful
when you need to import multiple members from the same module. Move allows grouping imports from the
same module and from the same package.
-
+module book::grouped_imports;
+
+// imports the `new` function and the `Character` struct from
+// the `module_one` module
+use book::module_one::{new, Character};
+
+/// Calls the `new` function from the `module_one` module.
+public fun create_character(): Character {
+ new()
+}
Single function imports are less common in Move, since the function names can overlap and cause
confusion. A recommended practice is to import the entire module and use the module path to access
the function. Types have unique names and should be imported individually.
To import members and the module itself in the group import, you can use the Self
keyword. The
Self
keyword refers to the module itself and can be used to import the module and its members.
-
+module book::self_imports;
+
+// imports the `Character` struct, and the `module_one` module
+use book::module_one::{Self, Character};
+
+/// Calls the `new` function from the `module_one` module.
+public fun create_character(): Character {
+ module_one::new()
+}
Resolving Name Conflicts
When importing multiple members from different modules, it is possible to have name conflicts. For
@@ -2210,7 +2291,15 @@
+module book::conflict_resolution;
+
+// `as` can be placed after any import, including group imports
+use book::module_one::{Self as mod, Character as Char};
+
+/// Calls the `new` function from the `module_one` module.
+public fun create(): Char {
+ mod::new()
+}
Adding an External Dependency
Every new package generated via the sui
binary features a Move.toml
file with a single
@@ -2234,7 +2323,10 @@
+module book::imports;
+
+use std::string; // std = 0x1, string is a module in the standard library
+use sui::coin; // sui = 0x2, coin is a module in the Sui Framework
Standard Library
@@ -2807,32 +2899,66 @@ Early Return
-
+module book::shop_price;
+
+use sui::{coin::Coin, sui::SUI};
+
+/// The price of an item in the shop.
+const ITEM_PRICE: u64 = 100;
+/// The owner of the shop, an address.
+const SHOP_OWNER: address = @0xa11ce;
+
+/// An item sold in the shop.
+public struct Item {}
+
+/// Purchase an item from the shop.
+public fun purchase(coin: Coin<SUI>): Item {
+ assert!(coin.value() == ITEM_PRICE, 0);
+
+ transfer::public_transfer(coin, SHOP_OWNER);
+
+ Item {}
+}
Naming Convention
Constants must start with a capital letter - this is enforced at the compiler level. For constants
used as a value, there's a convention to use uppercase letters and underscores to separate words.
It's a way to make constants stand out from other identifiers in the code. One exception is made for
error constants, which are written in ECamelCase.
-
+/// Price of the item used at the shop.
+const ITEM_PRICE: u64 = 100;
+
+/// Error constant.
+const EItemNotFound: u64 = 1;
Constants are Immutable
Constants can't be changed and assigned new values. They are part of the package bytecode, and
inherently immutable.
-module book::immutable_constants {
- const ITEM_PRICE: u64 = 100;
+module book::immutable_constants;
- // emits an error
- fun change_price() {
- ITEM_PRICE = 200;
- }
+const ITEM_PRICE: u64 = 100;
+
+// emits an error
+fun change_price() {
+ ITEM_PRICE = 200;
}
Using Config Pattern
A common use case for an application is to define a set of constants that are used throughout the
codebase. But due to constants being private to the module, they can't be accessed from other
modules. One way to solve this is to define a "config" module that exports the constants.
-
+module book::config;
+
+const ITEM_PRICE: u64 = 100;
+const TAX_RATE: u64 = 10;
+const SHIPPING_COST: u64 = 5;
+
+/// Returns the price of an item.
+public fun item_price(): u64 { ITEM_PRICE }
+/// Returns the tax rate.
+public fun tax_rate(): u64 { TAX_RATE }
+/// Returns the shipping cost.
+public fun shipping_cost(): u64 { SHIPPING_COST }
This way other modules can import and read the constants, and the update process is simplified. If
the constants need to be changed, only the config module needs to be updated during the package
@@ -2931,20 +3057,7 @@
Further
code into reusable units. Functions can take arguments and return a value. They are declared with
the fun
keyword at the module level. Just like any other module member, by default they're private
and can only be accessed from within the module.
-module book::math;
-
-/// Function takes two arguments of type `u64` and returns their sum.
-/// The `public` visibility modifier makes the function accessible from
-/// outside the module.
-public fun add(a: u64, b: u64): u64 {
- a + b
-}
-
-#[test]
-fun test_add() {
- let sum = add(1, 2);
- assert!(sum == 3, 0);
-}
+{{#include ../../../packages/samples/sources/move-basics/function_math.move:math}}
In this example, we define a function add
that takes two arguments of type u64
and returns their
sum. The function is called from the test_add
function, which is a test function located in the
@@ -2969,7 +3082,14 @@
Acces
consists of the module path and the function name separated by ::
. For example, if you have a
function called add
in the math
module in the book
package, the path to it will be
book::math::add
, or, if the module is imported, math::add
.
-
+module book::use_math;
+
+use book::math;
+
+fun call_add() {
+ // function is called via the path
+ let sum = math::add(1, 2);
+}
Multiple return values
Move functions can return multiple values, which is useful when you need to return more than one
@@ -3061,7 +3181,45 @@
Method Aliases<
and Villain
have similar field names and methods. And to avoid name conflicts, we prefixed methods
with hero_
and villain_
respectively. However, we can create aliases for these methods so that
they can be called on the instances of the structs without the prefix.
-
+module book::hero_and_villain;
+
+/// A struct representing a hero.
+public struct Hero has drop {
+ health: u8,
+}
+
+/// A struct representing a villain.
+public struct Villain has drop {
+ health: u8,
+}
+
+/// Create a new Hero.
+public fun new_hero(): Hero { Hero { health: 100 } }
+
+/// Create a new Villain.
+public fun new_villain(): Villain { Villain { health: 100 } }
+
+// Alias for the `hero_health` method. Will be imported automatically when
+// the module is imported.
+public use fun hero_health as Hero.health;
+
+public fun hero_health(hero: &Hero): u8 { hero.health }
+
+// Alias for the `villain_health` method. Will be imported automatically
+// when the module is imported.
+public use fun villain_health as Villain.health;
+
+public fun villain_health(villain: &Villain): u8 { villain.health }
+
+#[test]
+// Test the methods of the `Hero` and `Villain` structs.
+fun test_associated_methods() {
+ let hero = new_hero();
+ assert!(hero.health() == 100, 1);
+
+ let villain = new_villain();
+ assert!(villain.health() == 100, 3);
+}
As you can see, in the test function, we called the health
method on the instances of Hero
and
Villain
without the prefix. The compiler will automatically associate the methods with the
@@ -3072,7 +3230,29 @@
Standard Library and
associate it with the Hero
struct. It will allow serializing the Hero
struct to a vector of
bytes.
-
+// TODO: better example (external module...)
+module book::hero_to_bytes;
+
+// Alias for the `bcs::to_bytes` method. Imported aliases should be defined
+// in the top of the module.
+// public use fun bcs::to_bytes as Hero.to_bytes;
+
+/// A struct representing a hero.
+public struct Hero has drop {
+ health: u8,
+ mana: u8,
+}
+
+/// Create a new Hero.
+public fun new(): Hero { Hero { health: 100, mana: 100 } }
+
+#[test]
+// Test the methods of the `Hero` struct.
+fun test_hero_serialize() {
+ // let mut hero = new();
+ // let serialized = hero.to_bytes();
+ // assert!(serialized.length() == 3, 1);
+}
Further reading
@@ -3087,60 +3267,60 @@ Further
Internal Visibility
A function or a struct defined in a module which has no visibility modifier is private to the
module. It can't be called from other modules.
-module book::internal_visibility {
- // This function can be called from other functions in the same module
- fun internal() { /* ... */ }
+module book::internal_visibility;
- // Same module -> can call internal()
- fun call_internal() {
- internal();
- }
+// This function can be called from other functions in the same module
+fun internal() { /* ... */ }
+
+// Same module -> can call internal()
+fun call_internal() {
+ internal();
}
-module book::try_calling_internal {
- use book::internal_visibility;
+module book::try_calling_internal;
- // Different module -> can't call internal()
- fun try_calling_internal() {
- internal_visibility::internal();
- }
+use book::internal_visibility;
+
+// Different module -> can't call internal()
+fun try_calling_internal() {
+ internal_visibility::internal();
}
Public Visibility
A struct or a function can be made public by adding the public
keyword before the fun
or
struct
keyword.
-module book::public_visibility {
- // This function can be called from other modules
- public fun public() { /* ... */ }
-}
+module book::public_visibility;
+
+// This function can be called from other modules
+public fun public() { /* ... */ }
A public function can be imported and called from other modules. The following code will compile:
module book::try_calling_public {
- use book::public_visibility;
- // Different module -> can call public()
- fun try_calling_public() {
- public_visibility::public();
- }
+use book::public_visibility;
+
+// Different module -> can call public()
+fun try_calling_public() {
+ public_visibility::public();
}
Package Visibility
Move 2024 introduces the package visibility modifier. A function with package visibility can be
called from any module within the same package. It can't be called from other packages.
-module book::package_visibility {
- public(package) fun package_only() { /* ... */ }
-}
+module book::package_visibility;
+
+public(package) fun package_only() { /* ... */ }
A package function can be called from any module within the same package:
-module book::try_calling_package {
- use book::package_visibility;
+module book::try_calling_package;
- // Same package `book` -> can call package_only()
- fun try_calling_package() {
- package_visibility::package_only();
- }
+use book::package_visibility;
+
+// Same package `book` -> can call package_only()
+fun try_calling_package() {
+ package_visibility::package_only();
}
Ownership and Scope
@@ -3159,21 +3339,21 @@ Ownership
A variable defined in a function scope is owned by this scope. The runtime goes through the function
scope and executes every expression and statement. Once the function scope end, the variables
defined in it are dropped or deallocated.
-module book::ownership {
- public fun owner() {
- let a = 1; // a is owned by the `owner` function
- } // a is dropped here
-
- public fun other() {
- let b = 2; // b is owned by the `other` function
- } // b is dropped here
-
- #[test]
- fun test_owner() {
- owner();
- other();
- // a & b is not valid here
- }
+module book::ownership;
+
+public fun owner() {
+ let a = 1; // a is owned by the `owner` function
+} // a is dropped here
+
+public fun other() {
+ let b = 2; // b is owned by the `other` function
+} // b is dropped here
+
+#[test]
+fun test_owner() {
+ owner();
+ other();
+ // a & b is not valid here
}
In the example above, the variable a
is owned by the owner
function, and the variable b
is
@@ -3182,70 +3362,70 @@
Ownership
Returning a Value
If we changed the owner
function to return the variable a
, then the ownership of a
would be
transferred to the caller of the function.
-module book::ownership {
- public fun owner(): u8 {
- let a = 1; // a defined here
- a // scope ends, a is returned
- }
+module book::ownership;
- #[test]
- fun test_owner() {
- let a = owner();
- // a is valid here
- } // a is dropped here
+public fun owner(): u8 {
+ let a = 1; // a defined here
+ a // scope ends, a is returned
}
+
+#[test]
+fun test_owner() {
+ let a = owner();
+ // a is valid here
+} // a is dropped here
Passing by Value
Additionally, if we passed the variable a
to another function, the ownership of a
would be
transferred to this function. When performing this operation, we move the value from one scope to
another. This is also called move semantics.
-module book::ownership {
- public fun owner(): u8 {
- let a = 10;
- a
- } // a is returned
-
- public fun take_ownership(v: u8) {
- // v is owned by `take_ownership`
- } // v is dropped here
-
- #[test]
- fun test_owner() {
- let a = owner();
- take_ownership(a);
- // a is not valid here
- }
+module book::ownership;
+
+public fun owner(): u8 {
+ let a = 10;
+ a
+} // a is returned
+
+public fun take_ownership(v: u8) {
+ // v is owned by `take_ownership`
+} // v is dropped here
+
+#[test]
+fun test_owner() {
+ let a = owner();
+ take_ownership(a);
+ // a is not valid here
}
Scopes with Blocks
Each function has a main scope, and it can also have sub-scopes via the use of blocks. A block is a
sequence of statements and expressions, and it has its own scope. Variables defined in a block are
owned by this block, and when the block ends, the variables are dropped.
-module book::ownership {
- public fun owner() {
- let a = 1; // a is owned by the `owner` function's scope
+module book::ownership;
+
+public fun owner() {
+ let a = 1; // a is owned by the `owner` function's scope
+ {
+ let b = 2; // b is owned by the block
{
- let b = 2; // b is owned by the block
- {
- let c = 3; // c is owned by the block
- }; // c is dropped here
- }; // b is dropped here
- // a = b; // error: b is not valid here
- // a = c; // error: c is not valid here
- } // a is dropped here
-}
+ let c = 3; // c is owned by the block
+ }; // c is dropped here
+ }; // b is dropped here
+ // a = b; // error: b is not valid here
+ // a = c; // error: c is not valid here
+} // a is dropped here
However, shall we use the return value of a block, the ownership of the variable is transferred to
the caller of the block.
-module book::ownership {
- public fun owner(): u8 {
- let a = 1; // a is owned by the `owner` function's scope
- let b = {
- let c = 2; // c is owned by the block
- c // c is returned
- }; // c is dropped here
- a + b // both a and b are valid here
- }
+module book::ownership;
+
+public fun owner(): u8 {
+ let a = 1; // a is owned by the `owner` function's scope
+ let b = {
+ let c = 2; // c is owned by the block
+ c // c is returned
+ }; // c is dropped here
+ a + b // both a and b are valid here
}
Copyable Types
@@ -3354,7 +3534,6 @@ Layout
public fun purchase(/* pass a Coin */): Card {
Card { uses: USES }
}
-}
Reference
@@ -3682,21 +3861,21 @@ The
-module book::testing {
- // Test attribute is placed before the `fun` keyword. Can be both above or
- // right before the `fun` keyword: `#[test] fun my_test() { ... }`
- // The name of the test would be `book::testing::simple_test`.
- #[test]
- fun simple_test() {
- let sum = 2 + 2;
- assert!(sum == 4, 1);
- }
+module book::testing;
- // The name of the test would be `book::testing::more_advanced_test`.
- #[test] fun more_advanced_test() {
- let sum = 2 + 2 + 2;
- assert!(sum == 4, 1);
- }
+// Test attribute is placed before the `fun` keyword. Can be both above or
+// right before the `fun` keyword: `#[test] fun my_test() { ... }`
+// The name of the test would be `book::testing::simple_test`.
+#[test]
+fun simple_test() {
+ let sum = 2 + 2;
+ assert!(sum == 4);
+}
+
+// The name of the test would be `book::testing::more_advanced_test`.
+#[test] fun more_advanced_test() {
+ let sum = 2 + 2 + 2;
+ assert!(sum == 4);
}
Running Tests
@@ -3721,21 +3900,20 @@ module book::testing_failure {
+module book::testing_failure;
- const EInvalidArgument: u64 = 1;
+const EInvalidArgument: u64 = 1;
- #[test]
- #[expected_failure(abort_code = 0)]
- fun test_fail() {
- abort 0 // aborts with 0
- }
+#[test]
+#[expected_failure(abort_code = 0)]
+fun test_fail() {
+ abort 0 // aborts with 0
+}
- // attributes can be grouped together
- #[test, expected_failure(abort_code = EInvalidArgument)]
- fun test_fail_1() {
- abort 1 // aborts with 1
- }
+// attributes can be grouped together
+#[test, expected_failure(abort_code = EInvalidArgument)]
+fun test_fail_1() {
+ abort 1 // aborts with 1
}
The abort_code
argument can use constants defined in the tests module as well as imported from
@@ -3745,29 +3923,29 @@
module book::testing {
- // Public function which uses the `secret` function.
- public fun multiply_by_secret(x: u64): u64 {
- x * secret()
- }
+module book::testing;
+
+// Public function which uses the `secret` function.
+public fun multiply_by_secret(x: u64): u64 {
+ x * secret()
+}
- /// Private function which is not available to the public.
- fun secret(): u64 { 100 }
+/// Private function which is not available to the public.
+fun secret(): u64 { 100 }
- #[test_only]
- /// This function is only available for testing purposes in tests and other
- /// test-only functions. Mind the visibility - for `#[test_only]` it is
- /// common to use `public` visibility.
- public fun secret_for_testing(): u64 {
- secret()
- }
+#[test_only]
+/// This function is only available for testing purposes in tests and other
+/// test-only functions. Mind the visibility - for `#[test_only]` it is
+/// common to use `public` visibility.
+public fun secret_for_testing(): u64 {
+ secret()
+}
- #[test]
- // In the test environment we have access to the `secret_for_testing` function.
- fun test_multiply_by_secret() {
- let expected = secret_for_testing() * 2;
- assert!(multiply_by_secret(2) == expected, 1);
- }
+#[test]
+// In the test environment we have access to the `secret_for_testing` function.
+fun test_multiply_by_secret() {
+ let expected = secret_for_testing() * 2;
+ assert!(multiply_by_secret(2) == expected);
}
Functions marked with the #[test_only]
will be available to the test environment, and to the other
@@ -4640,7 +4818,19 @@
+// In the same package as the `shop` module
+module book::bank;
+
+public struct Bank has key {
+ id: UID,
+ /* ... */
+}
+
+fun init(ctx: &mut TxContext) {
+ transfer::share_object(Bank {
+ id: object::new(ctx)
+ });
+}
init
features
The function is called on publish, if it is present in the module and follows the rules:
@@ -4732,7 +4922,20 @@ Using init
for Admin Capability
A very common practice is to create a single AdminCap
object on package publish. This way, the
application can have a setup phase where the admin account prepares the state of the application.
-
+module book::admin_cap;
+
+/// The capability granting the admin privileges in the system.
+/// Created only once in the `init` function.
+public struct AdminCap has key { id: UID }
+
+/// Create the AdminCap object on package publish and transfer it to the
+/// package owner.
+fun init(ctx: &mut TxContext) {
+ transfer::transfer(
+ AdminCap { id: object::new(ctx) },
+ ctx.sender()
+ )
+}
Address check vs Capability
Utilizing objects as capabilities is a relatively new concept in blockchain programming. And in
@@ -4741,10 +4944,28 @@
+/// Error code for unauthorized access.
+const ENotAuthorized: u64 = 0;
+
+/// The application admin address.
+const APPLICATION_ADMIN: address = @0xa11ce;
+
+/// Creates a new user in the system. Requires the sender to be the application
+/// admin.
+public fun new(ctx: &mut TxContext): User {
+ assert!(ctx.sender() == APPLICATION_ADMIN, ENotAuthorized);
+ User { id: object::new(ctx) }
+}
And now, let's see how the same function would look like with the capability:
-
+/// Grants the owner the right to create new users in the system.
+public struct AdminCap {}
+
+/// Creates a new user in the system. Requires the `AdminCap` capability to be
+/// passed as the first argument.
+public fun new(_: &AdminCap, ctx: &mut TxContext): User {
+ User { id: object::new(ctx) }
+}
Using capabilities has several advantages over the address check:
@@ -4859,7 +5080,29 @@ VecSet
VecSet
is a collection type that stores a set of unique items. It is similar to a vector
, but it
does not allow duplicate items. This makes it useful for storing a collection of unique items, such
as a list of unique IDs or addresses.
-
+module book::collections_vec_set;
+
+use sui::vec_set::{Self, VecSet};
+
+public struct App has drop {
+ /// `VecSet` used in the struct definition
+ subscribers: VecSet<address>
+}
+
+#[test]
+fun vec_set_playground() {
+ let set = vec_set::empty<u8>(); // create an empty set
+ let mut set = vec_set::singleton(1); // create a set with a single item
+
+ set.insert(2); // add an item to the set
+ set.insert(3);
+
+ assert!(set.contains(&1), 0); // check if an item is in the set
+ assert!(set.size() == 3, 1); // get the number of items in the set
+ assert!(!set.is_empty(), 2); // check if the set is empty
+
+ set.remove(&2); // remove an item from the set
+}
VecSet will fail on attempt to insert an item that already exists in the set.
VecMap
@@ -4870,7 +5113,28 @@ VecMap
Keys in a VecMap
are unique, and each key can only be associated with a single value. If you try
to insert a key-value pair with a key that already exists in the map, the old value will be replaced
with the new value.
-
+module book::collections_vec_map;
+
+use std::string::String;
+use sui::vec_map::{Self, VecMap};
+
+public struct Metadata has drop {
+ name: String,
+ /// `VecMap` used in the struct definition
+ attributes: VecMap<String, String>
+}
+
+#[test]
+fun vec_map_playground() {
+ let mut map = vec_map::empty(); // create an empty map
+
+ map.insert(2, b"two".to_string()); // add a key-value pair to the map
+ map.insert(3, b"three".to_string());
+
+ assert!(map.contains(&2), 0); // check if a key is in the map
+
+ map.remove(&2); // remove a key-value pair from the map
+}
Limitations
Standard collection types are a great way to store typed data with guaranteed safety and
@@ -4885,7 +5149,15 @@
Limitations
This behavior is caught by the linter and will emit a warning: Comparing collections of type
'sui::vec_set::VecSet' may yield unexpected result
-
+let mut set1 = vec_set::empty();
+set1.insert(1);
+set1.insert(2);
+
+let mut set2 = vec_set::empty();
+set2.insert(2);
+set2.insert(1);
+
+assert!(set1 == set2); // aborts!
In the example above, the comparison will fail because the order of insertion is not guaranteed, and
the two VecSet
instances may have different orders of elements. And the comparison will fail even
@@ -5434,29 +5706,29 @@
Witness in Mo
It is often the case that the witness struct is not stored, and for that the function may require
the Drop ability for the type.
-module book::witness {
- /// A struct that requires a witness to be created.
- public struct Instance<T> { t: T }
+module book::witness;
- /// Create a new instance of `Instance<T>` with the provided T.
- public fun new<T>(witness: T): Instance<T> {
- Instance { t: witness }
- }
+/// A struct that requires a witness to be created.
+public struct Instance<T> { t: T }
+
+/// Create a new instance of `Instance<T>` with the provided T.
+public fun new<T>(witness: T): Instance<T> {
+ Instance { t: witness }
}
The only way to construct an Instance<T>
is to call the new
function with an instance of the
type T
. This is a basic example of the witness pattern in Move. A module providing a witness often
has a matching implementation, like the module book::witness_source
below:
-module book::witness_source {
- use book::witness::{Self, Instance};
+module book::witness_source;
- /// A struct used as a witness.
- public struct W {}
+use book::witness::{Self, Instance};
- /// Create a new instance of `Instance<W>`.
- public fun new_instance(): Instance<W> {
- witness::new(W {})
- }
+/// A struct used as a witness.
+public struct W {}
+
+/// Create a new instance of `Instance<W>`.
+public fun new_instance(): Instance<W> {
+ witness::new(W {})
}
The instance of the struct W
is passed into the new_instance
function to create an Instance<W>
, thereby
@@ -5928,16 +6200,16 @@
Further
Sui Verifier requires the type to be internal to the module.
// File: sui-framework/sources/event.move
-module sui::event {
- /// Emit a custom Move event, sending the data offchain.
- ///
- /// Used for creating custom indexes and tracking onchain
- /// activity in a way that suits a specific application the most.
- ///
- /// The type `T` is the main way to index the event, and can contain
- /// phantom parameters, eg `emit(MyEvent<phantom T>)`.
- public native fun emit<T: copy + drop>(event: T);
-}
+module sui::event;
+
+/// Emit a custom Move event, sending the data offchain.
+///
+/// Used for creating custom indexes and tracking onchain
+/// activity in a way that suits a specific application the most.
+///
+/// The type `T` is the main way to index the event, and can contain
+/// phantom parameters, eg `emit(MyEvent<phantom T>)`.
+public native fun emit<T: copy + drop>(event: T);
Emitting Events
Events are emitted using the emit
function in the sui::event
module. The function takes a single
diff --git a/programmability/capability.html b/programmability/capability.html
index 70d93815..118daabf 100644
--- a/programmability/capability.html
+++ b/programmability/capability.html
@@ -241,7 +241,20 @@
Using init
for Admin Capability
A very common practice is to create a single AdminCap
object on package publish. This way, the
application can have a setup phase where the admin account prepares the state of the application.
-
+module book::admin_cap;
+
+/// The capability granting the admin privileges in the system.
+/// Created only once in the `init` function.
+public struct AdminCap has key { id: UID }
+
+/// Create the AdminCap object on package publish and transfer it to the
+/// package owner.
+fun init(ctx: &mut TxContext) {
+ transfer::transfer(
+ AdminCap { id: object::new(ctx) },
+ ctx.sender()
+ )
+}
Address check vs Capability
Utilizing objects as capabilities is a relatively new concept in blockchain programming. And in
@@ -250,10 +263,28 @@
+/// Error code for unauthorized access.
+const ENotAuthorized: u64 = 0;
+
+/// The application admin address.
+const APPLICATION_ADMIN: address = @0xa11ce;
+
+/// Creates a new user in the system. Requires the sender to be the application
+/// admin.
+public fun new(ctx: &mut TxContext): User {
+ assert!(ctx.sender() == APPLICATION_ADMIN, ENotAuthorized);
+ User { id: object::new(ctx) }
+}
And now, let's see how the same function would look like with the capability:
-
+/// Grants the owner the right to create new users in the system.
+public struct AdminCap {}
+
+/// Creates a new user in the system. Requires the `AdminCap` capability to be
+/// passed as the first argument.
+public fun new(_: &AdminCap, ctx: &mut TxContext): User {
+ User { id: object::new(ctx) }
+}
Using capabilities has several advantages over the address check:
diff --git a/programmability/collections.html b/programmability/collections.html
index 14e27b3d..03bb2689 100644
--- a/programmability/collections.html
+++ b/programmability/collections.html
@@ -213,7 +213,29 @@ VecSet
VecSet
is a collection type that stores a set of unique items. It is similar to a vector
, but it
does not allow duplicate items. This makes it useful for storing a collection of unique items, such
as a list of unique IDs or addresses.
-
+module book::collections_vec_set;
+
+use sui::vec_set::{Self, VecSet};
+
+public struct App has drop {
+ /// `VecSet` used in the struct definition
+ subscribers: VecSet<address>
+}
+
+#[test]
+fun vec_set_playground() {
+ let set = vec_set::empty<u8>(); // create an empty set
+ let mut set = vec_set::singleton(1); // create a set with a single item
+
+ set.insert(2); // add an item to the set
+ set.insert(3);
+
+ assert!(set.contains(&1), 0); // check if an item is in the set
+ assert!(set.size() == 3, 1); // get the number of items in the set
+ assert!(!set.is_empty(), 2); // check if the set is empty
+
+ set.remove(&2); // remove an item from the set
+}
VecSet will fail on attempt to insert an item that already exists in the set.
VecMap
@@ -224,7 +246,28 @@ VecMap
Keys in a VecMap
are unique, and each key can only be associated with a single value. If you try
to insert a key-value pair with a key that already exists in the map, the old value will be replaced
with the new value.
-
+module book::collections_vec_map;
+
+use std::string::String;
+use sui::vec_map::{Self, VecMap};
+
+public struct Metadata has drop {
+ name: String,
+ /// `VecMap` used in the struct definition
+ attributes: VecMap<String, String>
+}
+
+#[test]
+fun vec_map_playground() {
+ let mut map = vec_map::empty(); // create an empty map
+
+ map.insert(2, b"two".to_string()); // add a key-value pair to the map
+ map.insert(3, b"three".to_string());
+
+ assert!(map.contains(&2), 0); // check if a key is in the map
+
+ map.remove(&2); // remove a key-value pair from the map
+}
Limitations
Standard collection types are a great way to store typed data with guaranteed safety and
@@ -239,7 +282,15 @@
Limitations
This behavior is caught by the linter and will emit a warning: Comparing collections of type
'sui::vec_set::VecSet' may yield unexpected result
-
+let mut set1 = vec_set::empty();
+set1.insert(1);
+set1.insert(2);
+
+let mut set2 = vec_set::empty();
+set2.insert(2);
+set2.insert(1);
+
+assert!(set1 == set2); // aborts!
In the example above, the comparison will fail because the order of insertion is not guaranteed, and
the two VecSet
instances may have different orders of elements. And the comparison will fail even
diff --git a/programmability/events.html b/programmability/events.html
index 2bf2452b..3fd71423 100644
--- a/programmability/events.html
+++ b/programmability/events.html
@@ -194,16 +194,16 @@
Events
Sui Verifier requires the type to be internal to the module.
// File: sui-framework/sources/event.move
-module sui::event {
- /// Emit a custom Move event, sending the data offchain.
- ///
- /// Used for creating custom indexes and tracking onchain
- /// activity in a way that suits a specific application the most.
- ///
- /// The type `T` is the main way to index the event, and can contain
- /// phantom parameters, eg `emit(MyEvent<phantom T>)`.
- public native fun emit<T: copy + drop>(event: T);
-}
+module sui::event;
+
+/// Emit a custom Move event, sending the data offchain.
+///
+/// Used for creating custom indexes and tracking onchain
+/// activity in a way that suits a specific application the most.
+///
+/// The type `T` is the main way to index the event, and can contain
+/// phantom parameters, eg `emit(MyEvent<phantom T>)`.
+public native fun emit<T: copy + drop>(event: T);
Emitting Events
Events are emitted using the emit
function in the sui::event
module. The function takes a single
diff --git a/programmability/module-initializer.html b/programmability/module-initializer.html
index 18584fab..1c8c978a 100644
--- a/programmability/module-initializer.html
+++ b/programmability/module-initializer.html
@@ -220,7 +220,19 @@
Module
}
In the same package, another module can have its own init
function, encapsulating distinct logic.
-
+// In the same package as the `shop` module
+module book::bank;
+
+public struct Bank has key {
+ id: UID,
+ /* ... */
+}
+
+fun init(ctx: &mut TxContext) {
+ transfer::share_object(Bank {
+ id: object::new(ctx)
+ });
+}
init
features
The function is called on publish, if it is present in the module and follows the rules:
diff --git a/programmability/witness-pattern.html b/programmability/witness-pattern.html
index fe79febd..58d88d3b 100644
--- a/programmability/witness-pattern.html
+++ b/programmability/witness-pattern.html
@@ -199,29 +199,29 @@ Witness in Mo
It is often the case that the witness struct is not stored, and for that the function may require
the Drop ability for the type.
-module book::witness {
- /// A struct that requires a witness to be created.
- public struct Instance<T> { t: T }
-
- /// Create a new instance of `Instance<T>` with the provided T.
- public fun new<T>(witness: T): Instance<T> {
- Instance { t: witness }
- }
+module book::witness;
+
+/// A struct that requires a witness to be created.
+public struct Instance<T> { t: T }
+
+/// Create a new instance of `Instance<T>` with the provided T.
+public fun new<T>(witness: T): Instance<T> {
+ Instance { t: witness }
}
The only way to construct an Instance<T>
is to call the new
function with an instance of the
type T
. This is a basic example of the witness pattern in Move. A module providing a witness often
has a matching implementation, like the module book::witness_source
below:
-module book::witness_source {
- use book::witness::{Self, Instance};
+module book::witness_source;
+
+use book::witness::{Self, Instance};
- /// A struct used as a witness.
- public struct W {}
+/// A struct used as a witness.
+public struct W {}
- /// Create a new instance of `Instance<W>`.
- public fun new_instance(): Instance<W> {
- witness::new(W {})
- }
+/// Create a new instance of `Instance<W>`.
+public fun new_instance(): Instance<W> {
+ witness::new(W {})
}
The instance of the struct W
is passed into the new_instance
function to create an Instance<W>
, thereby
diff --git a/reference/highlight.js b/reference/highlight.js
index 0c23b73a..a63c3615 100644
--- a/reference/highlight.js
+++ b/reference/highlight.js
@@ -441,7 +441,7 @@ hljs.registerLanguage('move', function(hljs) {
// module definition
scope: 'module',
begin: /\bmodule\b/,
- end: /\{/,
+ end: /[;{]/,
keywords: 'module',
contains: [
BLOCK_COMMENT,
diff --git a/searchindex.js b/searchindex.js
index 37968364..eca0a8cf 100644
--- a/searchindex.js
+++ b/searchindex.js
@@ -1 +1 @@
-Object.assign(window.search, {"doc_urls":["index.html#the-move-book","foreword.html#foreword","before-we-begin/index.html#before-we-begin","before-we-begin/install-sui.html#install-sui","before-we-begin/install-sui.html#download-binary","before-we-begin/install-sui.html#install-using-homebrew-macos","before-we-begin/install-sui.html#install-using-chocolatey-windows","before-we-begin/install-sui.html#build-using-cargo-macos-linux","before-we-begin/install-sui.html#troubleshooting","before-we-begin/ide-support.html#set-up-your-ide","before-we-begin/ide-support.html#vscode","before-we-begin/ide-support.html#intellij-idea","before-we-begin/ide-support.html#emacs","before-we-begin/ide-support.html#github-codespaces","before-we-begin/move-2024.html#move-2024","your-first-move/hello-world.html#hello-world","your-first-move/hello-world.html#create-a-new-package","your-first-move/hello-world.html#directory-structure","your-first-move/hello-world.html#manifest","your-first-move/hello-world.html#sources","your-first-move/hello-world.html#tests","your-first-move/hello-world.html#other-folders","your-first-move/hello-world.html#compiling-the-package","your-first-move/hello-world.html#running-tests","your-first-move/hello-world.html#next-steps","your-first-move/hello-world.html#further-reading","your-first-move/hello-sui.html#hello-sui","your-first-move/hello-sui.html#create-a-new-package","your-first-move/hello-sui.html#add-the-code","your-first-move/hello-sui.html#build-the-package","your-first-move/hello-sui.html#set-up-an-account","your-first-move/hello-sui.html#requesting-coins","your-first-move/hello-sui.html#publish","your-first-move/hello-sui.html#transaction-data","your-first-move/hello-sui.html#transaction-effects","your-first-move/hello-sui.html#events","your-first-move/hello-sui.html#object-changes","your-first-move/hello-sui.html#balance-changes","your-first-move/hello-sui.html#alternative-output","your-first-move/hello-sui.html#using-the-results","your-first-move/hello-sui.html#sending-transactions","your-first-move/hello-sui.html#prepare-the-variables","your-first-move/hello-sui.html#building-the-transaction-in-cli","your-first-move/hello-sui.html#passing-objects-to-functions","your-first-move/hello-sui.html#chaining-commands","your-first-move/hello-sui.html#conclusion","concepts/index.html#concepts","concepts/packages.html#package","concepts/packages.html#package-structure","concepts/packages.html#published-package","concepts/packages.html#links","concepts/manifest.html#package-manifest","concepts/manifest.html#sections","concepts/manifest.html#package","concepts/manifest.html#dependencies","concepts/manifest.html#resolving-version-conflicts-with-override","concepts/manifest.html#dev-dependencies","concepts/manifest.html#addresses","concepts/manifest.html#dev-addresses","concepts/manifest.html#toml-styles","concepts/manifest.html#further-reading","concepts/address.html#address","concepts/address.html#further-reading","concepts/what-is-an-account.html#account","concepts/what-is-an-account.html#further-reading","concepts/what-is-a-transaction.html#transaction","concepts/what-is-a-transaction.html#transaction-structure","concepts/what-is-a-transaction.html#inputs","concepts/what-is-a-transaction.html#commands","concepts/what-is-a-transaction.html#transaction-effects","move-basics/index.html#move-basics","move-basics/module.html#module","move-basics/module.html#module-declaration","move-basics/module.html#address--named-address","move-basics/module.html#module-members","move-basics/module.html#further-reading","move-basics/comments.html#comments","move-basics/comments.html#line-comment","move-basics/comments.html#block-comment","move-basics/comments.html#doc-comment","move-basics/primitive-types.html#primitive-types","move-basics/primitive-types.html#variables-and-assignment","move-basics/primitive-types.html#booleans","move-basics/primitive-types.html#integer-types","move-basics/primitive-types.html#operations","move-basics/primitive-types.html#casting-with-as","move-basics/primitive-types.html#overflow","move-basics/primitive-types.html#further-reading","move-basics/address.html#address-type","move-basics/address.html#conversion","move-basics/address.html#further-reading","move-basics/expression.html#expression","move-basics/expression.html#literals","move-basics/expression.html#operators","move-basics/expression.html#blocks","move-basics/expression.html#function-calls","move-basics/expression.html#control-flow-expressions","move-basics/struct.html#custom-types-with-struct","move-basics/struct.html#struct","move-basics/struct.html#create-and-use-an-instance","move-basics/struct.html#unpacking-a-struct","move-basics/struct.html#further-reading","move-basics/abilities-introduction.html#abilities-introduction","move-basics/abilities-introduction.html#what-are-abilities","move-basics/abilities-introduction.html#abilities-syntax","move-basics/abilities-introduction.html#overview","move-basics/abilities-introduction.html#no-abilities","move-basics/abilities-introduction.html#further-reading","move-basics/drop-ability.html#abilities-drop","move-basics/drop-ability.html#types-with-the-drop-ability","move-basics/drop-ability.html#further-reading","move-basics/importing-modules.html#importing-modules","move-basics/importing-modules.html#importing-a-module","move-basics/importing-modules.html#importing-members","move-basics/importing-modules.html#grouping-imports","move-basics/importing-modules.html#resolving-name-conflicts","move-basics/importing-modules.html#adding-an-external-dependency","move-basics/importing-modules.html#importing-a-module-from-another-package","move-basics/standard-library.html#standard-library","move-basics/standard-library.html#most-common-modules","move-basics/standard-library.html#exported-addresses","move-basics/standard-library.html#implicit-imports","move-basics/standard-library.html#importing-std-without-sui-framework","move-basics/standard-library.html#source-code","move-basics/vector.html#vector","move-basics/vector.html#vector-syntax","move-basics/vector.html#vector-operations","move-basics/vector.html#destroying-a-vector-of-non-droppable-types","move-basics/vector.html#further-reading","move-basics/option.html#option","move-basics/option.html#in-practice","move-basics/option.html#using-option","move-basics/string.html#string","move-basics/string.html#strings-are-bytes","move-basics/string.html#working-with-utf-8-strings","move-basics/string.html#definition","move-basics/string.html#creating-a-string","move-basics/string.html#common-operations","move-basics/string.html#safe-utf-8-operations","move-basics/string.html#utf-8-limitations","move-basics/string.html#ascii-strings","move-basics/control-flow.html#control-flow","move-basics/control-flow.html#conditional-statements","move-basics/control-flow.html#repeating-statements-with-loops","move-basics/control-flow.html#the-while-loop","move-basics/control-flow.html#infinite-loop","move-basics/control-flow.html#exiting-a-loop-early","move-basics/control-flow.html#skipping-an-iteration","move-basics/control-flow.html#early-return","move-basics/constants.html#constants","move-basics/constants.html#naming-convention","move-basics/constants.html#constants-are-immutable","move-basics/constants.html#using-config-pattern","move-basics/constants.html#links","move-basics/assert-and-abort.html#aborting-execution","move-basics/assert-and-abort.html#abort","move-basics/assert-and-abort.html#assert","move-basics/assert-and-abort.html#error-constants","move-basics/assert-and-abort.html#further-reading","move-basics/function.html#function","move-basics/function.html#function-declaration","move-basics/function.html#accessing-functions","move-basics/function.html#multiple-return-values","move-basics/function.html#further-reading","move-basics/struct-methods.html#struct-methods","move-basics/struct-methods.html#method-syntax","move-basics/struct-methods.html#method-aliases","move-basics/struct-methods.html#aliasing-an-external-modules-method","move-basics/struct-methods.html#further-reading","move-basics/visibility.html#visibility-modifiers","move-basics/visibility.html#internal-visibility","move-basics/visibility.html#public-visibility","move-basics/visibility.html#package-visibility","move-basics/ownership-and-scope.html#ownership-and-scope","move-basics/ownership-and-scope.html#ownership","move-basics/ownership-and-scope.html#returning-a-value","move-basics/ownership-and-scope.html#passing-by-value","move-basics/ownership-and-scope.html#scopes-with-blocks","move-basics/ownership-and-scope.html#copyable-types","move-basics/ownership-and-scope.html#further-reading","move-basics/copy-ability.html#abilities-copy","move-basics/copy-ability.html#copying-and-drop","move-basics/copy-ability.html#types-with-the-copy-ability","move-basics/copy-ability.html#further-reading","move-basics/references.html#references","move-basics/references.html#layout","move-basics/references.html#reference","move-basics/references.html#mutable-reference","move-basics/references.html#passing-by-value","move-basics/references.html#full-example","move-basics/generics.html#generics","move-basics/generics.html#in-the-standard-library","move-basics/generics.html#generic-syntax","move-basics/generics.html#multiple-type-parameters","move-basics/generics.html#why-generics","move-basics/generics.html#phantom-type-parameters","move-basics/generics.html#constraints-on-type-parameters","move-basics/generics.html#further-reading","move-basics/type-reflection.html#type-reflection","move-basics/type-reflection.html#in-practice","move-basics/type-reflection.html#further-reading","move-basics/testing.html#testing","move-basics/testing.html#the-test-attribute","move-basics/testing.html#running-tests","move-basics/testing.html#test-fail-cases-with-expected_failure","move-basics/testing.html#utilities-with-test_only","move-basics/testing.html#further-reading","object/index.html#object-model","object/digital-assets.html#move---language-for-digital-assets","object/digital-assets.html#summary","object/digital-assets.html#further-reading","object/evolution-of-move.html#evolution-of-move","object/evolution-of-move.html#summary","object/evolution-of-move.html#further-reading","object/object-model.html#what-is-an-object","object/object-model.html#summary","object/object-model.html#further-reading","object/ownership.html#ownership","object/ownership.html#account-owner-or-single-owner","object/ownership.html#shared-state","object/ownership.html#immutable-frozen-state","object/ownership.html#object-owner","object/ownership.html#summary","object/ownership.html#next-steps","object/fast-path-and-consensus.html#fast-path--consensus","object/fast-path-and-consensus.html#concurrency-challenge","object/fast-path-and-consensus.html#fast-path","object/fast-path-and-consensus.html#consensus-path","object/fast-path-and-consensus.html#objects-owned-by-objects","object/fast-path-and-consensus.html#summary","storage/index.html#using-objects","storage/key-ability.html#the-key-ability","storage/key-ability.html#object-definition","storage/key-ability.html#types-with-the-key-ability","storage/key-ability.html#next-steps","storage/key-ability.html#further-reading","storage/storage-functions.html#storage-functions","storage/storage-functions.html#overview","storage/storage-functions.html#ownership-and-references-a-quick-recap","storage/storage-functions.html#transfer","storage/storage-functions.html#freeze","storage/storage-functions.html#owned---frozen","storage/storage-functions.html#share","storage/storage-functions.html#special-case-shared-object-deletion","storage/storage-functions.html#next-steps","storage/store-ability.html#ability-store","storage/store-ability.html#definition","storage/store-ability.html#example","storage/store-ability.html#types-with-the-store-ability","storage/store-ability.html#further-reading","storage/uid-and-id.html#uid-and-id","storage/uid-and-id.html#fresh-uid-generation","storage/uid-and-id.html#uid-lifecycle","storage/uid-and-id.html#keeping-the-uid","storage/uid-and-id.html#proof-of-deletion","storage/uid-and-id.html#id","storage/uid-and-id.html#fresh_object_address","storage/transfer-restrictions.html#restricted-and-public-transfer","storage/transfer-restrictions.html#public-storage-operations","storage/transfer-restrictions.html#implications-of-store","programmability/index.html#advanced-programmability","programmability/transaction-context.html#transaction-context","programmability/transaction-context.html#reading-the-transaction-context","programmability/transaction-context.html#mutability","programmability/transaction-context.html#generating-unique-addresses","programmability/module-initializer.html#module-initializer","programmability/module-initializer.html#init-features","programmability/module-initializer.html#trust-and-security","programmability/module-initializer.html#next-steps","programmability/capability.html#pattern-capability","programmability/capability.html#capability-is-an-object","programmability/capability.html#using-init-for-admin-capability","programmability/capability.html#address-check-vs-capability","programmability/epoch-and-time.html#epoch-and-time","programmability/epoch-and-time.html#epoch","programmability/epoch-and-time.html#time","programmability/collections.html#collections","programmability/collections.html#vector","programmability/collections.html#vecset","programmability/collections.html#vecmap","programmability/collections.html#limitations","programmability/collections.html#summary","programmability/collections.html#next-steps","programmability/dynamic-fields.html#dynamic-fields","programmability/dynamic-fields.html#definition","programmability/dynamic-fields.html#usage","programmability/dynamic-fields.html#foreign-types-as-dynamic-fields","programmability/dynamic-fields.html#orphaned-dynamic-fields","programmability/dynamic-fields.html#custom-type-as-a-field-name","programmability/dynamic-fields.html#exposing-uid","programmability/dynamic-fields.html#dynamic-fields-vs-fields","programmability/dynamic-fields.html#limits","programmability/dynamic-fields.html#applications","programmability/dynamic-fields.html#next-steps","programmability/dynamic-object-fields.html#dynamic-object-fields","programmability/dynamic-object-fields.html#definition","programmability/dynamic-object-fields.html#usage--differences-with-dynamic-fields","programmability/dynamic-object-fields.html#pricing-differences","programmability/dynamic-object-fields.html#next-steps","programmability/dynamic-collections.html#dynamic-collections","programmability/dynamic-collections.html#common-concepts","programmability/dynamic-collections.html#bag","programmability/dynamic-collections.html#objectbag","programmability/dynamic-collections.html#table","programmability/dynamic-collections.html#objecttable","programmability/dynamic-collections.html#summary","programmability/dynamic-collections.html#linkedtable","programmability/witness-pattern.html#pattern-witness","programmability/witness-pattern.html#witness-in-move","programmability/witness-pattern.html#instantiating-a-generic-type","programmability/witness-pattern.html#one-time-witness","programmability/witness-pattern.html#summary","programmability/witness-pattern.html#next-steps","programmability/one-time-witness.html#one-time-witness","programmability/one-time-witness.html#definition","programmability/one-time-witness.html#enforcing-the-otw","programmability/one-time-witness.html#summary","programmability/publisher.html#publisher-authority","programmability/publisher.html#definition","programmability/publisher.html#usage","programmability/publisher.html#publisher-as-admin-role","programmability/publisher.html#role-on-sui","programmability/publisher.html#next-steps","programmability/display.html#object-display","programmability/display.html#background","programmability/display.html#object-display-1","programmability/display.html#creator-privilege","programmability/display.html#standard-fields","programmability/display.html#working-with-display","programmability/display.html#template-syntax","programmability/display.html#multiple-display-objects","programmability/display.html#further-reading","programmability/events.html#events","programmability/events.html#emitting-events","programmability/events.html#event-structure","programmability/sui-framework.html#sui-framework","programmability/sui-framework.html#core","programmability/sui-framework.html#collections","programmability/sui-framework.html#utilities","programmability/sui-framework.html#exported-addresses","programmability/sui-framework.html#implicit-imports","programmability/sui-framework.html#source-code","programmability/hot-potato-pattern.html#pattern-hot-potato","programmability/hot-potato-pattern.html#defining-a-hot-potato","programmability/hot-potato-pattern.html#example-usage","programmability/hot-potato-pattern.html#applications","programmability/hot-potato-pattern.html#borrowing","programmability/hot-potato-pattern.html#flash-loans","programmability/hot-potato-pattern.html#variable-path-execution","programmability/hot-potato-pattern.html#compositional-patterns","programmability/hot-potato-pattern.html#usage-in-the-sui-framework","programmability/hot-potato-pattern.html#summary","programmability/bcs.html#binary-canonical-serialization","programmability/bcs.html#format","programmability/bcs.html#using-bcs","programmability/bcs.html#encoding","programmability/bcs.html#encoding-a-struct","programmability/bcs.html#decoding","programmability/bcs.html#wrapper-api","programmability/bcs.html#decoding-vectors","programmability/bcs.html#decoding-option","programmability/bcs.html#decoding-structs","programmability/bcs.html#summary","guides/2024-migration-guide.html#move-2024-migration-guide","guides/2024-migration-guide.html#using-the-new-edition","guides/2024-migration-guide.html#migration-tool","guides/2024-migration-guide.html#mutable-bindings-with-let-mut","guides/2024-migration-guide.html#friends-are-deprecated","guides/2024-migration-guide.html#struct-visibility","guides/2024-migration-guide.html#method-syntax","guides/2024-migration-guide.html#methods-for-built-in-types","guides/2024-migration-guide.html#borrowing-operator","guides/2024-migration-guide.html#method-aliases","guides/upgradeability-practices.html#upgradeability-practices","guides/upgradeability-practices.html#versioning-objects","guides/upgradeability-practices.html#versioning-configuration-with-dynamic-fields","guides/upgradeability-practices.html#modular-architecture","guides/building-against-limits.html#building-against-limits","guides/building-against-limits.html#transaction-size","guides/building-against-limits.html#object-size","guides/building-against-limits.html#single-pure-argument-size","guides/building-against-limits.html#maximum-number-of-objects-created","guides/building-against-limits.html#maximum-number-of-dynamic-fields-created","guides/building-against-limits.html#maximum-number-of-events","guides/better-error-handling.html#better-error-handling","guides/better-error-handling.html#rule-1-handle-all-possible-scenarios","guides/better-error-handling.html#rule-2-abort-with-different-codes","guides/better-error-handling.html#rule-3-return-bool-instead-of-assert","guides/coding-conventions.html#coding-conventions","guides/coding-conventions.html#naming","guides/coding-conventions.html#module","guides/coding-conventions.html#constant","guides/coding-conventions.html#function","guides/coding-conventions.html#struct","guides/coding-conventions.html#struct-method","appendix/glossary.html#appendix-a-glossary","appendix/glossary.html#abilities","appendix/reserved-addresses.html#appendix-b-reserved-addresses","appendix/publications.html#appendix-c-publications","appendix/contributing.html#appendix-d-contributing","appendix/acknowledgements.html#appendix-e-acknowledgements"],"index":{"documentStore":{"docInfo":{"0":{"body":38,"breadcrumbs":4,"title":2},"1":{"body":288,"breadcrumbs":2,"title":1},"10":{"body":23,"breadcrumbs":6,"title":1},"100":{"body":73,"breadcrumbs":5,"title":2},"101":{"body":3,"breadcrumbs":5,"title":2},"102":{"body":43,"breadcrumbs":6,"title":2},"103":{"body":14,"breadcrumbs":5,"title":1},"104":{"body":39,"breadcrumbs":6,"title":2},"105":{"body":65,"breadcrumbs":5,"title":1},"106":{"body":37,"breadcrumbs":5,"title":1},"107":{"body":4,"breadcrumbs":6,"title":2},"108":{"body":135,"breadcrumbs":6,"title":2},"109":{"body":22,"breadcrumbs":7,"title":3},"11":{"body":16,"breadcrumbs":7,"title":2},"110":{"body":4,"breadcrumbs":6,"title":2},"111":{"body":30,"breadcrumbs":6,"title":2},"112":{"body":54,"breadcrumbs":6,"title":2},"113":{"body":20,"breadcrumbs":6,"title":2},"114":{"body":67,"breadcrumbs":6,"title":2},"115":{"body":38,"breadcrumbs":7,"title":3},"116":{"body":87,"breadcrumbs":7,"title":3},"117":{"body":53,"breadcrumbs":8,"title":4},"118":{"body":24,"breadcrumbs":6,"title":2},"119":{"body":81,"breadcrumbs":6,"title":2},"12":{"body":13,"breadcrumbs":6,"title":1},"120":{"body":11,"breadcrumbs":6,"title":2},"121":{"body":16,"breadcrumbs":6,"title":2},"122":{"body":28,"breadcrumbs":9,"title":5},"123":{"body":8,"breadcrumbs":6,"title":2},"124":{"body":18,"breadcrumbs":4,"title":1},"125":{"body":78,"breadcrumbs":5,"title":2},"126":{"body":51,"breadcrumbs":5,"title":2},"127":{"body":50,"breadcrumbs":8,"title":5},"128":{"body":3,"breadcrumbs":5,"title":2},"129":{"body":95,"breadcrumbs":4,"title":1},"13":{"body":19,"breadcrumbs":7,"title":2},"130":{"body":113,"breadcrumbs":4,"title":1},"131":{"body":56,"breadcrumbs":5,"title":2},"132":{"body":45,"breadcrumbs":4,"title":1},"133":{"body":65,"breadcrumbs":5,"title":2},"134":{"body":22,"breadcrumbs":7,"title":4},"135":{"body":24,"breadcrumbs":4,"title":1},"136":{"body":45,"breadcrumbs":5,"title":2},"137":{"body":73,"breadcrumbs":5,"title":2},"138":{"body":71,"breadcrumbs":7,"title":4},"139":{"body":38,"breadcrumbs":6,"title":3},"14":{"body":31,"breadcrumbs":6,"title":2},"140":{"body":3,"breadcrumbs":5,"title":2},"141":{"body":48,"breadcrumbs":6,"title":2},"142":{"body":131,"breadcrumbs":6,"title":2},"143":{"body":58,"breadcrumbs":7,"title":3},"144":{"body":106,"breadcrumbs":5,"title":1},"145":{"body":98,"breadcrumbs":6,"title":2},"146":{"body":99,"breadcrumbs":7,"title":3},"147":{"body":85,"breadcrumbs":6,"title":2},"148":{"body":89,"breadcrumbs":6,"title":2},"149":{"body":31,"breadcrumbs":4,"title":1},"15":{"body":69,"breadcrumbs":4,"title":2},"150":{"body":33,"breadcrumbs":5,"title":2},"151":{"body":23,"breadcrumbs":5,"title":2},"152":{"body":44,"breadcrumbs":6,"title":3},"153":{"body":6,"breadcrumbs":4,"title":1},"154":{"body":42,"breadcrumbs":6,"title":2},"155":{"body":42,"breadcrumbs":5,"title":1},"156":{"body":46,"breadcrumbs":5,"title":1},"157":{"body":72,"breadcrumbs":6,"title":2},"158":{"body":16,"breadcrumbs":6,"title":2},"159":{"body":98,"breadcrumbs":4,"title":1},"16":{"body":83,"breadcrumbs":5,"title":3},"160":{"body":56,"breadcrumbs":5,"title":2},"161":{"body":27,"breadcrumbs":5,"title":2},"162":{"body":87,"breadcrumbs":6,"title":3},"163":{"body":3,"breadcrumbs":5,"title":2},"164":{"body":23,"breadcrumbs":6,"title":2},"165":{"body":112,"breadcrumbs":6,"title":2},"166":{"body":97,"breadcrumbs":6,"title":2},"167":{"body":33,"breadcrumbs":8,"title":4},"168":{"body":4,"breadcrumbs":6,"title":2},"169":{"body":34,"breadcrumbs":6,"title":2},"17":{"body":21,"breadcrumbs":4,"title":2},"170":{"body":39,"breadcrumbs":6,"title":2},"171":{"body":38,"breadcrumbs":6,"title":2},"172":{"body":41,"breadcrumbs":6,"title":2},"173":{"body":25,"breadcrumbs":6,"title":2},"174":{"body":69,"breadcrumbs":5,"title":1},"175":{"body":29,"breadcrumbs":6,"title":2},"176":{"body":43,"breadcrumbs":6,"title":2},"177":{"body":99,"breadcrumbs":6,"title":2},"178":{"body":31,"breadcrumbs":6,"title":2},"179":{"body":5,"breadcrumbs":6,"title":2},"18":{"body":36,"breadcrumbs":3,"title":1},"180":{"body":103,"breadcrumbs":6,"title":2},"181":{"body":49,"breadcrumbs":6,"title":2},"182":{"body":22,"breadcrumbs":7,"title":3},"183":{"body":4,"breadcrumbs":6,"title":2},"184":{"body":78,"breadcrumbs":4,"title":1},"185":{"body":62,"breadcrumbs":4,"title":1},"186":{"body":79,"breadcrumbs":4,"title":1},"187":{"body":49,"breadcrumbs":5,"title":2},"188":{"body":51,"breadcrumbs":5,"title":2},"189":{"body":43,"breadcrumbs":5,"title":2},"19":{"body":78,"breadcrumbs":3,"title":1},"190":{"body":30,"breadcrumbs":4,"title":1},"191":{"body":21,"breadcrumbs":5,"title":2},"192":{"body":127,"breadcrumbs":5,"title":2},"193":{"body":202,"breadcrumbs":6,"title":3},"194":{"body":102,"breadcrumbs":4,"title":1},"195":{"body":129,"breadcrumbs":6,"title":3},"196":{"body":92,"breadcrumbs":6,"title":3},"197":{"body":3,"breadcrumbs":5,"title":2},"198":{"body":49,"breadcrumbs":6,"title":2},"199":{"body":71,"breadcrumbs":5,"title":1},"2":{"body":29,"breadcrumbs":4,"title":2},"20":{"body":89,"breadcrumbs":3,"title":1},"200":{"body":10,"breadcrumbs":6,"title":2},"201":{"body":19,"breadcrumbs":4,"title":1},"202":{"body":69,"breadcrumbs":5,"title":2},"203":{"body":43,"breadcrumbs":5,"title":2},"204":{"body":89,"breadcrumbs":7,"title":4},"205":{"body":91,"breadcrumbs":5,"title":2},"206":{"body":4,"breadcrumbs":5,"title":2},"207":{"body":53,"breadcrumbs":4,"title":2},"208":{"body":199,"breadcrumbs":9,"title":4},"209":{"body":41,"breadcrumbs":6,"title":1},"21":{"body":31,"breadcrumbs":3,"title":1},"210":{"body":31,"breadcrumbs":7,"title":2},"211":{"body":105,"breadcrumbs":6,"title":2},"212":{"body":28,"breadcrumbs":5,"title":1},"213":{"body":5,"breadcrumbs":6,"title":2},"214":{"body":169,"breadcrumbs":4,"title":1},"215":{"body":26,"breadcrumbs":4,"title":1},"216":{"body":4,"breadcrumbs":5,"title":2},"217":{"body":30,"breadcrumbs":4,"title":1},"218":{"body":62,"breadcrumbs":7,"title":4},"219":{"body":96,"breadcrumbs":5,"title":2},"22":{"body":144,"breadcrumbs":4,"title":2},"220":{"body":33,"breadcrumbs":6,"title":3},"221":{"body":78,"breadcrumbs":5,"title":2},"222":{"body":41,"breadcrumbs":4,"title":1},"223":{"body":12,"breadcrumbs":5,"title":2},"224":{"body":30,"breadcrumbs":8,"title":3},"225":{"body":59,"breadcrumbs":7,"title":2},"226":{"body":65,"breadcrumbs":7,"title":2},"227":{"body":29,"breadcrumbs":7,"title":2},"228":{"body":24,"breadcrumbs":8,"title":3},"229":{"body":38,"breadcrumbs":6,"title":1},"23":{"body":136,"breadcrumbs":4,"title":2},"230":{"body":33,"breadcrumbs":4,"title":2},"231":{"body":54,"breadcrumbs":6,"title":2},"232":{"body":86,"breadcrumbs":6,"title":2},"233":{"body":21,"breadcrumbs":7,"title":3},"234":{"body":18,"breadcrumbs":6,"title":2},"235":{"body":4,"breadcrumbs":6,"title":2},"236":{"body":22,"breadcrumbs":6,"title":2},"237":{"body":64,"breadcrumbs":5,"title":1},"238":{"body":99,"breadcrumbs":8,"title":4},"239":{"body":276,"breadcrumbs":5,"title":1},"24":{"body":18,"breadcrumbs":4,"title":2},"240":{"body":226,"breadcrumbs":5,"title":1},"241":{"body":55,"breadcrumbs":6,"title":2},"242":{"body":40,"breadcrumbs":5,"title":1},"243":{"body":145,"breadcrumbs":9,"title":5},"244":{"body":40,"breadcrumbs":6,"title":2},"245":{"body":14,"breadcrumbs":6,"title":2},"246":{"body":36,"breadcrumbs":5,"title":1},"247":{"body":58,"breadcrumbs":5,"title":1},"248":{"body":24,"breadcrumbs":7,"title":3},"249":{"body":4,"breadcrumbs":6,"title":2},"25":{"body":6,"breadcrumbs":4,"title":2},"250":{"body":44,"breadcrumbs":6,"title":2},"251":{"body":79,"breadcrumbs":7,"title":3},"252":{"body":29,"breadcrumbs":6,"title":2},"253":{"body":23,"breadcrumbs":6,"title":2},"254":{"body":56,"breadcrumbs":6,"title":2},"255":{"body":39,"breadcrumbs":5,"title":1},"256":{"body":19,"breadcrumbs":5,"title":1},"257":{"body":49,"breadcrumbs":8,"title":3},"258":{"body":188,"breadcrumbs":8,"title":3},"259":{"body":34,"breadcrumbs":7,"title":2},"26":{"body":26,"breadcrumbs":4,"title":2},"260":{"body":43,"breadcrumbs":4,"title":2},"261":{"body":119,"breadcrumbs":6,"title":2},"262":{"body":27,"breadcrumbs":7,"title":3},"263":{"body":47,"breadcrumbs":5,"title":1},"264":{"body":57,"breadcrumbs":7,"title":3},"265":{"body":113,"breadcrumbs":6,"title":2},"266":{"body":68,"breadcrumbs":6,"title":2},"267":{"body":56,"breadcrumbs":6,"title":2},"268":{"body":36,"breadcrumbs":6,"title":2},"269":{"body":40,"breadcrumbs":6,"title":2},"27":{"body":14,"breadcrumbs":5,"title":3},"270":{"body":132,"breadcrumbs":6,"title":2},"271":{"body":18,"breadcrumbs":8,"title":4},"272":{"body":158,"breadcrumbs":8,"title":4},"273":{"body":28,"breadcrumbs":6,"title":2},"274":{"body":74,"breadcrumbs":5,"title":1},"275":{"body":135,"breadcrumbs":5,"title":1},"276":{"body":28,"breadcrumbs":4,"title":1},"277":{"body":48,"breadcrumbs":4,"title":1},"278":{"body":31,"breadcrumbs":4,"title":1},"279":{"body":54,"breadcrumbs":4,"title":1},"28":{"body":120,"breadcrumbs":4,"title":2},"280":{"body":92,"breadcrumbs":4,"title":1},"281":{"body":39,"breadcrumbs":4,"title":1},"282":{"body":20,"breadcrumbs":5,"title":2},"283":{"body":81,"breadcrumbs":6,"title":2},"284":{"body":99,"breadcrumbs":5,"title":1},"285":{"body":235,"breadcrumbs":5,"title":1},"286":{"body":90,"breadcrumbs":8,"title":4},"287":{"body":117,"breadcrumbs":7,"title":3},"288":{"body":154,"breadcrumbs":8,"title":4},"289":{"body":149,"breadcrumbs":6,"title":2},"29":{"body":64,"breadcrumbs":4,"title":2},"290":{"body":25,"breadcrumbs":8,"title":4},"291":{"body":22,"breadcrumbs":5,"title":1},"292":{"body":32,"breadcrumbs":5,"title":1},"293":{"body":12,"breadcrumbs":6,"title":2},"294":{"body":59,"breadcrumbs":8,"title":3},"295":{"body":202,"breadcrumbs":6,"title":1},"296":{"body":173,"breadcrumbs":9,"title":4},"297":{"body":30,"breadcrumbs":7,"title":2},"298":{"body":36,"breadcrumbs":7,"title":2},"299":{"body":46,"breadcrumbs":6,"title":2},"3":{"body":20,"breadcrumbs":6,"title":2},"30":{"body":114,"breadcrumbs":5,"title":3},"300":{"body":78,"breadcrumbs":6,"title":2},"301":{"body":122,"breadcrumbs":5,"title":1},"302":{"body":13,"breadcrumbs":5,"title":1},"303":{"body":126,"breadcrumbs":5,"title":1},"304":{"body":13,"breadcrumbs":5,"title":1},"305":{"body":23,"breadcrumbs":5,"title":1},"306":{"body":3,"breadcrumbs":5,"title":1},"307":{"body":19,"breadcrumbs":6,"title":2},"308":{"body":135,"breadcrumbs":6,"title":2},"309":{"body":126,"breadcrumbs":7,"title":3},"31":{"body":105,"breadcrumbs":4,"title":2},"310":{"body":24,"breadcrumbs":7,"title":3},"311":{"body":19,"breadcrumbs":5,"title":1},"312":{"body":7,"breadcrumbs":6,"title":2},"313":{"body":19,"breadcrumbs":8,"title":3},"314":{"body":81,"breadcrumbs":6,"title":1},"315":{"body":34,"breadcrumbs":7,"title":2},"316":{"body":42,"breadcrumbs":6,"title":1},"317":{"body":30,"breadcrumbs":6,"title":2},"318":{"body":110,"breadcrumbs":5,"title":1},"319":{"body":31,"breadcrumbs":5,"title":1},"32":{"body":139,"breadcrumbs":3,"title":1},"320":{"body":76,"breadcrumbs":7,"title":3},"321":{"body":20,"breadcrumbs":6,"title":2},"322":{"body":19,"breadcrumbs":6,"title":2},"323":{"body":23,"breadcrumbs":5,"title":2},"324":{"body":89,"breadcrumbs":4,"title":1},"325":{"body":152,"breadcrumbs":5,"title":2},"326":{"body":36,"breadcrumbs":5,"title":2},"327":{"body":82,"breadcrumbs":5,"title":2},"328":{"body":80,"breadcrumbs":5,"title":2},"329":{"body":70,"breadcrumbs":5,"title":2},"33":{"body":84,"breadcrumbs":4,"title":2},"330":{"body":13,"breadcrumbs":6,"title":3},"331":{"body":8,"breadcrumbs":5,"title":2},"332":{"body":79,"breadcrumbs":4,"title":1},"333":{"body":111,"breadcrumbs":5,"title":2},"334":{"body":33,"breadcrumbs":5,"title":2},"335":{"body":34,"breadcrumbs":6,"title":2},"336":{"body":89,"breadcrumbs":5,"title":1},"337":{"body":54,"breadcrumbs":5,"title":1},"338":{"body":12,"breadcrumbs":5,"title":1},"339":{"body":20,"breadcrumbs":6,"title":2},"34":{"body":91,"breadcrumbs":4,"title":2},"340":{"body":22,"breadcrumbs":6,"title":2},"341":{"body":7,"breadcrumbs":6,"title":2},"342":{"body":77,"breadcrumbs":8,"title":3},"343":{"body":46,"breadcrumbs":8,"title":3},"344":{"body":132,"breadcrumbs":7,"title":2},"345":{"body":8,"breadcrumbs":6,"title":1},"346":{"body":27,"breadcrumbs":6,"title":1},"347":{"body":61,"breadcrumbs":7,"title":2},"348":{"body":141,"breadcrumbs":8,"title":3},"349":{"body":42,"breadcrumbs":7,"title":2},"35":{"body":12,"breadcrumbs":3,"title":1},"350":{"body":40,"breadcrumbs":8,"title":3},"351":{"body":33,"breadcrumbs":6,"title":1},"352":{"body":31,"breadcrumbs":6,"title":3},"353":{"body":93,"breadcrumbs":4,"title":1},"354":{"body":16,"breadcrumbs":5,"title":2},"355":{"body":68,"breadcrumbs":4,"title":1},"356":{"body":36,"breadcrumbs":5,"title":2},"357":{"body":19,"breadcrumbs":4,"title":1},"358":{"body":94,"breadcrumbs":5,"title":2},"359":{"body":74,"breadcrumbs":5,"title":2},"36":{"body":69,"breadcrumbs":4,"title":2},"360":{"body":67,"breadcrumbs":5,"title":2},"361":{"body":36,"breadcrumbs":5,"title":2},"362":{"body":25,"breadcrumbs":4,"title":1},"363":{"body":37,"breadcrumbs":7,"title":4},"364":{"body":21,"breadcrumbs":6,"title":3},"365":{"body":34,"breadcrumbs":5,"title":2},"366":{"body":102,"breadcrumbs":6,"title":3},"367":{"body":28,"breadcrumbs":5,"title":2},"368":{"body":19,"breadcrumbs":5,"title":2},"369":{"body":56,"breadcrumbs":5,"title":2},"37":{"body":28,"breadcrumbs":4,"title":2},"370":{"body":46,"breadcrumbs":6,"title":3},"371":{"body":74,"breadcrumbs":5,"title":2},"372":{"body":36,"breadcrumbs":5,"title":2},"373":{"body":133,"breadcrumbs":4,"title":2},"374":{"body":70,"breadcrumbs":4,"title":2},"375":{"body":64,"breadcrumbs":6,"title":4},"376":{"body":3,"breadcrumbs":4,"title":2},"377":{"body":45,"breadcrumbs":6,"title":3},"378":{"body":19,"breadcrumbs":5,"title":2},"379":{"body":32,"breadcrumbs":5,"title":2},"38":{"body":24,"breadcrumbs":4,"title":2},"380":{"body":43,"breadcrumbs":7,"title":4},"381":{"body":30,"breadcrumbs":7,"title":4},"382":{"body":17,"breadcrumbs":8,"title":5},"383":{"body":14,"breadcrumbs":6,"title":3},"384":{"body":101,"breadcrumbs":6,"title":3},"385":{"body":71,"breadcrumbs":8,"title":5},"386":{"body":82,"breadcrumbs":8,"title":5},"387":{"body":111,"breadcrumbs":9,"title":6},"388":{"body":0,"breadcrumbs":4,"title":2},"389":{"body":0,"breadcrumbs":3,"title":1},"39":{"body":39,"breadcrumbs":4,"title":2},"390":{"body":11,"breadcrumbs":3,"title":1},"391":{"body":13,"breadcrumbs":3,"title":1},"392":{"body":17,"breadcrumbs":3,"title":1},"393":{"body":25,"breadcrumbs":3,"title":1},"394":{"body":47,"breadcrumbs":4,"title":2},"395":{"body":47,"breadcrumbs":3,"title":2},"396":{"body":70,"breadcrumbs":2,"title":1},"397":{"body":46,"breadcrumbs":7,"title":4},"398":{"body":46,"breadcrumbs":5,"title":3},"399":{"body":16,"breadcrumbs":5,"title":3},"4":{"body":18,"breadcrumbs":6,"title":2},"40":{"body":32,"breadcrumbs":4,"title":2},"400":{"body":27,"breadcrumbs":5,"title":3},"41":{"body":27,"breadcrumbs":4,"title":2},"42":{"body":446,"breadcrumbs":5,"title":3},"43":{"body":183,"breadcrumbs":5,"title":3},"44":{"body":133,"breadcrumbs":4,"title":2},"45":{"body":34,"breadcrumbs":3,"title":1},"46":{"body":30,"breadcrumbs":2,"title":1},"47":{"body":66,"breadcrumbs":3,"title":1},"48":{"body":52,"breadcrumbs":4,"title":2},"49":{"body":31,"breadcrumbs":4,"title":2},"5":{"body":9,"breadcrumbs":8,"title":4},"50":{"body":6,"breadcrumbs":3,"title":1},"51":{"body":42,"breadcrumbs":4,"title":2},"52":{"body":0,"breadcrumbs":3,"title":1},"53":{"body":36,"breadcrumbs":3,"title":1},"54":{"body":59,"breadcrumbs":3,"title":1},"55":{"body":45,"breadcrumbs":6,"title":4},"56":{"body":28,"breadcrumbs":4,"title":2},"57":{"body":21,"breadcrumbs":3,"title":1},"58":{"body":34,"breadcrumbs":4,"title":2},"59":{"body":56,"breadcrumbs":4,"title":2},"6":{"body":10,"breadcrumbs":8,"title":4},"60":{"body":3,"breadcrumbs":4,"title":2},"61":{"body":94,"breadcrumbs":3,"title":1},"62":{"body":3,"breadcrumbs":4,"title":2},"63":{"body":45,"breadcrumbs":3,"title":1},"64":{"body":11,"breadcrumbs":4,"title":2},"65":{"body":26,"breadcrumbs":3,"title":1},"66":{"body":41,"breadcrumbs":4,"title":2},"67":{"body":72,"breadcrumbs":3,"title":1},"68":{"body":84,"breadcrumbs":3,"title":1},"69":{"body":90,"breadcrumbs":4,"title":2},"7":{"body":29,"breadcrumbs":9,"title":5},"70":{"body":40,"breadcrumbs":4,"title":2},"71":{"body":24,"breadcrumbs":4,"title":1},"72":{"body":65,"breadcrumbs":5,"title":2},"73":{"body":35,"breadcrumbs":6,"title":3},"74":{"body":53,"breadcrumbs":5,"title":2},"75":{"body":3,"breadcrumbs":5,"title":2},"76":{"body":36,"breadcrumbs":4,"title":1},"77":{"body":16,"breadcrumbs":5,"title":2},"78":{"body":42,"breadcrumbs":5,"title":2},"79":{"body":22,"breadcrumbs":5,"title":2},"8":{"body":8,"breadcrumbs":5,"title":1},"80":{"body":30,"breadcrumbs":6,"title":2},"81":{"body":56,"breadcrumbs":6,"title":2},"82":{"body":42,"breadcrumbs":5,"title":1},"83":{"body":75,"breadcrumbs":6,"title":2},"84":{"body":68,"breadcrumbs":5,"title":1},"85":{"body":49,"breadcrumbs":5,"title":1},"86":{"body":27,"breadcrumbs":5,"title":1},"87":{"body":6,"breadcrumbs":6,"title":2},"88":{"body":68,"breadcrumbs":6,"title":2},"89":{"body":59,"breadcrumbs":5,"title":1},"9":{"body":39,"breadcrumbs":8,"title":3},"90":{"body":3,"breadcrumbs":6,"title":2},"91":{"body":32,"breadcrumbs":4,"title":1},"92":{"body":83,"breadcrumbs":4,"title":1},"93":{"body":37,"breadcrumbs":4,"title":1},"94":{"body":60,"breadcrumbs":4,"title":1},"95":{"body":44,"breadcrumbs":5,"title":2},"96":{"body":43,"breadcrumbs":6,"title":3},"97":{"body":24,"breadcrumbs":6,"title":3},"98":{"body":156,"breadcrumbs":4,"title":1},"99":{"body":94,"breadcrumbs":6,"title":3}},"docs":{"0":{"body":"This is the Move Book - a comprehensive guide to the Move programming language and the Sui blockchain. The book is intended for developers who are interested in learning about Move and building on Sui. The book is in active development and a work in progress. If you have any feedback or suggestions, feel free to open an issue or a pull request on the GitHub repository . If you're looking for The Move Reference, you can find it here .","breadcrumbs":"The Move Book » The Move Book","id":"0","title":"The Move Book"},"1":{"body":"This book is dedicated to Move, a smart contract language that captures the essence of safe programming with digital assets. Move is designed around the following values: Secure by default: Insecure languages are a serious barrier both to accessible smart contract development and to mainstream adoption of digital assets. The first duty of a smart contract language is to prevent as many potential safety issues as possible (e.g. re-entrancy, missing access control checks, arithmetic overflow, ...) by construction. Any changes to Move should preserve or enhance its existing security guarantees. Expressive by nature: Move must enable programmers to write any smart contract they can imagine. But we care as much about the way it feels to write Move as we do about what Move allows you to do - the language should be rich enough that the features needed for a task are available, and minimal enough that the choice is obvious. The Move toolchain should be a productivity enhancer and a thought partner. Intuitive for all: Smart contracts are only one part of a useful application. Move should understand the broader context of its usage and design with both the smart contract developer and the application developer in mind. It should be easy for developers to learn how to read Move-managed state, build Move powered transactions, and write new Move code. The core technical elements of Move are: Safe, familiar, and flexible abstractions for digital assets via programmable objects . A rich ability system (inspired by linear types) that gives programmers extreme control of how values are created, destroyed, stored, copied, and transferred. A module system with strong encapsulation features to enable code reuse while maintaining this control. Dynamic fields for creating hierarchical relationships between objects. Programmable transaction blocks (PTBs) to enable atomic client-side composition of Move-powered APIs. Move was born in 2018 as part of Facebook's Libra project. It was publicly revealed in 2019, the first Move-powered network launched in 2020. As of April 2024, there are numerous Move-powered chains in production with several more in the works. Move is an embedded language with a platform-agnostic core, which means it takes on a slightly different personality in each chain that uses it. Creating a new programming language and bootstrapping a community around it is an ambitious, long term project. A language has to be an order of magnitude better than alternatives in relevant ways to have a chance, but even then the quality of the community matters more than the technical fundamentals. Move is a young language, but it's off to a good start in terms of both differentiation and community. A small, but fanatical group of smart contract programmers and core contributors united by the Move values are pushing the boundaries of what smart contracts can do, the applications they can enable, and who can (safely) write them. If that inspires you, read on! — Sam Blackshear, creator of Move","breadcrumbs":"Foreword » Foreword","id":"1","title":"Foreword"},"10":{"body":"VSCode is a free and open source IDE from Microsoft. Move (Extension) is a language server extension for Move maintained by MystenLabs . Move Syntax a simple syntax highlighting extension for Move by Damir Shamanaev .","breadcrumbs":"Before we begin » Set up your IDE » VSCode","id":"10","title":"VSCode"},"100":{"body":"Structs are non-discardable by default, meaning that the initiated struct value must be used: either stored or unpacked . Unpacking a struct means deconstructing it into its fields. This is done using the let keyword followed by the struct name and the field names. // Unpack the `Artist` struct and create a new variable `name`\n// with the value of the `name` field.\nlet Artist { name } = artist; In the example above we unpack the Artist struct and create a new variable name with the value of the name field. Because the variable is not used, the compiler will raise a warning. To suppress the warning, you can use the underscore _ to indicate that the variable is intentionally unused. // Unpack the `Artist` struct and ignore the `name` field.\nlet Artist { name: _ } = artist;","breadcrumbs":"Move Basics » Struct » Unpacking a struct","id":"100","title":"Unpacking a struct"},"101":{"body":"Structs in the Move Reference.","breadcrumbs":"Move Basics » Struct » Further reading","id":"101","title":"Further reading"},"102":{"body":"Move has a unique type system which allows customizing type abilities . In the previous section , we introduced the struct definition and how to use it. However, the instances of the Artist and Record structs had to be unpacked for the code to compile. This is default behavior of a struct without abilities . Throughout the book you will see chapters with name Ability: , where is the name of the ability. These chapters will cover the ability in detail, how it works, and how to use it in Move.","breadcrumbs":"Move Basics » Abilities: Introduction » Abilities: Introduction","id":"102","title":"Abilities: Introduction"},"103":{"body":"Abilities are a way to allow certain behaviors for a type. They are a part of the struct declaration and define which behaviours are allowed for the instances of the struct.","breadcrumbs":"Move Basics » Abilities: Introduction » What are Abilities?","id":"103","title":"What are Abilities?"},"104":{"body":"Abilities are set in the struct definition using the has keyword followed by a list of abilities. The abilities are separated by commas. Move supports 4 abilities: copy, drop, key, and store, each of them is used to define a specific behaviour for the struct instances. /// This struct has the `copy` and `drop` abilities.\nstruct VeryAble has copy, drop { // field: Type1, // field2: Type2, // ...\n}","breadcrumbs":"Move Basics » Abilities: Introduction » Abilities syntax","id":"104","title":"Abilities syntax"},"105":{"body":"A quick overview of the abilities: All of the built-in types, except references, have copy, drop and store abilities. References have copy and drop. copy - allows the struct to be copied . Explained in the Ability: Copy chapter. drop - allows the struct to be dropped or discarded . Explained in the Ability: Drop chapter. key - allows the struct to be used as a key in a storage. Explained in the Ability: Key chapter. store - allows the struct to be stored in structs with the key ability. Explained in the Ability: Store chapter. While it is important to mention them here, we will go in detail about each ability in the following chapters and give a proper context on how to use them.","breadcrumbs":"Move Basics » Abilities: Introduction » Overview","id":"105","title":"Overview"},"106":{"body":"A struct without abilities cannot be discarded, or copied, or stored in the storage. We call such a struct a Hot Potato . It is a joke, but it is also a good way to remember that a struct without abilities is like a hot potato - it can only be passed around and requires special handling. Hot Potato is one of the most powerful patterns in Move, we go in detail about it in the Hot Potato chapter.","breadcrumbs":"Move Basics » Abilities: Introduction » No abilities","id":"106","title":"No abilities"},"107":{"body":"Type Abilities in the Move Reference.","breadcrumbs":"Move Basics » Abilities: Introduction » Further reading","id":"107","title":"Further reading"},"108":{"body":"The drop ability - the simplest of them - allows the instance of a struct to be ignored or discarded . In many programming languages this behavior is considered default. However, in Move, a struct without the drop ability is not allowed to be ignored. This is a safety feature of the Move language, which ensures that all assets are properly handled. An attempt to ignore a struct without the drop ability will result in a compilation error. module book::drop_ability; /// This struct has the `drop` ability.\npublic struct IgnoreMe has drop { a: u8, b: u8,\n} /// This struct does not have the `drop` ability.\npublic struct NoDrop {} #[test]\n// Create an instance of the `IgnoreMe` struct and ignore it.\n// Even though we constructed the instance, we don't need to unpack it.\nfun test_ignore() { let no_drop = NoDrop {}; let _ = IgnoreMe { a: 1, b: 2 }; // no need to unpack // The value must be unpacked for the code to compile. let NoDrop {} = no_drop; // OK\n} The drop ability is often used on custom collection types to eliminate the need for special handling of the collection when it is no longer needed. For example, a vector type has the drop ability, which allows the vector to be ignored when it is no longer needed. However, the biggest feature of Move's type system is the ability to not have drop. This ensures that the assets are properly handled and not ignored. A struct with a single drop ability is called a Witness . We explain the concept of a Witness in the Witness and Abstract Implementation section.","breadcrumbs":"Move Basics » Ability: Drop » Abilities: Drop","id":"108","title":"Abilities: Drop"},"109":{"body":"All native types in Move have the drop ability. This includes: bool unsigned integers vector address All of the types defined in the standard library have the drop ability as well. This includes: Option String TypeName","breadcrumbs":"Move Basics » Ability: Drop » Types with the drop Ability","id":"109","title":"Types with the drop Ability"},"11":{"body":"IntelliJ IDEA is a commercial IDE from JetBrains. Move Language Plugin provides a Move language extension for IntelliJ IDEA by Pontem Network .","breadcrumbs":"Before we begin » Set up your IDE » IntelliJ IDEA","id":"11","title":"IntelliJ IDEA"},"110":{"body":"Type Abilities in the Move Reference.","breadcrumbs":"Move Basics » Ability: Drop » Further reading","id":"110","title":"Further reading"},"111":{"body":"Move achieves high modularity and code reuse by allowing module imports. Modules within the same package can import each other, and a new package can depend on already existing packages and use their modules too. This section will cover the basics of importing modules and how to use them in your own code.","breadcrumbs":"Move Basics » Importing Modules » Importing Modules","id":"111","title":"Importing Modules"},"112":{"body":"Modules defined in the same package can import each other. The use keyword is followed by the module path, which consists of the package address (or alias) and the module name separated by ::. // File: sources/module_one.move\nmodule book::module_one; /// Struct defined in the same module.\npublic struct Character has drop {} /// Simple function that creates a new `Character` instance.\npublic fun new(): Character { Character {} } Another module defined in the same package can import the first module using the use keyword. // File: sources/module_two.move","breadcrumbs":"Move Basics » Importing Modules » Importing a Module","id":"112","title":"Importing a Module"},"113":{"body":"You can also import specific members from a module. This is useful when you only need a single function or a single type from a module. The syntax is the same as for importing a module, but you add the member name after the module path.","breadcrumbs":"Move Basics » Importing Modules » Importing Members","id":"113","title":"Importing Members"},"114":{"body":"Imports can be grouped into a single use statement using the curly braces {}. This is useful when you need to import multiple members from the same module. Move allows grouping imports from the same module and from the same package. Single function imports are less common in Move, since the function names can overlap and cause confusion. A recommended practice is to import the entire module and use the module path to access the function. Types have unique names and should be imported individually. To import members and the module itself in the group import, you can use the Self keyword. The Self keyword refers to the module itself and can be used to import the module and its members.","breadcrumbs":"Move Basics » Importing Modules » Grouping Imports","id":"114","title":"Grouping Imports"},"115":{"body":"When importing multiple members from different modules, it is possible to have name conflicts. For example, if you import two modules that both have a function with the same name, you will need to use the module path to access the function. It is also possible to have modules with the same name in different packages. To resolve the conflict and avoid ambiguity, Move offers the as keyword to rename the imported member.","breadcrumbs":"Move Basics » Importing Modules » Resolving Name Conflicts","id":"115","title":"Resolving Name Conflicts"},"116":{"body":"Every new package generated via the sui binary features a Move.toml file with a single dependency on the Sui Framework package. The Sui Framework depends on the Standard Library package. And both of these packages are available in default configuration. Package dependencies are defined in the Package Manifest as follows: [dependencies]\nSui = { git = \"https://github.com/MystenLabs/sui.git\", subdir = \"crates/sui-framework/packages/sui-framework\", rev = \"framework/testnet\" }\nLocal = { local = \"../my_other_package\" } The dependencies section contains a list of package dependencies. The key is the name of the package, and the value is either a git import table or a local path. The git import contains the URL of the package, the subdirectory where the package is located, and the revision of the package. The local path is a relative path to the package directory. If a dependency is added to the Move.toml file, the compiler will automatically fetch (and later refetch) the dependencies when building the package.","breadcrumbs":"Move Basics » Importing Modules » Adding an External Dependency","id":"116","title":"Adding an External Dependency"},"117":{"body":"Normally, packages define their addresses in the [addresses] section, so you can use the alias instead of the address. For example, instead of 0x2::coin module, you would use sui::coin. The sui alias is defined in the Sui Framework package. Similarly, the std alias is defined in the Standard Library package and can be used to access the standard library modules. To import a module from another package, you use the use keyword followed by the module path. The module path consists of the package address (or alias) and the module name separated by ::.","breadcrumbs":"Move Basics » Importing Modules » Importing a Module from Another Package","id":"117","title":"Importing a Module from Another Package"},"118":{"body":"The Move Standard Library provides functionality for native types and operations. It is a standard collection of modules which do not interact with the storage, but provide basic tools for working and manipulating the data. It is the only dependency of the Sui Framework , and is imported together with it.","breadcrumbs":"Move Basics » Standard Library » Standard Library","id":"118","title":"Standard Library"},"119":{"body":"In this book we go into detail about most of the modules in the Standard Library, however, it is also helpful to give an overview of the features, so that you can get a sense of what is available and which module implements it. Module Description Chapter std::string Provides basic string operations String std::ascii Provides basic ASCII operations String std::option Implements an Option Option std::vector Native operations on the vector type Vector std::bcs Contains the bcs::to_bytes() function BCS std::address Contains a single address::length function Address std::type_name Allows runtime type reflection Type Reflection std::hash Hashing functions: sha2_256 and sha3_256 Cryptography and Hashing std::debug Contains debugging functions, which are available in only in test mode Debugging std::bit_vector Provides operations on bit vectors - std::fixed_point32 Provides the FixedPoint32 type -","breadcrumbs":"Move Basics » Standard Library » Most Common Modules","id":"119","title":"Most Common Modules"},"12":{"body":"Emacs is a free and open source text editor. move-mode is a Move mode for Emacs by Ashok Menon .","breadcrumbs":"Before we begin » Set up your IDE » Emacs","id":"12","title":"Emacs"},"120":{"body":"Standard Library exports one named address - std = 0x1. [addresses]\nstd = \"0x1\"","breadcrumbs":"Move Basics » Standard Library » Exported Addresses","id":"120","title":"Exported Addresses"},"121":{"body":"Some of the modules are imported implicitly, and are available in the module without explicit use import. For Standard Library, these modules and types are: std::vector std::option std::option::Option","breadcrumbs":"Move Basics » Standard Library » Implicit Imports","id":"121","title":"Implicit Imports"},"122":{"body":"The Move Standard Library can be imported to the package directly. However, std alone is not enough to build a meaningful application, as it does not provide any storage capabilities, and can't interact with the on-chain state. MoveStdlib = { git = \"https://github.com/MystenLabs/sui.git\", subdir = \"crates/sui-framework/packages/move-stdlib\", rev = \"framework/mainnet\" }","breadcrumbs":"Move Basics » Standard Library » Importing std without Sui Framework","id":"122","title":"Importing std without Sui Framework"},"123":{"body":"The source code of the Move Standard Library is available in the Sui repository .","breadcrumbs":"Move Basics » Standard Library » Source Code","id":"123","title":"Source Code"},"124":{"body":"Vectors are a native way to store collections of elements in Move. They are similar to arrays in other programming languages, but with a few differences. In this section, we introduce the vector type and its operations.","breadcrumbs":"Move Basics » Vector » Vector","id":"124","title":"Vector"},"125":{"body":"The vector type is defined using the vector keyword followed by the type of the elements in angle brackets. The type of the elements can be any valid Move type, including other vectors. Move has a vector literal syntax that allows you to create vectors using the vector keyword followed by square brackets containing the elements (or no elements for an empty vector). // An empty vector of bool elements.\nlet empty: vector = vector[]; // A vector of u8 elements.\nlet v: vector = vector[10, 20, 30]; // A vector of vector elements.\nlet vv: vector> = vector[ vector[10, 20], vector[30, 40]\n]; The vector type is a built-in type in Move, and does not need to be imported from a module. However, vector operations are defined in the std::vector module, and you need to import the module to use them.","breadcrumbs":"Move Basics » Vector » Vector syntax","id":"125","title":"Vector syntax"},"126":{"body":"The standard library provides methods to manipulate vectors. The following are some of the most commonly used operations: push_back: Adds an element to the end of the vector. pop_back: Removes the last element from the vector. length: Returns the number of elements in the vector. is_empty: Returns true if the vector is empty. remove: Removes an element at a given index. let mut v = vector[10u8, 20, 30]; assert!(v.length() == 3, 0);\nassert!(!v.is_empty(), 1); v.push_back(40);\nlet last_value = v.pop_back(); assert!(last_value == 40, 2);","breadcrumbs":"Move Basics » Vector » Vector operations","id":"126","title":"Vector operations"},"127":{"body":"A vector of non-droppable types cannot be discarded. If you define a vector of types without drop ability, the vector value cannot be ignored. However, if the vector is empty, compiler requires an explicit call to destroy_empty function. /// A struct without `drop` ability.\npublic struct NoDrop {} #[test]\nfun test_destroy_empty() { // Initialize a vector of `NoDrop` elements. let v = vector[]; // While we know that `v` is empty, we still need to call // the explicit `destroy_empty` function to discard the vector. v.destroy_empty();\n}","breadcrumbs":"Move Basics » Vector » Destroying a Vector of non-droppable types","id":"127","title":"Destroying a Vector of non-droppable types"},"128":{"body":"Vector in the Move Reference.","breadcrumbs":"Move Basics » Vector » Further reading","id":"128","title":"Further reading"},"129":{"body":"Option is a type that represents an optional value which may or may not exist. The concept of Option in Move is borrowed from Rust, and it is a very useful primitive in Move. Option is defined in the Standard Library , and is defined as follows: File: move-stdlib/source/option.move // File: move-stdlib/source/option.move\n/// Abstraction of a value that may or may not be present.\nstruct Option has copy, drop, store { vec: vector\n} The 'std::option' module is implicitly imported in every module, and you don't need to add an import. The Option is a generic type which takes a type parameter Element. It has a single field vec which is a vector of Element. Vector can have length 0 or 1, and this is used to represent the presence or absence of a value. Option type has two variants: Some and None. Some variant contains a value and None variant represents the absence of a value. The Option type is used to represent the absence of a value in a type-safe way, and it is used to avoid the need for empty or undefined values.","breadcrumbs":"Move Basics » Option » Option","id":"129","title":"Option"},"13":{"body":"The Web-based IDE from Github can be run right in the browser and provides almost a full-featured VSCode experience. Github Codespaces Move Syntax is also available in the extensions marketplace.","breadcrumbs":"Before we begin » Set up your IDE » Github Codespaces","id":"13","title":"Github Codespaces"},"130":{"body":"To showcase why Option type is necessary, let's look at an example. Consider an application which takes a user input and stores it in a variable. Some fields are required, and some are optional. For example, a user's middle name is optional. While we could use an empty string to represent the absence of a middle name, it would require extra checks to differentiate between an empty string and a missing middle name. Instead, we can use the Option type to represent the middle name. module book::user_registry; use std::string::String; /// A struct representing a user record.\npublic struct User has drop { first_name: String, middle_name: Option, last_name: String,\n} /// Create a new `User` struct with the given fields.\npublic fun register( first_name: String, middle_name: Option, last_name: String,\n): User { User { first_name, middle_name, last_name }\n} In the example above, the middle_name field is of type Option. This means that the middle_name field can either contain a String value or be empty. This makes it clear that the middle name is optional, and it avoids the need for extra checks to differentiate between an empty string and a missing middle name.","breadcrumbs":"Move Basics » Option » In Practice","id":"130","title":"In Practice"},"131":{"body":"To use the Option type, you need to import the std::option module and use the Option type. You can then create an Option value using the some or none methods. // `option::some` creates an `Option` value with a value.\nlet mut opt = option::some(b\"Alice\"); // `option.is_some()` returns true if option contains a value.\nassert!(opt.is_some(), 1); // internal value can be `borrow`ed and `borrow_mut`ed.\nassert!(opt.borrow() == &b\"Alice\", 0); // `option.extract` takes the value out of the option, leaving the option empty.\nlet inner = opt.extract(); // `option.is_none()` returns true if option is None.\nassert!(opt.is_none(), 2);","breadcrumbs":"Move Basics » Option » Using Option","id":"131","title":"Using Option"},"132":{"body":"While Move does not have a built-in type to represent strings, it does have two standard implementations for strings in the Standard Library . The std::string module defines a String type and methods for UTF-8 encoded strings, and the second module, std::ascii, provides an ASCII String type and its methods. Sui execution environment automatically converts bytevector into String in transaction inputs. So in many cases, a String does not need to be constructed in the Transaction Block .","breadcrumbs":"Move Basics » String » String","id":"132","title":"String"},"133":{"body":"No matter which type of string you use, it is important to know that strings are just bytes. The wrappers provided by the string and ascii modules are just that: wrappers. They do provide safety checks and methods to work with strings, but at the end of the day, they are just vectors of bytes. module book::custom_string; /// Anyone can implement a custom string-like type by wrapping a vector.\npublic struct MyString { bytes: vector,\n} /// Implement a `from_bytes` function to convert a vector of bytes to a string.\npublic fun from_bytes(bytes: vector): MyString { MyString { bytes }\n} /// Implement a `bytes` function to convert a string to a vector of bytes.\npublic fun bytes(self: &MyString): &vector { &self.bytes\n}","breadcrumbs":"Move Basics » String » Strings are bytes","id":"133","title":"Strings are bytes"},"134":{"body":"While there are two types of strings in the standard library, the string module should be considered the default. It has native implementations of many common operations, and hence is more efficient than the ascii module, which is fully implemented in Move.","breadcrumbs":"Move Basics » String » Working with UTF-8 Strings","id":"134","title":"Working with UTF-8 Strings"},"135":{"body":"The String type in the std::string module is defined as follows: // File: move-stdlib/sources/string.move\n/// A `String` holds a sequence of bytes which is guaranteed to be in utf8 format.\npublic struct String has copy, drop, store { bytes: vector,\n}","breadcrumbs":"Move Basics » String » Definition","id":"135","title":"Definition"},"136":{"body":"To create a new UTF-8 String instance, you can use the string::utf8 method. The Standard Library provides an alias .to_string() on the vector for convenience. // the module is `std::string` and the type is `String`\nuse std::string::{Self, String}; // strings are normally created using the `utf8` function\n// type declaration is not necessary, we put it here for clarity\nlet hello: String = string::utf8(b\"Hello\"); // The `.to_string()` alias on the `vector` is more convenient\nlet hello = b\"Hello\".to_string();","breadcrumbs":"Move Basics » String » Creating a String","id":"136","title":"Creating a String"},"137":{"body":"UTF8 String provides a number of methods to work with strings. The most common operations on strings are: concatenation, slicing, and getting the length. Additionally, for custom string operations, the bytes() method can be used to get the underlying byte vector. let mut str = b\"Hello,\".to_string();\nlet another = b\" World!\".to_string(); // append(String) adds the content to the end of the string\nstr.append(another); // `sub_string(start, end)` copies a slice of the string\nstr.sub_string(0, 5); // \"Hello\" // `length()` returns the number of bytes in the string\nstr.length(); // 12 (bytes) // methods can also be chained! Get a length of a substring\nstr.sub_string(0, 5).length(); // 5 (bytes) // whether the string is empty\nstr.is_empty(); // false // get the underlying byte vector for custom operations\nlet bytes: &vector = str.bytes();","breadcrumbs":"Move Basics » String » Common Operations","id":"137","title":"Common Operations"},"138":{"body":"The default utf8 method may abort if the bytes passed into it are not valid UTF-8. If you are not sure that the bytes you are passing are valid, you should use the try_utf8 method instead. It returns an Option, which contains no value if the bytes are not valid UTF-8, and a string otherwise. Hint: the name that starts with try_* indicates that the function returns an Option with the expected result or none if the operation fails. It is a common naming convention borrowed from Rust. // this is a valid UTF-8 string\nlet hello = b\"Hello\".try_to_string(); assert!(hello.is_some(), 0); // abort if the value is not valid UTF-8 // this is not a valid UTF-8 string\nlet invalid = b\"\\xFF\".try_to_string(); assert!(invalid.is_none(), 0); // abort if the value is valid UTF-8","breadcrumbs":"Move Basics » String » Safe UTF-8 Operations","id":"138","title":"Safe UTF-8 Operations"},"139":{"body":"The string module does not provide a way to access individual characters in a string. This is because UTF-8 is a variable-length encoding, and the length of a character can be anywhere from 1 to 4 bytes. Similarly, the length() method returns the number of bytes in the string, not the number of characters. However, methods like sub_string and insert check character boundaries and will abort when the index is in the middle of a character.","breadcrumbs":"Move Basics » String » UTF-8 Limitations","id":"139","title":"UTF-8 Limitations"},"14":{"body":"Move 2024 is the new edition of the Move language maintained by Mysten Labs. All of the examples in this book are written in Move 2024. If you're used to the pre-2024 version of Move, please, refer to the Move 2024 Migration Guide to learn about the changes and improvements in the new edition.","breadcrumbs":"Before we begin » Move 2024 » Move 2024","id":"14","title":"Move 2024"},"140":{"body":"This section is coming soon!","breadcrumbs":"Move Basics » String » ASCII Strings","id":"140","title":"ASCII Strings"},"141":{"body":"Control flow statements are used to control the flow of execution in a program. They are used to make decisions, to repeat a block of code, and to exit a block of code early. Move has the following control flow statements (explained in detail below): if and if-else - making decisions on whether to execute a block of code loop and while loops - repeating a block of code break and continue statements - exiting a loop early return statement - exiting a function early","breadcrumbs":"Move Basics » Control Flow » Control Flow","id":"141","title":"Control Flow"},"142":{"body":"The if expression is used to make decisions in a program. It evaluates a boolean expression and executes a block of code if the expression is true. Paired with else, it can execute a different block of code if the expression is false. The syntax for the if expression is: if () ;\nif () else ; Just like any other expression, if requires a semicolon, if there are other expressions following it. The else keyword is optional, except for the case when the resulting value is assigned to a variable. We will cover this below. #[test]\nfun test_if() { let x = 5; // `x > 0` is a boolean expression. if (x > 0) { std::debug::print(&b\"X is bigger than 0\".to_string()) };\n} Let's see how we can use if and else to assign a value to a variable: #[test]\nfun test_if_else() { let x = 5; let y = if (x > 0) { 1 } else { 0 }; assert!(y == 1, 0);\n} Here we assign the value of the if expression to the variable y. If x is greater than 0, y will be assigned the value 1, otherwise 0. The else block is necessary, because both branches must return a value of the same type. If we omit the else block, the compiler will throw an error. Conditional expressions are one of the most important control flow statements in Move. They can use either user provided input or some already stored data to make decisions. In particular, they are used in the assert! macro to check if a condition is true, and if not, to abort execution. We will get to it very soon!","breadcrumbs":"Move Basics » Control Flow » Conditional Statements","id":"142","title":"Conditional Statements"},"143":{"body":"Loops are used to execute a block of code multiple times. Move has two built-in types of loops: loop and while. In many cases they can be used interchangeably, but usually while is used when the number of iterations is known in advance, and loop is used when the number of iterations is not known in advance or there are multiple exit points. Loops are helpful when dealing with collections, such as vectors, or when we want to repeat a block of code until a certain condition is met. However, it is important to be careful with loops, as they can lead to infinite loops, which can lead to gas exhaustion and the transaction being aborted.","breadcrumbs":"Move Basics » Control Flow » Repeating Statements with Loops","id":"143","title":"Repeating Statements with Loops"},"144":{"body":"The while statement is used to execute a block of code as long as a boolean expression is true. Just like we've seen with if, the boolean expression is evaluated before each iteration of the loop. Just like conditional statements, the while loop is an expression and requires a semicolon if there are other expressions following it. The syntax for the while loop is: while () { ; }; Here is an example of a while loop with a very simple condition: // This function iterates over the `x` variable until it reaches 10, the\n// return value is the number of iterations it took to reach 10.\n//\n// If `x` is 0, then the function will return 10.\n// If `x` is 5, then the function will return 5.\nfun while_loop(mut x: u8): u8 { let mut y = 0; // This will loop until `x` is 10. // And will never run if `x` is 10 or more. while (x < 10) { y = y + 1; x = x + 1; }; y\n} #[test]\nfun test_while() { assert!(while_loop(0) == 10, 0); // 10 times assert!(while_loop(5) == 5, 0); // 5 times assert!(while_loop(10) == 0, 0); // loop never executed\n}","breadcrumbs":"Move Basics » Control Flow » The while loop","id":"144","title":"The while loop"},"145":{"body":"Now let's imagine a scenario where the boolean expression is always true. For example, if we literally passed true to the while condition. As you might expect, this would create an infinite loop, and this is almost what the loop statement works like. #[test, expected_failure(out_of_gas, location=Self)]\nfun test_infinite_while() { let mut x = 0; // This will loop forever. while (true) { x = x + 1; }; // This line will never be executed. assert!(x == 5, 0);\n} An infinite while, or while without a condition, is a loop. The syntax for it is simple: loop { ; }; Let's rewrite the previous example using loop instead of while: #[test, expected_failure(out_of_gas, location=Self)]\nfun test_infinite_loop() { let mut x = 0; // This will loop forever. loop { x = x + 1; }; // This line will never be executed. assert!(x == 5, 0);\n} Infinite loops on their own are not very useful in Move, since every operation in Move costs gas, and an infinite loop will lead to gas exhaustion. However, they can be used in combination with break and continue statements to create more complex loops.","breadcrumbs":"Move Basics » Control Flow » Infinite loop","id":"145","title":"Infinite loop"},"146":{"body":"As we already mentioned, infinite loops are rather useless on their own. And that's where we introduce the break and continue statements. They are used to exit a loop early, and to skip the rest of the current iteration, respectively. Syntax for the break statement is (without a semicolon): break The break statement is used to stop the execution of a loop and exit it early. It is often used in combination with a conditional statement to exit the loop when a certain condition is met. To illustrate this point, let's turn the infinite loop from the previous example into something that looks and behaves more like a while loop: #[test]\nfun test_break_loop() { let mut x = 0; // This will loop until `x` is 5. loop { x = x + 1; // If `x` is 5, then exit the loop. if (x == 5) { break // Exit the loop. } }; assert!(x == 5, 0);\n} Almost identical to the while loop, right? The break statement is used to exit the loop when x is 5. If we remove the break statement, the loop will run forever, just like the previous example.","breadcrumbs":"Move Basics » Control Flow » Exiting a Loop Early","id":"146","title":"Exiting a Loop Early"},"147":{"body":"The continue statement is used to skip the rest of the current iteration and start the next one. Similarly to break, it is used in combination with a conditional statement to skip the rest of the iteration when a certain condition is met. Syntax for the continue statement is (without a semicolon): continue The example below skips odd numbers and prints only even numbers from 0 to 10: #[test]\nfun test_continue_loop() { let mut x = 0; // This will loop until `x` is 10. loop { x = x + 1; // If `x` is odd, then skip the rest of the iteration. if (x % 2 == 1) { continue // Skip the rest of the iteration. }; std::debug::print(&x); // If `x` is 10, then exit the loop. if (x == 10) { break // Exit the loop. } }; assert!(x == 10, 0); // 10\n} break and continue statements can be used in both while and loop loops.","breadcrumbs":"Move Basics » Control Flow » Skipping an Iteration","id":"147","title":"Skipping an Iteration"},"148":{"body":"The return statement is used to exit a function early and return a value. It is often used in combination with a conditional statement to exit the function when a certain condition is met. The syntax for the return statement is: return Here is an example of a function that returns a value when a certain condition is met: /// This function returns `true` if `x` is greater than 0 and not 5,\n/// otherwise it returns `false`.\nfun is_positive(x: u8): bool { if (x == 5) { return false }; if (x > 0) { return true }; false\n} #[test]\nfun test_return() { assert!(is_positive(5) == false, 0); assert!(is_positive(0) == false, 0); assert!(is_positive(1) == true, 0);\n} Unlike in other languages, the return statement is not required for the last expression in a function. The last expression in a function block is automatically returned. However, the return statement is useful when we want to exit a function early if a certain condition is met.","breadcrumbs":"Move Basics » Control Flow » Early Return","id":"148","title":"Early Return"},"149":{"body":"Constants are immutable values that are defined at the module level. They often serve as a way to give names to static values that are used throughout a module. For example, if there's a default price for a product, you might define a constant for it. Constants are stored in the module's bytecode, and each time they are used, the value is copied.","breadcrumbs":"Move Basics » Constants » Constants","id":"149","title":"Constants"},"15":{"body":"In this chapter, you will learn how to create a new package, write a simple module, compile it, and run tests with the Move CLI. Make sure you have installed Sui and set up your IDE environment . Run the command below to test if Sui has been installed correctly. # It should print the client version. E.g. sui-client 1.22.0-036299745.\nsui client --version Move CLI is a command-line interface for the Move language; it is built into the Sui binary and provides a set of commands to manage packages, compile and test code. The structure of the chapter is as follows: Create a New Package Directory Structure Compiling the Package Running Tests","breadcrumbs":"Hello, World! » Hello, World!","id":"15","title":"Hello, World!"},"150":{"body":"Constants must start with a capital letter - this is enforced at the compiler level. For constants used as a value, there's a convention to use uppercase letters and underscores to separate words. It's a way to make constants stand out from other identifiers in the code. One exception is made for error constants , which are written in ECamelCase.","breadcrumbs":"Move Basics » Constants » Naming Convention","id":"150","title":"Naming Convention"},"151":{"body":"Constants can't be changed and assigned new values. They are part of the package bytecode, and inherently immutable. module book::immutable_constants { const ITEM_PRICE: u64 = 100; // emits an error fun change_price() { ITEM_PRICE = 200; }\n}","breadcrumbs":"Move Basics » Constants » Constants are Immutable","id":"151","title":"Constants are Immutable"},"152":{"body":"A common use case for an application is to define a set of constants that are used throughout the codebase. But due to constants being private to the module, they can't be accessed from other modules. One way to solve this is to define a \"config\" module that exports the constants. This way other modules can import and read the constants, and the update process is simplified. If the constants need to be changed, only the config module needs to be updated during the package upgrade.","breadcrumbs":"Move Basics » Constants » Using Config Pattern","id":"152","title":"Using Config Pattern"},"153":{"body":"Constants in the Move Reference Coding conventions for constants","breadcrumbs":"Move Basics » Constants » Links","id":"153","title":"Links"},"154":{"body":"A transaction can either succeed or fail. Successful execution applies all the changes made to objects and on-chain data, and the transaction is committed to the blockchain. Alternatively, if a transaction aborts, the changes are not applied. The abort keyword is used to abort a transaction and revert the changes made so far. It is important to note that there is no catch mechanism in Move. If a transaction aborts, the changes made so far are reverted, and the transaction is considered failed.","breadcrumbs":"Move Basics » Aborting Execution » Aborting Execution","id":"154","title":"Aborting Execution"},"155":{"body":"The abort keyword is used to abort the execution of a transaction. It is used in combination with an abort code, which will be returned to the caller of the transaction. The abort code is an integer of type u64. let user_has_access = true; // abort with a predefined constant if `user_has_access` is false\nif (!user_has_access) { abort 0\n}; // there's an alternative syntax using parenthesis`\nif (user_has_access) { abort(1)\n}; The code above will, of course, abort with abort code 1.","breadcrumbs":"Move Basics » Aborting Execution » Abort","id":"155","title":"Abort"},"156":{"body":"The assert! macro is a built-in macro that can be used to assert a condition. If the condition is false, the transaction will abort with the given abort code. The assert! macro is a convenient way to abort a transaction if a condition is not met. The macro shortens the code otherwise written with an if expression + abort. The code argument is required and has to be a u64 value. // aborts if `user_has_access` is `false` with abort code 0\nassert!(user_has_access, 0); // expands into:\nif (!user_has_access) { abort 0\n};","breadcrumbs":"Move Basics » Aborting Execution » assert!","id":"156","title":"assert!"},"157":{"body":"To make error codes more descriptive, it is a good practice to define error constants . Error constants are defined as const declarations and are usually prefixed with E followed by a camel case name. Error constants are no different from other constants and don't have special handling, however, they are used to increase the readability of the code and make it easier to understand the abort scenarios. /// Error code for when the user has no access.\nconst ENoAccess: u64 = 0;\n/// Trying to access a field that does not exist.\nconst ENoField: u64 = 1; /// Updates a record.\npublic fun update_record(/* ... , */ user_has_access: bool, field_exists: bool) { // asserts are way more readable now assert!(user_has_access, ENoAccess); assert!(field_exists, ENoField); /* ... */\n}","breadcrumbs":"Move Basics » Aborting Execution » Error constants","id":"157","title":"Error constants"},"158":{"body":"Abort and Assert in the Move Reference. We suggest reading the Better Error Handling guide to learn about best practices for error handling in Move.","breadcrumbs":"Move Basics » Aborting Execution » Further reading","id":"158","title":"Further reading"},"159":{"body":"Functions are the building blocks of Move programs. They are called from user transactions and from other functions and group executable code into reusable units. Functions can take arguments and return a value. They are declared with the fun keyword at the module level. Just like any other module member, by default they're private and can only be accessed from within the module. module book::math; /// Function takes two arguments of type `u64` and returns their sum.\n/// The `public` visibility modifier makes the function accessible from\n/// outside the module.\npublic fun add(a: u64, b: u64): u64 { a + b\n} #[test]\nfun test_add() { let sum = add(1, 2); assert!(sum == 3, 0);\n} In this example, we define a function add that takes two arguments of type u64 and returns their sum. The function is called from the test_add function, which is a test function located in the same module. In the test we compare the result of the add function with the expected value and abort the execution if the result is different.","breadcrumbs":"Move Basics » Function » Function","id":"159","title":"Function"},"16":{"body":"To create a new program, we will use the sui move new command followed by the name of the application. Our first program will be called hello_world. Note: In this and other chapters, if you see code blocks with lines starting with $ (dollar sign), it means that the following command should be run in a terminal. The sign should not be included. It's a common way of showing commands in terminal environments. $ sui move new hello_world The sui move command gives access to the Move CLI - a built-in compiler, test runner and a utility for all things Move. The new command followed by the name of the package will create a new package in a new folder. In our case, the folder name is \"hello_world\". We can view the contents of the folder to see that the package was created successfully. $ ls -l hello_world\nMove.toml\nsources\ntests","breadcrumbs":"Hello, World! » Create a New Package","id":"16","title":"Create a New Package"},"160":{"body":"There's a convention to call functions in Move with the snake_case naming convention. This means that the function name should be all lowercase with words separated by underscores. For example, do_something, add, get_balance, is_authorized, and so on. A function is declared with the fun keyword followed by the function name (a valid Move identifier), a list of arguments in parentheses, and a return type. The function body is a block of code that contains a sequence of statements and expressions. The last expression in the function body is the return value of the function. fun return_nothing() { // empty expression, function returns `()`\n}","breadcrumbs":"Move Basics » Function » Function declaration","id":"160","title":"Function declaration"},"161":{"body":"Just like any other module member, functions can be imported and accessed via a path. The path consists of the module path and the function name separated by ::. For example, if you have a function called add in the math module in the book package, the path to it will be book::math::add, or, if the module is imported, math::add.","breadcrumbs":"Move Basics » Function » Accessing functions","id":"161","title":"Accessing functions"},"162":{"body":"Move functions can return multiple values, which is useful when you need to return more than one value from a function. The return type of the function is a tuple of types. The return value is a tuple of expressions. fun get_name_and_age(): (vector, u8) { (b\"John\", 25)\n} Result of a function call with tuple return has to be unpacked into variables via let (tuple) syntax: // Tuple must be destructured to access its elements.\n// Name and age are declared as immutable variables.\nlet (name, age) = get_name_and_age();\nassert!(name == b\"John\", 0);\nassert!(age == 25, 0); If any of the declared values need to be declared as mutable, the mut keyword is placed before the variable name: // declare name as mutable, age as immutable\nlet (mut name, age) = get_name_and_age(); If some of the arguments are not used, they can be ignored with the _ symbol: // ignore the name, only use the age\nlet (_, age) = get_name_and_age();","breadcrumbs":"Move Basics » Function » Multiple return values","id":"162","title":"Multiple return values"},"163":{"body":"Functions in the Move Reference.","breadcrumbs":"Move Basics » Function » Further reading","id":"163","title":"Further reading"},"164":{"body":"Move Compiler supports receiver syntax , which allows defining methods which can be called on instances of a struct. This is similar to the method syntax in other programming languages. It is a convenient way to define functions which operate on the fields of a struct.","breadcrumbs":"Move Basics » Struct Methods » Struct Methods","id":"164","title":"Struct Methods"},"165":{"body":"If the first argument of a function is a struct internal to the module, then the function can be called using the . operator. If the function uses a struct from another module, then method won't be associated with the struct by default. In this case, the function can be called using the standard function call syntax. When a module is imported, the methods are automatically associated with the struct. module book::hero; /// A struct representing a hero.\npublic struct Hero has drop { health: u8, mana: u8,\n} /// Create a new Hero.\npublic fun new(): Hero { Hero { health: 100, mana: 100 } } /// A method which casts a spell, consuming mana.\npublic fun heal_spell(hero: &mut Hero) { hero.health = hero.health + 10; hero.mana = hero.mana - 10;\n} /// A method which returns the health of the hero.\npublic fun health(hero: &Hero): u8 { hero.health } /// A method which returns the mana of the hero.\npublic fun mana(hero: &Hero): u8 { hero.mana } #[test]\n// Test the methods of the `Hero` struct.\nfun test_methods() { let mut hero = new(); hero.heal_spell(); assert!(hero.health() == 110, 1); assert!(hero.mana() == 90, 2);\n}","breadcrumbs":"Move Basics » Struct Methods » Method syntax","id":"165","title":"Method syntax"},"166":{"body":"For modules that define multiple structs and their methods, it is possible to define method aliases to avoid name conflicts, or to provide a better-named method for a struct. The syntax for aliases is: // for local method association\nuse fun function_path as Type.method_name; // exported alias\npublic use fun function_path as Type.method_name; Public aliases are only allowed for structs defined in the same module. If a struct is defined in another module, an alias can still be created but cannot be made public. In the example below, we changed the hero module and added another type - Villain. Both Hero and Villain have similar field names and methods. And to avoid name conflicts, we prefixed methods with hero_ and villain_ respectively. However, we can create aliases for these methods so that they can be called on the instances of the structs without the prefix. As you can see, in the test function, we called the health method on the instances of Hero and Villain without the prefix. The compiler will automatically associate the methods with the structs.","breadcrumbs":"Move Basics » Struct Methods » Method Aliases","id":"166","title":"Method Aliases"},"167":{"body":"It is also possible to associate a function defined in another module with a struct from the current module. Following the same approach, we can create an alias for the method defined in another module. Let's use the bcs::to_bytes method from the Standard Library and associate it with the Hero struct. It will allow serializing the Hero struct to a vector of bytes.","breadcrumbs":"Move Basics » Struct Methods » Aliasing an external module's method","id":"167","title":"Aliasing an external module's method"},"168":{"body":"Method Syntax in the Move Reference.","breadcrumbs":"Move Basics » Struct Methods » Further reading","id":"168","title":"Further reading"},"169":{"body":"Every module member has a visibility. By default, all module members are private - meaning they are only accessible within the module they are defined in. However, you can add a visibility modifier to make a module member public - visible outside the module, or public(package) - visible in the modules within the same package, or entry - can be called from a transaction but can't be called from other modules.","breadcrumbs":"Move Basics » Visibility Modifiers » Visibility Modifiers","id":"169","title":"Visibility Modifiers"},"17":{"body":"Move CLI will create a scaffold of the application and pre-create the directory structure and all necessary files. Let's see what's inside. hello_world\n├── Move.toml\n├── sources\n│ └── hello_world.move\n└── tests └── hello_world_tests.move","breadcrumbs":"Hello, World! » Directory Structure","id":"17","title":"Directory Structure"},"170":{"body":"A function or a struct defined in a module which has no visibility modifier is private to the module. It can't be called from other modules. module book::internal_visibility { // This function can be called from other functions in the same module fun internal() { /* ... */ } // Same module -> can call internal() fun call_internal() { internal(); }\n} module book::try_calling_internal { use book::internal_visibility; // Different module -> can't call internal() fun try_calling_internal() { internal_visibility::internal(); }\n}","breadcrumbs":"Move Basics » Visibility Modifiers » Internal Visibility","id":"170","title":"Internal Visibility"},"171":{"body":"A struct or a function can be made public by adding the public keyword before the fun or struct keyword. module book::public_visibility { // This function can be called from other modules public fun public() { /* ... */ }\n} A public function can be imported and called from other modules. The following code will compile: module book::try_calling_public { use book::public_visibility; // Different module -> can call public() fun try_calling_public() { public_visibility::public(); }\n}","breadcrumbs":"Move Basics » Visibility Modifiers » Public Visibility","id":"171","title":"Public Visibility"},"172":{"body":"Move 2024 introduces the package visibility modifier. A function with package visibility can be called from any module within the same package. It can't be called from other packages. module book::package_visibility { public(package) fun package_only() { /* ... */ }\n} A package function can be called from any module within the same package: module book::try_calling_package { use book::package_visibility; // Same package `book` -> can call package_only() fun try_calling_package() { package_visibility::package_only(); }\n}","breadcrumbs":"Move Basics » Visibility Modifiers » Package Visibility","id":"172","title":"Package Visibility"},"173":{"body":"Every variable in Move has a scope and an owner. The scope is the range of code where the variable is valid, and the owner is the scope that this variable belongs to. Once the owner scope ends, the variable is dropped. This is a fundamental concept in Move, and it is important to understand how it works.","breadcrumbs":"Move Basics » Ownership and Scope » Ownership and Scope","id":"173","title":"Ownership and Scope"},"174":{"body":"A variable defined in a function scope is owned by this scope. The runtime goes through the function scope and executes every expression and statement. Once the function scope end, the variables defined in it are dropped or deallocated. module book::ownership { public fun owner() { let a = 1; // a is owned by the `owner` function } // a is dropped here public fun other() { let b = 2; // b is owned by the `other` function } // b is dropped here #[test] fun test_owner() { owner(); other(); // a & b is not valid here }\n} In the example above, the variable a is owned by the owner function, and the variable b is owned by the other function. When each of these functions are called, the variables are defined, and when the function ends, the variables are discarded.","breadcrumbs":"Move Basics » Ownership and Scope » Ownership","id":"174","title":"Ownership"},"175":{"body":"If we changed the owner function to return the variable a, then the ownership of a would be transferred to the caller of the function. module book::ownership { public fun owner(): u8 { let a = 1; // a defined here a // scope ends, a is returned } #[test] fun test_owner() { let a = owner(); // a is valid here } // a is dropped here\n}","breadcrumbs":"Move Basics » Ownership and Scope » Returning a Value","id":"175","title":"Returning a Value"},"176":{"body":"Additionally, if we passed the variable a to another function, the ownership of a would be transferred to this function. When performing this operation, we move the value from one scope to another. This is also called move semantics . module book::ownership { public fun owner(): u8 { let a = 10; a } // a is returned public fun take_ownership(v: u8) { // v is owned by `take_ownership` } // v is dropped here #[test] fun test_owner() { let a = owner(); take_ownership(a); // a is not valid here }\n}","breadcrumbs":"Move Basics » Ownership and Scope » Passing by Value","id":"176","title":"Passing by Value"},"177":{"body":"Each function has a main scope, and it can also have sub-scopes via the use of blocks. A block is a sequence of statements and expressions, and it has its own scope. Variables defined in a block are owned by this block, and when the block ends, the variables are dropped. module book::ownership { public fun owner() { let a = 1; // a is owned by the `owner` function's scope { let b = 2; // b is owned by the block { let c = 3; // c is owned by the block }; // c is dropped here }; // b is dropped here // a = b; // error: b is not valid here // a = c; // error: c is not valid here } // a is dropped here\n} However, shall we use the return value of a block, the ownership of the variable is transferred to the caller of the block. module book::ownership { public fun owner(): u8 { let a = 1; // a is owned by the `owner` function's scope let b = { let c = 2; // c is owned by the block c // c is returned }; // c is dropped here a + b // both a and b are valid here }\n}","breadcrumbs":"Move Basics » Ownership and Scope » Scopes with Blocks","id":"177","title":"Scopes with Blocks"},"178":{"body":"Some types in Move are copyable , which means that they can be copied without transferring the ownership. This is useful for types that are small and cheap to copy, such as integers and booleans. Move compiler will automatically copy these types when they are passed to a function or returned from a function, or when they're moved to a scope and then accessed in their original scope.","breadcrumbs":"Move Basics » Ownership and Scope » Copyable Types","id":"178","title":"Copyable Types"},"179":{"body":"Local Variables and Scopes in the Move Reference.","breadcrumbs":"Move Basics » Ownership and Scope » Further reading","id":"179","title":"Further reading"},"18":{"body":"The Move.toml file, known as the package manifest , contains definitions and configuration settings for the package. It is used by the Move Compiler to manage package metadata, fetch dependencies, and register named addresses. We will explain it in detail in the Concepts chapter. By default, the package features one named address - the name of the package. [addresses]\nhello_world = \"0x0\"","breadcrumbs":"Hello, World! » Manifest","id":"18","title":"Manifest"},"180":{"body":"In Move, the copy ability on a type indicates that the instance or the value of the type can be copied. While this behavior may feel very natural when working with numbers or other simple types, it is not the default for custom types in Move. This is because Move is designed to express digital assets and resources, and inability to copy is a key element of the resource model. However, Move type system allows you to define custom types with the copy ability. public struct Copyable has copy {} In the example above, we define a custom type Copyable with the copy ability. This means that instances of Copyable can be copied, both implicitly and explicitly. let a = Copyable {};\nlet b = a; // `a` is copied to `b`\nlet c = *&b; // explicit copy via dereference operator let Copyable {} = a; // doesn't have `drop`\nlet Copyable {} = b; // doesn't have `drop`\nlet Copyable {} = c; // doesn't have `drop` In the example above, a is copied to b implicitly, and then explicitly copied to c using the dereference operator. If Copyable did not have the copy ability, the code would not compile, and the Move compiler would raise an error.","breadcrumbs":"Move Basics » Ability: Copy » Abilities: Copy","id":"180","title":"Abilities: Copy"},"181":{"body":"The copy ability is closely related to drop ability . If a type has the copy ability, very likely that it should have drop too. This is because the drop ability is required to clean up the resources when the instance is no longer needed. If a type has only copy , then managing its instances gets more complicated, as the values cannot be ignored. public struct Value has copy, drop {} All of the primitive types in Move behave as if they have the copy and drop abilities. This means that they can be copied and dropped, and the Move compiler will handle the memory management for them.","breadcrumbs":"Move Basics » Ability: Copy » Copying and Drop","id":"181","title":"Copying and Drop"},"182":{"body":"All native types in Move have the copy ability. This includes: bool unsigned integers vector address All of the types defined in the standard library have the copy ability as well. This includes: Option String TypeName","breadcrumbs":"Move Basics » Ability: Copy » Types with the copy Ability","id":"182","title":"Types with the copy Ability"},"183":{"body":"Type Abilities in the Move Reference.","breadcrumbs":"Move Basics » Ability: Copy » Further reading","id":"183","title":"Further reading"},"184":{"body":"In the Ownership and Scope section, we explained that when a value is passed to a function, it is moved to the function's scope. This means that the function becomes the owner of the value, and the original scope (owner) can no longer use it. This is an important concept in Move, as it ensures that the value is not used in multiple places at the same time. However, there are use cases when we want to pass a value to a function but retain the ownership. This is where references come into play. To illustrate this, let's consider a simple example - an application for a metro (subway) pass. We will look at 4 different scenarios: Card can be purchased at the kiosk for a fixed price Card can be shown to inspectors to prove that the passenger has a valid pass Card can be used at the turnstile to enter the metro, and spend a ride Card can be recycled once it's empty","breadcrumbs":"Move Basics » References » References","id":"184","title":"References"},"185":{"body":"The initial layout of the metro pass application is simple. We define the Card type and the USES constant that represents the number of rides for a single card. We also add an error constant for the case when the card is empty. module book::metro_pass {\n/// Error code for when the card is empty.\nconst ENoUses: u64 = 0; /// Number of uses for a metro pass card.\nconst USES: u8 = 3; /// A metro pass card\npublic struct Card { uses: u8 } /// Purchase a metro pass card.\npublic fun purchase(/* pass a Coin */): Card { Card { uses: USES }\n}\n}","breadcrumbs":"Move Basics » References » Layout","id":"185","title":"Layout"},"186":{"body":"References are a way to show a value to a function without giving up the ownership. In our case, when we show the Card to the inspector, we don't want to give up the ownership of it, and we don't allow them to spend the rides. We just want to allow reading the value of the Card and prove its ownership. To do so, in the function signature, we use the & symbol to indicate that we are passing a reference to the value, not the value itself. /// Show the metro pass card to the inspector.\npublic fun is_valid(card: &Card): bool { card.uses > 0\n} Now the function can't take the ownership of the card, and it can't spend the rides. But it can read its value. Worth noting, that a signature like this makes it impossible to call the function without a Card at all. This is an important property which allows the Capability Pattern which we will cover in the next chapters.","breadcrumbs":"Move Basics » References » Reference","id":"186","title":"Reference"},"187":{"body":"In some cases, we want to allow the function to change the value of the Card. For example, when we use the Card at the turnstile, we want to spend a ride. To implement it, we use the &mut keyword in the function signature. /// Use the metro pass card at the turnstile to enter the metro.\npublic fun enter_metro(card: &mut Card) { assert!(card.uses > 0, ENoUses); card.uses = card.uses - 1;\n} As you can see in the function body, the &mut reference allows mutating the value, and the function can spend the rides.","breadcrumbs":"Move Basics » References » Mutable Reference","id":"187","title":"Mutable Reference"},"188":{"body":"Lastly, let's give an illustration of what happens when we pass the value itself to the function. In this case, the function takes the ownership of the value, and the original scope can no longer use it. The owner of the Card can recycle it, and, hence, lose the ownership. /// Recycle the metro pass card.\npublic fun recycle(card: Card) { assert!(card.uses == 0, ENoUses); let Card { uses: _ } = card;\n} In the recycle function, the Card is taken by value and can be unpacked and destroyed. The original scope can't use it anymore.","breadcrumbs":"Move Basics » References » Passing by Value","id":"188","title":"Passing by Value"},"189":{"body":"To illustrate the full flow of the application, let's put all the pieces together in a test. #[test]\nfun test_card_2024() { // declaring variable as mutable because we modify it let mut card = purchase(); card.enter_metro(); // modify the card but don't move it assert!(card.is_valid(), 0); // read the card! card.enter_metro(); // modify the card but don't move it card.enter_metro(); // modify the card but don't move it card.recycle(); // move the card out of the scope\n}","breadcrumbs":"Move Basics » References » Full Example","id":"189","title":"Full Example"},"19":{"body":"The sources/ directory contains the source files. Move source files have .move extension, and are typically named after the module defined in the file. For example, in our case, the file name is hello_world.move and the Move CLI has already placed commented out code inside: /*\n/// Module: hello_world\nmodule hello_world::hello_world { }\n*/ The /* and */ are the comment delimiters in Move. Everything in between is ignored by the compiler and can be used for documentation or notes. We explain all ways to comment the code in the Basic Syntax . The commented out code is a module definition, it starts with the keyword module followed by a named address (or an address literal), and the module name. The module name is a unique identifier for the module and has to be unique within the package. The module name is used to reference the module from other modules or transactions.","breadcrumbs":"Hello, World! » Sources","id":"19","title":"Sources"},"190":{"body":"Generics are a way to define a type or function that can work with any type. This is useful when you want to write a function which can be used with different types, or when you want to define a type that can hold any other type. Generics are the foundation of many advanced features in Move, such as collections, abstract implementations, and more.","breadcrumbs":"Move Basics » Generics » Generics","id":"190","title":"Generics"},"191":{"body":"In this chapter we already mentioned the vector type, which is a generic type that can hold any other type. Another example of a generic type in the standard library is the Option type, which is used to represent a value that may or may not be present.","breadcrumbs":"Move Basics » Generics » In the Standard Library","id":"191","title":"In the Standard Library"},"192":{"body":"To define a generic type or function, a type signature needs to have a list of generic parameters enclosed in angle brackets (< and >). The generic parameters are separated by commas. /// Container for any type `T`.\npublic struct Container has drop { value: T,\n} /// Function that creates a new `Container` with a generic value `T`.\npublic fun new(value: T): Container { Container { value }\n} In the example above, Container is a generic type with a single type parameter T, the value field of the container stores the T. The new function is a generic function with a single type parameter T, and it returns a Container with the given value. Generic types must be initialed with a concrete type, and generic functions must be called with a concrete type. #[test]\nfun test_container() { // these three lines are equivalent let container: Container = new(10); // type inference let container = new(10); // create a new `Container` with a `u8` value let container = new(10u8); assert!(container.value == 10, 0x0); // Value can be ignored only if it has the `drop` ability. let Container { value: _ } = container;\n} In the test function test_generic we demonstrate three equivalent ways to create a new Container with a u8 value. Because numeric types need to be inferred, we specify the type of the number literal.","breadcrumbs":"Move Basics » Generics » Generic Syntax","id":"192","title":"Generic Syntax"},"193":{"body":"You can define a type or function with multiple type parameters. The type parameters are then separated by commas. /// A pair of values of any type `T` and `U`.\npublic struct Pair { first: T, second: U,\n} /// Function that creates a new `Pair` with two generic values `T` and `U`.\npublic fun new_pair(first: T, second: U): Pair { Pair { first, second }\n} In the example above, Pair is a generic type with two type parameters T and U, and the new_pair function is a generic function with two type parameters T and U. The function returns a Pair with the given values. The order of the type parameters is important, and it should match the order of the type parameters in the type signature. #[test]\nfun test_generic() { // these three lines are equivalent let pair_1: Pair = new_pair(10, true); // type inference let pair_2 = new_pair(10, true); // create a new `Pair` with a `u8` and `bool` values let pair_3 = new_pair(10u8, true); assert!(pair_1.first == 10, 0x0); assert!(pair_1.second, 0x0); // Unpacking is identical. let Pair { first: _, second: _ } = pair_1; let Pair { first: _, second: _ } = pair_2; let Pair { first: _, second: _ } = pair_3; } If we added another instance where we swapped type parameters in the new_pair function, and tried to compare two types, we'd see that the type signatures are different, and cannot be compared. #[test]\nfun test_swap_type_params() { let pair1: Pair = new_pair(10u8, true); let pair2: Pair = new_pair(true, 10u8); // this line will not compile // assert!(pair1 == pair2, 0x0); let Pair { first: pf1, second: ps1 } = pair1; // first1: u8, second1: bool let Pair { first: pf2, second: ps2 } = pair2; // first2: bool, second2: u8 assert!(pf1 == ps2, 0x0); // 10 == 10 assert!(ps1 == pf2, 0x0); // true == true\n} Types for variables pair1 and pair2 are different, and the comparison will not compile.","breadcrumbs":"Move Basics » Generics » Multiple Type Parameters","id":"193","title":"Multiple Type Parameters"},"194":{"body":"In the examples above we focused on instantiating generic types and calling generic functions to create instances of these types. However, the real power of generics is the ability to define shared behavior for the base, generic type, and then use it independently of the concrete types. This is especially useful when working with collections, abstract implementations, and other advanced features in Move. /// A user record with name, age, and some generic metadata\npublic struct User { name: String, age: u8, /// Varies depending on application. metadata: T,\n} In the example above, User is a generic type with a single type parameter T, with shared fields name and age, and the generic metadata field which can store any type. No matter what the metadata is, all of the instances of User will have the same fields and methods. /// Updates the name of the user.\npublic fun update_name(user: &mut User, name: String) { user.name = name;\n} /// Updates the age of the user.\npublic fun update_age(user: &mut User, age: u8) { user.age = age;\n}","breadcrumbs":"Move Basics » Generics » Why Generics?","id":"194","title":"Why Generics?"},"195":{"body":"In some cases, you may want to define a generic type with a type parameter that is not used in the fields or methods of the type. This is called a phantom type parameter . Phantom type parameters are useful when you want to define a type that can hold any other type, but you want to enforce some constraints on the type parameter. /// A generic type with a phantom type parameter.\npublic struct Coin { value: u64\n} The Coin type here does not contain any fields or methods that use the type parameter T. It is used to differentiate between different types of coins, and to enforce some constraints on the type parameter T. public struct USD {}\npublic struct EUR {} #[test]\nfun test_phantom_type() { let coin1: Coin = Coin { value: 10 }; let coin2: Coin = Coin { value: 20 }; // Unpacking is identical because the phantom type parameter is not used. let Coin { value: _ } = coin1; let Coin { value: _ } = coin2;\n} In the example above, we demonstrate how to create two different instances of Coin with different phantom type parameters USD and EUR. The type parameter T is not used in the fields or methods of the Coin type, but it is used to differentiate between different types of coins. It will make sure that the USD and EUR coins are not mixed up.","breadcrumbs":"Move Basics » Generics » Phantom Type Parameters","id":"195","title":"Phantom Type Parameters"},"196":{"body":"Type parameters can be constrained to have certain abilities. This is useful when you need the inner type to allow certain behavior, such as copy or drop . The syntax for constraining a type parameter is T: + . /// A generic type with a type parameter that has the `drop` ability.\npublic struct Droppable { value: T,\n} /// A generic struct with a type parameter that has the `copy` and `drop` abilities.\npublic struct CopyableDroppable { value: T, // T must have the `copy` and `drop` abilities\n} Move Compiler will enforce that the type parameter T has the specified abilities. If the type parameter does not have the specified abilities, the code will not compile. /// Type without any abilities.\npublic struct NoAbilities {} #[test]\nfun test_constraints() { // Fails - `NoAbilities` does not have the `drop` ability // let droppable = Droppable { value: 10 }; // Fails - `NoAbilities` does not have the `copy` and `drop` abilities // let copyable_droppable = CopyableDroppable { value: 10 };\n}","breadcrumbs":"Move Basics » Generics » Constraints on Type Parameters","id":"196","title":"Constraints on Type Parameters"},"197":{"body":"Generics in the Move Reference.","breadcrumbs":"Move Basics » Generics » Further Reading","id":"197","title":"Further Reading"},"198":{"body":"In programming languages reflection is the ability of a program to examine and modify its own structure and behavior. In Move, there's a limited form of reflection that allows you to inspect the type of a value at runtime. This is useful when you need to store type information in a homogeneous collection, or when you need to check if a type belongs to a package. Type reflection is implemented in the Standard Library module std::type_name. Expressed very roughly, it gives a single function get() which returns the name of the type T.","breadcrumbs":"Move Basics » Type Reflection » Type Reflection","id":"198","title":"Type Reflection"},"199":{"body":"The module is pretty straightforward, and operations allowed on the result are limited to getting a string representation and extracting the module and address of the type. module book::type_reflection; use std::ascii::String;\nuse std::type_name::{Self, TypeName}; /// A function that returns the name of the type `T` and its module and address.\npublic fun do_i_know_you(): (String, String, String) { let type_name: TypeName = type_name::get(); // there's a way to borrow let str: &String = type_name.borrow_string(); let module_name: String = type_name.get_module(); let address_str: String = type_name.get_address(); // and a way to consume the value let str = type_name.into_string(); (str, module_name, address_str)\n} #[test_only]\npublic struct MyType {} #[test]\nfun test_type_reflection() { let (type_name, module_name, _address_str) = do_i_know_you(); // assert!(module_name == b\"type_reflection\".to_ascii_string(), 1);\n}","breadcrumbs":"Move Basics » Type Reflection » In practice","id":"199","title":"In practice"},"2":{"body":"Move requires an environment to run and develop applications, and in this small chapter we will cover the prerequisites for the Move language: how to set up your IDE, how to install the compiler and what is Move 2024. If you are already familiar with these topics or have a CLI installed, you can skip this chapter and proceed to the next one .","breadcrumbs":"Before we begin » Before we begin","id":"2","title":"Before we begin"},"20":{"body":"The tests/ directory contains package tests. The compiler excludes these files in the regular build process but uses them in test and dev modes. The tests are written in Move and are marked with the #[test] attribute. Tests can be grouped in a separate module (then it's usually called module_name_tests.move ), or inside the module they're testing. Modules, imports, constants and functions can be annotated with #[test_only]. This attribute is used to exclude modules, functions or imports from the build process. This is useful when you want to add helpers for your tests without including them in the code that will be published on chain. The hello_world_tests.move file contains a commented out test module template: /*\n#[test_only]\nmodule hello_world::hello_world_tests { // uncomment this line to import the module // use hello_world::hello_world; const ENotImplemented: u64 = 0; #[test] fun test_hello_world() { // pass } #[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)] fun test_hello_world_fail() { abort ENotImplemented }\n}\n*/","breadcrumbs":"Hello, World! » Tests","id":"20","title":"Tests"},"200":{"body":"Type reflection is an important part of the language, and it is a crucial part of some of the more advanced patterns.","breadcrumbs":"Move Basics » Type Reflection » Further reading","id":"200","title":"Further reading"},"201":{"body":"A crucial part of any software development, and even more - blockchain development, is testing. Here, we will cover the basics of testing in Move and how to write and organize tests for your Move code.","breadcrumbs":"Move Basics » Testing » Testing","id":"201","title":"Testing"},"202":{"body":"Tests in Move are functions marked with the #[test] attribute. This attribute tells the compiler that the function is a test function, and it should be run when the tests are executed. Test functions are regular functions, but they must take no arguments and have no return value. They are excluded from the bytecode, and are never published. module book::testing { // Test attribute is placed before the `fun` keyword. Can be both above or // right before the `fun` keyword: `#[test] fun my_test() { ... }` // The name of the test would be `book::testing::simple_test`. #[test] fun simple_test() { let sum = 2 + 2; assert!(sum == 4, 1); } // The name of the test would be `book::testing::more_advanced_test`. #[test] fun more_advanced_test() { let sum = 2 + 2 + 2; assert!(sum == 4, 1); }\n}","breadcrumbs":"Move Basics » Testing » The #[test] attribute","id":"202","title":"The #[test] attribute"},"203":{"body":"To run tests, you can use the sui move test command. This command will first build the package in the test mode and then run all the tests found in the package. During test mode, modules from both sources/ and tests/ directories are processed, and the tests are executed. $ sui move test\n> INCLUDING DEPENDENCY Sui\n> INCLUDING DEPENDENCY MoveStdlib\n> BUILDING book\n> Running Move unit tests\n> ...","breadcrumbs":"Move Basics » Testing » Running Tests","id":"203","title":"Running Tests"},"204":{"body":"Tests for fail cases can be marked with #[expected_failure]. This attribute placed on a #[test] function tells the compiler that the test is expected to fail. This is useful when you want to test that a function fails when a certain condition is met. This attribute can only be placed on a #[test] function. The attribute can take an argument for abort code, which is the expected abort code when the test fails. If the test fails with a different abort code, the test will fail. If the execution did not abort, the test will also fail. module book::testing_failure { const EInvalidArgument: u64 = 1; #[test] #[expected_failure(abort_code = 0)] fun test_fail() { abort 0 // aborts with 0 } // attributes can be grouped together #[test, expected_failure(abort_code = EInvalidArgument)] fun test_fail_1() { abort 1 // aborts with 1 }\n} The abort_code argument can use constants defined in the tests module as well as imported from other modules. This is the only case where constants can be used and \"accessed\" in other modules.","breadcrumbs":"Move Basics » Testing » Test Fail Cases with #[expected_failure]","id":"204","title":"Test Fail Cases with #[expected_failure]"},"205":{"body":"In some cases, it is helpful to give the test environment access to some of the internal functions or features. It simplifies the testing process and allows for more thorough testing. However, it is important to remember that these functions should not be included in the final package. This is where the #[test_only] attribute comes in handy. module book::testing { // Public function which uses the `secret` function. public fun multiply_by_secret(x: u64): u64 { x * secret() } /// Private function which is not available to the public. fun secret(): u64 { 100 } #[test_only] /// This function is only available for testing purposes in tests and other /// test-only functions. Mind the visibility - for `#[test_only]` it is /// common to use `public` visibility. public fun secret_for_testing(): u64 { secret() } #[test] // In the test environment we have access to the `secret_for_testing` function. fun test_multiply_by_secret() { let expected = secret_for_testing() * 2; assert!(multiply_by_secret(2) == expected, 1); }\n} Functions marked with the #[test_only] will be available to the test environment, and to the other modules if their visibility is set so.","breadcrumbs":"Move Basics » Testing » Utilities with #[test_only]","id":"205","title":"Utilities with #[test_only]"},"206":{"body":"Unit Testing in the Move Reference.","breadcrumbs":"Move Basics » Testing » Further Reading","id":"206","title":"Further Reading"},"207":{"body":"This chapter describes the Object Model of Sui. It focuses on the theory and concepts behind the Object Model, preparing you for a practical dive into Sui Storage operations and resource ownership. For convenience and easier lookup, we split the chapter into several sections, each covering a specific aspect of the Object Model. In no way should this chapter be considered a comprehensive guide to the Object Model. It is only a high-level overview of the concepts and principles behind the Object Model. For a more detailed description, refer to the Sui Documentation .","breadcrumbs":"Object Model » Object Model","id":"207","title":"Object Model"},"208":{"body":"Smart-contract programming languages have historically focused on defining and managing digital assets. For example, the ERC-20 standard in Ethereum pioneered a set of standards to interact with digital currency tokens, establishing a blueprint for creating and managing digital currencies on the blockchain. Subsequently, the introduction of the ERC-721 standard marked a significant evolution, popularising the concept of non-fungible tokens (NFTs), which represent unique, indivisible assets. These standards laid the groundwork for the complex digital assets we see today. However, Ethereum's programming model lacked a native representation of assets. In other words, externally, a Smart Contract behaved like an asset, but the language itself did not have a way to inherently represent assets. From the start, Move aimed to provide a first-class abstraction for assets, opening up new avenues for thinking about and programming assets. It is important to highlight which properties are essential for an asset: Ownership: Every asset is associated with an owner(s), mirroring the straightforward concept of ownership in the physical world—just as you own a car, you can own a digital asset. Move enforces ownership in such a way that once an asset is moved , the previous owner completely loses any control over it. This mechanism ensures a clear and secure change of ownership. Non-copyable: In the real world, unique items cannot be duplicated effortlessly. Move applies this principle to digital assets, ensuring they cannot be arbitrarily copied within the program. This property is crucial for maintaining the scarcity and uniqueness of digital assets, mirroring the intrinsic value of physical assets. Non-discardable: Just as you cannot accidentally lose a house or a car without a trace, Move ensures that no asset can be discarded or lost in a program. Instead, assets must be explicitly transferred or destroyed. This property guarantees the deliberate handling of digital assets, preventing accidental loss and ensuring accountability in asset management. Move managed to encapsulate these properties in its design, becoming an ideal language for digital assets.","breadcrumbs":"Object Model » Language for Digital Assets » Move - Language for Digital Assets","id":"208","title":"Move - Language for Digital Assets"},"209":{"body":"Move was designed to provide a first-class abstraction for digital assets, enabling developers to create and manage assets natively. Essential properties of digital assets include ownership, non-copyability, and non-discardability, which Move enforces in its design. Move's asset model mirrors real-world asset management, ensuring secure and accountable asset ownership and transfer.","breadcrumbs":"Object Model » Language for Digital Assets » Summary","id":"209","title":"Summary"},"21":{"body":"Additionally, Move CLI supports the examples/ folder. The files there are treated similarly to the ones placed under the tests/ folder - they're only built in the test and dev modes. They are to be examples of how to use the package or how to integrate it with other packages. The most popular use case is for documentation purposes and library packages.","breadcrumbs":"Hello, World! » Other Folders","id":"21","title":"Other Folders"},"210":{"body":"Move: A Language With Programmable Resources (pdf) by Sam Blackshear, Evan Cheng, David L. Dill, Victor Gao, Ben Maurer, Todd Nowacki, Alistair Pott, Shaz Qadeer, Rain, Dario Russi, Stephane Sezer, Tim Zakian, Runtian Zhou*","breadcrumbs":"Object Model » Language for Digital Assets » Further reading","id":"210","title":"Further reading"},"211":{"body":"While Move was created to manage digital assets, its initial storage model was bulky and not well-suited for many use cases. For instance, if Alice wanted to transfer an asset X to Bob, Bob had to create a new \"empty\" resource, and then Alice could transfer asset X to Bob. This process was not intuitive and presented implementation challenges, partly due to the restrictive design of Diem . Another drawback of the original design was the lack of built-in support for a \"transfer\" operation, requiring every module to implement its own storage transfer logic. Additionally, managing heterogeneous collections of assets in a single account was particularly challenging. Sui addressed these challenges by redesigning the storage and ownership model of objects to more closely resemble real-world object interactions. With a native concept of ownership and transfer , Alice can directly transfer asset X to Bob. Furthermore, Bob can maintain a collection of different assets without any preparatory steps. These improvements laid the foundation for the Object Model in Sui.","breadcrumbs":"Object Model » Evolution of Move » Evolution of Move","id":"211","title":"Evolution of Move"},"212":{"body":"Move's initial storage model was not well-suited for managing digital assets, requiring complex and restrictive transfer operations. Sui introduced the Object Model, which provides a native concept of ownership, simplifying asset management and enabling heterogeneous collections.","breadcrumbs":"Object Model » Evolution of Move » Summary","id":"212","title":"Summary"},"213":{"body":"Why We Created Sui Move by Sam Blackshear","breadcrumbs":"Object Model » Evolution of Move » Further reading","id":"213","title":"Further reading"},"214":{"body":"The Object Model in Sui can be viewed as a high-level abstraction representing digital assets as objects . These objects have their own type and associated behaviors, a unique identifier, and support native storage operations like transfer and share . Designed to be intuitive and easy to use, the Object Model enables a wide range of use cases to be implemented with ease. Objects in Sui have the following properties: Type: Every object has a type, defining the structure and behavior of the object. Objects of different types cannot be mixed or used interchangeably, ensuring objects are used correctly according to their type system. Unique ID: Each object has a unique identifier, distinguishing it from other objects. This ID is generated upon the object's creation and is immutable. It's used to track and identify objects within the system. Owner: Every object is associated with an owner, who has control over the object. Ownership on Sui can be exclusive to an account, shared across the network, or frozen, allowing read-only access without modification or transfer capabilities. We will discuss ownership in more detail in the following sections. Data: Objects encapsulate their data, simplifying management and manipulation. The data structure and operations are defined by the object's type. Version: The transition from accounts to objects is facilitated by object versioning. Traditionally, blockchains use a nonce to prevent replay attacks. In Sui, the object's version acts as a nonce, preventing replay attacks for each object. Digest: Every object has a digest, which is a hash of the object's data. The digest is used to cryptographically verify the integrity of the object's data and ensures that it has not been tampered with. The digest is calculated when the object is created and is updated whenever the object's data changes.","breadcrumbs":"Object Model » What is an Object? » What is an Object?","id":"214","title":"What is an Object?"},"215":{"body":"Objects in Sui are high-level abstractions representing digital assets. Objects have a type, unique ID, owner, data, version, and digest. The Object Model simplifies asset management and enables a wide range of use cases.","breadcrumbs":"Object Model » What is an Object? » Summary","id":"215","title":"Summary"},"216":{"body":"Object Model in Sui Documentation.","breadcrumbs":"Object Model » What is an Object? » Further reading","id":"216","title":"Further reading"},"217":{"body":"Sui introduces four distinct ownership types for objects: single owner, shared state, immutable shared state, and object-owner. Each model offers unique characteristics and suits different use cases, enhancing flexibility and control in object management.","breadcrumbs":"Object Model » Ownership » Ownership","id":"217","title":"Ownership"},"218":{"body":"The account owner, also known as the single owner model, is the foundational ownership type in Sui. Here, an object is owned by a single account, granting that account exclusive control over the object within the behaviors associated with its type. This model embodies the concept of true ownership , where the account possesses complete authority over the object, making it inaccessible to others for modification or transfer. This level of ownership clarity is a significant advantage over other blockchain systems, where ownership definitions can be more ambiguous, and smart contracts may have the ability to alter or transfer assets without the owner's consent.","breadcrumbs":"Object Model » Ownership » Account Owner (or Single Owner)","id":"218","title":"Account Owner (or Single Owner)"},"219":{"body":"Single owner model has its limitations: for example, it is very tricky to implement a marketplace for digital assets without a shared state. For a generic marketplace scenario, imagine that Alice owns an asset X, and she wants to sell it by putting it into a shared marketplace. Then Bob can come and buy the asset directly from the marketplace. The reason why this is tricky is that it is impossible to write a smart contract that would \"lock\" the asset in Alice's account and take it out when Bob buys it. First, it will be a violation of the single owner model, and second, it requires a shared access to the asset. To facilitate a problem of shared data access, Sui has introduced a shared ownership model. In this model, an object can be shared with the network. Shared objects can be read and modified by any account on the network, and the rules of interaction are defined by the implementation of the object. Typical uses for shared objects are: marketplaces, shared resources, escrows, and other scenarios where multiple accounts need access to the same state.","breadcrumbs":"Object Model » Ownership » Shared State","id":"219","title":"Shared State"},"22":{"body":"Move is a compiled language, and as such, it requires the compilation of source files into Move Bytecode. It contains only necessary information about the module, its members, and types, and excludes comments and some identifiers (for example, for constants). To demonstrate these features, let's replace the contents of the sources/hello_world.move file with the following: /// The module `hello_world` under named address `hello_world`.\n/// The named address is set in the `Move.toml`.\nmodule hello_world::hello_world { // Imports the `String` type from the Standard Library use std::string::String; /// Returns the \"Hello, World!\" as a `String`. public fun hello_world(): String { b\"Hello, World!\".to_string() }\n} During compilation, the code is built, but not run. A compiled package only includes functions that can be called by other modules or in a transaction. We will explain these concepts in the Concepts chapter. But now, let's see what happens when we run the sui move build . # run from the `hello_world` folder\n$ sui move build # alternatively, if you didn't `cd` into it\n$ sui move build --path hello_world It should output the following message on your console. UPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git\nINCLUDING DEPENDENCY Sui\nINCLUDING DEPENDENCY MoveStdlib\nBUILDING hello_world During the compilation, Move Compiler automatically creates a build folder where it places all fetched and compiled dependencies as well as the bytecode for the modules of the current package. If you're using a versioning system, such as Git, build folder should be ignored. For example, you should use a .gitignore file and add build to it.","breadcrumbs":"Hello, World! » Compiling the Package","id":"22","title":"Compiling the Package"},"220":{"body":"Sui also offers the frozen object model, where an object becomes permanently read-only. These immutable objects, while readable, cannot be modified or moved, providing a stable and constant state accessible to all network participants. Frozen objects are ideal for public data, reference materials, and other use cases where the state permanence is desirable.","breadcrumbs":"Object Model » Ownership » Immutable (Frozen) State","id":"220","title":"Immutable (Frozen) State"},"221":{"body":"The last ownership model in Sui is the object owner . In this model, an object is owned by another object. This feature allows creating complex relationships between objects, store large heterogenious collections, and implementing extensible and modular systems. Practically speaking, since the transactions are initiated by accounts, the transaction still accesses the parent object, but it can then access the child objects through the parent object. A use case we love to mention is a game character. Alice can own the Hero object from a game, and the Hero can own items: also represented as objects, like a \"Map\", or a \"Compass\". Alice may take the \"Map\" from the \"Hero\" object, and then send it to Bob, or sell it on a marketplace. With object owner, it becomes very natural to imagine how the assets can be structured and managed in relation to each other.","breadcrumbs":"Object Model » Ownership » Object Owner","id":"221","title":"Object Owner"},"222":{"body":"Single Owner: Objects are owned by a single account, granting exclusive control over the object. Shared State: Objects can be shared with the network, allowing multiple accounts to read and modify the object. Immutable State: Objects become permanently read-only, providing a stable and constant state. Object Owner: Objects can own other objects, enabling complex relationships and modular systems.","breadcrumbs":"Object Model » Ownership » Summary","id":"222","title":"Summary"},"223":{"body":"In the next section we will talk about transaction execution paths in Sui, and how the ownership models affect the transaction execution.","breadcrumbs":"Object Model » Ownership » Next Steps","id":"223","title":"Next Steps"},"224":{"body":"The Object Model allows for variable transaction execution paths, depending on the object's ownership type. The transaction execution path determines how the transaction is processed and validated by the network. In this section, we'll explore the different transaction execution paths in Sui and how they interact with the consensus mechanism.","breadcrumbs":"Object Model » Fast Path & Consensus » Fast Path & Consensus","id":"224","title":"Fast Path & Consensus"},"225":{"body":"At its core, blockchain technology faces a fundamental concurrency challenge: multiple parties may try to modify or access the same data simultaneously in a decentralized environment. This requires a system for sequencing and validating transactions to support the network's consistency. Sui addresses this challenge through a consensus mechanism, ensuring all nodes agree on the transactions' sequence and state. Consider a marketplace scenario where Alice and Bob simultaneously attempt to purchase the same asset. The network must resolve this conflict to prevent double-spending, ensuring that at most one transaction succeeds while the other is rightfully rejected.","breadcrumbs":"Object Model » Fast Path & Consensus » Concurrency Challenge","id":"225","title":"Concurrency Challenge"},"226":{"body":"However, not all transactions require the same level of validation and consensus. For example, if Alice wants to transfer an object that she owns to Bob, the network can process this transaction without sequencing it with respect to all other transactions in the network, as only Alice has the authority to access the object. This is known as the fast path execution, where transactions accessing account-owned objects are processed quickly without the need for extensive consensus. No concurrent data access -> simpler challenge -> fast path. Another ownership model that allows for fast path execution is the immutable state . Since immutable objects cannot change, transactions involving them can be processed quickly without the need to sequence them.","breadcrumbs":"Object Model » Fast Path & Consensus » Fast Path","id":"226","title":"Fast Path"},"227":{"body":"Transactions that do access shared state - on Sui it is represented with shared objects - require sequencing to ensure that the state is updated and consistented across all nodes. This is known as the execution through consensus , where transactions accessing shared objects are subject to the agreement process to maintain network consistency.","breadcrumbs":"Object Model » Fast Path & Consensus » Consensus Path","id":"227","title":"Consensus Path"},"228":{"body":"Lastly, it is important to mention that objects owned by other objects are subject to the same rules as the parent object. If the parent object is shared , the child object is also transitively shared. If the parent object is immutable, the child object is also immutable.","breadcrumbs":"Object Model » Fast Path & Consensus » Objects owned by Objects","id":"228","title":"Objects owned by Objects"},"229":{"body":"Fast Path: Transactions involving account-owned objects or immutable shared state are processed quickly without the need for extensive consensus. Consensus Path: Transactions involving shared objects require sequencing and consensus to ensure network integrity. Objects owned by Objects: Child objects inherit the ownership model of the parent object.","breadcrumbs":"Object Model » Fast Path & Consensus » Summary","id":"229","title":"Summary"},"23":{"body":"Before we get to testing, we should add a test. Move Compiler supports tests written in Move and provides the execution environment. The tests can be placed in both the source files and in the tests/ folder. Tests are marked with the #[test] attribute and are automatically discovered by the compiler. We explain tests in depth in the Testing section. Replace the contents of the tests/hello_world_tests.move with the following content: #[test_only]\nmodule hello_world::hello_world_tests { use hello_world::hello_world; #[test] fun test_hello_world() { assert!(hello_world::hello_world() == b\"Hello, World!\".to_string(), 0); }\n} Here we import the hello_world module, and call its hello_world function to test that the output is indeed the string \"Hello, World!\". Now, that we have tests in place, let's compile the package in the test mode and run tests. Move CLI has the test command for this: $ sui move test The output should be similar to the following: INCLUDING DEPENDENCY Sui\nINCLUDING DEPENDENCY MoveStdlib\nBUILDING hello_world\nRunning Move unit tests\n[ PASS ] 0x0::hello_world_tests::test_hello_world\nTest result: OK. Total tests: 1; passed: 1; failed: 0 If you're running the tests outside of the package folder, you can specify the path to the package: $ sui move test --path hello_world You can also run a single or multiple tests at once by specifying a string. All the tests names containing the string will be run: $ sui move test test_hello","breadcrumbs":"Hello, World! » Running Tests","id":"23","title":"Running Tests"},"230":{"body":"In the Object Model chapter we briefly explained the evolution of the Move language from an account-based model to an object-based model. In this chapter, we will dive deeper into the object model and explore how to use objects in your Sui applications. If you haven't read the Object Model chapter yet, we recommend you do so before continuing with this chapter.","breadcrumbs":"Using Objects » Using Objects","id":"230","title":"Using Objects"},"231":{"body":"In the Basic Syntax chapter we already covered two out of four abilities - Drop and Copy . They affect the behaviour of the value in a scope and are not directly related to storage. It is time to cover the key ability, which allows the struct to be stored. Historically, the key ability was created to mark the type as a key in the storage . A type with the key ability could be stored at top-level in the storage, and could be directly owned by an account or address. With the introduction of the Object Model , the key ability naturally became the defining ability for the object.","breadcrumbs":"Using Objects » Ability: Key » The Key Ability","id":"231","title":"The Key Ability"},"232":{"body":"A struct with the key ability is considered an object and can be used in the storage functions. The Sui Verifier will require the first field of the struct to be named id and have the type UID. public struct Object has key { id: UID, // required name: String,\n} /// Creates a new Object with a Unique ID\npublic fun new(name: String, ctx: &mut TxContext): Object { Object { id: object::new(ctx), // creates a new UID name, }\n} A struct with the key ability is still a struct, and can have any number of fields and associated functions. There is no special handling or syntax for packing, accessing or unpacking the struct. However, because the first field of an object struct must be of type UID - a non-copyable and non-droppable type (we will get to it very soon!), the struct transitively cannot have drop and copy abilities. Thus, the object is non-discardable by design.","breadcrumbs":"Using Objects » Ability: Key » Object Definition","id":"232","title":"Object Definition"},"233":{"body":"Due to the UID requirement for types with key, none of the native types in Move can have the key ability, nor can any of the Standard Library types. The key ability is only present in the Sui Framework and custom types.","breadcrumbs":"Using Objects » Ability: Key » Types with the key Ability","id":"233","title":"Types with the key Ability"},"234":{"body":"Key ability defines the object in Move, and objects are intended to be stored . In the next section we present the sui::transfer module, which provides native storage functions for objects.","breadcrumbs":"Using Objects » Ability: Key » Next Steps","id":"234","title":"Next Steps"},"235":{"body":"Type Abilities in the Move Reference.","breadcrumbs":"Using Objects » Ability: Key » Further reading","id":"235","title":"Further reading"},"236":{"body":"The module that defines main storage operations is sui::transfer. It is implicitly imported in all packages that depend on the Sui Framework , so, like other implicitly imported modules (e.g. std::option or std::vector), it does not require adding a use statement.","breadcrumbs":"Using Objects » Storage Functions » Storage Functions","id":"236","title":"Storage Functions"},"237":{"body":"The transfer module provides functions to perform all three storage operations matching ownership types which we explained before: On this page we will only talk about so-called restricted storage operations, later we will cover public ones, after the store ability is introduced. Transfer - send an object to an address, put it into account owned state; Share - put an object into a shared state, so it is available to everyone; Freeze - put an object into immutable state, so it becomes a public constant and can never change. The transfer module is a go-to for most of the storage operations, except a special case with Dynamic Fields awaits us in the next chapter.","breadcrumbs":"Using Objects » Storage Functions » Overview","id":"237","title":"Overview"},"238":{"body":"In the Ownership and Scope and References chapters, we covered the basics of ownership and references in Move. It is important that you understand these concepts when using storage functions. Here is a quick recap of the most important points: The move semantics in Move means that the value is moved from one scope to another. In other words, if an instance of a type is passed to a function by value , it is moved to the function scope and can't be accessed in the caller scope anymore. To maintain the ownership of the value, you can pass it by reference . Either by immutable reference &T or mutable reference &mut T. Then the value is borrowed and can be accessed in the caller scope, however the owner stays the same. /// Moved by value\npublic fun take(value: T) { /* value is moved here! */ abort 0 } /// For immutable reference\npublic fun borrow(value: &T) { /* value is borrowed here! can be read */ abort 0 } /// For mutable reference\npublic fun borrow_mut(value: &mut T) { /* value is mutably borrowed here! */ abort 0 }","breadcrumbs":"Using Objects » Storage Functions » Ownership and References: A Quick Recap","id":"238","title":"Ownership and References: A Quick Recap"},"239":{"body":"The transfer::transfer function is a public function used to transfer an object to another address. Its signature is as follows, only accepts a type with the key ability and an address of the recipient. Please, note that the object is passed into the function by value , therefore it is moved to the function scope and then moved to the recipient address: // File: sui-framework/sources/transfer.move\npublic fun transfer(obj: T, recipient: address); In the next example, you can see how it can be used in a module that defines and sends an object to the transaction sender. module book::transfer_to_sender { /// A struct with `key` is an object. The first field is `id: UID`! public struct AdminCap has key { id: UID } /// `init` function is a special function that is called when the module /// is published. It is a good place to create application objects. fun init(ctx: &mut TxContext) { // Create a new `AdminCap` object, in this scope. let admin_cap = AdminCap { id: object::new(ctx) }; // Transfer the object to the transaction sender. transfer::transfer(admin_cap, ctx.sender()); // admin_cap is gone! Can't be accessed anymore. } /// Transfers the `AdminCap` object to the `recipient`. Thus, the recipient /// becomes the owner of the object, and only they can access it. public fun transfer_admin_cap(cap: AdminCap, recipient: address) { transfer::transfer(cap, recipient); }\n} When the module is published, the init function will get called, and the AdminCap object which we created there will be transferred to the transaction sender. The ctx.sender() function returns the sender address for the current transaction. Once the AdminCap has been transferred to the sender, for example, to 0xa11ce, the sender, and only the sender, will be able to access the object. The object is now account owned . Account owned objects are a subject to true ownership - only the account owner can access them. This is a fundamental concept in the Sui storage model. Let's extend the example with a function that uses AdminCap to authorize a mint of a new object and its transfer to another address: /// Some `Gift` object that the admin can `mint_and_transfer`.\npublic struct Gift has key { id: UID } /// Creates a new `Gift` object and transfers it to the `recipient`.\npublic fun mint_and_transfer( _: &AdminCap, recipient: address, ctx: &mut TxContext\n) { let gift = Gift { id: object::new(ctx) }; transfer::transfer(gift, recipient);\n} The mint_and_transfer function is a public function that \"could\" be called by anyone, but it requires an AdminCap object to be passed as the first argument by reference. Without it, the function will not be callable. This is a simple way to restrict access to privileged functions called Capability . Because the AdminCap object is account owned , only 0xa11ce will be able to call the mint_and_transfer function. The Gifts sent to recipients will also be account owned , each gift being unique and owned exclusively by the recipient. A quick recap: transfer function is used to send an object to an address; The object becomes account owned and can only be accessed by the recipient; Functions can be gated by requiring an object to be passed as an argument, creating a capability .","breadcrumbs":"Using Objects » Storage Functions » Transfer","id":"239","title":"Transfer"},"24":{"body":"In this section, we explained the basics of a Move package: its structure, the manifest, the build, and test flows. On the next page , we will write an application and see how the code is structured and what the language can do.","breadcrumbs":"Hello, World! » Next Steps","id":"24","title":"Next Steps"},"240":{"body":"The transfer::freeze_object function is public function used to put an object into an immutable state. Once an object is frozen , it can never be changed, and it can be accessed by anyone by immutable reference. The function signature is as follows, only accepts a type with the key ability . Just like all other storage functions, it takes the object by value : // File: sui-framework/sources/transfer.move\npublic fun freeze_object(obj: T); Let's expand on the previous example and add a function that allows the admin to create a Config object and freeze it: /// Some `Config` object that the admin can `create_and_freeze`.\npublic struct Config has key { id: UID, message: String\n} /// Creates a new `Config` object and freezes it.\npublic fun create_and_freeze( _: &AdminCap, message: String, ctx: &mut TxContext\n) { let config = Config { id: object::new(ctx), message }; // Freeze the object so it becomes immutable. transfer::freeze_object(config);\n} /// Returns the message from the `Config` object.\n/// Can access the object by immutable reference!\npublic fun message(c: &Config): String { c.message } Config is an object that has a message field, and the create_and_freeze function creates a new Config and freezes it. Once the object is frozen, it can be accessed by anyone by immutable reference. The message function is a public function that returns the message from the Config object. Config is now publicly available by its ID, and the message can be read by anyone. Function definitions are not connected to the object's state. It is possible to define a function that takes a mutable reference to an object that is used as frozen. However, it won't be callable on a frozen object. The message function can be called on an immutable Config object, however, two functions below are not callable on a frozen object: // === Functions below can't be called on a frozen object! === /// The function can be defined, but it won't be callable on a frozen object.\n/// Only immutable references are allowed.\npublic fun message_mut(c: &mut Config): &mut String { &mut c.message } /// Deletes the `Config` object, takes it by value.\n/// Can't be called on a frozen object!\npublic fun delete_config(c: Config) { let Config { id, message: _ } = c; id.delete()\n} To summarize: transfer::freeze_object function is used to put an object into an immutable state; Once an object is frozen , it can never be changed, deleted or transferred, and it can be accessed by anyone by immutable reference;","breadcrumbs":"Using Objects » Storage Functions » Freeze","id":"240","title":"Freeze"},"241":{"body":"Since the transfer::freeze_object signature accepts any type with the key ability, it can take an object that was created in the same scope, but it can also take an object that was owned by an account. This means that the freeze_object function can be used to freeze an object that was transferred to the sender. For security concerns, we would not want to freeze the AdminCap object - it would be a security risk to allow access to it to anyone. However, we can freeze the Gift object that was minted and transferred to the recipient: Single Owner -> Immutable conversion is possible! /// Freezes the `Gift` object so it becomes immutable.\npublic fun freeze_gift(gift: Gift) { transfer::freeze_object(gift);\n}","breadcrumbs":"Using Objects » Storage Functions » Owned -> Frozen","id":"241","title":"Owned -> Frozen"},"242":{"body":"The transfer::share_object function is a public function used to put an object into a shared state. Once an object is shared , it can be accessed by anyone by a mutable reference (hence, immutable too). The function signature is as follows, only accepts a type with the key ability : // File: sui-framework/sources/transfer.move\npublic fun share_object(obj: T); Once an object is shared , it is publicly available as a mutable reference.","breadcrumbs":"Using Objects » Storage Functions » Share","id":"242","title":"Share"},"243":{"body":"While the shared object can't normally be taken by value, there is one special case where it can - if the function that takes it deletes the object. This is a special case in the Sui storage model, and it is used to allow the deletion of shared objects. To show how it works, we will create a function that creates and shares a Config object and then another one that deletes it: /// Creates a new `Config` object and shares it.\npublic fun create_and_share(message: String, ctx: &mut TxContext) { let config = Config { id: object::new(ctx), message }; // Share the object so it becomes shared. transfer::share_object(config);\n} The create_and_share function creates a new Config object and shares it. The object is now publicly available as a mutable reference. Let's create a function that deletes the shared object: /// Deletes the `Config` object, takes it by value.\n/// Can be called on a shared object!\npublic fun delete_config(c: Config) { let Config { id, message: _ } = c; id.delete()\n} The delete_config function takes the Config object by value and deletes it, and the Sui Verifier would allow this call. However, if the function returned the Config object back or attempted to freeze or transfer it, the Sui Verifier would reject the transaction. // Won't work!\npublic fun transfer_shared(c: Config, to: address) { transfer::transfer(c, to);\n} To summarize: share_object function is used to put an object into a shared state; Once an object is shared , it can be accessed by anyone by a mutable reference; Shared objects can be deleted, but they can't be transferred or frozen.","breadcrumbs":"Using Objects » Storage Functions » Special Case: Shared Object Deletion","id":"243","title":"Special Case: Shared Object Deletion"},"244":{"body":"Now that you know main features of the transfer module, you can start building more complex applications on Sui that involve storage operations. In the next chapter, we will cover the Store Ability which allows storing data inside objects and relaxes transfer restrictions which we barely touched on here. And after that we will cover the UID and ID types which are the most important types in the Sui storage model.","breadcrumbs":"Using Objects » Storage Functions » Next Steps","id":"244","title":"Next Steps"},"245":{"body":"Now that you have an understanding of top-level storage functions which are enabled by the key ability, we can talk about the last ability in the list - store.","breadcrumbs":"Using Objects » Ability: Store » Ability: Store","id":"245","title":"Ability: Store"},"246":{"body":"The store is a special ability that allows a type to be stored in objects. This ability is required for the type to be used as a field in a struct that has the key ability. Another way to put it is that the store ability allows the value to be wrapped in an object. The store ability also relaxes restrictions on transfer operations. We talk about it more in the Restricted and Public Transfer section.","breadcrumbs":"Using Objects » Ability: Store » Definition","id":"246","title":"Definition"},"247":{"body":"In previous sections we already used types with the key ability: all objects must have a UID field, which we used in examples; we also used the Storable type as a part of the Config struct. The Config type also has the store ability. /// This type has the `store` ability.\npublic struct Storable has store {} /// Config contains a `Storable` field which must have the `store` ability.\npublic struct Config has key, store { id: UID, stores: Storable,\n} /// MegaConfig contains a `Config` field which has the `store` ability.\npublic struct MegaConfig has key { id: UID, config: Config, // there it is!\n}","breadcrumbs":"Using Objects » Ability: Store » Example","id":"247","title":"Example"},"248":{"body":"All native types (except for references) in Move have the store ability. This includes: bool unsigned integers vector address All of the types defined in the standard library have the store ability as well. This includes: Option String TypeName","breadcrumbs":"Using Objects » Ability: Store » Types with the store Ability","id":"248","title":"Types with the store Ability"},"249":{"body":"Type Abilities in the Move Reference.","breadcrumbs":"Using Objects » Ability: Store » Further reading","id":"249","title":"Further reading"},"25":{"body":"Package Manifest section Package in The Move Reference","breadcrumbs":"Hello, World! » Further Reading","id":"25","title":"Further Reading"},"250":{"body":"The UID type is defined in the sui::object module and is a wrapper around an ID which, in turn, wraps the address type. The UIDs on Sui are guaranteed to be unique, and can't be reused after the object was deleted. // File: sui-framework/sources/object.move\n/// UID is a unique identifier of an object\npublic struct UID has store { id: ID\n} /// ID is a wrapper around an address\npublic struct ID has store, drop { bytes: address\n}","breadcrumbs":"Using Objects » UID and ID » UID and ID","id":"250","title":"UID and ID"},"251":{"body":"UID is derived from the tx_hash and an index which is incremented for each new UID. The derive_id function is implemented in the sui::tx_context module, and that is why TxContext is required for UID generation. Sui Verifier will not allow using a UID that wasn't created in the same function. That prevents UIDs from being pre-generated and reused after the object was unpacked. New UID is created with the object::new(ctx) function. It takes a mutable reference to TxContext, and returns a new UID. let ctx = &mut tx_context::dummy();\nlet uid = object::new(ctx); On Sui, UID acts as a representation of an object, and allows defining behaviors and features of an object. One of the key-features - Dynamic Fields - is possible because of the UID type being explicit. Additionally, it allows the Transfer To Object (TTO) which we will explain later in this chapter.","breadcrumbs":"Using Objects » UID and ID » Fresh UID generation:","id":"251","title":"Fresh UID generation:"},"252":{"body":"The UID type is created with the object::new(ctx) function, and it is destroyed with the object::delete(uid) function. The object::delete consumes the UID by value , and it is impossible to delete it unless the value was unpacked from an Object. let ctx = &mut tx_context::dummy(); let char = Character { id: object::new(ctx)\n}; let Character { id } = char;\nid.delete();","breadcrumbs":"Using Objects » UID and ID » UID lifecycle","id":"252","title":"UID lifecycle"},"253":{"body":"The UID does not need to be deleted immediately after the object struct is unpacked. Sometimes it may carry Dynamic Fields or objects transferred to it via Transfer To Object . In such cases, the UID may be kept and stored in a separate object.","breadcrumbs":"Using Objects » UID and ID » Keeping the UID","id":"253","title":"Keeping the UID"},"254":{"body":"The ability to return the UID of an object may be utilized in pattern called proof of deletion . It is a rarely used technique, but it may be useful in some cases, for example, the creator or an application may incentivize the deletion of an object by exchanging the deleted IDs for some reward. In framework development this method could be used to ignore / bypass certain restrictions on \"taking\" the object. If there's a container that enforces certain logic on transfers, like Kiosk does, there could be a special scenario of skipping the checks by providing a proof of deletion. This is one of the open topics for exploration and research, and it may be used in various ways.","breadcrumbs":"Using Objects » UID and ID » Proof of Deletion","id":"254","title":"Proof of Deletion"},"255":{"body":"When talking about UID we should also mention the ID type. It is a wrapper around the address type, and is used to represent an address-pointer. Usually, ID is used to point at an object, however, there's no restriction, and no guarantee that the ID points to an existing object. ID can be received as a transaction argument in a Transaction Block . Alternatively, ID can be created from an address value using to_id() function.","breadcrumbs":"Using Objects » UID and ID » ID","id":"255","title":"ID"},"256":{"body":"TxContext provides the fresh_object_address function which can be utilized to create unique addresses and ID - it may be useful in some application that assign unique identifiers to user actions - for example, an order_id in a marketplace.","breadcrumbs":"Using Objects » UID and ID » fresh_object_address","id":"256","title":"fresh_object_address"},"257":{"body":"Storage Operations that we described in the previous sections are restricted by default - they can only be called in the module defining the object. In other terms, the type must be internal to the module to be used in storage operations. This restriction is implemented in the Sui Verifier and is enforced at the bytecode level. However, to allow objects to be transferred and stored in other modules, these restrictions can be relaxed. The sui::transfer module offers a set of public_* functions that allow calling storage operations in other modules. The functions are prefixed with public_ and are available to all modules and transactions.","breadcrumbs":"Using Objects » Restricted and Public Transfer » Restricted and Public Transfer","id":"257","title":"Restricted and Public Transfer"},"258":{"body":"The sui::transfer module provides the following public functions. They are almost identical to the ones we already covered, but can be called from any module. // File: sui-framework/sources/transfer.move\n/// Public version of the `transfer` function.\npublic fun public_transfer(object: T, to: address) {} /// Public version of the `share_object` function.\npublic fun public_share_object(object: T) {} /// Public version of the `freeze_object` function.\npublic fun public_freeze_object(object: T) {} To illustrate the usage of these functions, consider the following example: module A defines an ObjectK with key and ObjectKS with key + store abilities, and module B tries to implement a transfer function for these objects. In this example we use transfer::transfer, but the behaviour is identical for share_object and freeze_object functions. /// Defines `ObjectK` and `ObjectKS` with `key` and `key + store`\n/// abilities respectively\nmodule book::transfer_a { public struct ObjectK has key { id: UID } public struct ObjectKS has key, store { id: UID }\n} /// Imports the `ObjectK` and `ObjectKS` types from `transfer_a` and attempts\n/// to implement different `transfer` functions for them\nmodule book::transfer_b { // types are not internal to this module use book::transfer_a::{ObjectK, ObjectKS}; // Fails! ObjectK is not `store`, and ObjectK is not internal to this module public fun transfer_k(k: ObjectK, to: address) { sui::transfer::transfer(k, to); } // Fails! ObjectKS has `store` but the function is not public public fun transfer_ks(ks: ObjectKS, to: address) { sui::transfer::transfer(ks, to); } // Fails! ObjectK is not `store`, `public_transfer` requires `store` public fun public_transfer_k(k: ObjectK) { sui::transfer::public_transfer(k); } // Works! ObjectKS has `store` and the function is public public fun public_transfer_ks(y: ObjectKS, to: address) { sui::transfer::public_transfer(y, to); }\n} To expand on the example above: transfer_k fails because ObjectK is not internal to module transfer_b transfer_ks fails because ObjectKS is not internal to module transfer_b public_transfer_k fails because ObjectK does not have the store ability public_transfer_ks works because ObjectKS has the store ability and the transfer is public","breadcrumbs":"Using Objects » Restricted and Public Transfer » Public Storage Operations","id":"258","title":"Public Storage Operations"},"259":{"body":"The decision on whether to add the store ability to a type should be made carefully. On one hand, it is de-facto a requirement for the type to be usable by other applications. On the other hand, it allows wrapping and changing the intended storage model. For example, a character may be intended to be owned by accounts, but with the store ability it can be frozen (cannot be shared - this transition is restricted).","breadcrumbs":"Using Objects » Restricted and Public Transfer » Implications of store","id":"259","title":"Implications of store"},"26":{"body":"In the previous section we created a new package and demonstrated the basic flow of creating, building, and testing a Move package. In this section, we will write a simple application that uses the storage model and can be interacted with. To do this, we will create a simple todo list application.","breadcrumbs":"Hello, Sui! » Hello, Sui!","id":"26","title":"Hello, Sui!"},"260":{"body":"In previous chapters we've covered the basics of Move and Sui Storage Model . Now it's time to dive deeper into the advanced topics of Sui programmability. This chapter introduces more complex concepts, practices and features of Move and Sui that are essential for building more sophisticated applications. It is intended for developers who are already familiar with the basics of Move and Sui, and are looking to expand their knowledge and skills.","breadcrumbs":"Advanced Programmability » Advanced Programmability","id":"260","title":"Advanced Programmability"},"261":{"body":"Every transaction has the execution context. The context is a set of predefined variables that are available to the program during execution. For example, every transaction has a sender address, and the transaction context contains a variable that holds the sender address. The transaction context is available to the program through the TxContext struct. The struct is defined in the sui::tx_context module and contains the following fields: // File: sui-framework/sources/tx_context.move\n/// Information about the transaction currently being executed.\n/// This cannot be constructed by a transaction--it is a privileged object created by\n/// the VM and passed in to the entrypoint of the transaction as `&mut TxContext`.\nstruct TxContext has drop { /// The address of the user that signed the current transaction sender: address, /// Hash of the current transaction tx_hash: vector, /// The current epoch number epoch: u64, /// Timestamp that the epoch started at epoch_timestamp_ms: u64, /// Counter recording the number of fresh id's created while executing /// this transaction. Always 0 at the start of a transaction ids_created: u64\n} Transaction context cannot be constructed manually or directly modified. It is created by the system and passed to the function as a reference in a transaction. Any function called in a Transaction has access to the context and can pass it into the nested calls. TxContext has to be the last argument in the function signature.","breadcrumbs":"Advanced Programmability » Transaction Context » Transaction Context","id":"261","title":"Transaction Context"},"262":{"body":"With only exception of the ids_created, all of the fields in the TxContext have getters. The getters are defined in the sui::tx_context module and are available to the program. The getters don't require &mut because they don't modify the context. public fun some_action(ctx: &TxContext) { let me = ctx.sender(); let epoch = ctx.epoch(); let digest = ctx.digest(); // ...\n}","breadcrumbs":"Advanced Programmability » Transaction Context » Reading the Transaction Context","id":"262","title":"Reading the Transaction Context"},"263":{"body":"The TxContext is required to create new objects (or just UIDs) in the system. New UIDs are derived from the transaction digest, and for the digest to be unique, there needs to be a changing parameter. Sui uses the ids_created field for that. Every time a new UID is created, the ids_created field is incremented by one. This way, the digest is always unique. Internally, it is represented as the derive_id function: // File: sui-framework/sources/tx_context.move\nnative fun derive_id(tx_hash: vector, ids_created: u64): address;","breadcrumbs":"Advanced Programmability » Transaction Context » Mutability","id":"263","title":"Mutability"},"264":{"body":"The underlying derive_id function can also be utilized in your program to generate unique addresses. The function itself is not exposed, but a wrapper function fresh_object_address is available in the sui::tx_context module. It may be useful if you need to generate a unique identifier in your program. // File: sui-framework/sources/tx_context.move\n/// Create an `address` that has not been used. As it is an object address, it will never\n/// occur as the address for a user.\n/// In other words, the generated address is a globally unique object ID.\npublic fun fresh_object_address(ctx: &mut TxContext): address { let ids_created = ctx.ids_created; let id = derive_id(*&ctx.tx_hash, ids_created); ctx.ids_created = ids_created + 1; id\n}","breadcrumbs":"Advanced Programmability » Transaction Context » Generating unique addresses","id":"264","title":"Generating unique addresses"},"265":{"body":"A common use case in many applications is to run certain code just once when the package is published. Imagine a simple store module that needs to create the main Store object upon its publication. In Sui, this is achieved by defining an init function within the module. This function will automatically be called when the module is published. All of the modules' init functions are called during the publishing process. Currently, this behavior is limited to the publish command and does not extend to package upgrades . module book::shop; /// The Capability which grants the Shop owner the right to manage\n/// the shop.\npublic struct ShopOwnerCap has key, store { id: UID } /// The singular Shop itself, created in the `init` function.\npublic struct Shop has key { id: UID, /* ... */\n} // Called only once, upon module publication. It must be\n// private to prevent external invocation.\nfun init(ctx: &mut TxContext) { // Transfers the ShopOwnerCap to the sender (publisher). transfer::transfer(ShopOwnerCap { id: object::new(ctx) }, ctx.sender()); // Shares the Shop object. transfer::share_object(Shop { id: object::new(ctx) });\n} In the same package, another module can have its own init function, encapsulating distinct logic.","breadcrumbs":"Advanced Programmability » Module Initializer » Module Initializer","id":"265","title":"Module Initializer"},"266":{"body":"The function is called on publish, if it is present in the module and follows the rules: The function has to be named init, be private and have no return values. Takes one or two arguments: One Time Witness (optional) and TxContext . With TxContext always being the last argument. fun init(ctx: &mut TxContext) { /* ... */}\nfun init(otw: OTW, ctx: &mut TxContext) { /* ... */ } TxContext can also be passed as immutable reference: &TxContext. However, practically speaking, it should always be &mut TxContext since the init function can't access the onchain state and to create new objects it requires the mutable reference to the context. fun init(ctx: &TxContext) { /* ... */}\nfun init(otw: OTW, ctx: &TxContext) { /* ... */ }","breadcrumbs":"Advanced Programmability » Module Initializer » init features","id":"266","title":"init features"},"267":{"body":"While init function can be used to create sensitive objects once, it is important to know that the same object (eg. StoreOwnerCap from the first example) can still be created in another function. Especially given that new functions can be added to the module during an upgrade. So the init function is a good place to set up the initial state of the module, but it is not a security measure on its own. There are ways to guarantee that the object was created only once, such as the One Time Witness . And there are ways to limit or disable the upgrade of the module, which we will cover in the Package Upgrades chapter.","breadcrumbs":"Advanced Programmability » Module Initializer » Trust and security","id":"267","title":"Trust and security"},"268":{"body":"As follows from the definition, the init function is guaranteed to be called only once when the module is published. So it is a good place to put the code that initializes module's objects and sets up the environment and configuration. For example, if there's a Capability which is required for certain actions, it should be created in the init function. In the next chapter we will talk about the Capability pattern in more detail.","breadcrumbs":"Advanced Programmability » Module Initializer » Next steps","id":"268","title":"Next steps"},"269":{"body":"In programming, a capability is a token that gives the owner the right to perform a specific action. It is a pattern that is used to control access to resources and operations. A simple example of a capability is a key to a door. If you have the key, you can open the door. If you don't have the key, you can't open the door. A more practical example is an Admin Capability which allows the owner to perform administrative operations, which regular users cannot.","breadcrumbs":"Advanced Programmability » Pattern: Capability » Pattern: Capability","id":"269","title":"Pattern: Capability"},"27":{"body":"Following the same flow as in Hello, World! , we will create a new package called todo_list. $ sui move new todo_list","breadcrumbs":"Hello, Sui! » Create a New Package","id":"27","title":"Create a New Package"},"270":{"body":"In the Sui Object Model , capabilities are represented as objects. An owner of an object can pass this object to a function to prove that they have the right to perform a specific action. Due to strict typing, the function taking a capability as an argument can only be called with the correct capability. There's a convention to name capabilities with the Cap suffix, for example, AdminCap or KioskOwnerCap. module book::capability; use std::string::String;\nuse sui::event; /// The capability granting the application admin the right to create new\n/// accounts in the system.\npublic struct AdminCap has key, store { id: UID } /// The user account in the system.\npublic struct Account has key, store { id: UID, name: String\n} /// A simple `Ping` event with no data.\npublic struct Ping has copy, drop { by: ID } /// Creates a new account in the system. Requires the `AdminCap` capability\n/// to be passed as the first argument.\npublic fun new(_: &AdminCap, name: String, ctx: &mut TxContext): Account { Account { id: object::new(ctx), name, }\n} /// Account, and any other objects, can also be used as a Capability in the\n/// application. For example, to emit an event.\npublic fun send_ping(acc: &Account) { event::emit(Ping { by: acc.id.to_inner() })\n} /// Updates the account name. Can only be called by the `Account` owner.\npublic fun update(account: &mut Account, name: String) { account.name = name;\n}","breadcrumbs":"Advanced Programmability » Pattern: Capability » Capability is an Object","id":"270","title":"Capability is an Object"},"271":{"body":"A very common practice is to create a single AdminCap object on package publish. This way, the application can have a setup phase where the admin account prepares the state of the application.","breadcrumbs":"Advanced Programmability » Pattern: Capability » Using init for Admin Capability","id":"271","title":"Using init for Admin Capability"},"272":{"body":"Utilizing objects as capabilities is a relatively new concept in blockchain programming. And in other smart-contract languages, authorization is often performed by checking the address of the sender. This pattern is still viable on Sui, however, overall recommendation is to use capabilities for better security, discoverability, and code organization. Let's look at how the new function that creates a user would look like if it was using the address check: And now, let's see how the same function would look like with the capability: Using capabilities has several advantages over the address check: Migration of admin rights is easier with capabilities due to them being objects. In case of address, if the admin address changes, all the functions that check the address need to be updated - hence, require a package upgrade . Function signatures are more descriptive with capabilities. It is clear that the new function requires the AdminCap to be passed as an argument. And this function can't be called without it. Object Capabilities don't require extra checks in the function body, and hence, decrease the chance of a developer mistake. An owned Capability also serves in discovery. The owner of the AdminCap can see the object in their account (via a Wallet or Explorer), and know that they have the admin rights. This is less transparent with the address check. However, the address approach has its own advantages. For example, if an address is multisig, and transaction building gets more complex, it might be easier to check the address. Also, if there's a central object of the application that is used in every function, it can store the admin address, and this would simplify migration. The central object approach is also valuable for revokable capabilities, where the admin can revoke the capability from the user.","breadcrumbs":"Advanced Programmability » Pattern: Capability » Address check vs Capability","id":"272","title":"Address check vs Capability"},"273":{"body":"Sui has two ways of accessing the current time: Epoch and Time. The former represents operational periods in the system and changed roughly every 24 hours. The latter represents the current time in milliseconds since the Unix Epoch. Both can be accessed freely in the program.","breadcrumbs":"Advanced Programmability » Epoch and Time » Epoch and Time","id":"273","title":"Epoch and Time"},"274":{"body":"Epochs are used to separate the system into operational periods. During an epoch the validator set is fixed, however, at the epoch boundary, the validator set can be changed. Epochs play a crucial role in the consensus algorithm and are used to determine the current validator set. They are also used as measurement in the staking mechanism. Epoch can be read from the transaction context : public fun current_epoch(ctx: &TxContext) { let epoch = ctx.epoch(); // ...\n} It is also possible to get the unix timestamp of the epoch start: public fun current_epoch_start(ctx: &TxContext) { let epoch_start = ctx.epoch_timestamp_ms(); // ...\n} Normally, epochs are used in staking and system operations, however, in custom scenarios they can be used to emulate 24h periods. They are critical if an application relies on the staking logic or needs to know the current validator set.","breadcrumbs":"Advanced Programmability » Epoch and Time » Epoch","id":"274","title":"Epoch"},"275":{"body":"For a more precise time measurement, Sui provides the Clock object. It is a system object that is updated during checkpoints by the system, which stores the current time in milliseconds since the Unix Epoch. The Clock object is defined in the sui::clock module and has a reserved address 0x6. Clock is a shared object, but a transaction attempting to access it mutably will fail. This limitation allows parallel access to the Clock object, which is important for maintaining performance. // File: sui-framework/clock.move\n/// Singleton shared object that exposes time to Move calls. This\n/// object is found at address 0x6, and can only be read (accessed\n/// via an immutable reference) by entry functions.\n///\n/// Entry Functions that attempt to accept `Clock` by mutable\n/// reference or value will fail to verify, and honest validators\n/// will not sign or execute transactions that use `Clock` as an\n/// input parameter, unless it is passed by immutable reference.\nstruct Clock has key { id: UID, /// The clock's timestamp, which is set automatically by a /// system transaction every time consensus commits a /// schedule, or by `sui::clock::increment_for_testing` during /// testing. timestamp_ms: u64,\n} There is only one public function available in the Clock module - timestamp_ms. It returns the current time in milliseconds since the Unix Epoch. use sui::clock::Clock; /// Clock needs to be passed as an immutable reference.\npublic fun current_time(clock: &Clock) { let time = clock.timestamp_ms(); // ...\n}","breadcrumbs":"Advanced Programmability » Epoch and Time » Time","id":"275","title":"Time"},"276":{"body":"Collection types are a fundamental part of any programming language. They are used to store a collection of data, such as a list of items. The vector type has already been covered in the vector section , and in this chapter we will cover the vector-based collection types offered by the Sui Framework .","breadcrumbs":"Advanced Programmability » Collections » Collections","id":"276","title":"Collections"},"277":{"body":"While we have previously covered the vector type in the vector section , it is worth going over it again in a new context. This time we will cover the usage of the vector type in objects and how it can be used in an application. module book::collections_vector; use std::string::String; /// The Book that can be sold by a `BookStore`\npublic struct Book has key, store { id: UID, name: String\n} /// The BookStore that sells `Book`s\npublic struct BookStore has key, store { id: UID, books: vector\n}","breadcrumbs":"Advanced Programmability » Collections » Vector","id":"277","title":"Vector"},"278":{"body":"VecSet is a collection type that stores a set of unique items. It is similar to a vector, but it does not allow duplicate items. This makes it useful for storing a collection of unique items, such as a list of unique IDs or addresses. VecSet will fail on attempt to insert an item that already exists in the set.","breadcrumbs":"Advanced Programmability » Collections » VecSet","id":"278","title":"VecSet"},"279":{"body":"VecMap is a collection type that stores a map of key-value pairs. It is similar to a VecSet, but it allows you to associate a value with each item in the set. This makes it useful for storing a collection of key-value pairs, such as a list of addresses and their balances, or a list of user IDs and their associated data. Keys in a VecMap are unique, and each key can only be associated with a single value. If you try to insert a key-value pair with a key that already exists in the map, the old value will be replaced with the new value.","breadcrumbs":"Advanced Programmability » Collections » VecMap","id":"279","title":"VecMap"},"28":{"body":"To speed things up and focus on the application logic, we will provide the code for the todo list application. Replace the contents of the sources/todo_list.move file with the following code: Note: while the contents may seem overwhelming at first, we will break it down in the following sections. Try to focus on what's at hand right now. /// Module: todo_list\nmodule todo_list::todo_list; use std::string::String; /// List of todos. Can be managed by the owner and shared with others.\npublic struct TodoList has key, store { id: UID, items: vector\n} /// Create a new todo list.\npublic fun new(ctx: &mut TxContext): TodoList { let list = TodoList { id: object::new(ctx), items: vector[] }; (list)\n} /// Add a new todo item to the list.\npublic fun add(list: &mut TodoList, item: String) { list.items.push_back(item);\n} /// Remove a todo item from the list by index.\npublic fun remove(list: &mut TodoList, index: u64): String { list.items.remove(index)\n} /// Delete the list and the capability to manage it.\npublic fun delete(list: TodoList) { let TodoList { id, items: _ } = list; id.delete();\n} /// Get the number of items in the list.\npublic fun length(list: &TodoList): u64 { list.items.length()\n}","breadcrumbs":"Hello, Sui! » Add the code","id":"28","title":"Add the code"},"280":{"body":"Standard collection types are a great way to store typed data with guaranteed safety and consistency. However, they are limited by the type of data they can store - the type system won't allow you to store a wrong type in a collection; and they're limited in size - by the object size limit. They will work for relatively small-sized sets and lists, but for larger collections you may need to use a different approach. Another limitations on collection types is inability to compare them. Because the order of insertion is not guaranteed, an attempt to compare a VecSet to another VecSet may not yield the expected results. This behavior is caught by the linter and will emit a warning: Comparing collections of type 'sui::vec_set::VecSet' may yield unexpected result In the example above, the comparison will fail because the order of insertion is not guaranteed, and the two VecSet instances may have different orders of elements. And the comparison will fail even if the two VecSet instances contain the same elements.","breadcrumbs":"Advanced Programmability » Collections » Limitations","id":"280","title":"Limitations"},"281":{"body":"Vector is a native type that allows storing a list of items. VecSet is built on top of vector and allows storing sets of unique items. VecMap is used to store key-value pairs in a map-like structure. Vector-based collections are strictly typed and limited by the object size limit and are best suited for small-sized sets and lists.","breadcrumbs":"Advanced Programmability » Collections » Summary","id":"281","title":"Summary"},"282":{"body":"In the next section we will cover Dynamic Fields - an important primitive that allows for Dynamic Collections - a way to store large collections of data in a more flexible, yet more expensive way.","breadcrumbs":"Advanced Programmability » Collections » Next Steps","id":"282","title":"Next Steps"},"283":{"body":"Sui Object model allows objects to be attached to other objects as dynamic fields . The behavior is similar to how a Map works in other programming languages. However, unlike a Map which in Move would be strictly typed (we have covered it in the Collections section), dynamic fields allow attaching objects of any type. A similar approach from the world of frontend development would be a JavaScript Object type which allows storing any type of data dynamically. There's no limit to the number of dynamic fields that can be attached to an object. Thus, dynamic fields can be used to store large amounts of data that don't fit into the object limit size. Dynamic Fields allow for a wide range of applications, from splitting data into smaller parts to avoid object size limit to attaching objects as a part of application logic.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Dynamic Fields","id":"283","title":"Dynamic Fields"},"284":{"body":"Dynamic Fields are defined in the sui::dynamic_field module of the Sui Framework . They are attached to object's UID via a name , and can be accessed using that name. There can be only one field with a given name attached to an object. File: sui-framework/sources/dynamic_field.move /// Internal object used for storing the field and value\npublic struct Field has key { /// Determined by the hash of the object ID, the field name /// value and it's type, i.e. hash(parent.id || name || Name) id: UID, /// The value for the name of this field name: Name, /// The value bound to this field value: Value,\n} As the definition shows, dynamic fields are stored in an internal Field object, which has the UID generated in a deterministic way based on the object ID, the field name, and the field type. The Field object contains the field name and the value bound to it. The constraints on the Name and Value type parameters define the abilities that the key and value must have.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Definition","id":"284","title":"Definition"},"285":{"body":"The methods available for dynamic fields are straightforward: a field can be added with add, removed with remove, and read with borrow and borrow_mut. Additionally, the exists_ method can be used to check if a field exists (for stricter checks with type, there is an exists_with_type method). module book::dynamic_fields; // a very common alias for `dynamic_field` is `df` since the\n// module name is quite long\nuse sui::dynamic_field as df;\nuse std::string::String; /// The object that we will attach dynamic fields to.\npublic struct Character has key { id: UID\n} // List of different accessories that can be attached to a character.\n// They must have the `store` ability.\npublic struct Hat has key, store { id: UID, color: u32 }\npublic struct Mustache has key, store { id: UID } #[test]\nfun test_character_and_accessories() { let ctx = &mut tx_context::dummy(); let mut character = Character { id: object::new(ctx) }; // Attach a hat to the character's UID df::add( &mut character.id, b\"hat_key\", Hat { id: object::new(ctx), color: 0xFF0000 } ); // Similarly, attach a mustache to the character's UID df::add( &mut character.id, b\"mustache_key\", Mustache { id: object::new(ctx) } ); // Check that the hat and mustache are attached to the character // assert!(df::exists_(&character.id, b\"hat_key\"), 0); assert!(df::exists_(&character.id, b\"mustache_key\"), 1); // Modify the color of the hat let hat: &mut Hat = df::borrow_mut(&mut character.id, b\"hat_key\"); hat.color = 0x00FF00; // Remove the hat and mustache from the character let hat: Hat = df::remove(&mut character.id, b\"hat_key\"); let mustache: Mustache = df::remove(&mut character.id, b\"mustache_key\"); // Check that the hat and mustache are no longer attached to the character assert!(!df::exists_(&character.id, b\"hat_key\"), 0); assert!(!df::exists_(&character.id, b\"mustache_key\"), 1); sui::test_utils::destroy(character); sui::test_utils::destroy(mustache); sui::test_utils::destroy(hat);\n}\n} In the example above, we define a Character object and two different types of accessories that could never be put together in a vector. However, dynamic fields allow us to store them together in a single object. Both objects are attached to the Character via a vector (bytestring literal), and can be accessed using their respective keys. As you can see, when we attached the accessories to the Character, we passed them by value . In other words, both values were moved to a new scope, and their ownership was transferred to the Character object. If we changed the ownership of Character object, the accessories would have been moved with it. And the last important property of dynamic fields we should highlight is that they are accessed through their parent . This means that the Hat and Mustache objects are not directly accessible and follow the same rules as the parent object.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Usage","id":"285","title":"Usage"},"286":{"body":"Dynamic fields allow objects to carry data of any type, including those defined in other modules. This is possible due to their generic nature and relatively weak constraints on the type parameters. Let's illustrate this by attaching a few different values to a Character object. let mut character = Character { id: object::new(ctx) }; // Attach a `String` via a `vector` name\ndf::add(&mut character.id, b\"string_key\", b\"Hello, World!\".to_string()); // Attach a `u64` via a `u32` name\ndf::add(&mut character.id, 1000u32, 1_000_000_000u64); // Attach a `bool` via a `bool` name\ndf::add(&mut character.id, true, false); In this example we showed how different types can be used for both name and the value of a dynamic field. The String is attached via a vector name, the u64 is attached via a u32 name, and the bool is attached via a bool name. Anything is possible with dynamic fields!","breadcrumbs":"Advanced Programmability » Dynamic Fields » Foreign Types as Dynamic Fields","id":"286","title":"Foreign Types as Dynamic Fields"},"287":{"body":"To prevent orphaned dynamic fields, please, use Dynamic Collection Types such as Bag as they track the dynamic fields and won't allow unpacking if there are attached fields. The object::delete() function, which is used to delete a UID, does not track the dynamic fields, and cannot prevent dynamic fields from becoming orphaned. Once the parent UID is deleted, the dynamic fields are not automatically deleted, and they become orphaned. This means that the dynamic fields are still stored in the blockchain, but they will never become accessible again. let hat = Hat { id: object::new(ctx), color: 0xFF0000 };\nlet mut character = Character { id: object::new(ctx) }; // Attach a `Hat` via a `vector` name\ndf::add(&mut character.id, b\"hat_key\", hat); // ! DO NOT do this in your code\n// ! Danger - deleting the parent object\nlet Character { id } = character;\nid.delete(); // ...`Hat` is now stuck in a limbo, it will never be accessible again Orphaned objects are not a subject to storage rebate, and the storage fees will remain unclaimed. One way to avoid orphaned dynamic fields during unpacking of an object is to return the UID and store it somewhere temporarily until the dynamic fields are removed and handled properly.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Orphaned Dynamic Fields","id":"287","title":"Orphaned Dynamic Fields"},"288":{"body":"In the examples above, we used primitive types as field names since they have the required set of abilities. But dynamic fields get even more interesting when we use custom types as field names. This allows for a more structured way of storing data, and also allows for protecting the field names from being accessed by other modules. /// A custom type with fields in it.\npublic struct AccessoryKey has copy, drop, store { name: String } /// An empty key, can be attached only once.\npublic struct MetadataKey has copy, drop, store {} Two field names that we defined above are AccessoryKey and MetadataKey. The AccessoryKey has a String field in it, hence it can be used multiple times with different name values. The MetadataKey is an empty key, and can be attached only once. let mut character = Character { id: object::new(ctx) }; // Attaching via an `AccessoryKey { name: b\"hat\" }`\ndf::add( &mut character.id, AccessoryKey { name: b\"hat\".to_string() }, Hat { id: object::new(ctx), color: 0xFF0000 }\n);\n// Attaching via an `AccessoryKey { name: b\"mustache\" }`\ndf::add( &mut character.id, AccessoryKey { name: b\"mustache\".to_string() }, Mustache { id: object::new(ctx) }\n); // Attaching via a `MetadataKey`\ndf::add(&mut character.id, MetadataKey {}, 42); As you can see, custom types do work as field names but as long as they can be constructed by the module, in other words - if they are internal to the module and defined in it. This limitation on struct packing can open up new ways in the design of the application. This approach is used in the Object Capability pattern, where an application can authorize a foreign object to perform operations in it while not exposing the capabilities to other modules.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Custom Type as a Field Name","id":"288","title":"Custom Type as a Field Name"},"289":{"body":"Mutable access to UID is a security risk. Exposing UID of your type as a mutable reference can lead to unwanted modifications or removal of the object's dynamic fields. Additionally, it affects the Transfer to Object and Dynamic Object Fields . Make sure to understand the implications before exposing the UID as a mutable reference. Because dynamic fields are attached to UIDs, their usage in other modules depends on whether the UID can be accessed. By default struct visibility protects the id field and won't let other modules access it directly. However, if there's a public accessor method that returns a reference to UID, dynamic fields can be read in other modules. /// Exposes the UID of the character, so that other modules can read\n/// dynamic fields.\npublic fun uid(c: &Character): &UID { &c.id\n} In the example above, we show how to expose the UID of a Character object. This solution may work for some applications, however, it is important to remember that exposed UID allows reading any dynamic field attached to the object. If you need to expose the UID only within the package, use a restrictive visibility, like public(package), or even better - use more specific accessor methods that would allow only reading specific fields. /// Only allow modules in the same package to access the UID.\npublic(package) fun uid_package(c: &Character): &UID { &c.id\n} /// Allow borrowing dynamic fields from the character.\npublic fun borrow( c: &Character, n: Name\n): &Value { df::borrow(&c.id, n)\n}","breadcrumbs":"Advanced Programmability » Dynamic Fields » Exposing UID","id":"289","title":"Exposing UID"},"29":{"body":"To make sure that we did everything correctly, let's build the package by running the sui move build command. If everything is correct, you should see the output similar to the following: $ sui move build\nUPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git\nINCLUDING DEPENDENCY Sui\nINCLUDING DEPENDENCY MoveStdlib\nBUILDING todo_list If there are no errors following this output, you have successfully built the package. If there are errors, make sure that: The code is copied correctly The file name and the package name is correct There are not many other reasons for the code to fail at this stage. But if you are still having issues, try looking up the structure of the package in this location .","breadcrumbs":"Hello, Sui! » Build the package","id":"29","title":"Build the package"},"290":{"body":"Dynamic Fields are more expensive than regular fields, as they require additional storage and costs for accessing them. Their flexibility comes at a price, and it is important to understand the implications when making a decision between using dynamic fields and regular fields.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Dynamic Fields vs Fields","id":"290","title":"Dynamic Fields vs Fields"},"291":{"body":"Dynamic Fields are not subject to the object size limit , and can be used to store large amounts of data. However, they are still subject to the dynamic fields created limit , which is set to 1000 fields per transaction.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Limits","id":"291","title":"Limits"},"292":{"body":"Dynamic Fields can play a crucial role in applications of any complexity. They open up a variety of different use cases, from storing heterogeneous data to attaching objects as part of the application logic. They allow for certain upgradeability practices based on the ability to define them later and change the type of the field.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Applications","id":"292","title":"Applications"},"293":{"body":"In the next section we will cover Dynamic Object Fields and explain how they differ from dynamic fields, and what are the implications of using them.","breadcrumbs":"Advanced Programmability » Dynamic Fields » Next Steps","id":"293","title":"Next Steps"},"294":{"body":"This section expands on the Dynamic Fields . Please, read it first to understand the basics of dynamic fields. Another variation of dynamic fields is dynamic object fields , which have certain differences from regular dynamic fields. In this section, we will cover the specifics of dynamic object fields and explain how they differ from regular dynamic fields. General recommendation is to avoid using dynamic object fields in favor of (just) dynamic fields, especially if there's no need for direct discovery through the ID. The extra costs of dynamic object fields may not be justified by the benefits they provide.","breadcrumbs":"Advanced Programmability » Dynamic Object Fields » Dynamic Object Fields","id":"294","title":"Dynamic Object Fields"},"295":{"body":"Dynamic Object Fields are defined in the sui::dynamic_object_fields module in the Sui Framework . They are similar to dynamic fields in many ways, but unlike them, dynamic object fields have an extra constraint on the Value type. The Value must have a combination of key and store, not just store as in the case of dynamic fields. They're less explicit in their framework definition, as the concept itself is more abstract: File: sui-framework/sources/dynamic_object_fields.move /// Internal object used for storing the field and the name associated with the\n/// value. The separate type is necessary to prevent key collision with direct\n/// usage of dynamic_field\npublic struct Wrapper has copy, drop, store { name: Name,\n} Unlike Field type in the Dynamic Fields section, the Wrapper type only stores the name of the field. The value is the object itself, and is not wrapped . The constraints on the Value type become visible in the methods available for dynamic object fields. Here's the signature for the add function: /// Adds a dynamic object field to the object `object: &mut UID` at field\n/// specified by `name: Name`. Aborts with `EFieldAlreadyExists` if the object\n/// already has that field with that name.\npublic fun add( // we use &mut UID in several spots for access control object: &mut UID, name: Name, value: Value,\n) { /* implementation omitted */ } The rest of the methods which are identical to the ones in the Dynamic Fields section have the same constraints on the Value type. Let's list them for reference: add - adds a dynamic object field to the object remove - removes a dynamic object field from the object borrow - borrows a dynamic object field from the object borrow_mut - borrows a mutable reference to a dynamic object field from the object exists_ - checks if a dynamic object field exists exists_with_type - checks if a dynamic object field exists with a specific type Additionally, there is an id method which returns the ID of the Value object without specifying its type.","breadcrumbs":"Advanced Programmability » Dynamic Object Fields » Definition","id":"295","title":"Definition"},"296":{"body":"The main difference between dynamic fields and dynamic object fields is that the latter allows storing only objects as values. This means that you can't store primitive types like u64 or bool. It may be considered a limitation, if not for the fact that dynamic object fields are not wrapped into a separate object. The relaxed requirement for wrapping keeps the object available for off-chain discovery via its ID. However, this property may not be outstanding if wrapped object indexing is implemented, making the dynamic object fields a redundant feature. module book::dynamic_object_field; use std::string::String; // there are two common aliases for the long module name: `dof` and\n// `ofield`. Both are commonly used and met in different projects.\nuse sui::dynamic_object_field as dof;\nuse sui::dynamic_field as df; /// The `Character` that we will use for the example\npublic struct Character has key { id: UID } /// Metadata that doesn't have the `key` ability\npublic struct Metadata has store, drop { name: String } /// Accessory that has the `key` and `store` abilities.\npublic struct Accessory has key, store { id: UID } #[test]\nfun equip_accessory() { let ctx = &mut tx_context::dummy(); let mut character = Character { id: object::new(ctx) }; // Create an accessory and attach it to the character let hat = Accessory { id: object::new(ctx) }; // Add the hat to the character. Just like with `dynamic_fields` dof::add(&mut character.id, b\"hat_key\", hat); // However for non-key structs we can only use `dynamic_field` df::add(&mut character.id, b\"metadata_key\", Metadata { name: b\"John\".to_string() }); // Borrow the hat from the character let hat_id = dof::id(&character.id, b\"hat_key\").extract(); // Option let hat_ref: &Accessory = dof::borrow(&character.id, b\"hat_key\"); let hat_mut: &mut Accessory = dof::borrow_mut(&mut character.id, b\"hat_key\"); let hat: Accessory = dof::remove(&mut character.id, b\"hat_key\"); // Clean up, Metadata is an orphan now. sui::test_utils::destroy(hat); sui::test_utils::destroy(character);\n}","breadcrumbs":"Advanced Programmability » Dynamic Object Fields » Usage & Differences with Dynamic Fields","id":"296","title":"Usage & Differences with Dynamic Fields"},"297":{"body":"Dynamic Object Fields come a little more expensive than dynamic fields. Because of their internal structure, they require 2 objects: the Wrapper for Name and the Value. Because of this, the cost of adding and accessing object fields (loading 2 objects compared to 1 for dynamic fields) is higher.","breadcrumbs":"Advanced Programmability » Dynamic Object Fields » Pricing Differences","id":"297","title":"Pricing Differences"},"298":{"body":"Both dynamic field and dynamic object fields are powerful features which allow for innovative solutions in applications. However, they are relatively low-level and require careful handling to avoid orphaned fields. In the next section, we will introduce a higher-level abstraction - Dynamic Collections - which can help with managing dynamic fields and objects more effectively.","breadcrumbs":"Advanced Programmability » Dynamic Object Fields » Next Steps","id":"298","title":"Next Steps"},"299":{"body":"Sui Framework offers a variety of collection types that build on the dynamic fields and dynamic object fields concepts. These collections are designed to be a safer and more understandable way to store and manage dynamic fields and objects. For each collection type we will specify the primitive they use, and the specific features they offer. Unlike dynamic (object) fields which operate on UID, collection types have their own type and allow calling associated functions .","breadcrumbs":"Advanced Programmability » Dynamic Collections » Dynamic Collections","id":"299","title":"Dynamic Collections"},"3":{"body":"Move is a compiled language, so you need to install a compiler to be able to write and run Move programs. The compiler is included into the Sui binary, which can be installed or downloaded using one of the methods below.","breadcrumbs":"Before we begin » Install Sui » Install Sui","id":"3","title":"Install Sui"},"30":{"body":"To publish and interact with the package, we need to set up an account. For the sake of simplicity and demonstration purposes, we will use sui devnet environment. If you already have an account set up, you can skip this step. If you are doing it for the first time, you will need to create a new account. To do this, run the sui client command, then the CLI will prompt you with multiple questions. The answers are marked below with >: $ sui client\nConfig file [\"/path/to/home/.sui/sui_config/client.yaml\"] doesn't exist, do you want to connect to a Sui Full node server [y/N]?\n> y\nSui Full node server URL (Defaults to Sui Testnet if not specified) :\n>\nSelect key scheme to generate keypair (0 for ed25519, 1 for secp256k1, 2: for secp256r1):\n> 0 After you have answered the questions, the CLI will generate a new keypair and save it to the configuration file. You can now use this account to interact with the network. To check that we have the account set up correctly, run the sui client active-address command: $ sui client active-address\n0x.... The command will output the address of your account, it starts with 0x followed by 64 characters.","breadcrumbs":"Hello, Sui! » Set up an account","id":"30","title":"Set up an account"},"300":{"body":"All of the collection types share the same set of methods, which are: add - adds a field to the collection remove - removes a field from the collection borrow - borrows a field from the collection borrow_mut - borrows a mutable reference to a field from the collection contains - checks if a field exists in the collection length - returns the number of fields in the collection is_empty - checks if the length is 0 All collection types support index syntax for borrow and borrow_mut methods. If you see square brackets in the examples, they are translated into borrow and borrow_mut calls. let hat: &Hat = &bag[b\"key\"];\nlet hat_mut: &mut Hat = &mut bag[b\"key\"]; // is equivalent to\nlet hat: &Hat = bag.borrow(b\"key\");\nlet hat_mut: &mut Hat = bag.borrow_mut(b\"key\"); In the examples we won't focus on these functions, but rather on the differences between the collection types.","breadcrumbs":"Advanced Programmability » Dynamic Collections » Common Concepts","id":"300","title":"Common Concepts"},"301":{"body":"Bag, as the name suggests, acts as a \"bag\" of heterogeneous values. It is a simple, non-generic type that can store any data. Bag will never allow orphaned fields, as it tracks the number of fields and can't be destroyed if it's not empty. // File: sui-framework/sources/bag.move\npublic struct Bag has key, store { /// the ID of this bag id: UID, /// the number of key-value pairs in the bag size: u64,\n} Due to Bag storing any types, the extra methods it offers is: contains_with_type - checks if a field exists with a specific type Used as a struct field: /// Imported from the `sui::bag` module.\nuse sui::bag::{Self, Bag}; /// An example of a `Bag` as a struct field.\npublic struct Carrier has key { id: UID, bag: Bag\n} Using the Bag: let mut bag = bag::new(ctx); // bag has the `length` function to get the number of elements\nassert!(bag.length() == 0, 0); bag.add(b\"my_key\", b\"my_value\".to_string()); // length has changed to 1\nassert!(bag.length() == 1, 1); // in order: `borrow`, `borrow_mut` and `remove`\n// the value type must be specified\nlet field_ref: &String = &bag[b\"my_key\"];\nlet field_mut: &mut String = &mut bag[b\"my_key\"];\nlet field: String = bag.remove(b\"my_key\"); // length is back to 0 - we can unpack\nbag.destroy_empty();","breadcrumbs":"Advanced Programmability » Dynamic Collections » Bag","id":"301","title":"Bag"},"302":{"body":"Defined in the sui::object_bag module. Identical to Bag , but uses dynamic object fields internally. Can only store objects as values.","breadcrumbs":"Advanced Programmability » Dynamic Collections » ObjectBag","id":"302","title":"ObjectBag"},"303":{"body":"Table is a typed dynamic collection that has a fixed type for keys and values. It is defined in the sui::table module. // File: sui-framework/sources/table.move\npublic struct Table has key, store { /// the ID of this table id: UID, /// the number of key-value pairs in the table size: u64,\n} Used as a struct field: /// Imported from the `sui::table` module.\nuse sui::table::{Self, Table}; /// Some record type with `store`\npublic struct Record has store { /* ... */ } /// An example of a `Table` as a struct field.\npublic struct UserRegistry has key { id: UID, table: Table\n} Using the Table: #[test] fun test_table() {\nlet ctx = &mut tx_context::dummy(); // Table requires explicit type parameters for the key and value\n// ...but does it only once in initialization.\nlet mut table = table::new(ctx); // table has the `length` function to get the number of elements\nassert!(table.length() == 0, 0); table.add(@0xa11ce, b\"my_value\".to_string());\ntable.add(@0xb0b, b\"another_value\".to_string()); // length has changed to 2\nassert!(table.length() == 2, 2); // in order: `borrow`, `borrow_mut` and `remove`\nlet addr_ref = &table[@0xa11ce];\nlet addr_mut = &mut table[@0xa11ce]; // removing both values\nlet _addr = table.remove(@0xa11ce);\nlet _addr = table.remove(@0xb0b); // length is back to 0 - we can unpack\ntable.destroy_empty();","breadcrumbs":"Advanced Programmability » Dynamic Collections » Table","id":"303","title":"Table"},"304":{"body":"Defined in the sui::object_table module. Identical to Table , but uses dynamic object fields internally. Can only store objects as values.","breadcrumbs":"Advanced Programmability » Dynamic Collections » ObjectTable","id":"304","title":"ObjectTable"},"305":{"body":"Bag - a simple collection that can store any type of data ObjectBag - a collection that can store only objects Table - a typed dynamic collection that has a fixed type for keys and values ObjectTable - same as Table, but can only store objects","breadcrumbs":"Advanced Programmability » Dynamic Collections » Summary","id":"305","title":"Summary"},"306":{"body":"This section is coming soon!","breadcrumbs":"Advanced Programmability » Dynamic Collections » LinkedTable","id":"306","title":"LinkedTable"},"307":{"body":"Witness is a pattern of proving an existence by constructing a proof. In the context of programming, witness is a way to prove a certain property of a system by providing a value that can only be constructed if the property holds.","breadcrumbs":"Advanced Programmability » Pattern: Witness » Pattern: Witness","id":"307","title":"Pattern: Witness"},"308":{"body":"In the Struct section we have shown that a struct can only be created - or packed - by the module defining it. Hence, in Move, a module proves ownership of the type by constructing it. This is one of the most important patterns in Move, and it is widely used for generic type instantiation and authorization. Practically speaking, for the witness to be used, there has to be a function that expects a witness as an argument. In the example below it is the new function that expects a witness of the T type to create a Instance instance. It is often the case that the witness struct is not stored, and for that the function may require the Drop ability for the type. module book::witness { /// A struct that requires a witness to be created. public struct Instance { t: T } /// Create a new instance of `Instance` with the provided T. public fun new(witness: T): Instance { Instance { t: witness } }\n} The only way to construct an Instance is to call the new function with an instance of the type T. This is a basic example of the witness pattern in Move. A module providing a witness often has a matching implementation, like the module book::witness_source below: module book::witness_source { use book::witness::{Self, Instance}; /// A struct used as a witness. public struct W {} /// Create a new instance of `Instance`. public fun new_instance(): Instance { witness::new(W {}) }\n} The instance of the struct W is passed into the new_instance function to create an Instance, thereby proving that the module book::witness_source owns the type W.","breadcrumbs":"Advanced Programmability » Pattern: Witness » Witness in Move","id":"308","title":"Witness in Move"},"309":{"body":"Witness allows generic types to be instantiated with a concrete type. This is useful for inheriting associated behaviors from the type with an option to extend them, if the module provides the ability to do so. // File: sui-framework/sources/balance.move\n/// A Supply of T. Used for minting and burning.\npublic struct Supply has key, store { id: UID, value: u64\n} /// Create a new supply for type T with the provided witness.\npublic fun create_supply(_w: T): Supply { Supply { value: 0 }\n} /// Get the `Supply` value.\npublic fun supply_value(supply: &Supply): u64 { supply.value\n} In the example above, which is borrowed from the balance module of the Sui Framework , the Supply a generic struct that can be constructed only by supplying a witness of the type T. The witness is taken by value and discarded - hence the T must have the drop ability. The instantiated Supply can then be used to mint new Balance's, where T is the type of the supply. // File: sui-framework/sources/balance.move\n/// Storable balance.\nstruct Balance has store { value: u64\n} /// Increase supply by `value` and create a new `Balance` with this value.\npublic fun increase_supply(self: &mut Supply, value: u64): Balance { assert!(value < (18446744073709551615u64 - self.value), EOverflow); self.value = self.value + value; Balance { value }\n}","breadcrumbs":"Advanced Programmability » Pattern: Witness » Instantiating a Generic Type","id":"309","title":"Instantiating a Generic Type"},"31":{"body":"In devnet and testnet environments, the CLI provides a way to request coins to your account, so you can interact with the network. To request coins, run the sui client faucet command: $ sui client faucet\nRequest successful. It can take up to 1 minute to get the coin. Run sui client gas to check your gas coins. After waiting a little bit, you can check that the Coin object was sent to your account by running the sui client balance command: $ sui client balance\n╭────────────────────────────────────────╮\n│ Balance of coins owned by this address │\n├────────────────────────────────────────┤\n│ ╭──────────────────────────────────╮ │\n│ │ coin balance (raw) balance │ │\n│ ├──────────────────────────────────┤ │\n│ │ Sui 1000000000 1.00 SUI │ │\n│ ╰──────────────────────────────────╯ │\n╰────────────────────────────────────────╯ Alternatively, you can query objects owned by your account, by running the sui client objects command. The actual output will be different, because the object ID is unique, and so is digest, but the structure will be similar: $ sui client objects\n╭───────────────────────────────────────────────────────────────────────────────────────╮\n│ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │\n│ │ objectId │ 0x4ea1303e4f5e2f65fc3709bc0fb70a3035fdd2d53dbcff33e026a50a742ce0de │ │\n│ │ version │ 4 │ │\n│ │ digest │ nA68oa8gab/CdIRw+240wze8u0P+sRe4vcisbENcR4U= │ │\n│ │ objectType │ 0x0000..0002::coin::Coin │ │\n│ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │\n╰───────────────────────────────────────────────────────────────────────────────────────╯ Now that we have the account set up and the coins in the account, we can interact with the network. We will start by publishing the package to the network.","breadcrumbs":"Hello, Sui! » Requesting Coins","id":"31","title":"Requesting Coins"},"310":{"body":"While a struct can be created any number of times, there are cases where a struct should be guaranteed to be created only once. For this purpose, Sui provides the \"One-Time Witness\" - a special witness that can only be used once. We explain it in more detail in the next section .","breadcrumbs":"Advanced Programmability » Pattern: Witness » One Time Witness","id":"310","title":"One Time Witness"},"311":{"body":"Witness is a pattern of proving a certain property by constructing a proof. In Move, a module proves ownership of a type by constructing it. Witness is often used for generic type instantiation and authorization.","breadcrumbs":"Advanced Programmability » Pattern: Witness » Summary","id":"311","title":"Summary"},"312":{"body":"In the next section, we will learn about the One Time Witness pattern.","breadcrumbs":"Advanced Programmability » Pattern: Witness » Next Steps","id":"312","title":"Next Steps"},"313":{"body":"While regular Witness is a great way to statically prove the ownership of a type, there are cases where we need to ensure that a Witness is instantiated only once. And this is the purpose of the One Time Witness (OTW).","breadcrumbs":"Advanced Programmability » One Time Witness » One Time Witness","id":"313","title":"One Time Witness"},"314":{"body":"The OTW is a special type of Witness that can be used only once. It cannot be manually created and it is guaranteed to be unique per module. Sui Adapter treats a type as an OTW if it follows these rules: Has only drop ability. Has no fields. Is not a generic type. Named after the module with all uppercase letters. Here is an example of an OTW: module book::one_time; /// The OTW for the `book::one_time` module.\n/// Only `drop`, no fields, no generics, all uppercase.\npublic struct ONE_TIME has drop {} /// Receive the instance of `ONE_TIME` as the first argument.\nfun init(otw: ONE_TIME, ctx: &mut TxContext) { // do something with the OTW\n} The OTW cannot be constructed manually, and any code attempting to do so will result in a compilation error. The OTW can be received as the first argument in the module initializer . And because the init function is called only once per module, the OTW is guaranteed to be instantiated only once.","breadcrumbs":"Advanced Programmability » One Time Witness » Definition","id":"314","title":"Definition"},"315":{"body":"To check if a type is an OTW, sui::types module of the Sui Framework offers a special function is_one_time_witness that can be used to check if the type is an OTW. use sui::types; const ENotOneTimeWitness: u64 = 1; /// Takes an OTW as an argument, aborts if the type is not OTW.\npublic fun takes_witness(otw: T) { assert!(types::is_one_time_witness(&otw), ENotOneTimeWitness);\n}","breadcrumbs":"Advanced Programmability » One Time Witness » Enforcing the OTW","id":"315","title":"Enforcing the OTW"},"316":{"body":"The OTW pattern is a great way to ensure that a type is used only once. Most of the developers should understand how to define and receive the OTW, while the OTW checks and enforcement is mostly needed in libraries and frameworks. For example, the sui::coin module requires an OTW in the coin::create_currency method, therefore enforcing that the coin::TreasuryCap is created only once. OTW is a powerful tool that lays the foundation for the Publisher object, which we will cover in the next section.","breadcrumbs":"Advanced Programmability » One Time Witness » Summary","id":"316","title":"Summary"},"317":{"body":"In application design and development, it is often needed to prove publisher authority. This is especially important in the context of digital assets, where the publisher may enable or disable certain features for their assets. The Publisher Object is an object, defined in the Sui Framework , that allows the publisher to prove their authority over a type .","breadcrumbs":"Advanced Programmability » Publisher Authority » Publisher Authority","id":"317","title":"Publisher Authority"},"318":{"body":"The Publisher object is defined in the sui::package module of the Sui Framework. It is a very simple, non-generic object that can be initialized once per module (and multiple times per package) and is used to prove the authority of the publisher over a type. To claim a Publisher object, the publisher must present a One Time Witness to the package::claim function. // File: sui-framework/sources/package.move\npublic struct Publisher has key, store { id: UID, package: String, module_name: String,\n} If you're not familiar with the One Time Witness, you can read more about it here . Here's a simple example of claiming a Publisher object in a module: module book::publisher; use sui::package::{Self, Publisher}; /// Some type defined in the module.\npublic struct Book {} /// The OTW for the module.\npublic struct PUBLISHER has drop {} /// Uses the One Time Witness to claim the Publisher object.\nfun init(otw: PUBLISHER, ctx: &mut TxContext) { // Claim the Publisher object. let publisher: Publisher = sui::package::claim(otw, ctx); // Usually it is transferred to the sender. // It can also be stored in another object. transfer::public_transfer(publisher, ctx.sender())\n}","breadcrumbs":"Advanced Programmability » Publisher Authority » Definition","id":"318","title":"Definition"},"319":{"body":"The Publisher object has two functions associated with it which are used to prove the publisher's authority over a type: // Checks if the type is from the same module, hence the `Publisher` has the\n// authority over it.\nassert!(publisher.from_module(), 0); // Checks if the type is from the same package, hence the `Publisher` has the\n// authority over it.\nassert!(publisher.from_package(), 0);","breadcrumbs":"Advanced Programmability » Publisher Authority » Usage","id":"319","title":"Usage"},"32":{"body":"To publish the package to the network, we will use the sui client publish command. The command will automatically build the package and use its bytecode to publish in a single transaction. We are using the --gas-budget argument during publishing. It specifies how much gas we are willing to spend on the transaction. We won't touch on this topic in this section, but it's important to know that every transaction in Sui costs gas, and the gas is paid in SUI coins. The gas-budget is specified in MISTs . 1 SUI equals 10^9 MISTs. For the sake of demonstration, we will use 100,000,000 MISTs, which is 0.1 SUI. # run this from the `todo_list` folder\n$ sui client publish --gas-budget 100000000 # alternatively, you can specify path to the package\n$ sui client publish --gas-budget 100000000 todo_list The output of the publish command is rather lengthy, so we will show and explain it in parts. $ sui client publish --gas-budget 100000000\nUPDATING GIT DEPENDENCY https://github.com/MystenLabs/sui.git\nINCLUDING DEPENDENCY Sui\nINCLUDING DEPENDENCY MoveStdlib\nBUILDING todo_list\nSuccessfully verified dependencies on-chain against source.\nTransaction Digest: GpcDV6JjjGQMRwHpEz582qsd5MpCYgSwrDAq1JXcpFjW As you can see, when we run the publish command, the CLI first builds the package, then verifies the dependencies on-chain, and finally publishes the package. The output of the command is the transaction digest, which is a unique identifier of the transaction and can be used to query the transaction status.","breadcrumbs":"Hello, Sui! » Publish","id":"32","title":"Publish"},"320":{"body":"For small applications or simple use cases, the Publisher object can be used as an admin capability . While in the broader context, the Publisher object has control over system configurations, it can also be used to manage the application's state. /// Some action in the application gated by the Publisher object.\npublic fun admin_action(cap: &Publisher, /* app objects... */ param: u64) { assert!(cap.from_module(), ENotAuthorized); // perform application-specific action\n} However, Publisher misses some native properties of Capabilities , such as type safety and expressiveness. The signature for the admin_action is not very explicit, can be called by anyone else. And due to Publisher object being standard, there now is a risk of unauthorized access if the from_module check is not performed. So it's important to be cautious when using the Publisher object as an admin role.","breadcrumbs":"Advanced Programmability » Publisher Authority » Publisher as Admin Role","id":"320","title":"Publisher as Admin Role"},"321":{"body":"Publisher is required for certain features on Sui. Object Display can be created only by the Publisher, and TransferPolicy - an important component of the Kiosk system - also requires the Publisher object to prove ownership of the type.","breadcrumbs":"Advanced Programmability » Publisher Authority » Role on Sui","id":"321","title":"Role on Sui"},"322":{"body":"In the next chapter we will cover the first feature that requires the Publisher object - Object Display - a way to describe objects for clients, and standardize metadata. A must-have for user-friendly applications.","breadcrumbs":"Advanced Programmability » Publisher Authority » Next Steps","id":"322","title":"Next Steps"},"323":{"body":"Objects on Sui are explicit in their structure and behavior and can be displayed in an understandable way. However, to support richer metadata for clients, there's a standard and efficient way of \"describing\" them to the client - the Display object defined in the Sui Framework .","breadcrumbs":"Advanced Programmability » Display » Object Display","id":"323","title":"Object Display"},"324":{"body":"Historically, there were different attempts to agree on a standard structure of an object so it can be displayed in a user interface. One of the approaches was to define certain fields in the object struct which, when present, would be used in the UI. This approach was not flexible enough and required developers to define the same fields in every object, and sometimes the fields did not make sense for the object. /// An attempt to standardize the object structure for display.\npublic struct CounterWithDisplay has key { id: UID, /// If this field is present it will be displayed in the UI as `name`. name: String, /// If this field is present it will be displayed in the UI as `description`. description: String, // ... image: String, /// Actual fields of the object. counter: u64, // ...\n} If any of the fields contained static data, it would be duplicated in every object. And, since Move does not have interfaces, it is not possible to know if an object has a specific field without \"manually\" checking the object's type, which makes the client fetching more complex.","breadcrumbs":"Advanced Programmability » Display » Background","id":"324","title":"Background"},"325":{"body":"To address these issues, Sui introduces a standard way of describing an object for display. Instead of defining fields in the object struct, the display metadata is stored in a separate object, which is associated with the type. This way, the display metadata is not duplicated, and it is easy to extend and maintain. Another important feature of Sui Display is the ability to define templates and use object fields in those templates. Not only it allows for a more flexible display, but it also frees the developer from the need to define the same fields with the same names and types in every object. The Object Display is natively supported by the Sui Fullnode, and the client can fetch the display metadata for any object if the object type has a Display associated with it. module book::arena; use std::string::String;\nuse sui::package;\nuse sui::display; /// The One Time Witness to claim the `Publisher` object.\npublic struct ARENA has drop {} /// Some object which will be displayed.\npublic struct Hero has key { id: UID, class: String, level: u64,\n} /// In the module initializer we create the `Publisher` object, and then\n/// the Display for the `Hero` type.\nfun init(otw: ARENA, ctx: &mut TxContext) { let publisher = package::claim(otw, ctx); let mut display = display::new(&publisher, ctx); display.add( b\"name\".to_string(), b\"{class} (lvl. {level})\".to_string() ); display.add( b\"description\".to_string(), b\"One of the greatest heroes of all time. Join us!\".to_string() ); display.add( b\"link\".to_string(), b\"https://example.com/hero/{id}\".to_string() ); display.add( b\"image_url\".to_string(), b\"https://example.com/hero/{class}.jpg\".to_string() ); // Update the display with the new data. // Must be called to apply changes. display.update_version(); transfer::public_transfer(publisher, ctx.sender()); transfer::public_transfer(display, ctx.sender());\n}","breadcrumbs":"Advanced Programmability » Display » Object Display","id":"325","title":"Object Display"},"326":{"body":"While the objects can be owned by accounts and may be a subject to True Ownership , the Display can be owned by the creator of the object. This way, the creator can update the display metadata and apply the changes globally without the need to update every object. The creator can also transfer Display to another account or even build an application around the object with custom functionality to manage the metadata.","breadcrumbs":"Advanced Programmability » Display » Creator Privilege","id":"326","title":"Creator Privilege"},"327":{"body":"The fields that are supported most widely are: name - A name for the object. The name is displayed when users view the object. description - A description for the object. The description is displayed when users view the object. link - A link to the object to use in an application. image_url - A URL or a blob with the image for the object. thumbnail_url - A URL to a smaller image to use in wallets, explorers, and other products as a preview. project_url - A link to a website associated with the object or creator. creator - A string that indicates the object creator. Please, refer to the Sui Documentation for the most up-to-date list of supported fields. While there's a standard set of fields, the Display object does not enforce them. The developer can define any fields they need, and the client can use them as they see fit. Some applications may require additional fields, and omit other, and the Display is flexible enough to support them.","breadcrumbs":"Advanced Programmability » Display » Standard Fields","id":"327","title":"Standard Fields"},"328":{"body":"The Display object is defined in the sui::display module. It is a generic struct that takes a phantom type as a parameter. The phantom type is used to associate the Display object with the type it describes. The fields of the Display object are a VecMap of key-value pairs, where the key is the field name and the value is the field value. The version field is used to version the display metadata, and is updated on the update_display call. File: sui-framework/sources/display.move struct Display has key, store { id: UID, /// Contains fields for display. Currently supported /// fields are: name, link, image and description. fields: VecMap, /// Version that can only be updated manually by the Publisher. version: u16\n} The Publisher object is required to a new Display, since it serves as the proof of ownership of type.","breadcrumbs":"Advanced Programmability » Display » Working with Display","id":"328","title":"Working with Display"},"329":{"body":"Currently, Display supports simple string interpolation and can use struct fields (and paths) in its templates. The syntax is trivial - {path} is replaced with the value of the field at the path. The path is a dot-separated list of field names, starting from the root object in case of nested fields. /// Some common metadata for objects.\npublic struct Metadata has store { name: String, description: String, published_at: u64\n} /// The type with nested Metadata field.\npublic struct LittlePony has key, store { id: UID, image_url: String, metadata: Metadata\n} The Display for the type LittlePony above could be defined as follows: { \"name\": \"Just a pony\", \"image_url\": \"{image_url}\", \"description\": \"{metadata.description}\"\n}","breadcrumbs":"Advanced Programmability » Display » Template Syntax","id":"329","title":"Template Syntax"},"33":{"body":"The section titled TransactionData contains the information about the transaction we just sent. It features fields like sender, which is your address, the gas_budget set with the --gas-budget argument, and the Coin we used for payment. It also prints the Commands that were run by the CLI. In this example, the commands Publish and TransferObject were run - the latter transfers a special object UpgradeCap to the sender. ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Transaction Data │\n├──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ Gas Owner: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ Gas Budget: 100000000 MIST │\n│ Gas Price: 1000 MIST │\n│ Gas Payment: │\n│ ┌── │\n│ │ ID: 0x4ea1303e4f5e2f65fc3709bc0fb70a3035fdd2d53dbcff33e026a50a742ce0de │\n│ │ Version: 7 │\n│ │ Digest: AXYPnups8A5J6pkvLa6RekX2ye3qur66EZ88mEbaUDQ1 │\n│ └── │\n│ │\n│ Transaction Kind: Programmable │\n│ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │\n│ │ Input Objects │ │\n│ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │\n│ │ 0 Pure Arg: Type: address, Value: \"0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1\" │ │\n│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n│ ╭─────────────────────────────────────────────────────────────────────────╮ │\n│ │ Commands │ │\n│ ├─────────────────────────────────────────────────────────────────────────┤ │\n│ │ 0 Publish: │ │\n│ │ ┌ │ │\n│ │ │ Dependencies: │ │\n│ │ │ 0x0000000000000000000000000000000000000000000000000000000000000001 │ │\n│ │ │ 0x0000000000000000000000000000000000000000000000000000000000000002 │ │\n│ │ └ │ │\n│ │ │ │\n│ │ 1 TransferObjects: │ │\n│ │ ┌ │ │\n│ │ │ Arguments: │ │\n│ │ │ Result 0 │ │\n│ │ │ Address: Input 0 │ │\n│ │ └ │ │\n│ ╰─────────────────────────────────────────────────────────────────────────╯ │\n│ │\n│ Signatures: │\n│ gebjSbVwZwTkizfYg2XIuzdx+d66VxFz8EmVaisVFiV3GkDay6L+hQG3n2CQ1hrWphP6ZLc7bd1WRq4ss+hQAQ== │\n│ │\n╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯","breadcrumbs":"Hello, Sui! » Transaction Data","id":"33","title":"Transaction Data"},"330":{"body":"There's no restriction to how many Display objects can be created for a specific T. However, the most recently updated Display will be used by the fullnode.","breadcrumbs":"Advanced Programmability » Display » Multiple Display Objects","id":"330","title":"Multiple Display Objects"},"331":{"body":"Sui Object Display is Sui Documentation Publisher - the representation of the creator","breadcrumbs":"Advanced Programmability » Display » Further Reading","id":"331","title":"Further Reading"},"332":{"body":"Events are a way to notify off-chain listeners about on-chain events. They are used to emit additional information about the transaction that is not stored - and, hence, can't be accessed - on-chain. Events are emitted by the sui::event module located in the Sui Framework . Any custom type with the copy and drop abilities can be emitted as an event. Sui Verifier requires the type to be internal to the module. // File: sui-framework/sources/event.move\nmodule sui::event { /// Emit a custom Move event, sending the data offchain. /// /// Used for creating custom indexes and tracking onchain /// activity in a way that suits a specific application the most. /// /// The type `T` is the main way to index the event, and can contain /// phantom parameters, eg `emit(MyEvent)`. public native fun emit(event: T);\n}","breadcrumbs":"Advanced Programmability » Events » Events","id":"332","title":"Events"},"333":{"body":"Events are emitted using the emit function in the sui::event module. The function takes a single argument - the event to be emitted. The event data is passed by value, module book::events; use sui::coin::Coin;\nuse sui::sui::SUI;\nuse sui::event; /// The item that can be purchased.\npublic struct Item has key { id: UID } /// Event emitted when an item is purchased. Contains the ID of the item and\n/// the price for which it was purchased.\npublic struct ItemPurchased has copy, drop { item: ID, price: u64\n} /// A marketplace function which performs the purchase of an item.\npublic fun purchase(coin: Coin, ctx: &mut TxContext) { let item = Item { id: object::new(ctx) }; // Create an instance of `ItemPurchased` and pass it to `event::emit`. event::emit(ItemPurchased { item: object::id(&item), price: coin.value() }); // Omitting the rest of the implementation to keep the example simple. abort 0\n} The Sui Verifier requires the type passed to the emit function to be internal to the module . So emitting a type from another module will result in a compilation error. Primitive types, although they match the copy and drop requirement, are not allowed to be emitted as events.","breadcrumbs":"Advanced Programmability » Events » Emitting Events","id":"333","title":"Emitting Events"},"334":{"body":"Events are a part of the transaction result and are stored in the transaction effects . As such, they natively have the sender field which is the address who sent the transaction. So adding a \"sender\" field to the event is not necessary. Similarly, event metadata contains the timestamp. But it is important to note that the timestamp is relative to the node and may vary a little from node to node.","breadcrumbs":"Advanced Programmability » Events » Event Structure","id":"334","title":"Event Structure"},"335":{"body":"Sui Framework is a default dependency set in the Package Manifest . It depends on the Standard Library and provides Sui-specific features, including the interaction with the storage, and Sui-specific native types and modules. For convenience, we grouped the modules in the Sui Framework into multiple categories. But they're still part of the same framework.","breadcrumbs":"Advanced Programmability » Sui Framework » Sui Framework","id":"335","title":"Sui Framework"},"336":{"body":"Module Description Chapter sui::address Adds conversion methods to the address type Address sui::transfer Implements the storage operations for Objects It starts with an Object sui::tx_context Contains the TxContext struct and methods to read it Transaction Context sui::object Defines the UID and ID type, required for creating objects It starts with an Object sui::clock Defines the Clock type and its methods Epoch and Time sui::dynamic_field Implements methods to add, use and remove dynamic fields Dynamic Fields sui::dynamic_object_field Implements methods to add, use and remove dynamic object fields Dynamic Object Fields sui::event Allows emitting events for off-chain listeners Events sui::package Defines the Publisher type and package upgrade methods Publisher , Package Upgrades sui::display Implements the Display object and ways to create and update it Display","breadcrumbs":"Advanced Programmability » Sui Framework » Core","id":"336","title":"Core"},"337":{"body":"Module Description Chapter sui::vec_set Implements a set type Collections sui::vec_map Implements a map with vector keys Collections sui::table Implements the Table type and methods to interact with it Dynamic Collections sui::linked_table Implements the LinkedTable type and methods to interact with it Dynamic Collections sui::bag Implements the Bag type and methods to interact with it Dynamic Collections sui::object_table Implements the ObjectTable type and methods to interact with it Dynamic Collections sui::object_bag Implements the ObjectBag type and methods to interact with it Dynamic Collections","breadcrumbs":"Advanced Programmability » Sui Framework » Collections","id":"337","title":"Collections"},"338":{"body":"Module Description Chapter sui::bcs Implements the BCS encoding and decoding functions Binary Canonical Serialization","breadcrumbs":"Advanced Programmability » Sui Framework » Utilities","id":"338","title":"Utilities"},"339":{"body":"Sui Framework exports two named addresses: sui = 0x2 and std = 0x1 from the std dependency. [addresses]\nsui = \"0x2\" # Exported from the MoveStdlib dependency\nstd = \"0x1\"","breadcrumbs":"Advanced Programmability » Sui Framework » Exported Addresses","id":"339","title":"Exported Addresses"},"34":{"body":"Transaction Effects contains the status of the transaction, the changes that the transaction made to the state of the network and the objects involved in the transaction. ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Transaction Effects │\n├───────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Digest: GpcDV6JjjGQMRwHpEz582qsd5MpCYgSwrDAq1JXcpFjW │\n│ Status: Success │\n│ Executed Epoch: 411 │\n│ │\n│ Created Objects: │\n│ ┌── │\n│ │ ID: 0x160f7856e13b27e5a025112f361370f4efc2c2659cb0023f1e99a8a84d1652f3 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ Version: 8 │\n│ │ Digest: 8y6bhwvQrGJHDckUZmj2HDAjfkyVqHohhvY1Fvzyj7ec │\n│ └── │\n│ ┌── │\n│ │ ID: 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe │\n│ │ Owner: Immutable │\n│ │ Version: 1 │\n│ │ Digest: Ein91NF2hc3qC4XYoMUFMfin9U23xQmDAdEMSHLae7MK │\n│ └── │\n│ Mutated Objects: │\n│ ┌── │\n│ │ ID: 0x4ea1303e4f5e2f65fc3709bc0fb70a3035fdd2d53dbcff33e026a50a742ce0de │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ Version: 8 │\n│ │ Digest: 7ydahjaM47Gyb33PB4qnW2ZAGqZvDuWScV6sWPiv7LTc │\n│ └── │\n│ Gas Object: │\n│ ┌── │\n│ │ ID: 0x4ea1303e4f5e2f65fc3709bc0fb70a3035fdd2d53dbcff33e026a50a742ce0de │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ Version: 8 │\n│ │ Digest: 7ydahjaM47Gyb33PB4qnW2ZAGqZvDuWScV6sWPiv7LTc │\n│ └── │\n│ Gas Cost Summary: │\n│ Storage Cost: 10404400 MIST │\n│ Computation Cost: 1000000 MIST │\n│ Storage Rebate: 978120 MIST │\n│ Non-refundable Storage Fee: 9880 MIST │\n│ │\n│ Transaction Dependencies: │\n│ 7Ukrc5GqdFqTA41wvWgreCdHn2vRLfgQ3YMFkdks72Vk │\n│ 7d4amuHGhjtYKujEs9YkJARzNEn4mRbWWv3fn4cdKdyh │\n╰───────────────────────────────────────────────────────────────────────────────────────────────────╯","breadcrumbs":"Hello, Sui! » Transaction Effects","id":"34","title":"Transaction Effects"},"340":{"body":"Just like with Standard Library , some of the modules and types are imported implicitly in the Sui Framework. This is the list of modules and types that are available without explicit use import: sui::object sui::object::ID sui::object::UID sui::tx_context sui::tx_context::TxContext sui::transfer","breadcrumbs":"Advanced Programmability » Sui Framework » Implicit Imports","id":"340","title":"Implicit Imports"},"341":{"body":"The source code of the Sui Framework is available in the Sui repository .","breadcrumbs":"Advanced Programmability » Sui Framework » Source Code","id":"341","title":"Source Code"},"342":{"body":"A case in the abilities system - a struct without any abilities - is called hot potato . It cannot be stored (not as an object nor as a field in another struct ), it cannot be copied or discarded . Hence, once constructed, it must be gracefully unpacked by its module , or the transaction will abort due to unused value without drop. If you're familiar with languages that support callbacks , you can think of a hot potato as an obligation to call a callback function. If you don't call it, the transaction will abort. The name comes from the children's game where a ball is passed quickly between players, and none of the players want to be the last one holding it when the music stops, or they are out of the game. This is the best illustration of the pattern - the instance of a hot-potato struct is passed between calls, and none of the modules can keep it.","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Pattern: Hot Potato","id":"342","title":"Pattern: Hot Potato"},"343":{"body":"A hot potato can be any struct with no abilities. For example, the following struct is a hot potato: public struct Request {} Because the Request has no abilities and cannot be stored or ignored, the module must provide a function to unpack it. For example: /// Constructs a new `Request`\npublic fun new_request(): Request { Request {} } /// Unpacks the `Request`. Due to the nature of the hot potato, this function\n/// must be called to avoid aborting the transaction.\npublic fun confirm_request(request: Request) { let Request {} = request;\n}","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Defining a Hot Potato","id":"343","title":"Defining a Hot Potato"},"344":{"body":"In the following example, the Promise hot potato is used to ensure that the borrowed value, when taken from the container, is returned back to it. The Promise struct contains the ID of the borrowed object, and the ID of the container, ensuring that the borrowed value was not swapped for another and is returned to the correct container. /// A generic container for any Object with `key + store`. The Option type\n/// is used to allow taking and putting the value back.\npublic struct Container has key { id: UID, value: Option,\n} /// A Hot Potato struct that is used to ensure the borrowed value is returned.\npublic struct Promise { /// The ID of the borrowed object. Ensures that there wasn't a value swap. id: ID, /// The ID of the container. Ensures that the borrowed value is returned to /// the correct container. container_id: ID,\n} /// A module that allows borrowing the value from the container.\npublic fun borrow_val(container: &mut Container): (T, Promise) { assert!(container.value.is_some()); let value = container.value.extract(); let id = object::id(&value); (value, Promise { id, container_id: object::id(container) })\n} /// Put the taken item back into the container.\npublic fun return_val( container: &mut Container, value: T, promise: Promise\n) { let Promise { id, container_id } = promise; assert!(object::id(container) == container_id); assert!(object::id(&value) == id); container.value.fill(value);\n}","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Example Usage","id":"344","title":"Example Usage"},"345":{"body":"Below we list some of the common use cases for the hot potato pattern.","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Applications","id":"345","title":"Applications"},"346":{"body":"As shown in the example above , the hot potato is very effective for borrowing with a guarantee that the borrowed value is returned to the correct container. While the example focuses on a value stored inside an Option, the same pattern can be applied to any other storage type, say a dynamic field .","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Borrowing","id":"346","title":"Borrowing"},"347":{"body":"Canonical example of the hot potato pattern is flash loans. A flash loan is a loan that is borrowed and repaid in the same transaction. The borrowed funds are used to perform some operations, and the repaid funds are returned to the lender. The hot potato pattern ensures that the borrowed funds are returned to the lender. An example usage of this pattern may look like this: // Borrow the funds from the lender.\nlet (asset_a, potato) = lender.borrow(amount); // Perform some operations with the borrowed funds.\nlet asset_b = dex.trade(loan);\nlet proceeds = another_contract::do_something(asset_b); // Keep the commission and return the rest to the lender.\nlet pay_back = proceeds.split(amount, ctx);\nlender.repay(pay_back, potato);\ntransfer::public_transfer(proceeds, ctx.sender());","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Flash Loans","id":"347","title":"Flash Loans"},"348":{"body":"The hot potato pattern can be used to introduce variation in the execution path. For example, if there is a module which allows purchasing a Phone for some \"Bonus Points\" or for USD, the hot potato can be used to decouple the purchase from the payment. The approach is very similar to how some shops work - you take the item from the shelf, and then you go to the cashier to pay for it. /// A `Phone`. Can be purchased in a store.\npublic struct Phone has key, store { id: UID } /// A ticket that must be paid to purchase the `Phone`.\npublic struct Ticket { amount: u64 } /// Return the `Phone` and the `Ticket` that must be paid to purchase it.\npublic fun purchase_phone(ctx: &mut TxContext): (Phone, Ticket) { ( Phone { id: object::new(ctx) }, Ticket { amount: 100 } )\n} /// The customer may pay for the `Phone` with `BonusPoints` or `SUI`.\npublic fun pay_in_bonus_points(ticket: Ticket, payment: Coin) { let Ticket { amount } = ticket; assert!(payment.value() == amount); abort 0 // omitting the rest of the function\n} /// The customer may pay for the `Phone` with `USD`.\npublic fun pay_in_usd(ticket: Ticket, payment: Coin) { let Ticket { amount } = ticket; assert!(payment.value() == amount); abort 0 // omitting the rest of the function\n} This decoupling technique allows separating the purchase logic from the payment logic, making the code more modular and easier to maintain. The Ticket could be split into its own module, providing a basic interface for the payment, and the shop implementation could be extended to support other goods without changing the payment logic.","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Variable-path execution","id":"348","title":"Variable-path execution"},"349":{"body":"Hot potato can be used to link together different modules in a compositional way. Its module may define ways to interact with the hot potato, for example, stamp it with a type signature, or to extract some information from it. This way, the hot potato can be passed between different modules, and even different packages within the same transaction. The most important compositional pattern is the Request Pattern , which we will cover in the next section.","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Compositional Patterns","id":"349","title":"Compositional Patterns"},"35":{"body":"If there were any events emitted, you would see them in this section. Our package does not use events, so the section is empty. ╭─────────────────────────────╮\n│ No transaction block events │\n╰─────────────────────────────╯","breadcrumbs":"Hello, Sui! » Events","id":"35","title":"Events"},"350":{"body":"The pattern is used in various forms in the Sui Framework. Here are some examples: sui::borrow - uses hot potato to ensure that the borrowed value is returned to the correct container. sui::transfer_policy - defines a TransferRequest - a hot potato which can only be consumed if all conditions are met. sui::token - in the Closed Loop Token system, an ActionRequest carries the information about the performed action and collects approvals similarly to TransferRequest.","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Usage in the Sui Framework","id":"350","title":"Usage in the Sui Framework"},"351":{"body":"A hot potato is a struct without abilities, it must come with a way to create and destroy it. Hot potatoes are used to ensure that some action is taken before the transaction ends, similar to a callback. Most common use cases for hot potato are borrowing, flash loans, variable-path execution, and compositional patterns.","breadcrumbs":"Advanced Programmability » Pattern: Hot Potato » Summary","id":"351","title":"Summary"},"352":{"body":"Binary Canonical Serialization (BCS) is a binary encoding format for structured data. It was originally designed in Diem, and became the standard serialization format for Move. BCS is simple, efficient, deterministic, and easy to implement in any programming language. The full format specification is available in the BCS repository .","breadcrumbs":"Advanced Programmability » BCS » Binary Canonical Serialization","id":"352","title":"Binary Canonical Serialization"},"353":{"body":"BCS is a binary format that supports unsigned integers up to 256 bits, options, booleans, unit (empty value), fixed and variable-length sequences, and maps. The format is designed to be deterministic, meaning that the same data will always be serialized to the same bytes. \"BCS is not a self-describing format. As such, in order to deserialize a message, one must know the message type and layout ahead of time\" from the README Integers are stored in little-endian format, and variable-length integers are encoded using a variable-length encoding scheme. Sequences are prefixed with their length as ULEB128, enumerations are stored as the index of the variant followed by the data, and maps are stored as an ordered sequence of key-value pairs. Structs are treated as a sequence of fields, and the fields are serialized in the order they are defined in the struct. The fields are serialized using the same rules as the top-level data.","breadcrumbs":"Advanced Programmability » BCS » Format","id":"353","title":"Format"},"354":{"body":"The Sui Framework includes the sui::bcs module for encoding and decoding data. Encoding functions are native to the VM, and decoding functions are implemented in Move.","breadcrumbs":"Advanced Programmability » BCS » Using BCS","id":"354","title":"Using BCS"},"355":{"body":"To encode data, use the bcs::to_bytes function, which converts data references into byte vectors. This function supports encoding any types, including structs. // File: move-stdlib/sources/bcs.move\npublic native fun to_bytes(t: &T): vector; The following example shows how to encode a struct using BCS. The to_bytes function can take any value and encode it as a vector of bytes. use sui::bcs; // 0x01 - a single byte with value 1 (or 0 for false)\nlet bool_bytes = bcs::to_bytes(&true);\n// 0x2a - just a single byte\nlet u8_bytes = bcs::to_bytes(&42u8);\n// 0x2a00000000000000 - 8 bytes\nlet u64_bytes = bcs::to_bytes(&42u64);\n// address is a fixed sequence of 32 bytes\n// 0x0000000000000000000000000000000000000000000000000000000000000002\nlet addr = bcs::to_bytes(&@sui);","breadcrumbs":"Advanced Programmability » BCS » Encoding","id":"355","title":"Encoding"},"356":{"body":"Structs encode similarly to simple types. Here is how to encode a struct using BCS: let data = CustomData { num: 42, string: b\"hello, world!\".to_string(), value: true\n}; let struct_bytes = bcs::to_bytes(&data); let mut custom_bytes = vector[];\ncustom_bytes.append(bcs::to_bytes(&42u8));\ncustom_bytes.append(bcs::to_bytes(&b\"hello, world!\".to_string()));\ncustom_bytes.append(bcs::to_bytes(&true)); // struct is just a sequence of fields, so the bytes should be the same!\nassert!(&struct_bytes == &custom_bytes, 0);","breadcrumbs":"Advanced Programmability » BCS » Encoding a Struct","id":"356","title":"Encoding a Struct"},"357":{"body":"Because BCS does not self-describe and Move is statically typed, decoding requires prior knowledge of the data type. The sui::bcs module provides various functions to assist with this process.","breadcrumbs":"Advanced Programmability » BCS » Decoding","id":"357","title":"Decoding"},"358":{"body":"BCS is implemented as a wrapper in Move. The decoder takes the bytes by value, and then allows the caller to peel off the data by calling different decoding functions, prefixed with peel_*. The data is split off the bytes, and the remainder bytes are kept in the wrapper until the into_remainder_bytes function is called. use sui::bcs; // BCS instance should always be declared as mutable\nlet mut bcs = bcs::new(x\"010000000000000000\"); // Same bytes can be read differently, for example: Option\nlet value: Option = bcs.peel_option_u64(); assert!(value.is_some(), 0);\nassert!(value.borrow() == &0, 1); let remainder = bcs.into_remainder_bytes(); assert!(remainder.length() == 0, 2); There is a common practice to use multiple variables in a single let statement during decoding. It makes code a little bit more readable and helps to avoid unnecessary copying of the data. let mut bcs = bcs::new(x\"0101010F0000000000F00000000000\"); // mind the order!!!\n// handy way to peel multiple values\nlet (bool_value, u8_value, u64_value) = ( bcs.peel_bool(), bcs.peel_u8(), bcs.peel_u64()\n);","breadcrumbs":"Advanced Programmability » BCS » Wrapper API","id":"358","title":"Wrapper API"},"359":{"body":"While most of the primitive types have a dedicated decoding function, vectors need special handling, which depends on the type of the elements. For vectors, first you need to decode the length of the vector, and then decode each element in a loop. let mut bcs = bcs::new(x\"0101010F0000000000F00000000000\"); // bcs.peel_vec_length() peels the length of the vector :)\nlet mut len = bcs.peel_vec_length();\nlet mut vec = vector[]; // then iterate depending on the data type\nwhile (len > 0) { vec.push_back(bcs.peel_u64()); // or any other type len = len - 1;\n}; assert!(vec.length() == 1, 0); For most common scenarios, bcs module provides a basic set of functions for decoding vectors: peel_vec_address(): vector peel_vec_bool(): vector peel_vec_u8(): vector peel_vec_u64(): vector peel_vec_u128(): vector peel_vec_vec_u8(): vector> - vector of byte vectors","breadcrumbs":"Advanced Programmability » BCS » Decoding Vectors","id":"359","title":"Decoding Vectors"},"36":{"body":"These are the changes to objects that transaction has made. In our case, we have created a new UpgradeCap object which is a special object that allows the sender to upgrade the package in the future, mutated the Gas object, and published a new package. Packages are also objects on Sui. ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Object Changes │\n├──────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Created Objects: │\n│ ┌── │\n│ │ ObjectID: 0x160f7856e13b27e5a025112f361370f4efc2c2659cb0023f1e99a8a84d1652f3 │\n│ │ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ ObjectType: 0x2::package::UpgradeCap │\n│ │ Version: 8 │\n│ │ Digest: 8y6bhwvQrGJHDckUZmj2HDAjfkyVqHohhvY1Fvzyj7ec │\n│ └── │\n│ Mutated Objects: │\n│ ┌── │\n│ │ ObjectID: 0x4ea1303e4f5e2f65fc3709bc0fb70a3035fdd2d53dbcff33e026a50a742ce0de │\n│ │ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │\n│ │ Version: 8 │\n│ │ Digest: 7ydahjaM47Gyb33PB4qnW2ZAGqZvDuWScV6sWPiv7LTc │\n│ └── │\n│ Published Objects: │\n│ ┌── │\n│ │ PackageID: 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe │\n│ │ Version: 1 │\n│ │ Digest: Ein91NF2hc3qC4XYoMUFMfin9U23xQmDAdEMSHLae7MK │\n│ │ Modules: todo_list │\n│ └── │\n╰──────────────────────────────────────────────────────────────────────────────────────────────────╯","breadcrumbs":"Hello, Sui! » Object Changes","id":"36","title":"Object Changes"},"360":{"body":"Option is represented as a vector of either 0 or 1 element. To read an option, you would treat it like a vector and check its length (first byte - either 1 or 0). let mut bcs = bcs::new(x\"00\");\nlet is_some = bcs.peel_bool(); assert!(is_some == false, 0); let mut bcs = bcs::new(x\"0101\");\nlet is_some = bcs.peel_bool();\nlet value = bcs.peel_u8(); assert!(is_some == true, 1);\nassert!(value == 1, 2); If you need to decode an option of a custom type, use the method in the code snippet above. The most common scenarios, bcs module provides a basic set of functions for decoding Option's: peel_option_address(): Option peel_option_bool(): Option peel_option_u8(): Option peel_option_u64(): Option peel_option_u128(): Option","breadcrumbs":"Advanced Programmability » BCS » Decoding Option","id":"360","title":"Decoding Option"},"361":{"body":"Structs are decoded field by field, and there is no standard function to automatically decode bytes into a Move struct, and it would have been a violation of the Move's type system. Instead, you need to decode each field manually. // some bytes... let mut bcs = bcs::new(x\"0101010F0000000000F00000000000\"); let (age, is_active, name) = ( bcs.peel_u8(), bcs.peel_bool(), bcs.peel_vec_u8().to_string()\n); let user = User { age, is_active, name };","breadcrumbs":"Advanced Programmability » BCS » Decoding Structs","id":"361","title":"Decoding Structs"},"362":{"body":"Binary Canonical Serialization is an efficient binary format for structured data, ensuring consistent serialization across platforms. The Sui Framework provides comprehensive tools for working with BCS, allowing extensive functionality through built-in functions.","breadcrumbs":"Advanced Programmability » BCS » Summary","id":"362","title":"Summary"},"363":{"body":"Move 2024 is the new edition of the Move language that is maintained by Mysten Labs. This guide is intended to help you understand the differences between the 2024 edition and the previous version of the Move language. This guide provides a high-level overview of the changes in the new edition. For a more detailed and exhaustive list of changes, refer to the Sui Documentation .","breadcrumbs":"2024 Migration Guide » Move 2024 Migration Guide","id":"363","title":"Move 2024 Migration Guide"},"364":{"body":"To use the new edition, you need to specify the edition in the move file. The edition is specified in the move file using the edition keyword. Currently, the only available edition is 2024.beta. edition = \"2024.beta\";","breadcrumbs":"2024 Migration Guide » Using the New Edition","id":"364","title":"Using the New Edition"},"365":{"body":"The Move CLI has a migration tool that updates the code to the new edition. To use the migration tool, run the following command: $ sui move migrate The migration tool will update the code to use the let mut syntax, the new public modifier for strucs, and the public(package) function visibility instead of friend declarations.","breadcrumbs":"2024 Migration Guide » Migration Tool","id":"365","title":"Migration Tool"},"366":{"body":"Move 2024 introduces let mut syntax to declare mutable variables. The let mut syntax is used to declare a mutable variable that can be changed after it is declared. let mut declaration is now required for mutable variables. Compiler will emit an error if you try to reassign a variable without the mut keyword. // Move 2020\nlet x: u64 = 10;\nx = 20; // Move 2024\nlet mut x: u64 = 10;\nx = 20; Additionally, the mut keyword is used in tuple destructuring and function arguments to declare mutable variables. // takes by value and mutates\nfun takes_by_value_and_mutates(mut v: Value): Value { v.field = 10; v\n} // `mut` should be placed before the variable name\nfun destruct() { let (x, y) = point::get_point(); let (mut x, y) = point::get_point(); let (mut x, mut y) = point::get_point();\n} // in struct unpack\nfun unpack() { let Point { x, mut y } = point::get_point(); let Point { mut x, mut y } = point::get_point();\n}","breadcrumbs":"2024 Migration Guide » Mutable bindings with let mut","id":"366","title":"Mutable bindings with let mut"},"367":{"body":"In Move 2024, the friend keyword is deprecated. Instead, you can use the public(package) visibility modifier to make functions visible to other modules in the same package. // Move 2020\nfriend book::friend_module;\npublic(friend) fun protected_function() {} // Move 2024\npublic(package) fun protected_function_2024() {}","breadcrumbs":"2024 Migration Guide » Friends are Deprecated","id":"367","title":"Friends are Deprecated"},"368":{"body":"In Move 2024, structs get a visibility modifier. Currently, the only available visibility modifier is public. // Move 2020\nstruct Book {} // Move 2024\npublic struct Book {}","breadcrumbs":"2024 Migration Guide » Struct Visibility","id":"368","title":"Struct Visibility"},"369":{"body":"In the new edition, functions which have a struct as the first argument are associated with the struct. This means that the function can be called using the dot notation. Methods defined in the same module with the type are automatically exported. Methods are automatically exported if the type is defined in the same module as the method. It is impossible to export methods for types defined in other modules. However, you can create custom aliases for methods in the module scope. public fun count(c: &Counter): u64 { /* ... */ } fun use_counter() { // move 2020 let count = counter::count(&c); // move 2024 let count = c.count();\n}","breadcrumbs":"2024 Migration Guide » Method Syntax","id":"369","title":"Method Syntax"},"37":{"body":"This last section contains changes to SUI Coins, in our case, we have spent around 0.015 SUI, which in MIST is 10,500,000. You can see it under the amount field in the output. ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Balance Changes │\n├───────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ ┌── │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ CoinType: 0x2::sui::SUI │\n│ │ Amount: -10426280 │\n│ └── │\n╰───────────────────────────────────────────────────────────────────────────────────────────────────╯","breadcrumbs":"Hello, Sui! » Balance Changes","id":"37","title":"Balance Changes"},"370":{"body":"In Move 2024, some of the native and standard types received associated methods. For example, the vector type has a to_string method that converts the vector into a UTF8 string. fun aliases() { // vector to string and ascii string let str: String = b\"Hello, World!\".to_string(); let ascii: ascii::String = b\"Hello, World!\".to_ascii_string(); // address to bytes let bytes = @0xa11ce.to_bytes();\n} For the full list of built-in aliases, refer to the Standard Library and Sui Framework source code.","breadcrumbs":"2024 Migration Guide » Methods for Built-in Types","id":"370","title":"Methods for Built-in Types"},"371":{"body":"Some of the built-in types support borrowing operators. The borrowing operator is used to get a reference to the element at the specified index. The borrowing operator is defined as []. fun play_vec() { let v = vector[1,2,3,4]; let first = &v[0]; // calls vector::borrow(v, 0) let first_mut = &mut v[0]; // calls vector::borrow_mut(v, 0) let first_copy = v[0]; // calls *vector::borrow(v, 0)\n} Types that support the borrowing operator are: vector sui::vec_map::VecMap sui::table::Table sui::bag::Bag sui::object_table::ObjectTable sui::object_bag::ObjectBag sui::linked_table::LinkedTable To implement the borrowing operator for a custom type, you need to add a #[syntax(index)] attribute to the methods. #[syntax(index)]\npublic fun borrow(c: &List, key: String): &T { /* ... */ } #[syntax(index)]\npublic fun borrow_mut(c: &mut List, key: String): &mut T { /* ... */ }","breadcrumbs":"2024 Migration Guide » Borrowing Operator","id":"371","title":"Borrowing Operator"},"372":{"body":"In Move 2024, methods can be associated with types. The alias can be defined for any type locally to the module; or publicly, if the type is defined in the same module. // my_module.move\n// Local: type is foreign to the module\nuse fun my_custom_function as vector.do_magic; // sui-framework/kiosk/kiosk.move\n// Exported: type is defined in the same module\npublic use fun kiosk_owner_cap_for as KioskOwnerCap.kiosk;","breadcrumbs":"2024 Migration Guide » Method Aliases","id":"372","title":"Method Aliases"},"373":{"body":"To talk about best practices for upgradeability, we need to first understand what can be upgraded in a package. The base premise of upgradeability is that an upgrade should not break public compatibility with the previous version. The parts of the module which can be used in dependent packages should not change their static signature. This applies to modules - a module can not be removed from a package, public structs - they can be used in function signatures and public functions - they can be called from other packages. // module can not be removed from the package\nmodule book::upgradable { // dependencies can be changed (if they are not used in public signatures) use std::string::String; use sui::event; // can be removed // public structs can not be removed and can't be changed public struct Book has key { id: UID, title: String, } // public structs can not be removed and can't be changed public struct BookCreated has copy, drop { /* ... */ } // public functions can not be removed and their signature can never change // but the implementation can be changed public fun create_book(ctx: &mut TxContext): Book { create_book_internal(ctx) // can be removed and changed event::emit(BookCreated { /* ... */ }) } // package-visibility functions can be removed and changed public(package) fun create_book_package(ctx: &mut TxContext): Book { create_book_internal(ctx) } // entry functions can be removed and changed as long they're not public entry fun create_book_entry(ctx: &mut TxContext): Book { create_book_internal(ctx) } // private functions can be removed and changed fun create_book_internal(ctx: &mut TxContext): Book { abort 0 }\n}","breadcrumbs":"Upgradability Practices » Upgradeability Practices","id":"373","title":"Upgradeability Practices"},"374":{"body":"To discard previous versions of the package, the objects can be versioned. As long as the object contains a version field, and the code which uses the object expects and asserts a specific version, the code can be force-migrated to the new version. Normally, after an upgrade, admin functions can be used to update the version of the shared state, so that the new version of code can be used, and the old version aborts with a version mismatch. module book::versioned_state { const EVersionMismatch: u64 = 0; const VERSION: u8 = 1; /// The shared state (can be owned too) public struct SharedState has key { id: UID, version: u8, /* ... */ } public fun mutate(state: &mut SharedState) { assert!(state.version == VERSION, EVersionMismatch); // ... }\n}","breadcrumbs":"Upgradability Practices » Versioning objects","id":"374","title":"Versioning objects"},"375":{"body":"There's a common pattern in Sui which allows changing the stored configuration of an object while retaining the same object signature. This is done by keeping the base object simple and versioned and adding an actual configuration object as a dynamic field. Using this anchor pattern, the configuration can be changed with package upgrades while keeping the same base object signature. module book::versioned_config { use sui::vec_map::VecMap; use std::string::String; /// The base object public struct Config has key { id: UID, version: u16 } /// The actual configuration public struct ConfigV1 has store { data: Bag, metadata: VecMap } // ...\n}","breadcrumbs":"Upgradability Practices » Versioning configuration with dynamic fields","id":"375","title":"Versioning configuration with dynamic fields"},"376":{"body":"This section is coming soon!","breadcrumbs":"Upgradability Practices » Modular architecture","id":"376","title":"Modular architecture"},"377":{"body":"To guarantee the safety and security of the network, Sui has certain limits and restrictions. These limits are in place to prevent abuse and to ensure that the network remains stable and efficient. This guide provides an overview of these limits and restrictions, and how to build your application to work within them. The limits are defined in the protocol configuration and are enforced by the network. If any of the limits are exceeded, the transaction will either be rejected or aborted. The limits, being a part of the protocol, can only be changed through a network upgrade.","breadcrumbs":"Building against Limits » Building against Limits","id":"377","title":"Building against Limits"},"378":{"body":"The size of a transaction is limited to 128KB. This includes the size of the transaction payload, the size of the transaction signature, and the size of the transaction metadata. If a transaction exceeds this limit, it will be rejected by the network.","breadcrumbs":"Building against Limits » Transaction Size","id":"378","title":"Transaction Size"},"379":{"body":"The size of an object is limited to 256KB. This includes the size of the object data. If an object exceeds this limit, it will be rejected by the network. While a single object cannot bypass this limit, for more extensive storage options, one could use a combination of a base object with other attached to it using dynamic fields (eg Bag).","breadcrumbs":"Building against Limits » Object Size","id":"379","title":"Object Size"},"38":{"body":"It is possible to specify the --json flag during publishing to get the output in JSON format. This is useful if you want to parse the output programmatically or store it for later use. $ sui client publish --gas-budget 100000000 --json","breadcrumbs":"Hello, Sui! » Alternative Output","id":"38","title":"Alternative Output"},"380":{"body":"The size of a single pure argument is limited to 16KB. A transaction argument bigger than this limit will result in execution failure. So in order to create a vector of more than ~500 addresses (given that a single address is 32 bytes), it needs to be joined dynamically either in Transaction Block or in a Move function. Standard functions like vector::append() can join two vectors of ~16KB resulting in a ~32KB of data as a single value.","breadcrumbs":"Building against Limits » Single Pure Argument Size","id":"380","title":"Single Pure Argument Size"},"381":{"body":"The maximum number of objects that can be created in a single transaction is 2048. If a transaction attempts to create more than 2048 objects, it will be rejected by the network. This also affects dynamic fields , as both the key and the value are objects. So the maximum number of dynamic fields that can be created in a single transaction is 1024.","breadcrumbs":"Building against Limits » Maximum Number of Objects created","id":"381","title":"Maximum Number of Objects created"},"382":{"body":"The maximum number of dynamic fields that can be created in a single object is 1024. If an object attempts to create more than 1024 dynamic fields, it will be rejected by the network.","breadcrumbs":"Building against Limits » Maximum Number of Dynamic Fields created","id":"382","title":"Maximum Number of Dynamic Fields created"},"383":{"body":"The maximum number of events that can be emitted in a single transaction is 1024. If a transaction attempts to emit more than 1024 events, it will be aborted.","breadcrumbs":"Building against Limits » Maximum Number of Events","id":"383","title":"Maximum Number of Events"},"384":{"body":"Whenever execution encounters an abort, transaction fails and abort code is returned to the caller. Move VM returns the module name that aborted the transaction and the abort code. This behavior is not fully transparent to the caller of the transaction, especially when a single function contains multiple calls to the same function which may abort. In this case, the caller will not know which call aborted the transaction, and it will be hard to debug the issue or provide meaningful error message to the user. module book::module_a { use book::module_b; public fun do_something() { let field_1 = module_b::get_field(1); // may abort with 0 /* ... a lot of logic ... */ let field_2 = module_b::get_field(2); // may abort with 0 /* ... some more logic ... */ let field_3 = module_b::get_field(3); // may abort with 0 }\n} The example above illustrates the case when a single function contains multiple calls which may abort. If the caller of the do_something function receives an abort code 0, it will be hard to understand which call to module_b::get_field aborted the transaction. To address this problem, there are common patterns that can be used to improve error handling.","breadcrumbs":"Better error handling » Better error handling","id":"384","title":"Better error handling"},"385":{"body":"It is considered a good practice to provide a safe \"check\" function that returns a boolean value indicating whether an operation can be performed safely. If the module_b provides a function has_field that returns a boolean value indicating whether a field exists, the do_something function can be rewritten as follows: module book::module_a { use book::module_b; const ENoField: u64 = 0; public fun do_something() { assert!(module_b::has_field(1), ENoField); let field_1 = module_b::get_field(1); /* ... */ assert!(module_b::has_field(2), ENoField); let field_2 = module_b::get_field(2); /* ... */ assert!(module_b::has_field(3), ENoField); let field_3 = module_b::get_field(3); }\n} By adding custom checks before each call to module_b::get_field, the developer of the module_a takes control over the error handling. And it allows implementing the second rule.","breadcrumbs":"Better error handling » Rule 1: Handle all possible scenarios","id":"385","title":"Rule 1: Handle all possible scenarios"},"386":{"body":"The second trick, once the abort codes are handled by the caller module, is to use different abort codes for different scenarios. This way, the caller module can provide a meaningful error message to the user. The module_a can be rewritten as follows: module book::module_a { use book::module_b; const ENoFieldA: u64 = 0; const ENoFieldB: u64 = 1; const ENoFieldC: u64 = 2; public fun do_something() { assert!(module_b::has_field(1), ENoFieldA); let field_1 = module_b::get_field(1); /* ... */ assert!(module_b::has_field(2), ENoFieldB); let field_2 = module_b::get_field(2); /* ... */ assert!(module_b::has_field(3), ENoFieldC); let field_3 = module_b::get_field(3); }\n} Now, the caller module can provide a meaningful error message to the user. If the caller receives an abort code 0, it can be translated to \"Field 1 does not exist\". If the caller receives an abort code 1, it can be translated to \"Field 2 does not exist\". And so on.","breadcrumbs":"Better error handling » Rule 2: Abort with different codes","id":"386","title":"Rule 2: Abort with different codes"},"387":{"body":"A developer is often tempted to add a public function that would assert all the conditions and abort the execution. However, it is a better practice to create a function that returns a boolean value instead. This way, the caller module can handle the error and provide a meaningful error message to the user. module book::some_app_assert { const ENotAuthorized: u64 = 0; public fun do_a() { assert_is_authorized(); // ... } public fun do_b() { assert_is_authorized(); // ... } /// Don't do this public fun assert_is_authorized() { assert!(/* some condition */ true, ENotAuthorized); }\n} This module can be rewritten as follows: module book::some_app { const ENotAuthorized: u64 = 0; public fun do_a() { assert!(is_authorized(), ENotAuthorized); // ... } public fun do_b() { assert!(is_authorized(), ENotAuthorized); // ... } public fun is_authorized(): bool { /* some condition */ true } // a private function can still be used to avoid code duplication for a case // when the same condition with the same abort code is used in multiple places fun assert_is_authorized() { assert!(is_authorized(), ENotAuthorized); }\n} Utilizing these three rules will make the error handling more transparent to the caller of the transaction, and it will allow other developers to use custom abort codes in their modules.","breadcrumbs":"Better error handling » Rule 3: Return bool instead of assert","id":"387","title":"Rule 3: Return bool instead of assert"},"388":{"body":"","breadcrumbs":"Coding Conventions » Coding Conventions","id":"388","title":"Coding Conventions"},"389":{"body":"","breadcrumbs":"Coding Conventions » Naming","id":"389","title":"Naming"},"39":{"body":"After the package is published on chain, we can interact with it. To do this, we need to find the address (object ID) of the package. It's under the Published Objects section of the Object Changes output. The address is unique for each package, so you will need to copy it from the output. In this example, the address is: 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe Now that we have the address, we can interact with the package. In the next section, we will show how to interact with the package by sending transactions.","breadcrumbs":"Hello, Sui! » Using the Results","id":"39","title":"Using the Results"},"390":{"body":"Module names should be in snake_case. Module names should be descriptive and should not be too long. module book::conventions { /* ... */ }\nmodule book::common_pracices { /* ... */ }","breadcrumbs":"Coding Conventions » Module","id":"390","title":"Module"},"391":{"body":"Constants should be in SCREAMING_SNAKE_CASE. Error constants should be in EPascalCase const MAX_PRICE: u64 = 1000;\nconst EInvalidInput: u64 = 0;","breadcrumbs":"Coding Conventions » Constant","id":"391","title":"Constant"},"392":{"body":"Function names should be in snake_case. Function names should be descriptive. public fun add(a: u64, b: u64): u64 { a + b }\npublic fun create_if_not_exists() { /* ... */ }","breadcrumbs":"Coding Conventions » Function","id":"392","title":"Function"},"393":{"body":"Struct names should be in PascalCase. Struct fields should be in snake_case. Capabilities should be suffixed with Cap. public struct Hero has key { id: UID value: u64, another_value: u64,\n} public struct AdminCap has key { id: UID }","breadcrumbs":"Coding Conventions » Struct","id":"393","title":"Struct"},"394":{"body":"Struct methods should be in snake_case. If there's multiple structs with the same method, the method should be prefixed with the struct name. In this case, an alias can be added to the method using use fun. public fun value(h: &Hero): u64 { h.value } public use fun hero_health as Hero.health;\npublic fun hero_health(h: &Hero): u64 { h.another_value } public use fun boar_health as Boar.health;\npublic fun boar_health(b: &Boar): u64 { b.another_value }","breadcrumbs":"Coding Conventions » Struct Method","id":"394","title":"Struct Method"},"395":{"body":"Fast Path - term used to describe a transaction that does not involve shared objects, and can be executed without the need for consensus. Parallel Execution - term used to describe the ability of the Sui runtime to execute transactions in parallel, including the ones that involve shared objects. Internal Type - type that is defined within the module. Fields of this type can not be accessed from outside the module, and, in case of \"key\"-only abilities, can not be used in public_* transfer functions.","breadcrumbs":"A - Glossary » Appendix A: Glossary","id":"395","title":"Appendix A: Glossary"},"396":{"body":"key - ability that allows the struct to be used as a key in the storage. On Sui, the key ability marks an object and requires the first field to be a id: UID. store - ability that allows the struct to be stored inside other objects. This ability relaxes restrictions applied to internal structs, allowing public_* transfer functions to accept them as arguments. It also enables the object to be stored as a dynamic field. copy - ability that allows the struct to be copied. On Sui, the copy ability conflicts with the key ability, and can not be used together with it. drop - ability that allows the struct to be ignored or discarded. On Sui, the drop ability cannot be used together with the key ability, as objects are not allowed to be ignored.","breadcrumbs":"A - Glossary » Abilities","id":"396","title":"Abilities"},"397":{"body":"Reserved addresses are special addresses that have a specific purpose on Sui. They stay the same between environments and are used for specific native operations. 0x1 - address of the Standard Library (alias std) 0x2 - address of the Sui Framework (alias sui) 0x5 - address of the SuiSystem object 0x6 - address of the system Clock object 0x8 - address of the system Random object 0x403 - address of the DenyList system object","breadcrumbs":"B - Reserved Addresses » Appendix B: Reserved Addresses","id":"397","title":"Appendix B: Reserved Addresses"},"398":{"body":"This section lists publications related to Move and Sui. The Move Borrow Checker by Sam Blackshear, John Mitchell, Todd Nowacki, Shaz Qadeer. Resources: A Safe Language Abstraction for Money by Sam Blackshear, David L. Dill, Shaz Qadeer, Clark W. Barrett, John C. Mitchell, Oded Padon, Yoni Zohar. Robust Safety for Move by Marco Patrignani, Sam Blackshear","breadcrumbs":"C - Publications » Appendix C: Publications","id":"398","title":"Appendix C: Publications"},"399":{"body":"To contribute to this book, please, submit a pull request to the GitHub repository . The repository contains the source files for the book, written in mdBook format.","breadcrumbs":"D - Contributing » Appendix D: Contributing","id":"399","title":"Appendix D: Contributing"},"4":{"body":"You can download the latest Sui binary from the releases page . The binary is available for macOS, Linux and Windows. For education purposes and development, we recommend using the mainnet version.","breadcrumbs":"Before we begin » Install Sui » Download Binary","id":"4","title":"Download Binary"},"40":{"body":"To demonstrate the interaction with the todo_list package, we will send a transaction to create a new list and add an item to it. Transactions are sent via the sui client ptb command, it allows using the Transaction Blocks at full capacity. The command may look big and complex, but we go through it step by step.","breadcrumbs":"Hello, Sui! » Sending Transactions","id":"40","title":"Sending Transactions"},"400":{"body":"The Rust Book has been a great inspiration for this book. I am personally grateful to the authors of the book, Steve Klabnik and Carol Nichols, for their work, as I have learned a lot from it. This book is a small tribute to their work and an attempt to bring a similar learning experience to the Move community.","breadcrumbs":"E - Acknowledgements » Appendix E: Acknowledgements","id":"400","title":"Appendix E: Acknowledgements"},"41":{"body":"Before we construct the command, let's store the values we will use in the transaction. Replace the 0x4.... with the address of the package you have published. And MY_ADDRESS variable will be automatically set to your address from the CLI output. $ export PACKAGE_ID=0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe\n$ export MY_ADDRESS=$(sui client active-address)","breadcrumbs":"Hello, Sui! » Prepare the Variables","id":"41","title":"Prepare the Variables"},"42":{"body":"Now to building an actual transaction. The transaction will consist of two parts: we will call the new function in the todo_list package to create a new list, and then we will transfer the list object to our account. The transaction will look like this: $ sui client ptb \\\n--gas-budget 100000000 \\\n--assign sender @$MY_ADDRESS \\\n--move-call $PACKAGE_ID::todo_list::new \\\n--assign list \\\n--transfer-objects \"[list]\" sender In this command, we are using the ptb subcommand to build a transaction. Parameters that follow it define the actual commands and actions that the transaction will perform. The first two calls we make are utility calls to set the sender address to the command inputs and set the gas budget for the transaction. # sets the gas budget for the transaction\n--gas-budget 100000000 \\n\n# registers a variable \"sender=@...\"\n--assign sender @$MY_ADDRESS \\n Then we perform the actual call to a function in the package. We use the --move-call followed by the package ID, the module name, and the function name. In this case, we are calling the new function in the todo_list package. # calls the \"new\" function in the \"todo_list\" package under the $PACKAGE_ID address\n--move-call $PACKAGE_ID::todo_list::new The function that we defined actually returns a value, which we want need to store. We use the --assign command to give a name to the returned value. In this case, we are calling it list. And then we transfer the object to our account. --move-call $PACKAGE_ID::todo_list::new \\\n# assigns the result of the \"new\" function to the \"list\" variable (from the previous step)\n--assign list \\\n# transfers the object to the sender\n--transfer-objects \"[list]\" sender Once the command is constructed, you can run it in the terminal. If everything is correct, you should see the output similar to the one we had in previous sections. The output will contain the transaction digest, the transaction data, and the transaction effects. Spoiler: Full transaction output Transaction Digest: BJwYEnuuMzU4Y8cTwMoJbbQA6cLwPmwxvsRpSmvThoK8\n╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Transaction Data │\n├──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ Gas Owner: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ Gas Budget: 100000000 MIST │\n│ Gas Price: 1000 MIST │\n│ Gas Payment: │\n│ ┌── │\n│ │ ID: 0xe5ddeb874a8d7ead328e9f2dd2ad8d25383ab40781a5f1aefa75600973b02bc4 │\n│ │ Version: 22 │\n│ │ Digest: DiBrBMshDiD9cThpaEgpcYSF76uV4hCoE1qRyQ3rnYCB │\n│ └── │\n│ │\n│ Transaction Kind: Programmable │\n│ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │\n│ │ Input Objects │ │\n│ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │\n│ │ 0 Pure Arg: Type: address, Value: \"0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1\" │ │\n│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n│ ╭──────────────────────────────────────────────────────────────────────────────────╮ │\n│ │ Commands │ │\n│ ├──────────────────────────────────────────────────────────────────────────────────┤ │\n│ │ 0 MoveCall: │ │\n│ │ ┌ │ │\n│ │ │ Function: new │ │\n│ │ │ Module: todo_list │ │\n│ │ │ Package: 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe │ │\n│ │ └ │ │\n│ │ │ │\n│ │ 1 TransferObjects: │ │\n│ │ ┌ │ │\n│ │ │ Arguments: │ │\n│ │ │ Result 0 │ │\n│ │ │ Address: Input 0 │ │\n│ │ └ │ │\n│ ╰──────────────────────────────────────────────────────────────────────────────────╯ │\n│ │\n│ Signatures: │\n│ C5Lie4dtP5d3OkKzFBa+xM0BiNoB/A4ItthDCRTRBUrEE+jXeNs7mP4AuGwi3nzfTskh29+R1j1Kba4Wdy3QDA== │\n│ │\n╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n╭───────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Transaction Effects │\n├───────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Digest: BJwYEnuuMzU4Y8cTwMoJbbQA6cLwPmwxvsRpSmvThoK8 │\n│ Status: Success │\n│ Executed Epoch: 1213 │\n│ │\n│ Created Objects: │\n│ ┌── │\n│ │ ID: 0x74973c4ea2e78dc409f60481e23761cee68a48156df93a93fbcceb77d1cacdf6 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ Version: 23 │\n│ │ Digest: DuHTozDHMsuA7cFnWRQ1Gb8FQghAEBaj3inasJxqYq1c │\n│ └── │\n│ Mutated Objects: │\n│ ┌── │\n│ │ ID: 0xe5ddeb874a8d7ead328e9f2dd2ad8d25383ab40781a5f1aefa75600973b02bc4 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ Version: 23 │\n│ │ Digest: 82fwKarGuDhtomr5oS6ZGNvZNw9QVXLSbPdQu6jQgNV7 │\n│ └── │\n│ Gas Object: │\n│ ┌── │\n│ │ ID: 0xe5ddeb874a8d7ead328e9f2dd2ad8d25383ab40781a5f1aefa75600973b02bc4 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ Version: 23 │\n│ │ Digest: 82fwKarGuDhtomr5oS6ZGNvZNw9QVXLSbPdQu6jQgNV7 │\n│ └── │\n│ Gas Cost Summary: │\n│ Storage Cost: 2318000 MIST │\n│ Computation Cost: 1000000 MIST │\n│ Storage Rebate: 978120 MIST │\n│ Non-refundable Storage Fee: 9880 MIST │\n│ │\n│ Transaction Dependencies: │\n│ FSz2fYXmKqTf77mFXNq5JK7cKY8agWja7V5yDKEgL8c3 │\n│ GgMZKTt482DYApbAZkPDtdssGHZLbxgjm2uMXhzJax8Q │\n╰───────────────────────────────────────────────────────────────────────────────────────────────────╯\n╭─────────────────────────────╮\n│ No transaction block events │\n╰─────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Object Changes │\n├───────────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Created Objects: │\n│ ┌── │\n│ │ ObjectID: 0x74973c4ea2e78dc409f60481e23761cee68a48156df93a93fbcceb77d1cacdf6 │\n│ │ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ ObjectType: 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList │\n│ │ Version: 23 │\n│ │ Digest: DuHTozDHMsuA7cFnWRQ1Gb8FQghAEBaj3inasJxqYq1c │\n│ └── │\n│ Mutated Objects: │\n│ ┌── │\n│ │ ObjectID: 0xe5ddeb874a8d7ead328e9f2dd2ad8d25383ab40781a5f1aefa75600973b02bc4 │\n│ │ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │\n│ │ Version: 23 │\n│ │ Digest: 82fwKarGuDhtomr5oS6ZGNvZNw9QVXLSbPdQu6jQgNV7 │\n│ └── │\n╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯\n╭───────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Balance Changes │\n├───────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ ┌── │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ CoinType: 0x2::sui::SUI │\n│ │ Amount: -2339880 │\n│ └── │\n╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ The section that we want to focus on is the \"Object Changes\". More specifically, the \"Created Objects\" part of it. It contains the object ID, the type and the version of the TodoList that you have created. We will use this object ID to interact with the list. ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ Object Changes │\n├───────────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ Created Objects: │\n│ ┌── │\n│ │ ObjectID: 0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553 │\n│ │ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ ObjectType: 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList │\n│ │ Version: 22 │\n│ │ Digest: HyWdUpjuhjLY38dLpg6KPHQ3bt4BqQAbdF5gB8HQdEqG │\n│ └── │\n│ Mutated Objects: │\n│ ┌── │\n│ │ ObjectID: 0xe5ddeb874a8d7ead328e9f2dd2ad8d25383ab40781a5f1aefa75600973b02bc4 │\n│ │ Sender: 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │\n│ │ Owner: Account Address ( 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 ) │\n│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │\n│ │ Version: 22 │\n│ │ Digest: DiBrBMshDiD9cThpaEgpcYSF76uV4hCoE1qRyQ3rnYCB │\n│ └── │\n╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ In this example the object ID is 0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553. And the owner should be your account address. We achieved this by transferring the object to the sender in the last command of the transaction. Another way to test that you have successfully created the list is to check the account objects. $ sui client objects It should have an object that looks similar to this: ╭ ... ╮\n│ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │\n│ │ objectId │ 0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553 │ │\n│ │ version │ 22 │ │\n│ │ digest │ /DUEiCLkaNSgzpZSq2vSV0auQQEQhyH9occq9grMBZM= │ │\n│ │ objectType │ 0x468d..29fe::todo_list::TodoList │ │\n│ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │\n| ... |","breadcrumbs":"Hello, Sui! » Building the Transaction in CLI","id":"42","title":"Building the Transaction in CLI"},"43":{"body":"The TodoList that we created in the previous step is an object that you can interact with as its owner. You can call functions defined in the todo_list module on this object. To demonstrate this, we will add an item to the list. First, we will add just one item, and in the second transaction we will add 3 and remove another one. Double check that you have variables set up from the previous step , and then add one more variable for the list object. $ export LIST_ID=0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553 Now we can construct the transaction to add an item to the list. The command will look like this: $ sui client ptb \\\n--gas-budget 100000000 \\\n--move-call $PACKAGE_ID::todo_list::add @$LIST_ID \"'Finish the Hello, Sui chapter'\" In this command, we are calling the add function in the todo_list package. The function takes two arguments: the list object and the item to add. The item is a string, so we need to wrap it in single quotes. The command will add the item to the list. If everything is correct, you should see the output similar to the one we had in previous sections. Now you can check the list object to see if the item was added. $ sui client object $LIST_ID The output should contain the item that you have added. ╭───────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n│ objectId │ 0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553 │\n│ version │ 24 │\n│ digest │ FGcXH8MGpMs5BdTnC62CQ3VLAwwexYg2id5DKU7Jr9aQ │\n│ objType │ 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList │\n│ owner │ ╭──────────────┬──────────────────────────────────────────────────────────────────────╮ │\n│ │ │ AddressOwner │ 0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1 │ │\n│ │ ╰──────────────┴──────────────────────────────────────────────────────────────────────╯ │\n│ prevTx │ EJVK6FEHtfTdCuGkNsU1HcrmUBEN6H6jshfcptnw8Yt1 │\n│ storageRebate │ 1558000 │\n│ content │ ╭───────────────────┬───────────────────────────────────────────────────────────────────────────────────────────╮ │\n│ │ │ dataType │ moveObject │ │\n│ │ │ type │ 0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList │ │\n│ │ │ hasPublicTransfer │ true │ │\n│ │ │ fields │ ╭───────┬───────────────────────────────────────────────────────────────────────────────╮ │ │\n│ │ │ │ │ id │ ╭────┬──────────────────────────────────────────────────────────────────────╮ │ │ │\n│ │ │ │ │ │ │ id │ 0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553 │ │ │ │\n│ │ │ │ │ │ ╰────┴──────────────────────────────────────────────────────────────────────╯ │ │ │\n│ │ │ │ │ items │ ╭─────────────────────────────────╮ │ │ │\n│ │ │ │ │ │ │ finish the Hello, Sui chapter │ │ │ │\n│ │ │ │ │ │ ╰─────────────────────────────────╯ │ │ │\n│ │ │ │ ╰───────┴───────────────────────────────────────────────────────────────────────────────╯ │ │\n│ │ ╰───────────────────┴───────────────────────────────────────────────────────────────────────────────────────────╯ │\n╰───────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ A JSON representation of the object can be obtained by adding the --json flag to the command. $ sui client object $LIST_ID --json { \"objectId\": \"0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553\", \"version\": \"24\", \"digest\": \"FGcXH8MGpMs5BdTnC62CQ3VLAwwexYg2id5DKU7Jr9aQ\", \"type\": \"0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList\", \"owner\": { \"AddressOwner\": \"0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1\" }, \"previousTransaction\": \"EJVK6FEHtfTdCuGkNsU1HcrmUBEN6H6jshfcptnw8Yt1\", \"storageRebate\": \"1558000\", \"content\": { \"dataType\": \"moveObject\", \"type\": \"0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList\", \"hasPublicTransfer\": true, \"fields\": { \"id\": { \"id\": \"0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553\" }, \"items\": [\"Finish the Hello, Sui chapter\"] } }\n}","breadcrumbs":"Hello, Sui! » Passing Objects to Functions","id":"43","title":"Passing Objects to Functions"},"44":{"body":"You can chain multiple commands in a single transaction. This shows the power of Transaction Blocks! Using the same list object, we will add three more items and remove one. The command will look like this: $ sui client ptb \\\n--gas-budget 100000000 \\\n--move-call $PACKAGE_ID::todo_list::add @$LIST_ID \"'Finish Concepts chapter'\" \\\n--move-call $PACKAGE_ID::todo_list::add @$LIST_ID \"'Read the Move Basics chapter'\" \\\n--move-call $PACKAGE_ID::todo_list::add @$LIST_ID \"'Learn about Object Model'\" \\\n--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 0 If previous commands were successful, this one should not be any different. You can check the list object to see if the items were added and removed. The JSON representation is a bit more readable! sui client object $LIST_ID --json { \"objectId\": \"0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553\", \"version\": \"25\", \"digest\": \"EDTXDsteqPGAGu4zFAj5bbQGTkucWk4hhuUquk39enGA\", \"type\": \"0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList\", \"owner\": { \"AddressOwner\": \"0x091ef55506ad814920adcef32045f9078f2f6e9a72f4cf253a1e6274157380a1\" }, \"previousTransaction\": \"7SXLGBSh31jv8G7okQ9mEgnw5MnTfvzzHEHpWf3Sa9gY\", \"storageRebate\": \"1922800\", \"content\": { \"dataType\": \"moveObject\", \"type\": \"0x468daa33dfcb3e17162bbc8928f6ec73744bb08d838d1b6eb94eac99269b29fe::todo_list::TodoList\", \"hasPublicTransfer\": true, \"fields\": { \"id\": { \"id\": \"0x20e0bede16de8a728ab25e228816b9059b45ebea49c8ad384e044580b2d3e553\" }, \"items\": [ \"Finish Concepts chapter\", \"Read the Move Basics chapter\", \"Learn about Object Model\" ] } }\n} Commands don't have to be in the same package or operate on the same object. Within a single transaction block, you can interact with multiple packages and objects. This is a powerful feature that allows you to build complex interactions on-chain!","breadcrumbs":"Hello, Sui! » Chaining Commands","id":"44","title":"Chaining Commands"},"45":{"body":"In this guide, we have shown how to publish a package on the Move blockchain and interact with it using the Sui CLI. We have demonstrated how to create a new list object, add items to it, and remove them. We have also shown how to chain multiple commands in a single transaction block. This guide should give you a good starting point for building your own applications on the Sui blockchain!","breadcrumbs":"Hello, Sui! » Conclusion","id":"45","title":"Conclusion"},"46":{"body":"In this chapter you will learn about the basic concepts of Sui and Move. You will learn what is a package, how to interact with it, what is an account and a transaction, and how data is stored on Sui. While this chapter is not a complete reference, and you should refer to the Sui Documentation for that, it will give you a good understanding of the basic concepts required to write Move programs on Sui.","breadcrumbs":"Concepts » Concepts","id":"46","title":"Concepts"},"47":{"body":"Move is a language for writing smart contracts - programs that are stored and run on the blockchain. A single program is organized into a package. A package is published on the blockchain and is identified by an address . A published package can be interacted with by sending transactions calling its functions. It can also act as a dependency for other packages. To create a new package, use the sui move new command. To learn more about the command, run sui move new --help. Package consists of modules - separate scopes that contain functions, types, and other items. package 0x... module a struct A1 fun hello_world() module b struct B1 fun hello_package()","breadcrumbs":"Concepts » Package » Package","id":"47","title":"Package"},"48":{"body":"Locally, a package is a directory with a Move.toml file and a sources directory. The Move.toml file - called the \"package manifest\" - contains metadata about the package, and the sources directory contains the source code for the modules. Package usually looks like this: sources/ my_module.move another_module.move ...\ntests/ ...\nexamples/ using_my_module.move\nMove.toml The tests directory is optional and contains tests for the package. Code placed into the tests directory is not published on-chain and is only availably in tests. The examples directory can be used for code examples, and is also not published on-chain.","breadcrumbs":"Concepts » Package » Package Structure","id":"48","title":"Package Structure"},"49":{"body":"During development, package doesn't have an address and it needs to be set to 0x0. Once a package is published, it gets a single unique address on the blockchain containing its modules' bytecode. A published package becomes immutable and can be interacted with by sending transactions. 0x... my_module: another_module: ","breadcrumbs":"Concepts » Package » Published Package","id":"49","title":"Published Package"},"5":{"body":"You can install Sui using the Homebrew package manager. brew install sui","breadcrumbs":"Before we begin » Install Sui » Install using Homebrew (MacOS)","id":"5","title":"Install using Homebrew (MacOS)"},"50":{"body":"Package Manifest Address Packages in the Move Reference.","breadcrumbs":"Concepts » Package » Links","id":"50","title":"Links"},"51":{"body":"The Move.toml is a manifest file that describes the package and its dependencies. It is written in TOML format and contains multiple sections, the most important of which are [package], [dependencies] and [addresses]. [package]\nname = \"my_project\"\nversion = \"0.0.0\"\nedition = \"2024\" [dependencies]\nSui = { git = \"https://github.com/MystenLabs/sui.git\", subdir = \"crates/sui-framework/packages/sui-framework\", rev = \"framework/testnet\" } [addresses]\nstd = \"0x1\"\nalice = \"0xA11CE\" [dev-addresses]\nalice = \"0xB0B\"","breadcrumbs":"Concepts » Manifest » Package Manifest","id":"51","title":"Package Manifest"},"52":{"body":"","breadcrumbs":"Concepts » Manifest » Sections","id":"52","title":"Sections"},"53":{"body":"The [package] section is used to describe the package. None of the fields in this section are published on chain, but they are used in tooling and release management; they also specify the Move edition for the compiler. name - the name of the package when it is imported; version - the version of the package, can be used in release management; edition - the edition of the Move language; currently, the only valid value is 2024.","breadcrumbs":"Concepts » Manifest » Package","id":"53","title":"Package"},"54":{"body":"The [dependencies] section is used to specify the dependencies of the project. Each dependency is specified as a key-value pair, where the key is the name of the dependency, and the value is the dependency specification. The dependency specification can be a git repository URL or a path to the local directory. # git repository\nSui = { git = \"https://github.com/MystenLabs/sui.git\", subdir = \"crates/sui-framework/packages/sui-framework\", rev = \"framework/testnet\" } # local directory\nMyPackage = { local = \"../my-package\" } Packages also import addresses from other packages. For example, the Sui dependency adds the std and sui addresses to the project. These addresses can be used in the code as aliases for the addresses.","breadcrumbs":"Concepts » Manifest » Dependencies","id":"54","title":"Dependencies"},"55":{"body":"Sometimes dependencies have conflicting versions of the same package. For example, if you have two dependencies that use different versions of the Sui package, you can override the dependency in the [dependencies] section. To do so, add the override field to the dependency. The version of the dependency specified in the [dependencies] section will be used instead of the one specified in the dependency itself. [dependencies]\nSui = { override = true, git = \"https://github.com/MystenLabs/sui.git\", subdir = \"crates/sui-framework/packages/sui-framework\", rev = \"framework/testnet\" }","breadcrumbs":"Concepts » Manifest » Resolving version conflicts with override","id":"55","title":"Resolving version conflicts with override"},"56":{"body":"It is possible to add [dev-dependencies] section to the manifest. It is used to override dependencies in the dev and test modes. For example, if you want to use a different version of the Sui package in the dev mode, you can add a custom dependency specification to the [dev-dependencies] section.","breadcrumbs":"Concepts » Manifest » Dev-dependencies","id":"56","title":"Dev-dependencies"},"57":{"body":"The [addresses] section is used to add aliases for the addresses. Any address can be specified in this section, and then used in the code as an alias. For example, if you add alice = \"0xA11CE\" to this section, you can use alice as 0xA11CE in the code.","breadcrumbs":"Concepts » Manifest » Addresses","id":"57","title":"Addresses"},"58":{"body":"The [dev-addresses] section is the same as [addresses], but only works for the test and dev modes. Important to note that it is impossible to introduce new aliases in this section, only override the existing ones. So in the example above, if you add alice = \"0xB0B\" to this section, the alice address will be 0xB0B in the test and dev modes, and 0xA11CE in the regular build.","breadcrumbs":"Concepts » Manifest » Dev-addresses","id":"58","title":"Dev-addresses"},"59":{"body":"The TOML format supports two styles for tables: inline and multiline. The examples above are using the inline style, but it is also possible to use the multiline style. You wouldn't want to use it for the [package] section, but it can be useful for the dependencies. # Inline style\n[dependencies]\nSui = { override = true, git = \"\", subdir = \"crates/sui-framework/packages/sui-framework\", rev = \"framework/testnet\" }\nMyPackage = { local = \"../my-package\" } # Multiline style\n[dependencies.Sui]\noverride = true\ngit = \"https://github.com/MystenLabs/sui.git\"\nsubdir = \"crates/sui-framework/packages/sui-framework\"\nrev = \"framework/testnet\" [dependencies.MyPackage]\nlocal = \"../my-package\"","breadcrumbs":"Concepts » Manifest » TOML styles","id":"59","title":"TOML styles"},"6":{"body":"You can install Sui using the Chocolatey package manager for Windows. choco install sui","breadcrumbs":"Before we begin » Install Sui » Install using Chocolatey (Windows)","id":"6","title":"Install using Chocolatey (Windows)"},"60":{"body":"Packages in the Move Reference.","breadcrumbs":"Concepts » Manifest » Further Reading","id":"60","title":"Further Reading"},"61":{"body":"Address is a unique identifier of a location on the blockchain. It is used to identify packages , accounts , and objects . Address has a fixed size of 32 bytes and is usually represented as a hexadecimal string prefixed with 0x. Addresses are case insensitive. 0xe51ff5cd221a81c3d6e22b9e670ddf99004d71de4f769b0312b68c7c4872e2f1 The address above is an example of a valid address. It is 64 characters long (32 bytes) and prefixed with 0x. Sui also has reserved addresses that are used to identify standard packages and objects. Reserved addresses are typically simple values that are easy to remember and type. For example, the address of the Standard Library is 0x1. Addresses, shorter than 32 bytes, are padded with zeros to the left. 0x1 = 0x0000000000000000000000000000000000000000000000000000000000000001 Here are some examples of reserved addresses: 0x1 - address of the Sui Standard Library (alias std) 0x2 - address of the Sui Framework (alias sui) 0x6 - address of the system Clock object You can find all reserved addresses in the Appendix B .","breadcrumbs":"Concepts » Address » Address","id":"61","title":"Address"},"62":{"body":"Address type in Move","breadcrumbs":"Concepts » Address » Further reading","id":"62","title":"Further reading"},"63":{"body":"An account is a way to identify a user. An account is generated from a private key, and is identified by an address. An account can own objects, and can send transactions. Every transaction has a sender, and the sender is identified by an address . Sui supports multiple cryptographic algorithms for account generation. The two supported curves are ed25519, secp256k1, and there is also a special way of generating an account - zklogin. The cryptographic agility - the unique feature of Sui - allows for flexibility in the account generation.","breadcrumbs":"Concepts » Account » Account","id":"63","title":"Account"},"64":{"body":"Cryptography in Sui in the Sui Blog Keys and Addresses in the Sui Docs Signatures in the Sui Docs","breadcrumbs":"Concepts » Account » Further Reading","id":"64","title":"Further Reading"},"65":{"body":"Transaction is a fundamental concept in the blockchain world. It is a way to interact with a blockchain. Transactions are used to change the state of the blockchain, and they are the only way to do so. In Move, transactions are used to call functions in a package, deploy new packages, and upgrade existing ones.","breadcrumbs":"Concepts » Transaction » Transaction","id":"65","title":"Transaction"},"66":{"body":"Every transaction explicitly specifies the objects it operates on! Transactions consist of: a sender - the account that signs the transaction; a list (or a chain) of commands - the operations to be executed; command inputs - the arguments for the commands: either pure - simple values like numbers or strings, or object - objects that the transaction will access; a gas object - the Coin object used to pay for the transaction; gas price and budget - the cost of the transaction;","breadcrumbs":"Concepts » Transaction » Transaction Structure","id":"66","title":"Transaction Structure"},"67":{"body":"Transaction inputs are the arguments for the transaction and are split between 2 types: Pure arguments: These are mostly primitive types with some extra additions. A pure argument can be: bool . unsigned integer (u8, u16, u32, u64, u128, u256). address . std::string::String , UTF8 strings. std::ascii::String , ASCII strings. vector , where T is a pure type. std::option::Option , where T is a pure type. std::object::ID , typically points to an object. See also What is an Object . Object arguments: These are objects or references of objects that the transaction will access. An object argument needs to be either a shared object, a frozen object, or an object that the transaction sender owns, in order for the transaction to be successfull. For more see Object Model .","breadcrumbs":"Concepts » Transaction » Inputs","id":"67","title":"Inputs"},"68":{"body":"Sui transactions may consist of multiple commands. Each command is a single built-in command (like publishing a package) or a call to a function in an already published package. The commands are executed in the order they are listed in the transaction, and they can use the results of the previous commands, forming a chain. Transaction either succeeds or fails as a whole. Schematically, a transaction looks like this (in pseudo-code): Inputs:\n- sender = 0xa11ce Commands:\n- payment = SplitCoins(Gas, [ 1000 ])\n- item = MoveCall(0xAAA::market::purchase, [ payment ])\n- TransferObjects(item, sender) In this example, the transaction consists of three commands: SplitCoins - a built-in command that splits a new coin from the passed object, in this case, the Gas object; MoveCall - a command that calls a function purchase in a package 0xAAA, module market with the given arguments - the payment object; TransferObjects - a built-in command that transfers the object to the recipient.","breadcrumbs":"Concepts » Transaction » Commands","id":"68","title":"Commands"},"69":{"body":"Transaction effects are the changes that a transaction makes to the blockchain state. More specifically, a transaction can change the state in the following ways: use the gas object to pay for the transaction; create, update, or delete objects; emit events; The result of the executed transaction consists of different parts: Transaction Digest - the hash of the transaction which is used to identify the transaction; Transaction Data - the inputs, commands and gas object used in the transaction; Transaction Effects - the status and the \"effects\" of the transaction, more specifically: the status of the transaction, updates to objects and their new versions, the gas object used, the gas cost of the transaction, and the events emitted by the transaction; Events - the custom events emitted by the transaction; Object Changes - the changes made to the objects, including the change of ownership ; Balance Changes - the changes made to the aggregate balances of the account involved in the transaction;","breadcrumbs":"Concepts » Transaction » Transaction Effects","id":"69","title":"Transaction Effects"},"7":{"body":"You can install and build Sui locally by using the Cargo package manager (requires Rust) cargo install --git https://github.com/MystenLabs/sui.git --bin sui --branch mainnet Make sure that your system has the latest Rust versions with the command below. rustup update stable","breadcrumbs":"Before we begin » Install Sui » Build using Cargo (MacOS, Linux)","id":"7","title":"Build using Cargo (MacOS, Linux)"},"70":{"body":"This chapter is all about the basic syntax of the Move language. It covers the basics of the language, such as types, modules, functions, and control flow. It focuses on the language without a storage model or a blockchain, and explains the essential concepts of the language. To learn features specific to Sui, such as storage functions and abilities, refer to the Using Objects chapter, however, it is recommended to start with this chapter first.","breadcrumbs":"Move Basics » Move Basics","id":"70","title":"Move Basics"},"71":{"body":"Module is the base unit of code organization in Move. Modules are used to group and isolate code, and all of the members of the module are private to the module by default. In this section you will learn how to define a module, how to declare its members and how to access them from other modules.","breadcrumbs":"Move Basics » Module » Module","id":"71","title":"Module"},"72":{"body":"Modules are declared using the module keyword followed by the package address, module name and the module body inside the curly braces {}. The module name should be in snake_case - all lowercase letters with underscores between words. Modules names must be unique in the package. Usually, a single file in the sources/ folder contains a single module. The file name should match the module name - for example, a donut_shop module should be stored in the donut_shop.move file. You can read more about coding conventions in the Coding Conventions section. Structs, functions, constants and imports all part of the module: Structs Functions Constants Imports Struct Methods","breadcrumbs":"Move Basics » Module » Module declaration","id":"72","title":"Module declaration"},"73":{"body":"Module address can be specified as both: an address literal (does not require the @ prefix) or a named address specified in the Package Manifest . In the example below, both are identical because there's a book = \"0x0\" record in the [addresses] section of the Move.toml. module 0x0::address_literal { /* ... */ }\nmodule book::named_address { /* ... */ } Addresses section in the Move.toml: # Move.toml\n[addresses]\nbook = \"0x0\"","breadcrumbs":"Move Basics » Module » Address / Named address","id":"73","title":"Address / Named address"},"74":{"body":"Module members are declared inside the module body. To illustrate that, let's define a simple module with a struct, a function and a constant: module book::my_block_module_with_members { // import use book::my_module; // a constant const CONST: u8 = 0; // a struct public struct Struct {} // method alias public use fun function as Struct.struct_fun; // function fun function(_: &Struct) { /* function body */ }\n} // module block allows multiple module definitions in the\n// same file but this is not a recommended practice\nmodule book::another_module_in_the_file { // ...\n}","breadcrumbs":"Move Basics » Module » Module members","id":"74","title":"Module members"},"75":{"body":"Modules in the Move Reference.","breadcrumbs":"Move Basics » Module » Further reading","id":"75","title":"Further reading"},"76":{"body":"Comments are a way to add notes or document your code. They are ignored by the compiler and don't result in the Move bytecode. You can use comments to explain what your code does, to add notes to yourself or other developers, to temporarily remove a part of your code, or to generate documentation. There are three types of comments in Move: line comment, block comment, and doc comment.","breadcrumbs":"Move Basics » Comments » Comments","id":"76","title":"Comments"},"77":{"body":"{{#include ../../../packages/samples/sources/move-basics/comments.move:line}} You can use double slash // to comment out the rest of the line. Everything after // will be ignored by the compiler. {{#include ../../../packages/samples/sources/move-basics/comments.move:line_2}}","breadcrumbs":"Move Basics » Comments » Line comment","id":"77","title":"Line comment"},"78":{"body":"Block comments are used to comment out a block of code. They start with /* and end with */. Everything between /* and */ will be ignored by the compiler. You can use block comments to comment out a single line or multiple lines. You can even use them to comment out a part of a line. {{#include ../../../packages/samples/sources/move-basics/comments.move:block}} This example is a bit extreme, but it shows how you can use block comments to comment out a part of a line.","breadcrumbs":"Move Basics » Comments » Block comment","id":"78","title":"Block comment"},"79":{"body":"Documentation comments are special comments that are used to generate documentation for your code. They are similar to block comments, but they start with three slashes /// and are placed before the definition of the item they document. {{#include ../../../packages/samples/sources/move-basics/comments.move:doc}}","breadcrumbs":"Move Basics » Comments » Doc comment","id":"79","title":"Doc comment"},"8":{"body":"For troubleshooting the installation process, please refer to the Install Sui Guide.","breadcrumbs":"Before we begin » Install Sui » Troubleshooting","id":"8","title":"Troubleshooting"},"80":{"body":"For simple values, Move has a number of built-in primitive types. They're the base that makes up all other types. The primitive types are: Booleans Unsigned Integers Address - covered in the next section However, before we get to the types, let's first look at how to declare and assign variables in Move.","breadcrumbs":"Move Basics » Primitive Types » Primitive Types","id":"80","title":"Primitive Types"},"81":{"body":"Variables are declared using the let keyword. They are immutable by default, but can be made mutable using the let mut keyword. The syntax for the let mut statement is: let [: ] = ;\nlet mut [: ] = ; Where: - the name of the variable - the type of the variable, optional