Learn everything Sui Blockchain and the way Move language is used in it i.e. "Sui Move".
The same knowledge can be replicated for other Move based blockchains with little tweaking.
- Sui is a blockchain network which uses Move language.
- Foundation name: Mysten Labs
flowchart TB
A[Sui Move] --created by--> B[Mysten Labs]
A --security focused by--> C[MoveBit]
- Sui was founded by Mysten Labs. Its five co-founders, as well as several Mysten employees, worked on developing the Diem blockchain and Move programming language. Four of the five co-founders worked at Facebook but left due to the strict regulatory restrictions in place and decided to work on their own blockchain.
- There has been some confusion that Mysten Labs is affiliated with Aptos, another Move-based blockchain, but the team has clarified that there is no relationship between the two projects except the fact that both teams have had prior experience at Facebook’s blockchain developments.
- Eventual consistency is the consensus model employed by Sui; if one honest validator certifies the transaction, all of the other honest validators will too eventually.
Almost by every blockchains that include finality.
- Instant finality for simple transactions (i.e. transactions that only involve owned objects) is achieved through multi-lane processing, which allows transactions to be processed in parallel. This means that the client receives confirmation of finality immediately, effectively removing the concept of a block time. Complex transactions (i.e. transactions that involve shared objects) require consensus and thus take longer (2-3s) to confirm.
- Sui provides these benefits to developers:
- Sui Move and object-centric data model (enables composable objects/NFTs)
- Asset-centric programming model
- Easier developer experience
- Traditional blockchains totally order all client transactions with respect to each other. This design requires reaching consensus across validators, which is effective but slow.
- Cons: Building an efficient synchronizer is harder in Sui than in traditional blockchains. The synchronizer sub-protocol allows validators to update each other by sharing data, and it allows slow validators to catch up. Building an efficient synchronizer for traditional blockchains is no easy task, but still simpler than in Sui.
-
Install prerequisites for your operating system (macOS).
-
cargo install --locked --git https://github.com/MystenLabs/sui.git --branch devnet sui
Check if the installation was successful:
❯ sui sui 1.5.0-5f0704aee Mysten Labs <build@mystenlabs.com> A Byzantine fault tolerant chain with low-latency finality and high throughput USAGE: sui <SUBCOMMAND> OPTIONS: -h, --help Print help information -V, --version Print version information SUBCOMMANDS: client Client for interacting with the Sui network console Start Sui interactive console fire-drill Tool for Fire Drill genesis Bootstrap and initialize a new sui network genesis-ceremony help Print this message or the help of the given subcommand(s) keytool Sui keystore tool move Tool to build and test Move applications network start Start sui network validator A tool for validators and validator candidates
NOTE:
devnet
is the default network. You can installtestnet
ormainnet
as well.To update to the latest stable version of Rust:
rustup update stable
-
Request SUI test tokens to use on Sui Devnet or Sui Testnet networks.
❯ curl --location --request POST 'https://faucet.devnet.sui.io/gas' \ --header 'Content-Type: application/json' \ --data-raw '{ "FixedAmountRequest": { "recipient": "0x7e86320466e04f808a5e046a65d1458b3a28def339ea9986e5874496ef66bd08" } }'
Just change the address:
0x...
to your address.
VSCode extensions (updated frequently):
- sui-move-analyzer
Dependent on move-analyzer
NOTE: Keep this in mind.
Watch this video to see the demo.
Refer this
# Build all module in a package with "Move.toml"
sui move build
# run all tests in the directory containing `Move.toml`
sui move test
# ignore compile warnings & continue to run tests
sui move test --ignore_compile_warnings
$ sui client publish --gas-budget 1000000
$ sui client call --function <function_name> --module <module_name> --package <package-address> --args <arg1> <arg2>... <argn> --gas-budget 1000000
At its core, Sui is a distributed ledger that stores a collection of programmable objects that each has a globally unique ID. Each object (owned or shared) is owned by a (at least) single address, and each address can own an arbitrary number of objects.
At a high-level, the Sui blockchain achieves great speed and scalability through optimization for simple transactions. This is done by making each transaction idempotent, meaning that transactions retain its end state no matter how many times it is repeated. This is especially important in blockchain and payment systems to protect from duplicated transactions, which may result in a recipient receiving a payment twice, for example.
Each transaction can be of 2 types:
- user-to-user
- asset manipulation
Asset/object types:
- Owned: can only be manipulated by the owner
- Shared: can be manipulated by more than one user
Simple transaction: a transaction that only involves owned objects gives the sender immediate confirmation of finality. Complex transaction: a transaction that involves shared objects requires consensus and thus takes longer (2-3s) to confirm.
Sui uses this distinction to forgo consensus for owned objects, resulting in very low latency for simple transactions since the client receives confirmation of finality immediately, effectively removing the concept of a block time. This is achieved via multi-lane processing, which eliminates the need for transactions to wait for completion of a previous transaction. However, this also means that a sender can only send one transaction at a time.
On the other hand, Sui uses the Narwhal and Tusk DAG-based mempool and efficient BFT consensus for more complex transactions involving shared objects.
-
Block explorers:
-
Block time: 0.55s
-
Finality time: 2-3s for complex (with shared objects) transactions. Immediate for simple transactions (with owned objects).
-
TPS: 297k. Source
-
parallel processing
-
Chain type: L1
-
Consensus algorithm: Proof of Stake (PoS) (with BFT). Precisely Narwhal and partially synchronous Bullshark. Validators on the Sui network approve and execute transactions in parallel using Byzantine Consistent Broadcast with high throughput.
-
Node types: full, light
-
Mainnet
- chain ID:
- rest_url: "https://fullnode.mainnet.sui.io:443"
- faucet:
-
Testnet
- chain ID:
- rest_url: "https://fullnode.testnet.sui.io:443"
-
Devnet
- chain ID: c42d763f
- rest_url: "https://fullnode.devnet.sui.io:443"
- faucet_url: "https://faucet.devnet.sui.io/gas"
curl --location --request POST 'https://faucet.devnet.sui.io/gas' \ --header 'Content-Type: application/json' \ --data-raw '{ "FixedAmountRequest": { "recipient": "<YOUR SUI ADDRESS>" } }'
-
Wallet: Petra
- keypair: public key (base64 format), address (hexadecimal format), secret recovery phrase (12 words by default, can be 12/15/18/24) are generated via
keytool
.
A transaction in Sui is a change to the blockchain. This may be a simple transaction affecting only single-writer, single-address objects, such as minting an NFT or transferring an NFT or another token. These transactions may bypass the consensus protocol in Sui.
More complex transactions affecting objects that are shared or owned by multiple addresses, such as asset management and other DeFi use cases, go through the Narwhal and Bullshark DAG-based mempool and efficient Byzantine Fault Tolerant (BFT) consensus.
Transaction lifecycle:
- Validate the transaction (signature verification with the original message)
- If the checks pass, the validator locks all owned-objects and signs the transaction bytes.
- It then returns the signature (transaction hash) to the client.
- The client repeats this process with multiple validators until it has collected signatures on its transaction from a quorum (majority i.e. 2/3), thereby forming a certificate.
Note that the process of collecting validator signatures on a transaction into a certificate and the process of submitting certificates can be performed in parallel. The client can simultaneously multicast transactions/certificates to an arbitrary number of validators.
Alternatively, a client can outsource either or both of these tasks to a third-party service provider. This provider must be trusted for liveness (e.g., it can refuse to form a certificate), but not for safety (e.g., it cannot change the effects of the transaction, and does not need the user's secret key).
More on certificates.
Transactions take objects as input and produce objects as output. More on objects.
causal order vs Total order: This is mainly related to transaction sequencing. Here, if T1 --produces--> O1, T2 --produces--> O2, then T1 --precedes--> T2. This is called total order. But, most of the transactions are ordered causally (when there is no dependency between them).
In summary, for simple transactions (owned objects), causal order is used and for complex transactions (shared objects), total order is used.
A smart contract is an object (called a Sui Move package), and these smart contracts manipulate objects on the Sui network:
- Sui Move Package
- Sui Move Module
- Sui Move Object
- Sui Move Module
graph TB
A[Package] --> B[Module-1]
A[Package] --> C[Module-2]
A[Package] --> D[Module-3]
B[Module-1] --> E[Object-1]
B[Module-1] --> F[Object-2]
B[Module-1] --> G[Object-3]
C[Module-2] --> H[Object-4]
C[Module-2] --> I[Object-5]
D[Module-3] --> J[Object-6]
Here, Package
is deployed at an address during $ sui client publish --gas-budget <amount>
.
- A package can have multiple modules (in bytecode).
- A package when published is immutable can never be changed or removed.
- A package object can depend on other package objects that were previously published to the Sui ledger.
- Each package has an unique ID.
- Each module name has a unique name.
- A module can have multiple functions.
- Sui Move module and package naming convention uses snake casing, i.e. this_is_snake_casing.
- A Sui module name uses the Rust path separator
::
to divide the package name and the module name, examples:unit_one::hello_world
-hello_world
module inunit_one
packagecapy::capy
-capy
module incapy
package
- Move: Coin (ERC20 equivalent) for supported blockchains.
- Deployed Packages:
- 0x1:
std
- 0x2:
sui
0x2::coin::Coin
Here,
coin
is the module name andCoin
is the struct name which has object ID.- Precisely, it's
0x2::coin::Coin<T>
whereT
is the generic type. So, creating your own coin is a charm on Sui.
- Precisely, it's
0x2::clock::Clock
Here, clock is the module name and Clock is the struct name which has object ID
0x6
; no new instances can be created. More❯ sui client call --package 0x2 --module clock --function 'access' --args '0x6' --gas-budget 100000
CLI command to get the latest network timestamp. Assume a 2-3s of delay due to finality.
0x2::event::emit()
Here, event is the module name and emit is the function name for emitting the event.
- 0x1:
-
In Sui/Aptos move, we have
CTRUD
operation over traditionalCRUD
.T
stands forTransfer
. -
Traditionally, the storage is in form of key-value pair, but in Sui/Aptos, we have objects that is stored in global storage and an address (Id) is assigned to it.
-
This object are stored in global storage & an address is assigned to.
-
In Sui, every object must have an owner. The owner can be either an address, another object, or "shared". For more, read object ownership
-
Basic unit: object, unlike other blockchains where the storage is key-value based.
-
Sui Move Object: typed data governed by a particular Sui Move module from a Sui Move package.
-
Each object value is a struct with fields that can contain primitive types (e.g. integers, addresses), other objects, and non-object structs. Each object can be:
- mutable:
- owned by an address (during creation)
- owned by another object
- shared publicly, not owned by any address or object
This is like traditional data sitting in the SC
- immutable
- mutable:
-
Unlike traditional smart contracts, where there the data sits on the contract itself, in Sui, the object can be taken out of the module during transfer. E.g.
transfer
vspublic_transfer
functions. We don't have to worry about Access Control in Sui unlike other blockchains. If we don't want to give unique ownership, just make theobject
as shared. And if we don't want to update the object, make it immutable. -
A Move
struct
has abilities:key
,drop
,store
,copy
. -
Objects in core, Aptos, and Sui Move are used to represent assets like USDT, the administrator permissions to a smart contract, chips in an on-chain poker game, and any other data that the smart contract is tracking.
-
Object
UID
(Unique Identifier) are supposed to be globally unique. They are- created through
object::new(&mut TxContext)
and - destroyed through
object::delete(UID)
.
- created through
-
An object at its core is just a collection of related data represented as a
struct
in Move. -
On Sui, in order for a Move object to be stored on-chain under an account, it must be also be a Sui object. Sui objects have the key ability (since they are stored on-chain) and a special first member of the struct named id with type
UID
. A bytecode verifier ensures that any struct with the key ability has the special first member. -
Object can have sub-objects like this:
/// A custom sui object. Every object must have the `key` attribute /// (indicating that it is allowed to be a key in the sui global object /// pool), and must have a field `id: UID` corresponding to its sui ObjId. /// Other object attributes present at the protocol level (authenticator, /// sequence number, TxDigest, ...) are intentionally not exposed here. struct Object has key { id: UID, /// Custom objects can have fields of arbitrary type... custom_field: u64, /// ... including other objects child_obj: ChildObject, /// ... and other global objects nested_obj: AnotherObject, } /// An object that can be stored inside global objects or other child /// objects, but cannot be placed in the global object pool on its own. /// Note that it doesn't need an ID field. struct ChildObject has store { a_field: bool, } /// An object that can live either in the global object pool or as a nested /// object. struct AnotherObject has key, store { id: UID, }
NOTE: look at the 2 different types of sub-objects -
ChildObject
andNestedObject
that has no key & has key ability respectively. But, all of them hasstore
ability. -
Abilities:
-
key
makes an object able to be stored on-chain on its own & can be put inside another object as nested object. If an object does not have key, it has to be stored under another object or destroyed before the contract finishes execution. -
store
allows an object to be stored under another object. Think of structs like boxes, and store allows this particular box to fit inside other boxes.NOTE:
store
is required for data persistence across modules. If an object hasstore
ability, the ownership can be transferred to another address via shifting from existing module to another module.In this image 🔝, the 1st one is the object (containing
key
ability) and the 2nd one is considered as thestruct
only (having nokey
ability). Here, the 2nd one doesn't need an UID, as it is a child object (onlykey
) of 1st one and doesn't need to be nested object (key
+store
). -
copy
allows an object to be copied. Non-copyable objects can’t be copied, but manual “copies” can theoretically still be created by the creating contract. For instance, fungible tokens can't be copied, but transferred only. -
drop
allows an object to be quietly destroyed. Simply allowing the object to go out of scope will destroy it.
-
-
A struct with
key
is just meant to act as template to create an object. And if it haskey
+drop
ability, it can be destroyed as it goes out of scope block. There are so many combinations with different abilities put together. Let's see them one by one:flowchart LR A[struct with `key`] --a template used to create an object--> B[object] B --during transfer via `transfer` to transfer the object--> C[address/object]
flowchart LR A[struct with `key` + `store`] --a template used to create an object--> B[object] B --during transfer via `public_transfer` to transfer the object outside of its module in terms of storage--> C[address/object]
flowchart LR A[struct with `key` + `drop`] --a template used to create an object & then destroyed when goes out of scope block--> B[object]
flowchart LR A[struct with `copy` + `drop`] --a template used for event--> B[Event]
flowchart LR A[struct with `store`] --can be child object of another--> B[object]
-
Immutable using
transfer::freeze_object(obj: key);
,transfer::public_freeze_object(obj: key+store);
. -
Shared using
transfer::share_object(obj: key);
,transfer::public_share_object(obj: key + store
. -
Aptos vs Sui:
-
TxContext
- In Aptos, the signer of a transaction is directly received as an argument passed into the function that was called from outside of Move. In Sui the TxContext type can be declared as an argument of a function just like you would with signer, and the signer can be obtained from TxContext using a library function. Also, TxContext is used to create UIDs in Sui Move. As of this writing, TxContext is only used for those purposes.
-
Global storage and the acquires keyword
- Aptos uses the global storage operators (move_to, move_from, etc.) from core Move. Functions that use global storage must have the appropriate acquires annotation. Unsurprisingly, acquires keyword is not used in Sui, along with the global storage operators since Sui does not use the Move global storage.
-
The
init(&mut TxContext)
function- Aptos supports an initialization function called upon module deploy with a similar signature. Sui will call the init function declared with this signature upon module deploy.
-
transfer::transfer(object, recipient)
is to transfer the ownership of an object (withkey
ability only) to another address. Here, the object remains stored in the same module itself.transfer::public_transfer(object, recipient)
is to transfer the ownership of an object (withkey
+store
abilities) to another address. Here, the object is taken out of the existing module.
For more, view this transfer.move
file.
- Visibility:
private
: only functions within modules can use,public
: callable from other modules,public(friend)
: callable from modules explicitly declared as friends (in the module declaration). sort of likeinternal
in solidity.
- The return type of a function is specified in the function signature after the function parameters, separated by a colon.
- A function's last line (of execution) without a semicolon is the return value; like in Rust.
- Entry functions typically have an instance of
TxContext
as the last parameter. This is a special parameter set by the Sui Move VM, and does not need to be specified by the user calling the function. - Entry functions:
public entry
: callable from any module/account in a transaction. This is similar toexternal
in solidity,extrinsic
(#[call_index] #[weight]
) in substrate.- Denoted by the keyword
entry
- returns nothing.
- (optional) have a mutable reference to an instance of the
TxContext
type as the last parameter.
-
We don't need
store
ability for events. -
Sample code:
/// Emitted when a new pool is created struct PoolCreated has copy, drop { /// object ID of the newly created pool pool_id: ID, token_x_name: TypeName, token_y_name: TypeName, }
// Error codes
/// The `sender` and `recipient` of the two escrowed objects do not match
const EMismatchedSenderRecipient: u64 = 0;
/// The `exchange_for` fields of the two escrowed objects do not match
const EMismatchedExchangeObject: u64 = 1;
-
SUI is the native currency (or protocol coin) of the blockchain.
-
Lowest unit: MIST
-
Highest unit: SUI
-
Decimal places or precision: 9 i.e.
1 SUI = 1e9 MIST
-
SUI is a Sui blockchain's native fungible token object of every account/address whenever created.
-
Code:
sui::sui
, wheresui
package is deployed into0x2
(like precompiled address). -
Now,
sui::sui::SUI
is the struct name of the coin object. It's basically name of the Sui blockchain's native coin that also acts as witness./// Name of the coin struct SUI has drop {}
-
Generic Coin SC:
0x2::coin::Coin<0x2::sui::SUI>
i.e.Coin<T>
- 0x2 is the package (named
sui
) address wherecoin
is one of the modules with generic structCoin<T>
. - Why generic? Because it can represent different types of coin.
- 0x2 is the package (named
-
Code snippet:
struct Coin<phantom T> has key, store { id: UID, value: u64 }
-
The reason that the
Coin
struct can represent different types of coin is that the struct definition is parameterized with a type parameter. When you create an instance of the Coin struct, you can pass it an arbitrary concrete Move type (e.g. another struct type) to distinguish different types of coins from one another. -
In particular, one type of custom coin already defined in Sui is Coin, which represents a token used to pay for Sui computations (more generally known as gas) - in this case, the concrete type used to parameterize the Coin struct is the SUI struct in the SUI module:
struct SUI has drop {}
-
There is a
public
(getter) function for getting the value of a coin's object ID. So, simply parse your gas coinpublic fun value<T>(self: &Coin<T>): u64 { self.value }
-
Coin
is wrapper aroundBalance
type.Balance
is a generic type that represents the balance of a particular coin type. The Balance type is defined in thecoin
module as follows:struct Balance<phantom T> has store { value: u64 }
-
Here, coin balance struct (not object as it doesn't have a key) used in the code is like
Balance<T>
- T: the type of coin (SUI, MyCoin, etc.)
- Balance: the balance of the coin T
public fun entry transfer(from: address, to: address, amount: Balance<T>) { ... }
-
SUI coin often called as "GasCoin". The SUI object of an address looks like this:
❯ sui client object 0x67e786d94eac270580eca1a5364b55efa59586ba6776e560bbae078ef82e5d8b ----- 0x2::coin::Coin<0x2::sui::SUI> (0x67e786d94eac270580eca1a5364b55efa59586ba6776e560bbae078ef82e5d8b[0xb4fa1]) ----- Owner: Account Address ( 0x7e86320466e04f808a5e046a65d1458b3a28def339ea9986e5874496ef66bd08 ) Version: 0xb4fa1 Storage Rebate: 988000 Previous Transaction: TransactionDigest(2DVMMiwTZEBuF9CV2WJmnco5rTvsHX9xGSYs7sMTHjUF) ----- Data ----- type: 0x2::coin::Coin<0x2::sui::SUI> balance: 9923059632 id: 0x67e786d94eac270580eca1a5364b55efa59586ba6776e560bbae078ef82e5d8b
This is how we get the balance's value of an active address from CLI:
-
Why
balance
is a separate module fromcoin
?- Because there are some low level APIs (basically underlying function) like
create_supply
for which we don't need the witness unlike the case where we do require witness doing the same using coin module. - Another reason is to create a regulated coin like USDC where we can use our own logic on top of the
balance
module. - Also, it doesn't have an id, hence it saves a little space when adding as a child object or inner struct of another struct. Like in a car example,
struct CarShop { id: UID, price: u64, balance: Balance<SUI> }
- Because there are some low level APIs (basically underlying function) like
- Sui Framework Documentaton
- Sui Typescript SDK (official)
- Sui Typescript SDK (community)
- Sui Rust SDK (official) 🌟
- Sui Golang SDK (community)
- Sui Python SDK (community)
- Sui Java SDK (community)
- Sui Kotlin SDK (community)
- Sui C# SDK (community)
- Sui Dart SDK(community)
1. Local dependency did not match its on-chain version at 0000000000000000000000000000000000000000000000000000000000000002::Sui::table_vec
- Cause: This error occurs when the local version of the Sui framework does not match the on-chain version. May be in
Move.toml
,testnet
dependency being used. - Solution: Update the
Move.toml
file to use thedevnet
(current network) dependency.
Before:
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
After:
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "devnet" }
- Sui Whitepaper
- Sui Move by Example
- Sui Move Intro Course 🧑🏻💻
- Sui Developer Portal
The site displays the Mainnet version of the documentation by default. The content changes based on the network you select from the dropdown.
- Developer cheatsheet from website, Github
- Sui SC Examples from Github
- Write Smart Contracts with Sui Move
- How Sui Differs from Other Blockchains
- How Sui Move differs from Core Move
- SUI OBJECTS-SECURITY PRINCIPLES AND BEST PRACTICES
- Programming with Objects on Sui from website, github
- Sui Github relevant folders: