Move合约学习与实践

1,389 阅读7分钟

在区块链L1层,除了以太系为主的用Solidity编写的智能合约之外,还有一类是Move语言编写的智能合约,本文以Aptos链为目标,使用Aptos CLI工具进行Move合约的编译、部署以及合约交互。

准备工作

  1. 安装Aptos CLI
  2. 下载aptos-core源码

安装Aptos CLI:aptos.dev/tools/aptos…

aptos-core代码:github.com/aptos-labs/…

一、创建账户

首先,创建一个工作目录,命令行执行

mkdir first-move-module

cd first-move-module

然后,初始化Aptos:

aptos init

在命令行交互时,会出现提示:

Configuring for profile default Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet],

选择devnet,然后出现让你输入私钥的提示:

Enter your private key as a hex literal (0x...) [Current: None | No input: Generate new key (or keep one if present)]

选择让工具包生成新的key,回车继续,得到:

No key given, generating key... Account 0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245 doesn't exist, creating it and funding it with 100000000 Octas Account 0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245 funded successfully Aptos CLI is now set up for account 0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245 as profile default! Run aptos --help for more information about commands { "Result": "Success" }

0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245这个地址就是这次随机生成的,作为default地址使用,比如为此账户注入资金时,可以使用命令: aptos account fund-with-faucet --account default

运行后得到:

{ "Result": "Added 100000000 Octas to account 0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245” }

二、编译Move合约

暂且不考虑Move合约的编写问题,先拷贝Aptos的示例合约:hello_blockchain.move和hello_blockchain_test.move到我们刚才创建的目录下(aptos-core/aptos-move/move-examples

注意要在此目录下创建Move.toml,写法参考示例合约中的Move.toml,但需要注意的是,要把dependencies的路径指向aptos-core下的aptos-framework。有两种写法:

  1. 配置本地aptos-framework路径
[dependencies]
AptosFramework = { local = "/Users/xxx/xxx/aptos-core/aptos-move/framework/aptos-framework" }

  1. 配置远程aptos-framework路径
[dependencies.AptosFramework] 
git = "<https://github.com/aptos-labs/aptos-core.git>" 
rev = "mainnet" 
subdir = "aptos-move/framework/aptos-framework"

然后进行编译:

aptos move compile --named-addresses hello_blockchain=default 
Compiling, may take a little while to download git dependencies... 
INCLUDING DEPENDENCY AptosFramework 
INCLUDING DEPENDENCY AptosStdlib 
INCLUDING DEPENDENCY MoveStdlib 
BUILDING Examples 
{ 
"Result": 
[ "4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245::message" ] 
}

可以运行测试合约的命令:

>> aptos move test --named-addresses hello_blockchain=default

INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING Examples
Running Move unit tests
[ PASS    ] 0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245::message::sender_can_set_message
[ PASS    ] 0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245::message_tests::sender_can_set_message
Test result: OK. Total tests: 2; passed: 2; failed: 0
{
  "Result": "Success"
}

三、发布合约到链上

>> aptos move publish --named-addresses hello_blockchain=default
 

Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING Examples
package size 1629 bytes
Do you want to submit a transaction for a range of [155500 - 233200] Octas at a gas unit price of 100 Octas? [yes/no] >
yes
{
  "Result": {
    "transaction_hash": "0xddb1712ce3beba1bc6673d63c9a0930828d8beab445a64e82e1298b2ec70aff0",
    "gas_used": 1555,
    "gas_unit_price": 100,
    "sender": "4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245",
    "sequence_number": 0,
    "success": true,
    "timestamp_us": 1715830600430913,
    "version": 17592951,
    "vm_status": "Executed successfully"
  }
}

这样执行成功后,这个module就存储在链上了,具体的存储位置是在执行部署的账户下,可以通过aptos account list 查看。

四、调用链上合约

在Move合约中,标记为entry的函数是交互的入口,在这个示例合约中是set_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;
        }
    }

使用默认账户调用此方法,命令如下:

aptos move run \
  --function-id 'default::message::set_message' \
  --args 'string:hello, blockchain'

得到结果:

{
  "Result": {
    "transaction_hash": "0xb1fc7b33e289d818a97108c949b9cb79ac13fd879028a9d92e9d36c5d6ce2df3",
    "gas_used": 446,
    "gas_unit_price": 100,
    "sender": "4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245",
    "sequence_number": 1,
    "success": true,
    "timestamp_us": 1715831045718481,
    "version": 17596976,
    "vm_status": "Executed successfully"
  }
}

显然这个调用作用是将一个‘hello, blockchain’的字符串写入一个MessageHolder的结构体中,我们可以通过下面这条查询判断是否在链上写入成功: curl https://api.devnet.aptoslabs.com/v1/accounts/4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245/resource/0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245::message::MessageHolder

执行后得到响应:

{"type":"0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245::message::MessageHolder","data":{"message":"hello, blockchain"}}

也就是说,通过调用set_message方法,我们把“hello, blockchain”这个字符串写入了地址为0x4c56adc7c6757b9947f459f7b676c7c8bd07ce690f082d5bb07ee3e15db34245的Account的一个叫MessageHolder的结构体下。

另一种验证方式,仍是通过查看Account下所有resource,运行aptos account list后,得到一个JSON响应体,格式化后可以看到此账户下的存储资源如下图所示:

aaaa.png

至此,一个简单的Move合约就创建并部署完成了。

目前,Aptos以及SUI都支持Move合约,与以太系的Solidity基本原理和存储结构虽然不太相同,但也相对容易理解,并且有配套的CLI工具和几种主流编程语言的SDK可以使用,不妨学习了解一下。