创建一个基础的 Anchor 程序

Anchor 框架是最流行的 Rust 框架,用于开发称为程序的链上 Solana 智能合约。

Anchor 框架是最流行的 Rust 框架,用于开发称为程序的链上 Solana 智能合约。

开始 Solana 的 Anchor 开发
anchor 安装

安装 Solana 和 Anchor CLI

如果您尚未设置 Solana 开发环境,您可以查看我们的其他文章,深入了解如何进行设置以及安装用于 Anchor 和 Solana 程序开发的所有工具。

创建一个新的 Anchor 项目

在命令行上,您可以使用 Anchor CLI 创建一个新项目:

anchor init project_name

这将创建一个以您当前工作目录命名的新目录,然后为您project_name搭建一个 Anchor工作区。该init命令还将为您初始化一个新的 git 存储库和 NPM 存储库(使用 Yarn)

然后您应该cd进入新项目的根目录,然后就可以开始工作了! PS:如果您使用 VS Code,请务必在编辑器工作区中打开新项目。

Anchor 项目的结构

您的新 Anchor 项目有一些在此init过程中创建的非常重要的文件和文件夹。

重要目录

  • app- 您项目的前端代码的未来所在地(也称为 JavaScript)
  • programs- 这是您项目中所有 Solana/Anchor 程序的所在地(也称为 Rust 代码),但稍后会详细介绍
  • tests- 基于 JavaScript 的端到端测试的主页(使用 Mocha 测试框架)
  • target- 已编译的 Solana 程序和 Anchor IDL 文件的主页(稍后也会详细介绍!)

重要文件

  • Anchor.toml- 包含 Anchor 工作区的配置信息和设置的清单文件
  • Cargo.toml- 包含Cargopackage.json包管理器设置(与Node/NPM 项目的文件相同 )
  • package.json- 您的前端将使用的标准 JavaScript 包文件

构建您的Anchor程序

与基于原始 rust 的链上 Solana 程序开发不同,Anchor 项目是使用 Anchor CLI 构建的。Anchor CLI 执行与原生 cargo-build-bpf 命令相同的 BPF 编译,但它还会添加 Anchor 魔法,生成 IDL 文件

要构建您的 Anchor 项目:

anchor build

初始Anchor构建

对于每个新项目,我建议build在您之后立即运行该命令, init因为 rust 编译器可能需要一些时间来编译所有使用的各种库。所以去喝点咖啡吧。

初始构建完成后,继续并重新运行anchor build 命令。我会等待....

它应该快得多。这是因为所有编译的 Rust 代码和包并不都需要从头开始重新编译。每次重建项目时,实际上只需要重新编译更改的文件。使每个后续构建都比初始构建快得多。

构建生成的关键文件

Anchor 构建命令不仅会将您的 rust 代码编译为可部署的 Solana 程序,还会生成一些其他文件。

为简单起见,我将仅介绍从构建命令生成的直接重要的文件/文件夹。

在您的target目录中,会生成以下重要文件:

  • deploy/project_name.so- Solana 程序二进制文件,可以直接部署到区块链
  • deploy/project_name-keypair.json- 将用于您的程序的新的系统密钥对文件
  • idl/project_name.json- 一个 JSON 文件,您的前端将使用该文件使与链上程序的交互变得更加容易
  • types/project_name.ts- 将由您的前端使用的自动生成的JavaScript包

Anchor.toml 文件

清单Anchor.toml文件是 Solana 程序和 Anchor 工作区的某些配置设置所在的位置。

为简单起见,我将仅关注 Anchor 介绍的2 个主要部分:

  • [programs.cluster], 和
  • [provider]

您可以Anchor.toml从官方 Anchor lang 文档中阅读有关该文件的更多信息。

Anchor.toml [programs.cluster]

"programs" 设置允许您在项目中保存每个 Solana 程序的字符串 program_id。您可以根据需要为每个集群设置不同的 program_id。

例如,以下代码存储了 localnet 集群的 program_id:

[programs.localnet]demo = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"

要为不同的集群存储不同的内容program_id,您可以使用如下内容:

[programs.localnet]demo = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [programs.testnet]demo = "3ze6Z4rR8r8PkYAYYV5yZTRLKAHQMBcJj4AZMg4MS1fG"

Anchor.toml [provider]

您可以指定 来provider存储所需的 Solanacluster和本地密钥对文件(也称为wallet),您希望拥有已部署程序的所有权。

[provider]cluster = "localnet"wallet = "~/.config/solana/id.json"

您的实际 Solana 程序代码

Solana 程序与常规的基于 Rust 的程序不同。 Solana 程序被创建为 Rust“”,可以加载到 Solana 区块链的 Sealevel 运行时中。

