# Hello Move | Move dApp极速入门（一）

By [Web3dAppDevCamp](https://paragraph.com/@apecoder) · 2022-07-30

---

> **作者：** 李大狗、王宁波、陈俊锋 @ NonceGeekDAO
> 
> 我们从二进制到汇编到各种高级语言，一直都在发展。智能合约也一样，从比特币的Script到以太坊的Solidity，往前跨了一大步，但是，Solidity能包打天下了吗？事实上，层出不穷的安全问题一直在困扰我们，在很大程度上限制了整个行业的发展。「安全」在金融场景，尤其是DeFi时代，比以往任何时候都更加重要，这些问题我们不能视而不见。如果区块链行业继续发展，势必会有更好的智能合约语言出来。
> 
> 在这些大前提下，新的智能合约语言Move被设计出来了。Move是面向「资源」编程的。我个人认为，「面向资源编程」是智能合约语言的一个变革，说白了，智能合约语言又往前迈了一大步。
> 
> ——jolestar
> 
> ——[https://starcoin.org/zh/developer/blog/starcoin\_move\_resource](https://starcoin.org/zh/developer/blog/starcoin_move_resource)
> 
> ——[https://mirror.xyz/jolestar.eth/sQ0nMCO3eNig6gCzqQO7xew1mn8oUi1-rKtfZKmGlNI](https://mirror.xyz/jolestar.eth/sQ0nMCO3eNig6gCzqQO7xew1mn8oUi1-rKtfZKmGlNI)

本系列将以 Starcoin 为例，讲解 Move 语言以及 Move dApp 的开发，及其背后的计算机原理。

本系列的全文更新中，见：

> [https://github.com/WeLightProject/Web3-dApp-Camp/tree/main/move-dapp](https://github.com/WeLightProject/Web3-dApp-Camp/tree/main/move-dapp)

同步的打卡任务：

> [https://github.com/WeLightProject/Web3-dApp-Camp/discussions/categories/projects-others](https://github.com/WeLightProject/Web3-dApp-Camp/discussions/categories/projects-others)

1 节点启动
------

下载最新的发行版 Starcoin 节点程序（MacOS 将其拷贝至`/usr/local/bin`目录即可）：

> [https://github.com/starcoinorg/starcoin/releases](https://github.com/starcoinorg/starcoin/releases)

在此以`Starcoin`网络为基础，展现如何启动一个节点：

> [https://starcoinorg.github.io/starcoin-cookbook/docs/getting-started/setup/](https://starcoinorg.github.io/starcoin-cookbook/docs/getting-started/setup/)

太长不看版——关键命令合集：

    # 启动一个本地 dev 节点
    $ starcoin -n dev
    # 启动一个本地 dev 节点的同时打开控制台，-d 参数可以确保每次打开控制台时都保有历史的数据而不是重开
    $ mkdir [folder_name]
    $ cd [folder_name]
    $ pwd
    $ starcoin -d [path_to_your_data_folder] -n dev console
    

starcoin 控制台命令：

    # 指定账户获得测试代币
    starcoin% dev get-coin 0xb7c46353c6c0e3a2559d5b12cad981e4 -v 100STC
    # 账户列表
    starcoin% account list
    # 单一账户情况查看
    starcoin% account show 0xb7c46353c6c0e3a2559d5b12cad981e4
    # 创建新账户
    starcoin% account create -p [pwd]
    

2 第一个 Move 合约 —— MyCounter
--------------------------

最小可实践例子：

> [https://github.com/starcoinorg/starcoin-cookbook/blob/main/examples/my-counter](https://github.com/starcoinorg/starcoin-cookbook/blob/main/examples/my-counter)

    module MyCounterAddr::MyCounter {
         use StarcoinFramework::Signer;
    
         struct Counter has key, store {
            value:u64,
         }
         public fun init(account: &signer){
            move_to(account, Counter{value:0});
         }
         public fun incr(account: &signer) acquires Counter {
            let counter = borrow_global_mut<Counter>(Signer::address_of(account));
            counter.value = counter.value + 1;
         }
    
         public(script) fun init_counter(account: signer){
            Self::init(&account)
         }
    
         public(script) fun incr_counter(account: signer)  acquires Counter {
            Self::incr(&account)
         }
    }
    

MyCounter 源码分析

module 是发布在特定地址下的打包在一起的一组函数和结构体。使用script时需要与已发布的module或标准库一起运行，而标准库本身就是在 0x1 地址下发布的一组module。

module MyCounterAddr::MyCounter{ } 则在该MyCounterAddr地址下(对应Move.toml下的MyCounterAddr = "0xb7c46353c6c0e3a2559d5b12cad981e4")创建一个module。

use StarcoinFramework::Signer，是使用标准库下的Signer module，Signer 是一种原生的类似 Resource 的不可复制的类型，它包含了交易发送者的地址。引入signer类型的原因之一是要明确显示哪些函数需要发送者权限，哪些不需要。因此，函数不能欺骗用户未经授权访问其 Resource。具体可参考[源码](https://github.com/starcoinorg/starcoin-framework/blob/main/sources/Signer.move)。

    struct Counter has key, store {
            value:u64,
         }
    

使用struct定义了一个叫做Counter的结构体，同时被 key,store两种限制符修饰，Move的类型系统灵活，每种类型都可以被四种限制符所修饰。这四种限制符我们称之为 abilities，它们定义了类型的值是否可以被复制、丢弃和存储。 这四种 abilities 限制符分别是: Copy, Drop, Store 和 Key.

它们的功能分别是：

*   Copy - 被修饰的值可以被复制。
    
*   Drop - 被修饰的值在作用域结束时可以被丢弃。
    
*   Key - 被修饰的值可以作为键值对全局状态进行访问。
    
*   Store - 被修饰的值可以被存储到全局状态。
    

这里用key,store修饰，则表示它不能被复制，也不能被丢弃或重新使用，但是它却可以被安全地存储和转移。

下面则是定义的方法，

    public fun init(account: &signer){
        move_to(account, Counter{value:0});
    }
    public fun incr(account: &signer) acquires Counter {
        let counter = borrow_global_mut<Counter>(Signer::address_of(account));
        counter.value = counter.value + 1;
    }
    

定义格式则是:

public fun 函数名(参数：参数类型){ }

move函数默认是私有函数，只能在定义它们的模块中访问。关键字 public 将更改函数的默认可见性并使其公开，即可以从外部访问。

init方法参数是一个&signer，意味着该方法必须是一个账户合法签名过后才可以调用，move\_to则是move的一个原语，作用是发布、添加Counter资源到 signer 的地址下。Move的账户模型，code和data是存储在一个账户地址下的。

下面是列举的常用原语

*   move\_to< T >(&signer, T)：发布、添加类型为 T 的资源到 signer 的地址下。
    
*   move\_from< T >(addr: address): T - 从地址下删除类型为 T 的资源并返回这个资源。
    
*   borrow\_global< T >(addr: address): &T - 返回地址下类型为 T 的资源的不可变引用。
    
*   borrow\_global\_mut< T >(addr: address): &mut T - 返回地址下类型为 T 的资源的可变引用。
    
*   exists< T >(address): bool：判断地址下是否有类型为 T 的资源。。
    

incr方法参数也是一个&signer，意味着该方法必须是一个账户合法签名过后才可以调用,

关键字 acquires，放在函数返回值之后，用来显式定义此函数获取的所有 Resource。

Signer::address\_of(account) 从签名者中拿到address

borrow\_global\_mut上面有介绍到，可变借用到address下到resource Counter，然后将Counter结构体下的value进行+1操作。

这下面的两个方法则是script方法,它与上面两个函数有什么区别呢？

*   public fun : 方法可以在任何模块中被调用。
    
*   public(script) fun：script function 是模块中的入口方法，表示该方法可以通过控制台发起一个交易来调用，就像本地执行脚本一样
    

下个版本的 Move 会用 public entry fun 替代 public(script) fun

Self则是代表自身module。

      public(script) fun init_counter(account: signer){
            Self::init(&account)
         }
    
         public(script) fun incr_counter(account: signer)  acquires Counter {
            Self::incr(&account)
         }
    

### 2.1 编译

下载第一个实例的源码：

    $ git clone git@github.com:WeLightProject/Web3-dApp-Camp.git
    $ cd Web3-dApp-Camp/move-dapp/my-counter
    

Move的包管理工具为Move Package Manager(mpm),它类似于Rust的Cargo或者Node的NPM。 可以通过`mpm package new my-counter`来创建一个新项目，典型的目录结构为:

    my-counter
    ├── Move.toml
    └── sources
        └── MyCounter.move 
    

*   sources用来存档Move的模块,它类似于与Java中的类文件。
    
*   Move.toml-用来存放配置文件：包括包的原数据、依赖和命名地址。
    
*   上述文件构成一个Move包(Move Package) 更详细的Move包管理参考[文档](https://starcoinorg.github.io/starcoin-cookbook/zh/docs/move/move-language/packages/)
    

修改`move.toml`中的地址为你用来部署的地址。

image-20220727123922351

编译：

    $ mpm release
    

接下来会在`release`文件夹中，看到你编译好的二进制文件。

image-20220727124134402

### 2.2 控制台部署

在 Starcoin Console 中执行如下命令即可部署：

    starcoin% dev deploy [path to blob] -s [addr] -b
    

> \-s 即--sender,-b即--blocking，表示阻塞等待命令执行完成

如果遇到账户被锁，用 `unlock`命令解锁即可。

    account unlock [addr] -p [pwd]
    

其中`pwd`即是在`1.2`中创建的密码。

部署成功后能看到：

image-20220727124625807

> 💡需要注意的是，在Move中代码存储在个人的地址上，而非像以太坊那样的公共地址上。因此合约部署后并不会创建新地址，当我们想要调用合约时需要采用部署合约人的地址+合约名来调用该合约。

### 2.3 控制台调用

> [https://starcoinorg.github.io/starcoin-cookbook/docs/move/interacting-with-the-contract](https://starcoinorg.github.io/starcoin-cookbook/docs/move/interacting-with-the-contract)

1.  调用 init\_counter 脚本函数来初始化资源。
    

    starcoin% account execute-function --function {MyCounterAddr-in-Move.toml}::MyCounter::init_counter -s 0x23dc2c167fcd16e28917765848e189ce -b
    

其中:

*   `{MyCounterAddr-in-Move.toml}::MyCounter::init_counter`为完整的函数链上地址，包括合约所在地址+包名+函数名。
    
*   \-s 即--sender,-b即--blocking，表示阻塞等待命令执行完成
    

1.  查看Counter资源
    

    starcoin% state get resource 0x23dc2c167fcd16e28917765848e189ce 0x23dc2c167fcd16e28917765848e189ce::MyCounter::Counter
    

在Move中合约的数据被称为`资源(resource)`，由于读取数据不改变链上状态，因此不需要-s -b，不会执行交易，也不消耗状态。

> 感兴趣的同学可以试着调用`incr_counter`，并再次查看`Counter`是否+1。

---

*Originally published on [Web3dAppDevCamp](https://paragraph.com/@apecoder/hello-move-move-dapp)*
