Aptos Tutorial
Aptos is a Layer 1 blockchain that allows developers to create their smart-contracts in Move language.
Aptos is focused on delivering the safest and most production-ready Layer 1 blockchain in the world. The team is comprised of the original creators, researchers, designers, and builders of Diem, the blockchain that was first built at Meta.
The key components of Aptos are AptosBFT consensus and the new Move language which allows developers to build safe and scalable decentralized applications.
With this tutorial you will start learning Move language and create your first smart contract for Aptos.
Move
Move is a smart-contract language created with a heavy focus on security. Built on Rust, it inherits features that prevent developers from inadvertently introducting critical vulnerabilities.
The key feature of Move is the ability to define custom resource types with semantics inspired by linear logic. A resource can never be copied, double spent or implicitly discarded, only moved between program storage locations. These safety guarantees are enforced statically by Move’s type system.
You can read the Move whitepaper here: https://developers.diem.com/papers/diem-move-a-language-with-programmable-resources/2019-06-18.pdf
In this tutorial, we're going to create a simple module that allows users to store their username in the blockchain. This will allow us to explore unique features of Move and prepare for more complex projects.
Aptos CLI
First we need to install the Aptos CLI for tools to interact with Aptos. Go to the Aptos CLI releases page, and download the zip file for your OS.
The archive contains the aptos
binary, which consists of:
aptos move
namespace has everything related to the Move language:compiler
test runner
deploying Move modules to the blockchain
executing transactions
aptos key
allows you to generate a new private keyaptos init
allows you to initialize your Aptos project in order to:set a private key for the project
specify REST API urls
Put the archive somewhere in your $PATH
.
Optionally, you can also install Pontem's Move extension for CLion or PyCharm (all 2021.1+ versions are supported), and add the Intellij-Move plugin there. It provides support for the Move language.
To do this, go to https://www.jetbrains.com/pycharm/download/ and follow instructions for your operating system.
After you install it, go to File -> Settings, select Plugins
on the left, then Marketplace
and search for the Move language
.
Creating a new project
Move projects are called Packages, and contain multiple Modules.
A Module is a smart-contract that combines types and functions, and provides a very unique set of rules and restrictions to manage relationships between them.
A Package is a set of related Move modules which share common dependencies and are often published to the same address in the Aptos blockchain.
First, create a directory and initialize it with a Package using the aptos binary:
This should be the resulting directory structure of our new Package:
sources/
- directory where you put your modules.
Move.toml
- manifest file for the package. Here, you define the package metadata, dependencies and addresses used in the code.
Let's add an address with name sender
and value 0x42
under [addresses]
. We're going to store all our modules in that address in the Aptos blockchain.
The move init
command automatically adds a dependency to the AptosFramework
package. It also transitively adds a MoveStdlib
dependency. Let's compile that empty package to make the Aptos CLI fetch the dependencies and inspect them.
Compiling for the first time could take a while because it fetches the whole aptos-core
repo from Github.
Fetched dependencies as well as build artifacts are stored in the build/
directory at the package root.
MoveStdlib
- standard library of the Move language consisting of modules that are indispensable such as functions to work with vectors and signers.
AptosFramework
- a set of modules specific to the Aptos blockchain, like the coin
module for an ERC20-like fungible token, and accoint
for the account metadata.
Resources and storage
Every user of the Aptos blockchain has their own object storage located at the user address.
There are built in methods which allow access to this storage from the Move code.
In that storage, smart-contracts store special structs called Resources. Those are marked with the has key
ability after the name of the struct.
To place a resource on a user address, a developer should have the &signer
argument in scope and call the move_to
function. The &signer
data type in the Move language represents the sender account of the current transaction, and is used mostly for resource store and access restrictions in modules. Developers can extract the address of the transaction sender using signer::address_of(&signer)
function.
All functions that fetch resource objects from storage require an annotation on the function signature. For that, add acquires ResourceName
after the return type.
Implementation
UserProfile
First, let's add a UserProfile
resource struct where we're going to store our username in a field of type String
. Resources in Move are marked with the has key
ability.
There's no text strings in Move. All text is represented as vector<u8>
objects or sequences of bytes. To use them more easily, the byte string literal was introduced, i.e. b"MyUser", b"MyString"
.
Later, the string
module was added to the standard library, which provides a String
struct that wraps vector<u8>
and ensures that it contains only UTF8 characters. In our usernames, we're going to use those.
Methods
Now let's add getter and setter methods for the username
:
Let's implement getter first. We need to retrieve the UserProfile
object from the user global storage. For that, we will use the borrow_global()
method which needs the address of the user store as a parameter. This also allows us to fetch the username of any other user.
We also add acquires UserProfile
which was explained earlier.
Implementation of the setter is a bit more complex. The setter will be a public entry function. Unlike the usual function which can be called only by other modules, entry functions can be called by sending a transaction to the Aptos blockchain containing arguments, generics and name/path in the function. Using public entry functions, users of your DApp can interact with deployed modules.
We need to create an if-statement with two branches:
first for the case when there's no
UserProfile
in the global storage, we need to create one with the correct usernamesecond is just to update username inside the existing
user_info
We also need &signer
here, as we want to only allow users themselves to change their profile.
Tests
Now, let's write a test for our module to make sure everything works correctly. Test functions could be added anywhere, but it's a nice convention to store them in the tests/{$MODULE_NAME}_tests.move
module.
Create a tests/
directory to the root of the package, and add user_info_tests.move
there. Test functions are marked with the #[test]
attribute, this way Move knows it's a test and can apply special treatment. We also mark the module itself with a #[test_only]
attribute to remove it from the public namespace.
To run this test:
Deployment to Aptos blockchain
Let's deploy our module to the Aptos blockchain and make it available for everyone.
We must initialize a new Aptos account and deployment configuration using the aptos init
command.
During execution of the command a new Aptos account will be created and test coins will be deposited to it so we can cover gas costs of the UserInfo
module publication.
Use default parameters proposed by the command:
The deployment config which contains the private key of the new account will be created in .aptos/config.yaml
in the root of the project. Don't share your private key with anyone! If you want to change the configuration or account, just run aptos init
again and the configuration will be overwritten.
Copy the new generated address, in our example it's 0x6E81B91A98226A2622B6993B9D14D3244FA8AFACF622AA3CB11A32C799E93C42
(don't forget to add 0x
prefix to the start of the address), and replace the sender
address in Move.toml
.
You will get something like this but with your own address:
Finally let's deploy the module using the aptos move publish
command:
You should get a similar output which means your module is now published.
Setting username for our account
After we deployed the user_info
module we can send a transaction to the Aptos blockchain which will call the user_info::set_username
function and set the username for our account.
To execute the set_username
function we need to utilize the aptos move run
command. In this example we will use the username AptosDev
as the username for our account which has to be provided as an argument. Also, function-id
should contain a path to the deployed contract and function name. In your case the path will be different because you are using your own account.
If the transaction is executed successfully, you will see an output similar to the one above. Now you can query your username. In this example, the URL to the query resource would be:
To interact with the module deployed by you: replace the address of the account and the address of the module resource in the url with your own.
Enjoy! Please share your feedback and more tutorial requests in our Discord and Telegram.
Last updated