AptosMove开发入门:从环境搭建到合约部署全流程实录随着Aptos生态的蓬勃发展,Move语言作为其核心技术栈,以其出色的安全性和表现力吸引了越来越多的开发者。然而,对于初学者而言,从零开始搭建环境、编写并成功部署第一个智能合约,往往会遇到教程中未曾提及的细节问题。本文并非一篇理
随着 Aptos 生态的蓬勃发展,Move 语言作为其核心技术栈,以其出色的安全性和表现力吸引了越来越多的开发者。然而,对于初学者而言,从零开始搭建环境、编写并成功部署第一个智能合约,往往会遇到教程中未曾提及的细节问题。
本文并非一篇理论文章,而是一份真实、完整、未经剪辑的“第一人称”实操记录。我们将跟随一个开发者的脚步,从安装 Aptos CLI 开始,一步步创建项目、编写代码、编译测试,直到最终将合约部署到测试网上。更重要的是,本文完整记录了部署过程中遇到的经典错误——“余额不足”,并详细展示了如何诊断问题、寻找解决方案(使用水龙头),最终扫清障碍。``
如果你正准备踏上 Aptos Move 的学习之旅,那么这篇充满“实战感”的笔记,希望能成为你手中最实用的地图。
brew update
brew install aptos
aptos --version
aptos 7.7.0
brew update
brew upgrade aptos
Move on Aptos
https://marketplace.visualstudio.com/items?itemName=AptosLabs.move-on-aptos
mcd 01-hello_blockchain # mkdir 01-hello_blockchain && cd 01-hello_blockchain
/Users/qiaopengjun/Code/Aptos/01-hello_blockchain
初始化Aptos配置,生成账户密钥
aptos init
: aptos Tool to initialize current directory for the aptos tool
Code/Aptos/01-hello_blockchain
➜ aptos init
Configuring for profile default
Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet]
testnet
Enter your private key as a hex literal (0x...) [Current: None | No input: Generate new key (or keep one if present)]
No key given, generating key...
---
Aptos CLI is now set up for account 0x1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd as profile default!
---
The account has not been funded on chain yet. To fund the account and get APT on testnet you must visit https://aptos.dev/network/faucet?address=0x1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd
Press [Enter] to go there now >
{
"Result": "Success"
}
aptos move init
: Creates a new Move package at the given location
查看帮助信息
aptos move init -h
Creates a new Move package at the given location
Usage: aptos move init [OPTIONS] --name <NAME>
Options:
--name <NAME> Name of the new Move package
--package-dir <PACKAGE_DIR> Directory to create the new Move package
--named-addresses <NAMED_ADDRESSES> Named addresses for the move binary [default: ]
--template <TEMPLATE> Template name for initialization [possible values: hello-blockchain]
--assume-yes Assume yes for all yes/no prompts
--assume-no Assume no for all yes/no prompts
--framework-git-rev <FRAMEWORK_GIT_REV> Git revision or branch for the Aptos framework
--framework-local-dir <FRAMEWORK_LOCAL_DIR> Local framework directory for the Aptos framework
--skip-fetch-latest-git-deps Skip pulling the latest git dependencies
-h, --help Print help (see more with '--help')
-V, --version Print version
实操
Code/Aptos/01-hello_blockchain took 3m 8.1s
➜ aptos move init --name hello-blockchain --template hello-blockchain
{
"Result": "Success"
}
Code/Aptos/01-hello_blockchain
➜ tree . -L 6 -I "docs|target"
.
├── Move.toml
├── scripts
├── sources
│ └── hello_blockchain.move
└── tests
4 directories, 2 files
hello_blockchain.move
文件module hello_blockchain::message {
use std::error;
use std::signer;
use std::string;
use aptos_framework::event;
#[test_only]
use std::debug;
//:!:>resource
struct MessageHolder has key {
message: string::String,
}
//<:!:resource
#[event]
struct MessageChange has drop, store {
account: address,
from_message: string::String,
to_message: string::String,
}
/// There is no message present
const ENO_MESSAGE: u64 = 0;
#[view]
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,
})
} else {
let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
let from_message = old_message_holder.message;
event::emit(MessageChange {
account: account_addr,
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 msg: string::String = string::utf8(b"Running test for sender_can_set_message...");
debug::print(&msg);
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
);
}
}
这是一个简单的 Aptos Move 智能合约,它允许任何用户在区块链上为自己的账户存储一条个人专属的字符串消息。合约提供了两个主要功能:一个需要本人签名才能设置或更新这条消息的入口函数,以及一个任何人都可以调用的只读函数来查看任意地址存储的消息。每次消息被更新时,合约还会广播一个“事件”,方便链下应用追踪这些变化。
这个合约就像一个为每个 Aptos 用户准备的“数字记事本”,我们来看看它是如何实现的。
合约定义了两种数据结构:
MessageHolder
(消息持有者)
struct MessageHolder has key {
message: string::String,
}
has key
是最重要的能力 (ability),它意味着每个账户地址下最多只能存放一个 MessageHolder
实例。这确保了每个用户只有一个专属的记事本。message
,用来存放字符串。MessageChange
(消息变更事件)
#[event]
struct MessageChange has drop, store {
account: address,
from_message: string::String,
to_message: string::String,
}
#[event]
注解标记。MessageHolder
那样永久存储状态,而是像一个“广播通知”。当一个用户的消息被修改时,合约就会发出这样一个事件。account
) 把消息从 from_message
改成了 to_message
”。set_message
(设置消息)
public entry fun set_message(account: signer, message: string::String)
entry fun
),意味着用户可以直接通过提交一笔交易来调用它。account: signer
非常关键。signer
代表着交易的签名者,它是一种密码学证明,确保了只有账户的拥有者(私钥持有者)才能调用这个函数来修改自己的消息。MessageHolder
。move_to
函数为该用户创建一个新的 MessageHolder
资源。borrow_global_mut
“借用”已有的 MessageHolder
进行修改,先发出一个 MessageChange
事件通知外界,然后用新消息覆盖旧消息。get_message
(获取消息)
#[view]
public fun get_message(addr: address): string::String
#[view]
)。视图函数是只读的,调用它不需要提交交易,也不消耗 Gas 费(通过 RPC 节点查询时)。address
作为参数,意味着任何人都可以查询任何地址存储的消息,实现了数据的公开透明。assert!(exists<...>)
检查这个地址下是否存在 MessageHolder
。如果不存在,函数会报错。borrow_global
“借用”这个 MessageHolder
并返回其中的 message
字段。sender_can_set_message
#[test(account = @0x1)]
public entry fun sender_can_set_message(account: signer)
#[test]
注解标记。aptos move test
命令时执行,用来验证合约逻辑的正确性。@0x1
) 调用 set_message
设置一条消息,然后调用 get_message
读取它,最后用 assert!
确认读出来的消息和设置的完全一样。Move.toml
文件[package]
name = "HelloBlockchainExample"
version = "1.0.0"
authors = []
[addresses]
hello_blockchain = "_"
[dev-addresses]
[dependencies.AptosFramework]
git = "https://github.com/aptos-labs/aptos-framework.git"
rev = "mainnet"
subdir = "aptos-framework"
[dev-dependencies]
项目根目录下的 Move.toml
文件是包的配置文件,它定义了项目名称、依赖项和地址别名等元数据。
Code/Aptos/01-hello_blockchain
➜ aptos move build --named-addresses hello_blockchain=default
Compiling, may take a little while to download git dependencies...
UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.git
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING HelloBlockchainExample
{
"Result": [
"1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd::message"
]
}
Code/Aptos/01-hello_blockchain took 10.3s
➜ aptos move compile --named-addresses hello_blockchain=default --skip-fetch-latest-git-deps
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING HelloBlockchainExample
{
"Result": [
"1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd::message"
]
}
Code/Aptos/01-hello_blockchain took 6.2s
➜ aptos move test --named-addresses hello_blockchain=default
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING HelloBlockchainExample
Running Move unit tests
[debug] "Running test for sender_can_set_message..."
[ PASS ] 0x1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd::message::sender_can_set_message
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
"Result": "Success"
}
Code/Aptos/01-hello_blockchain took 13.4s
➜ aptos move test --named-addresses hello_blockchain=default --skip-fetch-latest-git-deps
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING HelloBlockchainExample
Running Move unit tests
[debug] "Running test for sender_can_set_message..."
[ PASS ] 0x1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd::message::sender_can_set_message
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
"Result": "Success"
}
Code/Aptos/01-hello_blockchain took 12.6s
➜ aptos move deploy-object --address-name hello_blockchain
Compiling, may take a little while to download git dependencies...
UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.git
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING HelloBlockchainExample
Do you want to deploy this package at object address 0x379e5f2fc6fef74c558ae4f27f8064866591f039fac09af6ee6ca2f9fe917bb6 [yes/no] >
yes
package size 1791 bytes
{
"Error": "Simulation failed with status: MAX_GAS_UNITS_BELOW_MIN_TRANSACTION_GAS_UNITS"
}
aptos
命令行工具配置的默认账户地址aptos account lookup-address
{
"Result": "1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd"
}
aptos account balance
{
"Result": [
{
"asset_type": "coin",
"coin_type": "0x1::aptos_coin::AptosCoin",
"balance": 0
}
]
}
1ff96e...fd
的这个账户的余额aptos account balance --account 1ff96e1010f6118a00bea0deb0139cdde087809e944638e12cbf70ce25a948fd
{
"Result": [
{
"asset_type": "coin",
"coin_type": "0x1::aptos_coin::AptosCoin",
"balance": 0
}
]
}
aptos account balance
.aptos/config.yaml
文件,找到 default
配置,然后使用其中记录的账户地址(也就是 1ff96e...
)去链上查询余额。aptos account balance --account 1ff96e...fd
1ff96e...fd
的这个账户的余额。default
配置中的地址,直接使用你通过 --account
参数提供的地址。命令 | aptos account balance |
aptos account balance --account <地址> |
---|---|---|
查询目标 | 默认配置文件中的账户 | 明确指定的账户地址 |
便利性 | 更高,无需输入地址 | 较低,需要输入完整地址 |
精确性 | 依赖于配置文件 | 更高,不依赖配置文件 |
使用场景 | 快速查询自己的默认钱包 | 查询任何人的钱包、在脚本中使用 |
https://aptos.dev/zh/network/faucet
领水成功查看余额
aptos account balance
{
"Result": [
{
"asset_type": "coin",
"coin_type": "0x1::aptos_coin::AptosCoin",
"balance": 100000000
}
]
}
Code/Aptos/01-hello_blockchain
➜ aptos move deploy-object --address-name hello_blockchain
Compiling, may take a little while to download git dependencies...
UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.git
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING HelloBlockchainExample
Do you want to deploy this package at object address 0x379e5f2fc6fef74c558ae4f27f8064866591f039fac09af6ee6ca2f9fe917bb6 [yes/no] >
yes
package size 1791 bytes
Do you want to submit a transaction for a range of [265700 - 398500] Octas at a gas unit price of 100 Octas? [yes/no] >
yes
Transaction submitted: https://explorer.aptoslabs.com/txn/0x4ee645dad8cf4f042bbf44ca0d3f0ca0222503ecf367ec3ba6d442d208207e9e?network=testnet
Code was successfully deployed to object address 0x379e5f2fc6fef74c558ae4f27f8064866591f039fac09af6ee6ca2f9fe917bb6
{
"Result": "Success"
}
太棒了,恭喜你!🥳 这次部署成功了。
这个输出日志清晰地展示了部署智能合约的完整流程。我们来分解一下关键信息,这样你就能完全理解每一步的含义了。
Do you want to deploy this package at object address 0x379e...
Do you want to submit a transaction for a range of [265700 - 398500] Octas at a gas unit price of 100 Octas?
Octas
: 这是 APT 代币的最小单位,类似于人民币的“分”。1 APT = 100,000,000 Octas。[265700 - 398500] Octas
: CLI 估算出,完成这次部署交易,大约需要花费 26.5万 到 39.8万 Octas。因为你之前已经通过水龙头领了钱,所以现在有足够的余额来支付这笔费用。Transaction submitted: https://explorer.aptoslabs.com/txn/0x4ee6...
0x4ee6...
: 这是你的交易哈希 (Transaction Hash),是这次操作在区块链上的唯一凭证,就像一张收据的编号。https://explorer...
: 这是一个区块链浏览器的链接。你可以点击这个链接,在网页上看到这次部署交易的所有细节,比如谁部署的、部署了什么代码、花了多少 gas 费等等。Code was successfully deployed to object address 0x379e...
{ "Result": "Success" }
现在你的第一个智能合约已经成功活在 Aptos 测试网上了!🎉
至此,我们完整地走完了在 Aptos 网络上开发并部署第一个 Move 智能合约的全流程。回顾整个过程,我们不仅成功地安装了开发工具、初始化了项目、编写并测试了合约代码,更重要的是,我们亲身经历并解决了一个每个新手都会遇到的核心问题:账户需要有足够的 Gas 费才能在链上执行操作。
从最初因余额为零而部署失败,到学会查询账户、使用测试网水龙头获取资金,再到最终看到部署成功的喜悦,这个过程远比一帆风顺的教程更加宝贵。它让我们深刻理解了区块链交易的基本经济模型,也验证了动手实践是学习新技术的最佳路径。
现在,你的第一个智能合约已经成功“活”在了 Aptos 测试网上,这仅仅是探索 Web3 世界的开始。希望这篇详尽的实录能为你铺平道路,助你更有信心地迈出下一步。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!