Move 与 Aptos 开发笔记 Move 是面向数字资产的智能合约语言。它通过资源类型、所有权和能力约束减少资产被复制、重复使用或意外销毁的风险。Aptos Move 在基础 Move 语言之上提供账户、对象、事件和框架模块。
语言核心思想
资源安全:具有 key 能力的资源可进入全局存储,不能被隐式复制或丢弃。
所有权转移:值被移动后,原变量不能继续使用。
显式能力:copy、drop、store 和 key 控制类型允许执行的操作。
模块隔离:资源只能由定义它的模块直接创建和破坏。
静态验证:大量错误在编译阶段被拒绝,便于形式化分析。
1 2 3 4 5 6 7 flowchart LR A[源代码] --> B[解析与类型检查] B --> C[借用与能力检查] C --> D[编译为 Move 字节码] D --> E[字节码验证] E --> F[虚拟机执行] F --> G[更新链上资源]
模块、入口函数与脚本 模块用于定义结构体、函数、常量和事件,是长期部署的代码单元。入口函数可由交易直接调用。脚本是一次性交易逻辑,可以调用公开模块函数,但新项目通常优先使用模块中的 entry 函数组织入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module lesson::counter { use std::signer; struct Counter has key { value: u64, } public entry fun create(account: &signer) { move_to(account, Counter { value: 0 }); } public entry fun increase(account: &signer) acquires Counter { let address = signer::address_of(account); let counter = borrow_global_mut<Counter>(address); counter.value = counter.value + 1; } #[view] public fun value_of(owner: address): u64 acquires Counter { borrow_global<Counter>(owner).value } }
能力说明
能力
作用
copy
允许按值复制
drop
允许值在作用域结束时被丢弃
store
允许作为字段存入其他结构体
key
允许作为顶层资源进入全局存储
普通整数和布尔值通常可复制与丢弃。资产类结构体不应随意拥有 copy 或 drop,否则可能破坏唯一性约束。
值移动与借用 1 2 3 4 5 6 7 8 9 10 11 12 13 module lesson::ownership { struct Wallet has store { balance: u64, } public fun demo() { let wallet = Wallet { balance: 100 }; let wallet2 = wallet; // 此处不能再读取 wallet,因为所有权已经移动到 wallet2。 let Wallet { balance } = wallet2; assert!(balance == 100, 1); } }
不可变引用使用 &T,可变引用使用 &mut T。同一数据在存在可变借用时不能再被其他引用访问,这一约束用于避免并发修改和悬空引用。
基础类型与字符串 Move 常用基础类型包括 bool、u8、u16、u32、u64、u128、u256、address 和 vector<T>。Move 不提供普通浮点数,金融计算通常采用整数定点数。
1 2 3 4 5 6 use std::string::{Self, String}; let enabled: bool = true; let amount: u64 = 1024; let target: address = @0x2a; let message: String = string::utf8(b"hello world");
不同整数位宽之间不能隐式运算,需要显式转换并确认目标类型能够容纳原值。
Windows 与 WSL 环境 Windows 可在管理员 PowerShell 中安装 WSL,并将 WSL 2 作为默认版本:
1 2 3 wsl --install wsl --set-default-version 2 wsl --status
系统需要启用硬件虚拟化与虚拟机平台。安装完成后,可在 WSL 中运行 Linux 工具链,也可直接使用 Windows 版本的 Aptos CLI。具体安装包与命令应以所用 CLI 版本的官方文档为准。
Aptos CLI 与项目初始化 将 aptos 可执行文件所在目录加入 PATH 后,先验证命令:
1 2 aptos help aptos --version
初始化 Move 包与本地配置:
1 2 3 4 aptos move init --name lesson aptos init aptos move compile aptos move test
常见项目结构如下:
1 2 3 4 5 6 7 lesson/ ├── Move.toml ├── sources/ │ └── counter.move ├── tests/ └── .aptos/ └── config.yaml
Move.toml 保存包名、命名地址和依赖。sources 存放模块源码。.aptos/config.yaml 可能包含私钥和网络配置,不应提交到公开仓库。
Move.toml 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [package] name = "lesson" version = "1.0.0" [addresses] lesson = "_" [dev-addresses] lesson = "0x42" [dependencies.AptosFramework] git = "https://github.com/aptos-labs/aptos-core.git" rev = "mainnet" subdir = "aptos-move/framework/aptos-framework"
命名地址写为 _ 时,编译或发布命令必须从开发地址、配置文件或 --named-addresses 参数获得实际值。若地址已经写成固定的十六进制值,通常无需再次提供。
1 2 aptos move compile --named-addresses lesson=0 x42 aptos move test --named-addresses lesson=0 x42
依赖下载故障 首次编译可能需要拉取 Git 依赖。出现网络失败时,应依次检查代理、DNS、Git 是否可用、依赖仓库地址和分支是否存在。已有完整缓存时,可尝试跳过最新依赖获取:
1 aptos move compile --skip-fetch-latest-git-deps
该参数不能修复缺失或损坏的缓存。若需使用本地依赖,应在 Move.toml 中明确配置本地路径,并固定经过验证的版本。不要直接在依赖缓存目录中执行破坏性重置,因为缓存可能被多个项目共享。
单元测试 1 2 3 4 5 6 7 8 9 10 11 #[test_only] module lesson::counter_tests { use lesson::counter; #[test(account = @0x42)] fun create_and_increase(account: signer) { counter::create(&account); counter::increase(&account); assert!(counter::value_of(@0x42) == 1, 1001); } }
测试应覆盖正常路径、重复初始化、资源不存在、权限不足、整数边界和中止码。发布前还应执行格式化、静态检查和本地网络交互测试。
发布流程 1 2 3 4 5 6 7 8 9 10 11 12 sequenceDiagram participant Dev as 开发者 participant CLI as Aptos CLI participant Node as Aptos 节点 participant VM as Move VM Dev->>CLI: 编译与测试 CLI->>CLI: 生成字节码与元数据 Dev->>CLI: 发起发布交易 CLI->>Node: 签名并提交交易 Node->>VM: 验证并执行字节码 VM-->>Node: 写入模块与资源 Node-->>Dev: 返回交易结果
发布前必须确认网络、发布账户、命名地址、Gas 预算和依赖版本。模块升级还需要检查兼容性规则,避免修改已发布资源的存储布局。
原始操作截图