本篇會帶大家創建一個新項目,並部署到 Aptos 區塊鏈上。
設定部分沿用上一篇的設定即可,所以如果環境有問題的,最好先解決後在開始本篇內容。
會用到許多之前介紹的東西,如果有不熟悉的,記得再回去多看幾次,讓我們開始吧。
$ mkdir userinfo
$ cd userinfo
$ ~/bin/aptos move init --name UserInfo
成功的話會看到資料夾內容如下
sources: 放置 module 的目錄
Move.toml 包的清單文件。包含使用的數據和依賴項地址
userinfo/
├── sources/
└── Move.toml
在 Move.toml 文件新增 sender
| 上面步驟同時會自動將依賴項夾到 AptosFramework package 中
[addresses]
sender = "0x42"
使用下面指令編譯 package,同時也會順便檢查是否正確
$ ~/bin/aptos move compile
如果有任何問題會看到類似下面這警告
Warning: unknown field name found. Expected one of [package, build, addresses, dev-addresses, dependencies, dev-dependencies], but found 'address'
如果一切順利會看到這樣
{
"Result": []
}
這時候 package 裡會多一層 build 目錄:
build/
└── UserInfo
├── bytecode_modules
├── source_maps
└── sources
└── dependencies
├── AptosFramework
├── account.move
├── coin.move
........
└── MoveStdlib
├── string.move
├── signer.move
├── vector.move
........
MoveStdlid
- Move 語言的標準庫,例如處理向量和簽名者的函數
AptosFramework
- 一組特定於 Aptos 區塊鏈的 coin 模塊,例如用於類似 ERC20 的可替代 token 的模塊,以及 account 用於帳戶元數據的模塊。
在 Move 裡,每個用戶地址都有自己的對象儲存
可以使用 Move 提供的內置方法來訪問
/// 檢查對象是否存在於儲存中
fun exists<T>(addr): bool;
/// 返回對象的只讀引用
fun borrow_global<T>(addr): &T;
/// 返回對象的可變引用
fun borrow_global_mut<T>(addr): &mut T;
/// 將對象添加到儲存中
fun move_to<T>(&signer, T);
/// 從儲存中刪除對象
fun move_from<T>(addr): T;
要將資源放在用戶地址上,可以使用 &signer
在範圍內擁有參數並調用 move_to
函數。
&signer 數據類型代表當前交易的發送者帳戶,主要用於模塊中的資源儲存和訪問限制,開發者可以使用函數汲取交易發送者的地址。signer::address_of(&signer)
添加一個 UserProfile 資源結構。將其用戶名儲存在 type 中
我們在 userinfo/sources 裡面新增一個名為 Sender.move 的文件,內容如下:
module sender::user_info {
// 從位於地址 std 上的模塊字符串導入字符串類型
// std 是 AptosFramework 依賴的傳遞 std 地址,提供給所有 package 使用
use std::string::String;
struct UserProfile has key { username: String }
}
Move 原生型別沒有 string, 只有 vector<u8>
, 但本地標準庫提供一個 String struct
將 vector 包裝在裡面方便給開發者使用。也就是上面使用 std 拿出來的樣式。
我們需要添加 getter
和 setter
方法給 username
需要 UserProfile 從用戶全局儲存中檢索對象。我們在之前篇章有介紹過,可以使用 borrow_global()
來實現。
module sender::user_info {
use std::string::String;
struct UserProfile has key { username: String }
public fun get_username(user_addr: address): String acquires UserProfile {
borrow_global<UserProfile>(user_addr).username
}
}
這邊有兩個地方需要注意
acquires
在儲存中獲取對象資源的所有函數,都需要要加上此關鍵字,確保對全局儲存的引用是安全的。;
setter 的實現有點複雜。setter 將是一個公共入口函數。與只能由其他模塊調用的通常函數不同,入口函數可以通過向 Aptos 區塊鏈發送交易來調用,其中包含函數中的參數、泛型和名稱/路徑。使用公共入口功能。
創建 if 判斷
首先對於全局存儲中沒有的情況UserProfile
,if not 我們需要使用正確的用戶名創建一個
els 只更新現有的用戶名user_info
我們使用 entry
代表是公共入口函數
&signer 允許用戶修改個人資料
module sender::user_info {
use std::string::{String, utf8};
use std::signer;
struct UserProfile has key { username: String }
public fun get_username(user_addr: address): String acquires UserProfile {
borrow_global<UserProfile>(user_addr).username
}
public entry fun set_username(user_account: &signer, username_raw: vector<u8>) acquires UserProfile {
// 將 username_raw 包裝成 utf8
let username = utf8(username_raw);
// 透過 signer 拿到交易發送者地址
let user_addr = signer::address_of(user_account);
// 檢查資源是否存在於儲存中
if (!exists<UserProfile>(user_addr)) {
// 產生新的 userName
let info_store = UserProfile{ username: username };
// 發布 info_store 資源到帳戶 (user_account)
move_to(user_account, info_store);
} else {
// `borrow_global_mut` 是獲取可變引用,可以這樣改變儲存中的資源
let existing_info_store = borrow_global_mut<UserProfile>(user_addr);
existing_info_store.username = username;
}
}
}
備註
move_to
用法如下
需要一個 &signer 參數來發布 T 資源到帳戶,透過 &signer 確保經過身份認證的用戶才能更新。
move_to<T>(&signer, T)
恭喜,我們已經將完成一半,接下來會進行測試來確認一切是否正常,讓我們 Move to Day 29