Zihao

Make small but daily progress

0%

EOS合约基础教程--开发智能合约`Hello World`

1 开发工具

使用eosio-cpp工具来进行开发

1
2
brew tap eosio/eosio.cdt
brew install eosio.cdt

2 创建合约目录 hello

1
2
mkdir hello
cd hello

注意: 合约目录非常重要,因为 EOS 的合约部署是以目录来进行的。

3 创建一个合约文件 hello.cpp

1
touch hello.cpp

文件名无所谓,惯例是合约名 + .cpp

4 Hello World 合约

1
vim hello.cpp

合约内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class hello : public contract {
public:
using contract::contract;

[[eosio::action]]
void hi() {
print("Hello World");
}
};
EOSIO_DISPATCH( hello, (hi))

5 合约详解

5.1 引入头文件

最重要的头文件是 <eosiolib/eosio.hpp> ,该头文件包含了大量的其它头文件。

1
2
3
4
5
6
#pragma once
#include <eosiolib/action.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/contract.hpp>

源代码在 https://github.com/EOSIO/eosio.cdt/blob/master/libraries/eosiolib/eosio.hpp

5.2 宏定义

1
2
3
#define CONTRACT class [[eosio::contract]]
#define ACTION [[eosio::action]] void
#define TABLE struct [[eosio::table]]
这三个宏定义分别用来修饰一个 **合约** 、**动作** 、**表**

5.3 命名空间

引入命名空间 eosio 。EOS 把所有的类和函数都放在 eosio 命名空间下。

1
2
3
#include <eosiolib/eosio.hpp>

using namespace eosio;

5.4 继承

EOS 中所有的合约都必须继承自一个基础合约 eosio::contract 。 该基础合约在 <eosiolib/contract.hpp> 头文件中定义

1
2
3
4
5
#include <eosiolib/eosio.hpp>

using namespace eosio;

class hello : public contract {};

5.5 动作 action

EOS 合约可以包含一些动作 ( action ) 和一些用于存储数据的表 table,这些表都是一个结构体 struct。 如果不用存储数据,那么表是可以忽略的。

EOS 合约中的动作都需要 [[eosio::action]] 属性来修饰。

1
2
3
4
5
6
7
8
9
10
11
#include <eosiolib/eosio.hpp>

using namespace eosio;

class hello : public contract {
public:
using contract::contract;

[[eosio::action]]
void hi( name user ) {}
};

5.6 输出

需要输出信息,比如一些字符串等,可以使用 eosio::print() 方法。

eosio::print() 方法在 <eosiolib/print.hpp> 头文件中定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class hello : public contract {
public:
using contract::contract;

[[eosio::action]]
void hi() {
print("Hello World");
}
};

5.7 定义动作

一个 EOS 合约中可以有多个动作 action ,当 EOS 接收到一个事务后,会将该事务分发给相应的合约,或者说,调用相应的合约的动作。

为了确保合约的哪个动作可以调用,需要使用 EOSIO_DISPATCH 宏来告诉 EOS。

EOSIO_DISPATCH 宏在 <eosiolib/dispatcher.hpp> 头文件中定义,该宏的第一个参数是合约的名字,第二个参数,是多个小括号 () 扩起来的多个动作的方法名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class hello : public contract {
public:
using contract::contract;

[[eosio::action]]
void hi() {
print("Hello World");
}
};

EOSIO_DISPATCH( hello, (hi))

6 编译合约

使用 eosio-cpp 命令来编译合约

1
eosio-cpp -o hello.wasm hello.cpp --abigen

6.1 选项说明

选项 说明
-o hello.wasm 该选项用于指定合约编译后的输出文件。必须是以 .wasm 作为文件扩展名
hello.cpp 合约源文件
–abigen 该选项用户指定合约编译时同时生成 abi 文件

运行该命令输出结果如下

1
2
3
4
$ eosio-cpp -o hello.wasm hello.cpp --abigen
Warning, empty ricardian clause file
Warning, empty ricardian clause file
Warning, action <hi> does not have a ricardian contract

编译完成后,当前 hello 目录就会多出两个文件

1
2
$ ls 
hello.abi hello.cpp hello.wasm

7 部署合约

部署合约的前提就是需要存在一个账户。在前面的章节中,我们创建了一个 hello 账户,接下来我们就使用在这个账户上部署合约

部署合约需要使用到 cleos set contract 命令。

1
cleos set contract hello ../hello -p hello@active

7.1 选项说明

选项 说明
cleos set contract 部署合约的命令
hello 部署合约的账户,必须已经存在
../hello 合约所在的目录名,该目录下必须包含和目录相同的 .wasm 和 .abi 文件
-p hello@active 该选项用于指定权限,该权限必须包含 hello 账户的 active 权限

该命令的运行结果如下

1
2
3
4
5
Reading WASM from ../hello/hello.wasm...
Skipping set abi because the new abi is the same as the existing abi
Publishing contract...
executed transaction: e3863e68840c354c67b4955e14bed8792fb37f7b7166e1df4b5a30e0a7f714f2 1344 bytes 518 us
# eosio <= eosio::setcode {"account":"hello","vmtype":0,"vmversion":0,"code":"0061736d0100000001300960017f006000017f60027f7f01...

8 运行合约下的动作

只能说是 hello 账户下部署了一个合约,该合约下有一个动作 hi 。

如果我们要执行这个账户下的合约的 hi 动作,需要使用到 cleos push action 命令。该命令的语法如下

1
cleos push action hello hi '[]' -p hello@active

8.1 参数说明

选项 说明
cleos push action 执行合约动作的命令
hello 合约所在的账户名
hi 要执行的合约的动作,必须是已经导出的,也就是 EOSIO_DISPATCH( hello, (hi)) 中定义的
-p hello@active 用于执行合约的权限,可以是任意权限,只要该合约运行

运行上面的命令,输出结果如下

1
2
3
executed transaction: 843a65cd56420c6f749a10cf221fdf36716067cd1718c8348c38a304ba752ab0  96 bytes  270 us
# hello <= hello::hi ""
>> Hello World

看到最后的那个 >> Hello World 吗? 这个就是 print("Hello World"); 的输出结果

欢迎关注我的其它发布渠道