Smart Contracts - Encode Club Sui Series #2

In this second of six educational videos, you will learn how to write a smart contract on the Sui network.

Smart Contracts - Encode Club Sui Series #2

In the second of Encode Club’s Sui series, we walk through the process of writing a smart contract on the Sui network.

The Sui Foundation partnered with Encode Club to offer a series of six developer-focused videos. This series will range from the basics of Sui to tutorials about building smart contracts and working with objects in Sui.

Learning Highlights

Our lesson on writing a smart contract, hosted by Encode Club, gets you started building on Sui. We begin with preparing the coding environment and quickly get into object features. We learn about object properties, particularly ownership, through the example of a “Car” object.

Installing Sui

The easiest way to install the Sui binaries and any necessary prerequisites is to visit the Install Sui to Build documentation. Here you will find step-by-step instructions and links to all the downloads. If you’re just getting started, visiting this page will also help familiarize you with the Sui documentation.

Sui Package Layout and Manifest Syntax

Upon downloading the binaries, you’ll be able to use the command sui move new [package name] to create a new Move package in the current path directory. Running that command should create two items in the root directory of your workspace: a Move.toml file, and a directory called sources. Compiling Move code requires the Move.toml package manifest file and the sources subdirectory. Other optional subdirectories include: examples, scripts, and tests.

The main unit of Move code organization (and distribution) is a package. A package consists of a set of modules defined in separate files with the .move extension. These files include Move functions and type definitions. A package must include the Move.toml manifest file describing the package configuration, such as package metadata or package dependencies.


In contrast to many other blockchains where storage is centered around accounts and each account contains a key-value store, Sui's basic storage unit is an object. Objects are the building blocks of programming in Sui, and they are characterized by having the following two properties:

  • All objects are annotated with the has key keyword following their struct declaration.
  • All objects have id: UID as their first field.

struct Car has key {
	id: UID,
	speed: u8,
	acceleration: u8,
	handling: u8

This simple declaration in Sui names an object “Car”.

Object ownership can be classified using the following four methods:

Owned by an address: A Move object, upon creation in the Move code, can be transferred to an address. After the transfer, the address owns that object. An object owned by an address can be used (i.e. passed as a Move call parameter) only by transactions signed by that owner address. Owned objects can be passed as a Move call parameter in any of these three forms: read-only reference (&T), mutable reference (&mut T), and by-value (T).

Owned by another object: When an object is owned by another object, it's not wrapped. The child object still exists independently as a top-level object and can be accessed directly in Sui storage. We will cover this in greater detail in the fourth session as we discuss Dynamic Fields.

Immutable: You can't mutate an immutable object, and an immutable object doesn't have an exclusive owner. Anyone can use an immutable object in a Move call. All Move packages are immutable objects: you can't change a Move package after you publish it. You can convert a Move object into an immutable object using the freeze_object operation. You can only pass an immutable object in Move calls as a read-only reference (&T).

Shared: An object can be shared, meaning that anyone can read or write this object. In contrast to mutable owned objects (which are single-writer), shared objects require consensus to sequence reads and writes. In other blockchains, every object is shared. However, Sui programmers often have the choice to implement a particular use-case using shared objects, owned objects, or a combination. This choice can have implications for performance, security, and implementation complexity.


Several visibility modifiers limit or gate access to Move functions. public functions allow other modules to import a particular function. public(friend) functions allow modules given explicit permission to import a particular function. entry functions allow a function to be called directly, such as in a transaction. A special function called init gets executed only once, when its associated module is published. It always has the same signature and only one argument: ctx: &mut TxContext.


A capability is a special pattern in Move that gates access to certain functions. Capabilities can be used in conjunction with the init function to ensure that only one capability will ever exist, and that it will be sent to the module publisher.

struct AdminCapability has key { 
	id: UID 

fun init(ctx: &mut TxContext) {
	transfer::transfer(AdminCapability {
		id: object::new(ctx),
	}, tx_context::sender(ctx))

The AdminCapability object is instantiated and transferred to the address of the module publisher. This ensures that only one AdminCapability will ever exist.

Watch the Whole Series

  1. What's Sui?
  2. Smart Contracts
  3. Creating Objects and NFTs
  4. Dynamic Fields and Collections
  5. RPG Building Basics
  6. Deploy a Game to the Blockchain