前置条件
在开始之前,请确保你已安装rustup 以及最新版本的 rustc 和 cargo。目前,我们在 Rust v1.58.1+ 上进行测试。
你还需要安装 wasm32-unknown-unknown 目标以及 cargo-generate Rust crate。
你可以通过以下命令检查版本:
目标
- 创建并与一个智能合约交互,该合约可以增加计数器并将其重置为给定值
- 了解 CosmWasm 智能合约的基础知识,学习如何在 Injective 上部署它,并使用 Injective 工具与之交互
CosmWasm 合约基础
智能合约可以被视为一个单例对象的实例,其内部状态持久化在区块链上。用户可以通过发送 JSON 消息来触发状态更改,也可以通过发送格式化为 JSON 消息的请求来查询其状态。这些 JSON 消息与 Injective 区块链消息(如MsgSend 和 MsgExecuteContract)不同。
作为智能合约开发者,你的工作是定义组成智能合约接口的 3 个函数:
instantiate():在合约实例化期间调用的构造函数,用于提供初始状态execute():当用户想要调用智能合约上的方法时被调用query():当用户想要从智能合约获取数据时被调用
instantiate、一个 query 和两个 execute 方法。
从模板开始
在你的工作目录中,通过运行以下命令快速启动具有推荐文件夹结构和构建选项的智能合约:src/contract.rs 文件中,你会发现标准的 CosmWasm 入口点 instantiate()、execute() 和 query() 已正确暴露和连接。
状态
你可以在 CosmWasm 的文档中了解更多关于 CosmWasm 状态的信息。
State 处理数据库的状态,智能合约数据在其中存储和访问。
起始模板具有以下基本状态,一个单例结构体 State 包含:
count,一个 32 位整数,execute()消息将通过增加或重置它来与之交互。owner,MsgInstantiateContract的发送者address,它将决定某些执行消息是否被允许。
cosmwasm-storage,它为数据容器(如”singleton”和”bucket”)提供了方便的高级抽象,自动为常用类型(如结构体和 Rust 数字)提供序列化和反序列化。此外,cw-storage-plus crate 可用于更高效的存储机制。
注意 State 结构体如何同时持有 count 和 owner。此外,derive 属性用于自动实现一些有用的 trait:
Serialize:提供序列化Deserialize:提供反序列化Clone:使结构体可复制Debug:使结构体能够打印为字符串PartialEq:提供相等性比较JsonSchema:自动生成 JSON schema
Addr 指的是以 inj 为前缀的人类可读的 Injective 地址,例如 inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt。
InstantiateMsg
你可以在 CosmWasm 的文档中了解更多关于 InstantiateMsg 的信息。
MsgInstantiateContract 在区块链上实例化合约时,InstantiateMsg 会提供给合约。这为合约提供了其配置以及初始状态。
在 Injective 区块链上,合约代码的上传和合约的实例化被视为独立的事件,这与以太坊不同。这是为了允许一小组经过审查的合约原型作为多个实例存在,共享相同的基础代码,但使用不同的参数进行配置(想象一个规范的 ERC20,以及使用其代码的多个代币)。
示例
对于你的合约,合约创建者需要在 JSON 消息中提供初始状态。我们可以在下面的消息定义中看到,消息持有一个参数count,表示初始计数。
消息定义
合约逻辑
在contract.rs 中,你将定义你的第一个入口点 instantiate(),即合约被实例化并传递其 InstantiateMsg 的地方。从消息中提取计数并设置初始状态,其中:
count被分配为消息中的计数owner被分配为MsgInstantiateContract的发送者
ExecuteMsg
你可以在 CosmWasm 的文档中了解更多关于 ExecuteMsg 的信息。
ExecuteMsg 是通过 MsgExecuteContract 传递给 execute() 函数的 JSON 消息。与 InstantiateMsg 不同,ExecuteMsg 可以作为几种不同类型的消息存在,以适应智能合约可以向用户公开的不同类型的函数。execute() 函数将这些不同类型的消息多路复用到其适当的消息处理逻辑。
我们有两个 ExecuteMsg:Increment 和 Reset。
Increment没有输入参数,将 count 的值增加 1。Reset接受一个 32 位整数作为参数,并将count的值重置为输入参数。
示例
Increment 任何用户都可以将当前计数增加 1。Reset
只有所有者可以将计数重置为特定数字。有关实现细节,请参阅下面的逻辑。消息定义
对于ExecuteMsg,可以使用 enum 来多路复用你的合约可以理解的不同类型的消息。serde 属性将你的属性键重写为蛇形命名法和小写,因此在序列化和反序列化 JSON 时,你将得到 increment 和 reset 而不是 Increment 和 Reset。
逻辑
execute() 方法,它使用 Rust 的模式匹配将接收到的 ExecuteMsg 路由到适当的处理逻辑,根据接收到的消息分派 try_increment() 或 try_reset() 调用。
state 的项目。然后通过返回带有新状态的 Ok 结果来更新状态的计数。最后,它通过返回带有 Response 的 Ok 结果来终止合约的执行,确认成功。
QueryMsg
你可以在 CosmWasm 的文档中了解更多关于 QueryMsg 的信息。
GetCount 查询消息没有参数,返回 count 值。
有关实现细节,请参阅下面的逻辑。
示例
模板合约只支持一种类型的QueryMsg:
GetCount
请求:
消息定义
为了支持合约中的数据查询,你必须定义QueryMsg 格式(表示请求)以及提供查询输出的结构——在这种情况下是 CountResponse。你必须这样做,因为 query() 将通过结构化 JSON 将信息发送回用户,所以你必须让响应的形状为人所知。有关更多信息,请参阅生成 JSON Schema。
将以下内容添加到你的 src/msg.rs:
逻辑
query() 的逻辑与 execute() 类似;但是,由于 query() 在最终用户不进行交易的情况下被调用,因此省略了 env 参数,因为不需要任何信息。
单元测试
单元测试应该作为在链上部署代码之前的第一道保障。它们执行速度快,并且可以使用RUST_BACKTRACE=1 标志在失败时提供有用的回溯:
src/contract.rs 找到单元测试实现。
构建合约
现在我们理解并测试了合约,我们可以运行以下命令来构建合约。这将在我们在下一步优化合约之前检查任何初步错误。阅读更多关于为生产准备 Wasm 字节码的详细信息。
/code 并优化输出(如果你不想先 cd 到目录,可以使用绝对路径代替 $(pwd)):
CosmWasm 不建议使用 ARM64 版本的编译器,因为它产生的 Wasm 工件与 Intel/AMD 版本不同。对于发布/生产,只建议使用 Intel/AMD 优化器构建的合约。有关 CosmWasm 的说明,请参阅这里。
运行命令时你可能会收到 有关更多信息,请参阅 The Cargo Book。
Unable to update registry `crates-io` 错误。尝试将以下行添加到合约目录中的 Cargo.toml 文件并再次运行命令:artifacts 目录,其中包含 PROJECT_NAME.wasm 以及 checksums.txt,其中包含 Wasm 文件的 Sha256 哈希。Wasm 文件是确定性编译的(任何在相同 git 提交上运行相同 docker 的人都应该获得具有相同 Sha256 哈希的相同文件)。
安装 injectived
injectived 是连接到 Injective 并使你能够与 Injective 区块链交互的命令行界面和守护进程。
如果你想使用 CLI 在本地与智能合约交互,你必须安装 injectived。为此,你可以按照 injectived 安装指南中的安装指南进行操作。
或者,已准备好 Docker 镜像以使本教程更容易。
如果你从二进制文件安装
injectived,请忽略 docker 命令。
在公共端点部分,你可以找到正确的 —node 信息以与主网和测试网交互。directory_to_which_you_cloned_cw-template 必须是绝对路径。可以通过在 CosmWasm/cw-counter 目录内运行 pwd 命令轻松找到绝对路径。
打开一个新终端并进入 Docker 容器以初始化链:
jq 依赖项,稍后会用到:
testuser 的测试用户(提示时使用 12345678 作为密码)。我们只会使用测试用户来生成一个新的私钥,稍后将用于在测试网上签署消息:
你可以使用 Injective 测试水龙头 为你最近生成的测试地址请求测试网资金。
testuser 一个 Injective 测试网。在从水龙头请求 testnet 资金后,它应该也持有一些资金。
要确认,请在 Injective 测试网浏览器 上搜索你的地址以检查你的余额。
或者,你可以通过查询银行余额或使用 curl 来验证:
上传 Wasm 合约
现在是时候将你在前面步骤中编译的.wasm 文件上传到 Injective 测试网了。请注意,主网的程序不同,需要治理提案。
txhash 的交易。交易类型应该是 MsgStoreCode。
你可以在 Code 下查看 Injective 测试网上所有存储的代码。
有不同的方法可以找到你刚刚存储的代码:
- 在 Injective 浏览器代码列表上查找 TxHash;它很可能是最新的。
- 使用
injectived查询交易信息。
txhash 并验证合约已部署。
code_id 为 290:
code_id 导出为环境变量——我们需要它来实例化合约。你可以跳过此步骤并稍后手动添加,但请记下 ID。
生成 JSON Schema
虽然 Wasm 调用instantiate、execute 和 query 接受 JSON,但这不足以使用它们。我们需要向客户端公开预期消息的 schema。
为了利用 JSON-schema 自动生成,你应该注册每个需要 schema 的数据结构。
./schema 中输出 5 个文件,对应于合约接受的 3 种消息类型、查询响应消息和内部 State。
这些文件采用标准 JSON Schema 格式,应该可以被各种客户端工具使用,无论是自动生成编解码器,还是只是根据定义的 schema 验证传入的 JSON。
花一分钟生成 schema(在这里查看)并熟悉它,因为你将在下一步中需要它。
实例化合约
现在我们在 Injective 上有了代码,是时候实例化合约以与之交互了。提醒:在 CosmWasm 上,合约代码的上传和合约的实例化被视为独立的事件
查询合约
正如我们之前所知,我们唯一的 QueryMsg 是get_count。
count 是 99,正如我们实例化合约时设置的那样。
如果你查询同一个合约,你可能会收到不同的响应,因为其他人可能已经与合约交互并增加或重置了计数。
执行合约
现在让我们通过增加计数器来与合约交互。yes 12345678 | 自动将密码传递(管道)到 injectived tx wasm execute 的输入,这样你就不需要手动输入。
Cosmos 消息
除了定义自定义智能合约逻辑外,CosmWasm 还允许合约与底层 Cosmos SDK 功能交互。一个常见的用例是使用 Cosmos SDK 的 bank 模块将代币从合约发送到指定地址。示例:Bank Send
BankMsg::Send 消息允许合约将代币转移到另一个地址。这在各种场景中都很有用,例如分发奖励或向用户返还资金。
注意: 如果你想同时发送资金并在另一个合约上执行函数,不要使用 BankMsg::Send。相反,使用 WasmMsg::Execute 并设置相应的 funds 字段。
构造消息
你可以在合约的execute 函数中构造 BankMsg::Send 消息。此消息需要指定接收者地址和要发送的金额。以下是如何构造此消息的示例:
在智能合约中使用
在你的合约中,你可以向 ExecuteMsg 枚举添加一个新变体来处理此 bank send 功能。例如:execute 函数中,你可以添加一个 case 来处理此消息:
