# Sui Move Version: 0.1.0 Move 是一個重視資源移動的語言,由原本的 Diem 團隊開發,後來 Diem 團隊解散拆分為 Sui 跟 Aptos 兩大公鏈,Sui 跟 Aptos 都是採用 Move 來開發智能合約,但對於自身的架構做了些調整,所以在找教學時,會發現 Sui 上寫的智能合約跟 Aptos 的需要分開處理,因此這邊我們只討論 Sui Move。 這篇主要是對 Sui Move 有個大致上的了解,會有一些程式碼語法做舉例,但不會有實作步驟 > Note: 內容中會採用 Solidity 做對比,若沒有 Solidity 基礎的人可以忽略該部分 # Fundamental 在解釋 Sui Move 前,應該先了解 Sui 在基礎架構上做了哪些調整,個人認爲 Sui 最大的調整在於 Account 存儲的資料變多了,從 Solidity 一個 Account(address) 只能紀錄 nonce balance,到 Sui 當中,多了**能紀錄一個 object 的資料**,這個 object 在 Sui 裡面比較常用的詞是**資源**,但再通俗點說,就是 NFT / Fungible Token 等等資產,在程式中 object 以 struct 的資料型態為底,並在上面附加一些屬性(後面會介紹) //Ref solidity account link 在 Solidity 當中,紀錄 token 發行狀況通常是利用合約 mapping 的方式,也就是 address 對應 balance,但這些資料都是存在於合約裡面的,在區塊鏈上,並不依附於你個人,這意思是,假設這個合約要是之後損壞或遺失,你的 token 就會全數遺失,因為紀錄**你有多少 token** 的資訊存在於合約中 但如果是 Sui,調用合約所產生出來的 token 會是以 object 的形式,並且存在於 Account 底下,那在 Sui 上資料就會顯示,這個帳戶多了一個 token object 總結一下兩者差別,假設我需要找一個 0x12 這個 Account 有多少 USDT - 在 Solidity,我需要找到 USDT 的合約,並且調用合約中的 `balanceOf`,並且放入 0x12 當作參數來尋找 - 在 Sui,直接找尋這個 0x12 底下擁有的 object,並且 type 為 Token,找裡面的欄位來得到妳想要的資料,不需要知道發行這個 Token 得合約為何 # Module Move 是一個基於 Rust 改寫的語言,因此在裡面會看到很多與 Rust 相似的語法 在 Sui 中,建立 `module` keyword 就是建立合約,接著再宣告 package 跟 module,在 Sui 中發布合約都是以 package 為單位,一個 package 裡面可以裝數個 module,且在發布之後會給這個 package 一個 package id,類似於 Solidity 裡面的 contract address ```rust module package_name::module_name { // module code goes here } ``` # Object 在 Sui 中,建立 object 的方法是使用 `struct` 關鍵字,但如果只是 `struct` 還不能算是 Sui 的 object,要加上 **Abilities**,這些 abilities 可以看作是為 struct 添加屬性,例如 key 使得有個全域的 ID,copy 讓其 data 可以被複製等等 //todo Ref ability link 要成為 Sui object 必須要有 **key**,這樣在轉移的時侯才可以透過 id 知道要轉移哪個 object 底下例子可以看到,建立一個 SumObject object,包含以下資訊 - 有 key 這個 ability - 有以下欄位 - id,型別為 UID(from sui package) - number_1,型別為 u8 - number_2,型別為 u8 <!-- todo: store 的屬性要說明清楚 --> ```rust use sui::object::UID; // import package struct SumObject has key { id: UID, number_1: u8, number_2: u8, } ``` # Safety ## Without drop 這邊簡單寫一個 `create()`,用於創建一個新的 object 解釋一下這個 function,前三個參數用來創建 object,最後一個 ctx 可以把他當作呼叫這個 function 會帶的交易資訊,例如 sender 之類的,在 SDK 呼叫這個 function 會自動帶入這個參數 ```kotlin struct Color has key { id: UID, red: u8, green: u8, blue: u8 } public entry fun create(red: u8, green: u8, blue: u8, ctx: &mut TxContext){ let Color = new Color(red, gree, blue); } //Scope end ``` 再回過頭看這個 function,乍看之下沒有問題,但實際執行下去會報錯,錯誤訊息應該是這個 struct 沒有 drop 屬性,這邊就引出一個 Sui 對於 object 的安全保護機制,也就是 **object 被創建後不能被任意丟棄** Move 在 Scope 這部分的處理是,如果在 Scope 裡面創建的變數,脫離這個 Scope 後便會丟棄,這點跟 Rust 是一樣的(題外話,所以在 Rust 中傳入 struct 參數盡量會以 Reference 的形式傳入) 那以這個 function 為例,就是在 `create()` 這個 Scope 裡面建立 Color 這個 struct,接著在 //Scope end 這個地方,Color 被丟棄,但在 Sui 中,不允許資源被隨意丟棄 (除非有 drop 屬性) 因此,需要把創建出來的 object 做一個 transfer 的處理,明確定義這個 object 應該怎麼處理傳送到哪裡,而不是創建出來不管,放任被丟棄 這邊我們傳給對這個 function 發出交易的用戶 ```rust public entry fun create(red: u8, green: u8, blue: u8, ctx: &mut TxContext){ let Color = new Color(red, gree, blue); // 轉移給 sender transfer::public_transfer(color_object, tx_context::sender(ctx)) } ``` 這邊對 object 的處理,除了傳送給指定 Account 以外,也有其他選項,例如 Immutable / Share 等等,可以參考 //Ref ownership link ## onlyOwner 在 Solidity 中,我們要避免別人任意調用 function,通常會採用的做法是加上 `onlyOwner`的 modifier 來進行權限控管,但在 Sui 裡面,因為 object 架構的關係,從根本上避免了 onlyOwner 的問題 這邊寫一個能任意更動 token 發行量的 function ```rust public fun setSupply(treasury: &mut Treasury, amount: u8){ treasury.supply = amount; } ``` 這 function 需要將紀錄發行量的 treasury object 當作參數放入其中,並藉此更動發行量 但由於第一個參數是 object,在 Sui, object 的所有權依附在 account 底下,因此,能觸發這個 function 的 account,只有擁有 treasury 的 account,因為這樣才能將 object 當作參數放入 function 中 因為在 Sui 中,不能隨意存取別人的 object,將它當作參數放入,只能放入自己的 object,因此只要 treasury 的所有權沒有傳送出去,別人就不可能把不屬於他的 treasury object 放入上面的 function 藉此改變發行量 # 總結 ## 資源導向的設計 Sui 官方強調的是,他們是主打遊戲跟資產的公鏈,我認為,如果遊戲內的物品多為 NFT 的形式,那 Sui 在遊戲上的支援相比與 EVM 要好上許多,因為 object 本身就可以視為一種 NFT,以及讓 object 附於 account 的架構,更貼近於現實世界,每個人都有自己的資產,且不能隨意掠奪他人的資產,也讓開發上更為直覺 ## 同質化代幣 延續上點,因為所有的 object 都是類似於 NFT 的形式,因此反而在處理同質化代幣上變得有些難以處理,同質化代幣就相當於現實的錢,而 Sui 的紀錄上,token 是一堆一堆表示的,你可以隨意 merge 跟 split,並且每堆 token 都是一個 object,這個 object 裡面的欄位會紀錄 value 有多少,也因此,如果你想要傳送 token 給其他人,需要先將你想傳送的 vaule 從 object 中 split 出來,成為一個新的 object,再將這新 object 傳送出去,因為在 Sui 中,最小的傳送單位是 object,收到的人再將收到的 object merge 再一起 (object type 需要一樣) 也就是說,在 Sui 同質化代幣的處理上,需要不斷的 merge 跟 split,這部分要是開發者處理不好,就會造成多個散落的 object,在處理上比起 Solidity 單純做加減而言,是比較麻煩些 ## 開發者資源匱乏 新語言新架構,會遇到文件不完整的狀況並不稀奇,但 Sui 還有遇到一些特殊的問題,例如說,因為 Sui 是以 Move 為基礎下去改良,Sui 文件上只有紀錄改動的部分,並沒有一個文件紀錄所有語法,需要先分別找出 Move 的語法,再搭配 Sui Standard Library,拼湊在壹起才能有一個比較好的學習體驗 另外在新手引導沒有比較完整的流程,在 Sui Docu 中雖然不斷強調資源導向,創建資源以及轉移是 Sui 主要的特點,但沒有**先提出 object 在這裡面扮演的角色為何**,資源是什麼,沒有一個比較具體的例子,因此雖然能照著文件建立 module 建立 object,但卻容易陷入不知道怎麼用的窘境 (因此在上述 Fundamental 中,先建立一個比較完整的認識) 至於 Sui SDK 的部分就很單純,文件還不夠齊備,以 sui.js 為例,常見的範例太少,蠻容易遇到 type 不知道怎麼轉換的問題,進一步考驗開發者的功力