按照惯例,这些通常将其主要代码文件放在 Anchor 工作区的目录src/lib.rs中。 programs/program_name(例如,在我们的示例项目中,“ project_name ”,该文件将位于programs/project_name/src/lib.rs

默认的 Anchor 脚手架lib.rs应该如下所示:

库文件

use anchor_lang::prelude::*;
 
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
 
#[program]
pub mod demo {
    use super::*;
 
    pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
}
 
#[derive(Accounts)]
pub struct Initialize {}

这段代码实际上是您可以构建的最基本的 Solana Anchor 程序。它基本上什么也不做,只是创建自己。这段代码等效于大多数语言中的 return true;。让我们分解一下程序结构和正在发生的事情。

导入库

与大多数其他语言一样,在 Rust 中,也习惯于在 Rust 文件的顶部“导入”所有所需的项目/库/箱。在 Rust 中,使用 use 关键字来指定要导入到特定 Rust 文件中的 crate。

use anchor_lang::prelude::*;

对于每个基于 Anchor 的 Solana 程序,相同的基本导入 anchor_lang用于告诉 rust 编译器在我们的代码中使用 Anchor 框架。包括将其设置为命名空间的一部分,以便我们可以在“本地范围”中调用Anchor函数和"local scope"。

declare_id

接下来,我们使用declare_id宏静态定义Solana 程序的program_id (也称为公共地址)。得益于 Anchor 框架,这将为我们的程序提供一些额外的安全性。

这行代码应该位于每个 Anchor 程序中(特别是在lib.rs)中,并且按照惯例位于文件顶部的导入下方。

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

默认情况下,Anchor CLI(以及您获得 Anchor 程序示例的任何其他地方)都会有一个program_id包含此宏的集合。

如果您的本地源代码是用于部署程序的密钥文件的地址,您应该始终更改内部的值(稍后会详细介绍)。

Solana 程序的核心逻辑

接下来,我们创建一个公共模块,并program_name用 其上设置的program 属性命名

没有刻意玩文字游戏的意思,但让我们解释一下这个说法。

库文件

#[program]
pub mod program_name {
    use super::*;
 
    pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
}

我们的公共“模块”将充当程序内主要代码执行的容器。在其他语言中,类似于函数和方法的作用。

program属性是由Anchor特别定义的。它不仅会处理传统上在纯粹的Solana程序中编写的大量样板代码,还将在我们的程序上添加一些基本的安全检查。

因此,从某种意义上说,你可以将使用program属性定义我们的模块,类比于在其他静态类型语言中定义main()函数。在某种程度上。

use super::*;

接下来,我们将属性中的所有导入库program引入模块内的本地范围program_name

库文件

pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
  Ok(())
}

最后,我们定义一个非常简单的initialize函数,可用于在 Solana 区块链上实际执行代码(通过 RPC 调用)。

对于 Anchor 程序,每个端点函数都将采用一个或多个参数。第一个参数始终是Solana 程序中Context所需定义的参数。struct您可以根据程序的需要向函数添加更多参数。

您可以在 Anchor lang文档中阅读有关 Anchor 上下文的更多信息 。但要点是,这个上下文将为我们提供一种非常方便的方式来读取提交给我们函数的帐户。

我们还声明我们的函数Result在完成时将始终返回一个类型(也称为只有 2 个可能的结果)。这将允许我们的函数在成功时以及发生错误时initialize 返回。Ok(())Err()

所有 Solana 程序函数都需要返回Result某种类型。结果 Ok通常是 的空白值(),而Err()可以是开发人员决定返回的任何错误。很多时候,开发人员会定义一个特定的struct用于创建标准/可重用错误。我强烈建议您也这样做,特别是如果您要开源代码的话。为别人着想,为他人着想!

定义我们的基本struct

struct正如我之前指出的,我们需要定义Context.此结构将告诉 Anchor 将提供给您的函数的预期帐户。由于 Solana 运行时需要程序执行 将与之交互的所有帐户的完整列表,因此 Anchor 可以做一些有用的事情。

#[derive(Accounts)]
pub struct Initialize {}

在这个具体示例中,我们的initialize函数实际上并不与任何其他 Solana 帐户交互,因此我们的函数Initialize struct实际上可以保持为空。

我们还使用 Anchor 的derive(Accounts)宏将一些反序列化助手添加到我们的Initialize结构中。

Accounts当我们真正使用它们时,我将在后面的教程中详细讨论这些内容,但是您可以在 Anchor lang rust 文档上阅读这个宏

下一步是什么?

现在我们已经快速概述了最基本的 Anchor 程序,我们需要确保创建一个 Solana 钱包并将我们的程序部署到 Solana 集群。

💡

原文链接:Create a Basic Anchor Program
本文由SlerfTools翻译,转载请注明出处。

SlerfTools专为Solana设计的工具箱,致力于简化区块链操作,提供无编程全可视化界面,使发币管理流动性无代码创建Dapp等复杂过程变得安全简单。