Guide to Move on Aptos

The Aptos blockchain consists of validator nodes on which the consensus protocol runs. The consensus protocol agrees on the order of transactions and their output when executed on the Move Virtual Machine (MoveVM). Each validator node transfers transactions along with the current state of the blockchain ledger to the virtual machine. MoveVM processes these inputs to produce an output set of changes or storage delta. Once the consensus agrees and commits the output data, it becomes publicly available. In this guide, we’ll introduce you to the basic concepts of Move and how they apply to development on Aptos.

What is Move?

Move is a secure and robust programming language for Web3 that emphasizes restriction and access control. Assets in Move can be represented by a resource or stored in it. Constraint is provided by default because structures cannot be duplicated. Only structures that are explicitly defined at the bytecode level as a copy can be duplicated.

Access control comes from both the notion of accounts and module access privileges. A module in Move can either be a library or a program that can create, store, or transfer assets. Move ensures that only the public functions of a module can be accessed by other modules. If a structure has no public constructor, it can only be created in the module that defines it. Similarly, fields within struct can only be accessed and modified within its own module, that is, through public access and configuration functions.

Comparison with other virtual machines

Aptos / Move

  • Data storage — Data is stored within the owner account
  • Parallelization — Ability to parallelize at runtime in Aptos
  • Transaction security — Serial number
  • Security type — Modular structs and generics
  • Function call — Static transfer not on generics

Solana / SeaLevel

  • Data storage — Stored in the owner account associated with the program
  • Paralleling — Requires all accounts and programs accessed in a transaction
  • Transaction security — Uniqueness of transactions + transaction memorization
  • Security type — software
  • Function call — Static transfer


  • Data storage — Stored on the account associated with the smart contract
  • Parallelization — Nothing is currently being serialized
  • Transaction Security — Similar to serial numbers
  • Security type — Contract types
  • Function call — Dynamic transfer

Features of Aptos Move

Each MoveVM deployment has the ability to extend the MoveVM core with additional functions using the adapter layer. In addition, MoveVM has a structure to support standard operations, similar to the way a computer has an operating system.

Features of the Aptos Move adapter include:

  • Fine-grained storage, which eliminates the dependency between the amount of data stored on an account and the gas for the transactions associated with the account
  • Tables for storing key, value data within an account at scale
  • Parallelism with Block-STM, allowing transactions to run in parallel without user input

The Aptos framework comes with many useful libraries:

Token standard, allowing you to create NFT and other rich tokens without publishing a smart contract
The Coin standard, which allows you to create secure Coin-type tokens by publishing a trivial module.
Iterable table, allowing you to view all the entries in the table
Stacking and delegation framework
Service type_of to determine at runtime the address, module, and name of a structure of a given type
The multi signer system, which allows multiple signing entities
A timestamp service that provides a monotonically increasing clock, mapped to the real current time unixtime

And much more in the near future…

Key concepts in Move

  • Data should be stored in the account that owns it, not in the account that published the module.
  • Data flow should have minimal restrictions with an emphasis on the usability of the ecosystem
  • Preference for static type security over runtime security via generics.

Data ownership.

Data should be stored in the account that owns it, not in the account that published the module.

In Solidity, data is stored in the namespace of the account that created the contract. This is usually represented as an address map to a value or an instance identifier to an owner address.

In Solana, data is stored in a separate account associated with the contract.

In Move, the data can be stored in the module owner account, but this creates an ownership ambiguity problem and implies two problems:

This makes ownership ambiguous because the asset has no resource associated with the owner.
The creator of the module assumes responsibility for the lifetime of these resources, e.g., renting, restoring, etc.
Regarding the first point, by placing assets in trusted resources within an account, the owner can ensure that even a maliciously programmed module cannot modify those assets. In Move, we can program a standard orderbook structure and interface that will prevent applications built on top of it from gaining backdoor access to the account or its records in the orderbook.

Compare the following two coin storage strategies:

In the following, coins are placed on a single account with the owner listed as an index:

struct CoinStore has key {
    coins: table<address, Coin>,
Enter fullscreen mode Exit fullscreen mode

Instead, the preferred approach is to store coins on an account:

struct CoinStore has key {
    coin: Coin,
Enter fullscreen mode Exit fullscreen mode

This makes ownership obvious.

Data Stream

The data flow should have minimal restrictions with an emphasis on the usability of the ecosystem

Assets can be programmed to be completely restricted within a module by making it so that no interface ever represents struct in value form, but instead provides only functions to manipulate the data defined in the module. This restricts the availability of data only within the module and makes it impossible to export, which in turn prevents interaction with other modules. In particular, you can imagine a purchase contract that takes some Coin<T> as input and returns Ticket. If Coin<T> is defined only inside the module and is not exported outside the module, the applications for that Coin<T> are limited to what is defined in the module.

Compare the following two implementations of Coin translation with deposit and withdraw:

public fun transfer<T>(sender: &signer, recipient: address, amount: u64) {
    let coin = withdraw(&signer, amount);
    deposit(recipient, coin);
Enter fullscreen mode Exit fullscreen mode

Below are the restrictions on where Coin can be used outside of the module:

fun withdraw<T>(account: &signer, amount: u64): Coin<T>
fun deposit<T>(account: address, coin: Coin<T>)
Enter fullscreen mode Exit fullscreen mode

By adding public available devices for withdraw and deposit means, Coin can be taken out of the module, used by other modules, and returned to the module:

public fun withdraw<T>(account: &signer, amount: u64): Coin<T>
public fun deposit<T>(account: address, coin: Coin<T>)
Enter fullscreen mode Exit fullscreen mode

Types of security

In Move, given a particular structure, say, A, the different options can be distinguished in two ways:

Internal identifiers, such as GUIDs.
Generics, such as A<T>, where T is another structure

Internal identifiers can be handy because of their simplicity and ease of programming. However, Generics provide much higher safeguards, including explicit checks at compile time or validation, albeit at some cost.

Generics allow you to create completely separate types and resources, as well as the interfaces that these types expect. For example, orderbook can state that it expects two currencies for all orders, but one of them must be fixed, such as buy<T>(coin: Coin<Aptos>): Coin. This explicitly states that the user can buy any coin <T> but must pay for it coin<Aptos>.

The difficulty with generic occurs when it is desirable to store data on T. Move does not support static transfer to generics, so in functions like create<T>(...) : Coin<T>, T must either be a phantom type, i.e. used only as a type parameter in Coin, or must be specified as input in Create. Functions such as T cannot be called on T::function, even if every T implements that function.

In addition, for structures that can be created en masse, generics leads to a lot of new storage and resources related to data tracking and emission, which is probably less of an issue.

Because of this, we made a hard choice and created two "Token" standards: one for currency-related tokens called Coin and another for asset-related tokens or NFTs called Token. Coin uses static type security via generics, but is a much simpler contract. Whereas Token uses dynamic type safety through its own universal identifier and eschews generics because of the complexity that affects its ergonomics.

Data access

  • The signatory should be required to restrict access to add or remove assets from an account, except where this is clearly evident.

In Move, the module can define how resources can be accessed and their contents changed regardless of the presence of the signatory account owner. This means that a programmer could accidentally create a resource that allows other users to arbitrarily insert or remove assets from another user's account.

There are several examples in our design of where we allowed access and where we prevented it:

  • A token cannot be directly inserted into another user's account unless the user already has part of that token.
  • TokenTransfers allows a user to directly brand a token stored in another user's resource, effectively using an access control list to gain that access.
  • In Coin, a user can directly transfer to another user's account if they have a resource to store those coins

Less work on our tokens could allow users to give airdrop tokens directly to another user's account, which would add additional storage to their accounts and also make them owners of content they did not initially approve.

As a concrete example, let's go back to the previous Coin case with the withdraw function. If the withdraw function had instead been defined as follows:

public fun withdraw<T>(account: address, amount: u64): Coin<T>
Enter fullscreen mode Exit fullscreen mode

anyone would be able to withdraw coins from account

More Sources

  • The Move Book
  • Aptos Framework documentation
  • Getting Started

Оцените статью
Добавить комментарий