This how-to guide will give you an overview of Aptos and help you get started building with...
How to Get Started with Aptos: Smart Contracts with Move
This how-to guide will give you a brief but practical overview of getting started with Move compilation and deployments on Aptos.
What is Aptos? An Overview
Aptos addresses the critical demands of decentralized applications with an emphasis on scalability, safety, reliability, and upgradeability. Created from a collaborative effort involving over 350 developers worldwide, Aptos incorporates state-of-the-art innovations in its foundation. The platform is powered by an optimistic concurrency control engine known as Block-STM, as well as a BFT Proof-of-Stake mechanism known as Jolteon. The introduction of the Move programming language is a defining characteristic, offering a secure and adaptable environment for application development.
What is Move?
Move was originally developed for the Diem blockchain. Based on Rust, it was designed to be a universal language targeting the unique characteristics of smart contract programming. Move's first class abstractions for the concept of assets, transfers, and access control make for safer and more efficient programming. Move addresses the critical demands of decentralized applications with an emphasis on scalability, safety, reliability, and upgradeability. The recent native integration of Aptos Keyless Accounts is a revolutionary offering enabling familiar Web2 logins to support transacting on the Aptos blockchain. This novel architecture, emphasizing rigorous and rapid enhancement, positions Aptos as a clear leader in making Web3 technologies accessible on a global scale.
Feature |
Move |
Solidity |
Vyper |
Solana (Rust) |
Safety and Security |
Resource-oriented programming to prevent common bugs and vulnerabilities (e.g., double spending, reentrancy attacks) |
Typing and access control mechanisms but more prone to reentrancy and integer overflow issues |
Designed for security, aiming for simplicity and readability to reduce bugs |
Rust’s strong type system and ownership model enhances safety and concurrency control |
Performance |
High performance with Block-STM and Jolteon consensus for faster transaction finality |
Good performance, but gas costs can be high and may limit complex contract execution |
Slightly lower performance compared to Solidity due to more stringent safety features |
High performance with low-latency execution due to Rust and Solana’s Proof-of-History (PoH) |
Language Complexity |
Medium complexity, designed for both security and expressiveness |
Medium complexity with extensive features for smart contract development |
Low complexity, simpler syntax aimed at security and readability |
High complexity, leveraging Rust’s powerful but complex syntax and features |
Developer Ecosystem |
Growing, supported by Aptos and a collaborative community of developers |
Mature ecosystem with a vast array of tools, libraries, and community support |
Smaller ecosystem compared to Solidity, but growing with emphasis on security |
Growing ecosystem with strong support from Solana and a vibrant developer community |
Upgradability |
Designed with upgradability in mind, facilitating easier updates and enhancements |
Upgradability possible but requires careful planning and use of proxy contracts |
Supports upgradability, but with limited flexibility compared to Move |
Supports upgradability through program versioning and native Rust features |
Tooling and Libraries |
Developing tooling ecosystem with focus on safety and security tools |
Extensive tooling including Remix, Truffle, Hardhat, etc. |
Limited but growing tooling focused on security |
Strong tooling support from Rust ecosystem and Solana-specific development tools |
Concurrency |
Optimistic concurrency control with Block-STM, allowing efficient parallel execution |
Limited concurrency control, potential for state conflicts |
No native concurrency control, designed to be simple and secure |
Strong concurrency control with Rust’s ownership and concurrency features |
Getting Started with Validation Cloud's Aptos API
For developers eager to explore the benefits of the Aptos blockchain, Validation Cloud's Aptos API offers a powerful and seamless entry point.
To get started, our comprehensive documentation provides a clear roadmap for setting up your development environment and connecting to the Aptos Network. In the next section, we’ll walk you through setup, creating a Move contract, testing the contract, and publishing the contract. First, sign up for free at validationcloud.io, and create your Aptos API key.
Setup
Let’s get your Move environment prepared.
Setup a wallet: The Martian wallet extension is straightforward to install and start using quickly. You can create a new, non-production wallet, and export the hex literal from the wallet into your .env file.
Install Aptos CLI: Navigate to https://aptos.dev/tools/aptos-cli/install-cli/ and select the CLI for download to the appropriate system. For this tutorial, I am using an Apple Silicon MacBook. In this case, the installation was simply “brew install aptos.” The command “aptos help” with aptos-specific output demonstrates successful installation.
Setup IDE: Install the "move-analyzer extension” The IDE I am using today is VS Code.
Ensure you have Node.js and Yarn installed, and then set up a new TypeScript project:
mkdir aptos-hello && cd aptos-hello
aptos init
>>Select "mainnet"
>>Input a non-production private key hex literal (e.g. 0x...)
Validation Cloud Aptos API Key
- Create a ‘.env’ file in your project root to store your API key from app.validationcloud.io.
- Once you run “aptos init,” the .aptos directory will appear. A parameter “rest_url” will appear. For optimal performance, and persistent tracking and visualization of your RPC requests to the chain, you can replace it with your full Validation Cloud API URL
Note: Storing private keys in plaintext is strongly not advised for wallets containing any sort of significant balance or access controls. In those production scenarios, a more secure solution such as AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, or others would be expected.
1. Set Up Move.toml
Let’s start by connecting to Aptos and fetching some ledger info. The returned values will include the chainID, epoch, ledger version, timestamp, the git hash, and the blockheight, among a few other properties.
<Move.toml>
[package]
name = "hello_blockchain"
version = "0.0.1"
[addresses]
hello_blockchain = "0xbd2d212....." # Use your account's actual hex address
[dependencies.AptosFramework]
git = 'https://github.com/aptos-labs/aptos-core.git'
rev = "mainnet"
subdir = 'aptos-move/framework/aptos-framework'
Note the “mainnet” designation. If you are testing the contract, you can replace that value with “devnet.”
2. Prepare Hello World Move Contract
Within the .aptos/sources directory, name a new file called “hello_blockchain.move.” The following code defines a module hello_blockchain::message that allows accounts on the Aptos blockchain to store and manage a message string. The module uses several foundational libraries for basic operations and Aptos-specific functionalities like event handling and account management.
The core of this module is the Message Holder resource, which holds a message and an event handle for logging changes to the message. This resource is defined with has key, indicating it's a key resource for whatever account it's associated with. The MessageChangeEvent struct captures the old and new messages whenever a change occurs.
The set_message function allows an account to either set a message for the first time or update an existing message. It emits an event whenever the message is updated. If the MessageHolder does not exist for the account, it creates one and initializes it with the message and an event handle.
The get_message function retrieves the message for a given address, asserting that the MessageHolder exists. Otherwise, it throws an error specified by ENO_MESSAGE.
Additionally, a test function sender_can_set_message is included to ensure that the functionality of setting and retrieving a message works as expected. It initializes an account for testing, sets a message, and verifies that the message can be retrieved accurately.
<hello_blockchain.move>
module hello_blockchain::message {
use std::error;
use std::signer;
use std::string;
use aptos_framework::account;
use aptos_framework::event;
//:!:>resource
struct MessageHolder has key {
message: string::String,
message_change_events: event::EventHandle<MessageChangeEvent>,
}
//<:!:resource
struct MessageChangeEvent has drop, store {
from_message: string::String,
to_message: string::String,
}
/// There is no message present
const ENO_MESSAGE: u64 = 0;
public fun get_message(addr: address): string::String acquires MessageHolder {
assert!(exists<MessageHolder>(addr), error::not_found(ENO_MESSAGE));
*&borrow_global<MessageHolder>(addr).message
}
public entry fun set_message(account: signer, message: string::String)
acquires MessageHolder {
let account_addr = signer::address_of(&account);
if (!exists<MessageHolder>(account_addr)) {
move_to(&account, MessageHolder {
message,
message_change_events: account::new_event_handle<MessageChangeEvent>(&account),
})
} else {
let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
let from_message = *&old_message_holder.message;
event::emit_event(&mut old_message_holder.message_change_events, MessageChangeEvent {
from_message,
to_message: copy message,
});
old_message_holder.message = message;
}
}
#[test(account = @0x1)]
public entry fun sender_can_set_message(account: signer) acquires MessageHolder {
let addr = signer::address_of(&account);
aptos_framework::account::create_account_for_test(addr);
set_message(account, string::utf8(b"Hello, Blockchain"));
assert!(
get_message(addr) == string::utf8(b"Hello, Blockchain"),
ENO_MESSAGE
);
}
}
3. Compile Move Contract
Retrieving an account by its address on the Aptos blockchain, even if it only provides basic information such as the authentication key and sequence number, serves several practical purposes. The authentication key is crucial for confirming the identity and the cryptographic credentials associated with the account, which is fundamental for security and transaction verification processes. This key helps ensure that any transaction submitted is genuinely from the account holder. The sequence number of an account, on the other hand, is vital for transaction ordering. Each transaction from an account must have a sequence number greater than the last confirmed transaction.
aptos move compile --package-dir .aptos
4. Test the Contract
In the same directory as your hello_blockchain.move file above, create a new file called “hello_blockchain_test.move.” The following code below should go into that file.
This test code is part of a module named hello_blockchain::message_tests which validates the functionality of a separate module (hello_blockchain::message). It specifically tests the ability to set and retrieve a message on the Aptos blockchain for a specific account.
The get_account function generates a test signer from a list of signers created for testing purposes, ensuring that a valid account signer is available for the test.
The sender_can_set_message function, marked with the #[test] attribute, performs the core test:
- Retrieves a signer representing an account.
- Creates a test account on the blockchain using this signer's address.
- Sets a message "Hello, Blockchain" to this account using the message::set_message function.
- Asserts that the message retrieved from the blockchain is exactly "Hello, Blockchain," ensuring that the message setting functionality works as intended. If the retrieved message does not match the expected value, the test will fail.
<hello_blockchain_test.move>
#[test_only]
module hello_blockchain::message_tests {
use std::signer;
use std::unit_test;
use std::vector;
use std::string;
use hello_blockchain::message;
fun get_account(): signer {
vector::pop_back(&mut unit_test::create_signers_for_testing(1))
}
#[test]
public entry fun sender_can_set_message() {
let account = get_account();
let addr = signer::address_of(&account);
aptos_framework::account::create_account_for_test(addr);
message::set_message(account, string::utf8(b"Hello, Blockchain"));
assert!(
message::get_message(addr) == string::utf8(b"Hello, Blockchain"),
0
);
}
}
To run the test, issue the following command in the terminal:
aptos move test --package-dir .aptos
The following result is expected:
5. Publish the Contract
Let’s get this deployed to the chain.
aptos move publish --package-dir .aptos
You can correlate these deployments with the Aptos blockchain explorers and track your deployment.
Through these examples, you've learned how to effectively build, compile, test, and deploy a simple Move contract with the Aptos blockchain. This tutorial should serve as a foundation for building more complex applications on top of the Aptos platform. Stay tuned for future posts where we'll dive deeper into smart contract interactions and Node API possibilities.