<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Tiny (Tiny熊)</title>
    <link>https://soldev.cn/Tiny</link>
    <description>登链社区发起人</description>
    <language>en-us</language>
    <item>
      <title>100 个 Solana 日常技巧</title>
      <description>&lt;p&gt;100 个 Solana 日常技巧： &lt;a href="https://learnblockchain.cn/article/20213" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/article/20213&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;内容涵盖项目结构、Anchor 约束、代码学习、不变量、动态断言、全局程序状态、账户填充、PDA 种子、剩余账户安全、日志记录、并行处理、数学运算安全、重入、ATA 账户初始化、多重签名、事件响应计划、编程监控以及 Solana 账户模型等多个方面。&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Tue, 02 Sep 2025 11:26:29 +0800</pubDate>
      <link>https://soldev.cn/topics/204</link>
      <guid>https://soldev.cn/topics/204</guid>
    </item>
    <item>
      <title>Kit 中文文档上线</title>
      <description>&lt;p&gt;登链社区翻译的 Solana JavaScript SDK V2 - Kit 中文文档上线。 &lt;a href="https://learnblockchain.cn/docs/kit/docs/" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/docs/kit/docs/&lt;/a&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Thu, 10 Jul 2025 16:56:31 +0800</pubDate>
      <link>https://soldev.cn/topics/197</link>
      <guid>https://soldev.cn/topics/197</guid>
    </item>
    <item>
      <title>4 节课学习 最新 Solana 链交互的 JavaScript 客户端库 Gill </title>
      <description>&lt;p&gt;完整视频在这里： &lt;a href="https://learnblockchain.cn/video/play/1248?collection_id=31" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/video/play/1248?collection_id=31&lt;/a&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Tue, 10 Jun 2025 22:06:52 +0800</pubDate>
      <link>https://soldev.cn/topics/186</link>
      <guid>https://soldev.cn/topics/186</guid>
    </item>
    <item>
      <title>Solana 数据分析教程</title>
      <description>&lt;p&gt;登链最近翻译了 两个 Solana 数据分析教程：&lt;/p&gt;

&lt;p&gt;1 . 构建 Solana 数据仪表板（共 5 集）： &lt;a href="https://learnblockchain.cn/video/play/1196?collection_id=23" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/video/play/1196?collection_id=23&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Solana 数据分析教程（共 6 集）： &lt;a href="https://learnblockchain.cn/video/play/1252?collection_id=30" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/video/play/1252?collection_id=30&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>Tiny</author>
      <pubDate>Fri, 16 May 2025 08:14:39 +0800</pubDate>
      <link>https://soldev.cn/topics/177</link>
      <guid>https://soldev.cn/topics/177</guid>
    </item>
    <item>
      <title>Solana 全栈开发课程录播上线 </title>
      <description>&lt;p&gt;由 Solar 中文社区及 Solana 基金会支持、登链社区、Rebase 社区、Modex、OpenBuild 联合组织的 Solana 全栈开发课程，录播上线啦。&lt;/p&gt;

&lt;p&gt;课程链接：
&lt;a href="https://learnblockchain.cn/course/73" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/course/73&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;完整课程内容如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1178?course_id=73" rel="nofollow" target="_blank" title=""&gt;L1_Solana 共识与核心概念&lt;/a&gt; ｜&lt;a href="https://learnblockchain.cn/video/1178/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1179?course_id=73" rel="nofollow" target="_blank" title=""&gt;L2_Solana Playground 及 Anchor 框架&lt;/a&gt;  ｜ &lt;a href="https://learnblockchain.cn/video/1179/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1181?course_id=73" rel="nofollow" target="_blank" title=""&gt;L3_Web3.js:钱包接入、转账、交互&lt;/a&gt;  ｜ &lt;a href="https://learnblockchain.cn/video/1181/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1183?course_id=73" rel="nofollow" target="_blank" title=""&gt;L4_应用接入 Blinks&lt;/a&gt;  ｜ &lt;a href="https://learnblockchain.cn/video/1183/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1184?course_id=73" rel="nofollow" target="_blank" title=""&gt;L5_Anchor 开发与测试&lt;/a&gt;  ｜ &lt;a href="https://learnblockchain.cn/video/1184/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1186?course_id=73" rel="nofollow" target="_blank" title=""&gt;L6_创建 SPL Token 及 NFT&lt;/a&gt;  ｜ &lt;a href="https://learnblockchain.cn/video/1186/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1187?course_id=73" rel="nofollow" target="_blank" title=""&gt;L7_Token-2022 机制及调用&lt;/a&gt;  ｜  &lt;a href="https://learnblockchain.cn/video/1187/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1188?course_id=73" rel="nofollow" target="_blank" title=""&gt;L8_实现一个极简 Pump.fun&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1189?course_id=73" rel="nofollow" target="_blank" title=""&gt;L9_Pump.fun 使用、机制与实现&lt;/a&gt;  ｜ &lt;a href="https://learnblockchain.cn/video/1189/download" rel="nofollow" target="_blank" title=""&gt;课件&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1191?course_id=73" rel="nofollow" target="_blank" title=""&gt;L10_实现一个 AMM Swap_第一部分&lt;/a&gt; | &lt;a href="https://github.com/deanmlittle/solar-developers-open-course-amm" rel="nofollow" target="_blank" title=""&gt;代码&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learnblockchain.cn/video/play/1193?course_id=73" rel="nofollow" target="_blank" title=""&gt;L11_实现一个 Swap_2&lt;/a&gt;   | &lt;a href="https://github.com/deanmlittle/solar-developers-open-course-amm" rel="nofollow" target="_blank" title=""&gt;代码&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Tiny</author>
      <pubDate>Tue, 08 Apr 2025 10:50:06 +0800</pubDate>
      <link>https://soldev.cn/topics/166</link>
      <guid>https://soldev.cn/topics/166</guid>
    </item>
    <item>
      <title>Anchor 中文文档</title>
      <description>&lt;p&gt;登链翻译了 Anchor 文档，需要的同学请前往：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learnblockchain.cn/docs/anchor" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/docs/anchor&lt;/a&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Thu, 27 Feb 2025 14:38:23 +0800</pubDate>
      <link>https://soldev.cn/topics/151</link>
      <guid>https://soldev.cn/topics/151</guid>
    </item>
    <item>
      <title>18 小时 Solana 最全教程 ｜ 2024 训练营完整版翻译</title>
      <description>&lt;p&gt;YouTube 播放列表： 
&lt;a href="https://www.youtube.com/playlist?list=PLv56Dy_F1m4N22VlUkVNsy4OzBVInPJWJ" rel="nofollow" target="_blank"&gt;https://www.youtube.com/playlist?list=PLv56Dy_F1m4N22VlUkVNsy4OzBVInPJWJ&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;哔哩哔哩 视频合集：
&lt;a href="https://space.bilibili.com/581611011/channel/collectiondetail?sid=4304339" rel="nofollow" target="_blank"&gt;https://space.bilibili.com/581611011/channel/collectiondetail?sid=4304339&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;登链  &lt;a href="/UpchainDAO" class="user-mention" title="@UpchainDAO"&gt;&lt;i&gt;@&lt;/i&gt;UpchainDAO&lt;/a&gt;  和 Solar 中文社区联手推出。
 时完整版教程不容错过！&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Tue, 17 Dec 2024 20:11:24 +0800</pubDate>
      <link>https://soldev.cn/topics/122</link>
      <guid>https://soldev.cn/topics/122</guid>
    </item>
    <item>
      <title>运行 Solana 本地验证器 — 在本机测试程序</title>
      <description>&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;原文链接：&lt;a href="https://medium.com/web3-builders-alliance/local-validator-test-your-program-directly-from-your-pc-c66397b00bdf" rel="nofollow" target="_blank"&gt;https://medium.com/web3-builders-alliance/local-validator-test-your-program-directly-from-your-pc-c66397b00bdf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;译者：&lt;a href="https://learnblockchain.cn/people/19584" rel="nofollow" target="_blank" title=""&gt;AI 翻译官&lt;/a&gt;，校对：&lt;a href="https://learnblockchain.cn/people/412" rel="nofollow" target="_blank" title=""&gt;翻译小组&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;本文永久链接：&lt;a href="https://learnblockchain.cn/article/9363" rel="nofollow" target="_blank" title=""&gt;learnblockchain.cn/article…&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;本文提供了一个全面的概述，介绍如何使用 Solana 本地验证器以及如何在不依赖 Devnet 的情况下测试你的程序！&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1726738025839" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;近几个月来，Solana Devnet 遇到了几次可靠性问题，偶尔会离线。本周这个问题再次出现，引起了开发者们对这种不稳定性如何影响他们项目的重大担忧。&lt;/p&gt;

&lt;p&gt;令人惊讶的是，在这些讨论中，显然许多开发者并不熟悉 Localnet 测试环境。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1726738026452" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Trent.sol 最近的一条推文突显了这一知识差距：&lt;a href="https://x.com/trentdotsol/status/1781369947794317371" rel="nofollow" target="_blank" title=""&gt;https://x.com/trentdotsol/status/1781369947794317371&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;我今天的目标是解决这一知识差距&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这个问题对我来说感觉很奇怪，因为自从我开始学习智能合约开发以来，我一直更喜欢在本地验证器上测试我的程序。&lt;/p&gt;

&lt;p&gt;我发现这主要是因为它允许通过 explorer 直接调试，并且我可以享受 Solana 文档&lt;a href="https://docs.solanalabs.com/cli/examples/test-validator" rel="nofollow" target="_blank" title=""&gt;页面&lt;/a&gt;中包含的所有这些附加好处：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  无 RPC 速率限制&lt;/li&gt;
&lt;li&gt;  无空投限制&lt;/li&gt;
&lt;li&gt;  直接链上程序部署 (&lt;code&gt;--bpf-program ...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  从公共集群克隆账户，包括程序 (&lt;code&gt;--clone ...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  可配置的交易历史保留 (&lt;code&gt;--limit-ledger-size ...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  可配置的 Epoch 长度 (&lt;code&gt;--slots-per-epoch ...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  跳转到任意 Slot (&lt;code&gt;--warp-slot ...&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="那么，什么是本地验证器？"&gt;那么，什么是本地验证器？&lt;/h2&gt;
&lt;p&gt;本地验证器充当你的个人节点，提供一个沙盒环境用于测试应用程序，而无需连接到实时区块链网络。它操作一个本地测试账本，这是 Solana 账本的简化版本，预装了所有本地程序并启用了各种功能。这个账本是完全可定制的，以满足你特定的本地测试需求！&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1726738026698" title="" alt=""&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;来自本地验证器的源代码&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;你可以通过安装 &lt;a href="https://solana.com/it/developers/guides/getstarted/setup-local-development" rel="nofollow" target="_blank" title=""&gt;Solana 工具套件&lt;/a&gt;并运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-test-validator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;来设置你的本地验证器，
启动后，验证器将可通过&lt;code&gt;[http://127.0.0.1:8899](http://127.0.0.1:8899)&lt;/code&gt;访问，因此要建立与本地验证器的连接，请使用以下代码片段：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Connection } from "@solana/web3.js";  

(async () =&amp;gt; {  
  // 这将连接到你的本地验证器  
  const connection = new Connection("http://127.0.0.1:8899", "confirmed");  
})();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初始化时，本地验证器将在你的用户文件夹中生成一个名为&lt;code&gt;test-ledger&lt;/code&gt;的目录。该目录包含所有与验证器相关的数据，包括创建的账户和部署及导入的程序。要重置你的本地验证器，你可以：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; 删除&lt;code&gt;test-ledger&lt;/code&gt;文件夹&lt;/li&gt;
&lt;li&gt; 使用命令&lt;code&gt;solana-test-validator --reset&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;此外，&lt;code&gt;solana logs&lt;/code&gt;命令对于监控链上程序的&lt;code&gt;msg!()&lt;/code&gt;输出非常有用。&lt;/p&gt;
&lt;h2 id="获取一些 Localnet Solana"&gt;获取一些 Localnet Solana&lt;/h2&gt;
&lt;p&gt;要执行交易，钱包中必须有 SOL。&lt;/p&gt;

&lt;p&gt;在 Devnet 上，你可以通过水龙头或使用&lt;code&gt;requestAirdrop&lt;/code&gt;函数接收 SOL：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const airdropSignature = await connection.requestAirdrop(  
  keypair.publicKey,  
  LAMPORTS\_PER\_SOL  
);  

await connection.confirmTransaction(airdropSignature);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 localnet 上，也可以通过这种方式获取 SOL，但需要注意的是，空投到账户中的 Solana 不是来自水龙头，而是来自你的 CLI 默认密钥对，该密钥对在验证器的创世块中预加载了 1000 个 Localnet SOL。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;所以如果你使用 CLI 密钥对来测试你的程序，你不需要任何空投！&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="管理程序和账户"&gt;管理程序和账户&lt;/h2&gt;
&lt;p&gt;对于需要主网上特定程序和账户的测试，Solana CLI 可以帮助下载和加载这些元素到你的本地验证器。以下是如何管理这些内容：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;下载账户和程序：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;对于账户：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana account -u &amp;lt;source cluster&amp;gt; --output &amp;lt;output format&amp;gt; --output-file &amp;lt;destination file name/path&amp;gt; &amp;lt;address of account to fetch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于程序：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana program dump -u &amp;lt;source cluster&amp;gt; &amp;lt;address of account to fetch&amp;gt; &amp;lt;destination file name/path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;加载账户和程序：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;对于账户：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-test-validator --account &amp;lt;address to load the account to&amp;gt; &amp;lt;path to account file&amp;gt; --reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于程序：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-test-validator --bpf-program &amp;lt;address to load the program to&amp;gt; &amp;lt;path to program file&amp;gt; --reset
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="高级本地验证器设置"&gt;高级本地验证器设置&lt;/h2&gt;
&lt;p&gt;在探索了通过 CLI 创建自定义本地验证器的基础知识后，让我们探索更高级的技术，例如编写个性化的 bash 脚本以运行不同实例的验证器，并在其中配置不同的程序，以及配置 Anchor 以使用特定的账户和程序。&lt;/p&gt;
&lt;h2 id="第 1 部分：使用 Anchor 配置你的本地验证器"&gt;第 1 部分：使用 Anchor 配置你的本地验证器&lt;/h2&gt;
&lt;p&gt;从 anchor-lang &lt;a href="https://www.anchor-lang.com/docs/manifest" rel="nofollow" target="_blank" title=""&gt;文档&lt;/a&gt;中可以看到，anchor 允许通过其配置文件&lt;code&gt;anchor.toml&lt;/code&gt;进行广泛的自定义。在这一部分中，我们将重点关注&lt;code&gt;[test]&lt;/code&gt;部分，这是设置本地验证器的关键。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;startup_wait&lt;/code&gt;标志是一个关键设置，用于延迟&lt;code&gt;solana-test-validator&lt;/code&gt;的启动。当克隆多个账户时，这种延迟特别有用，因为它延长了验证器的启动时间以适应增加的负载：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[test]  
startup_wait = 10000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;[test.validator]&lt;/code&gt;部分允许你修改本地验证器的基本方面。这些设置通过&lt;code&gt;anchor test&lt;/code&gt;命令直接传递给&lt;code&gt;solana-test-validator&lt;/code&gt; CLI：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[test.validator]  
url = "https://api.mainnet-beta.solana.com"     # 这是克隆账户的集群 URL（见`test.validator.clone`）。  
warp_slot = 1337                                # 在启动验证器后将账本跳转`warp_slot`。  
slots_per_epoch = 5                             # 覆盖Epoch中的Slot数量。  
rpc_port = 1337                                 # 在此端口设置 JSON RPC，下一个端口用于 RPC websocket。  
limit_ledger_size = 1337                        # 在根槽中保留此数量的碎片。  
ledger = "test-ledger"                          # 设置账本位置。  
gossip_port = 1337                              # 验证器的 gossip 端口号。  
gossip_host = "127.0.0.1"                       # 验证器在 gossip 中广告的 gossip DNS 名称或 IP 地址。  
faucet_sol = 1337                               # 在创世块中给水龙头地址分配这么多 SOL。  
faucet_port = 1337                              # 在此端口启用水龙头。  
dynamic_port_range = "1337 - 13337"             # 用于动态分配端口的范围。  
bind_address = "0.0.0.0"                        # 绑定验证器端口的 IP 地址。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完成所有这些设置工作后，我们终于可以开始克隆我们的账户和程序了：&lt;/p&gt;

&lt;p&gt;你可以利用 &lt;code&gt;[test.validator.clone]&lt;/code&gt; 部分从指定的集群克隆账户到你的测试集群。如果账户链接到由 "BPF upgradeable loader" 管理的程序，Anchor 会自动克隆相关的程序数据账户：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[test.validator]  
url = "https://api.mainnet-beta.solana.com"  

[[test.validator.clone]]  
address = "7NL2qWArf2BbEBBH1vTRZCsoNqFATTddH6h8GkVvrLpG"  
[[test.validator.clone]]  
address = "2RaN5auQwMdg5efgCaVqpETBV8sacWGR8tkK4m9kjo5r"  
[[test.validator.clone]]  
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" # implicitly also clones PwDiXFxQsGra4sFFTT8r1QWRMd4vfumiWC1jfWNfdYT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要将 JSON 文件中的本地账户集成到你的验证器设置中，可以使用 &lt;code&gt;[test.validator.account]&lt;/code&gt; 标志无缝上传账户：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[test.validator.account]]  
address = "Ev8WSPQsGb4wfjybqff5eZNcS3n6HaMsBkMk9suAiuM"  
filename = "some_account.json"
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="第 2 部分：使用 Bash 脚本配置多个本地验证器"&gt;第 2 部分：使用 Bash 脚本配置多个本地验证器&lt;/h2&gt;
&lt;p&gt;这是一个高级教程，需要对 bash 脚本的基本操作有一定了解，如果你不熟悉 bash 脚本，请谨慎操作。但如果你不熟悉也不用担心，因为这主要是一个演示，预计 Valid8 CLI 工具发布后，将能够直接从终端创建和管理个性化和多个本地验证器。&lt;/p&gt;

&lt;p&gt;但让我们探索一下这个配置：&lt;/p&gt;

&lt;p&gt;首先在你的路径中创建一个专用文件夹来存储你打算与本地验证器一起使用的程序：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir ~/.local/share/valid8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，将指定地址中的程序数据下载到新创建的目录中：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana program dump -u m &amp;lt;address of account to fetch&amp;gt; ~/.local/share/valid8/&amp;lt;destination file name&amp;gt;.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后使用以下命令打开一个新的脚本文件：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /usr/local/bin/&amp;lt;name-of-the-validator&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;（如果 /user/local/bin 目录不存在，可以使用 &lt;code&gt;sudo mkdir -p -m 775 /usr/local/bin&lt;/code&gt; 创建它）。&lt;/p&gt;

&lt;p&gt;将以下代码粘贴到编辑器中并保存：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash  &lt;/span&gt;

&lt;span class="c"&gt;# Validator command  &lt;/span&gt;
&lt;span class="nv"&gt;COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"solana-test-validator -r --bpf-program &amp;lt;address of account to fetch&amp;gt; ~/.local/share/valid8/&amp;lt;destination file name&amp;gt;.so"&lt;/span&gt;  

&lt;span class="c"&gt;# Append any additional arguments passed to the script  &lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;arg &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="k"&gt;do  
    &lt;/span&gt;COMMAND+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="k"&gt;done&lt;/span&gt;  

&lt;span class="c"&gt;# Execute the command  &lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="nv"&gt;$COMMAND&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt; 如果需要包含多个程序，请根据需要附加 &lt;code&gt;--bpf-program&lt;/code&gt; 选项：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...] --bpf-program &amp;lt;another program address&amp;gt; ~/.local/share/valid8/&amp;lt;another destination file name&amp;gt;.so" [...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后我们需要确保脚本可以执行，因此我们修改其权限：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chmod +x /usr/local/bin/&amp;lt;name-of-the-validator&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后，在你的项目文件夹中测试新的验证器脚本：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;name-of-the-validator&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="示例：创建一个 Metaplex-Test-Validator"&gt;示例：创建一个 Metaplex-Test-Validator&lt;/h2&gt;
&lt;p&gt;让我们为 Metaplex-test-validator 创建一个专门的 bash 脚本，包含 &lt;code&gt;mpl-token-metadata&lt;/code&gt; 程序：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt;&amp;gt; solana program dump -u m metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ~/.local/share/valid8/metadata.so  

&amp;gt;&amp;gt; sudo nano /usr/local/bin/metaplex-test-validator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用与之前相同的脚本结构，指定 &lt;code&gt;mpl-token-metadata&lt;/code&gt; 程序：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash  &lt;/span&gt;

&lt;span class="c"&gt;# Validator command  &lt;/span&gt;
&lt;span class="nv"&gt;COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"solana-test-validator -r --bpf-program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ~/.local/share/valid8/metadata.so"&lt;/span&gt;  

&lt;span class="c"&gt;# Append any additional arguments passed to the script  &lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;arg &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="k"&gt;do  
    &lt;/span&gt;COMMAND+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="k"&gt;done&lt;/span&gt;  

&lt;span class="c"&gt;# Execute the command  &lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="nv"&gt;$COMMAND&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后通过使其可执行并测试它来完成：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt; sudo chmod +x /usr/local/bin/metaplex-test-validator  

&amp;gt;&amp;gt; metaplex-test-validator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;恭喜！&lt;/strong&gt; 你现在是本地验证器的高手了！希望本教程对你有所帮助，并且你学到了有价值的知识！&lt;/p&gt;

&lt;p&gt;特别感谢来自 Valid8 团队的 &lt;a href="https://twitter.com/bergabman" rel="nofollow" target="_blank" title=""&gt;Berg&lt;/a&gt;，&lt;a href="https://twitter.com/deanmlittle" rel="nofollow" target="_blank" title=""&gt;Dean&lt;/a&gt; 创建了 Valid8 的原始 Bash 脚本（你可以在 &lt;a href="https://gist.github.com/deanmlittle/e607650c80c80203116638ae0dd3c63f" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt; 找到），以及 &lt;a href="https://twitter.com/solanaturbine" rel="nofollow" target="_blank" title=""&gt;Turbin3&lt;/a&gt; 总是尝试在 Solana 上创建前沿概念。&lt;/p&gt;

&lt;p&gt;如果你想关注我的旅程并且不想错过我的下一个教程，请在 &lt;a href="https://twitter.com/L0STE_" rel="nofollow" target="_blank" title=""&gt;Twitter&lt;/a&gt; 上关注我！&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;我是 &lt;a href="https://learnblockchain.cn/people/19584" rel="nofollow" target="_blank" title=""&gt;AI 翻译官&lt;/a&gt;，为大家转译优秀英文文章，如有翻译不通的地方，在&lt;a href="https://github.com/lbc-team/Pioneer/blob/master/translations/9363.md" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;修改，还请包涵～&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>Tiny</author>
      <pubDate>Thu, 19 Sep 2024 17:51:33 +0800</pubDate>
      <link>https://soldev.cn/topics/69</link>
      <guid>https://soldev.cn/topics/69</guid>
    </item>
    <item>
      <title>转载：探讨 Blinks 和 Solana Actions  - 链上交互的新标准 </title>
      <description>&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;原文链接：&lt;a href="https://medium.com/web3-builders-alliance/the-next-standard-for-on-chain-interaction-a-deep-dive-into-blinks-and-actions-af76c0304af2" rel="nofollow" target="_blank"&gt;https://medium.com/web3-builders-alliance/the-next-standard-for-on-chain-interaction-a-deep-dive-into-blinks-and-actions-af76c0304af2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;译者：&lt;a href="https://learnblockchain.cn/people/19584" rel="nofollow" target="_blank" title=""&gt;AI 翻译官&lt;/a&gt;，校对：&lt;a href="https://learnblockchain.cn/people/412" rel="nofollow" target="_blank" title=""&gt;翻译小组&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;本文永久链接：&lt;a href="https://learnblockchain.cn/article/8527" rel="nofollow" target="_blank" title=""&gt;learnblockchain.cn/article…&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;在一个以不断切换上下文和注意力短暂为特征的时代，想象一下消除加密爱好者在社交媒体平台（如 Twitter）上与区块链应用程序互动时所面临的摩擦会带来什么...&lt;/p&gt;

&lt;p&gt;感谢 Dialect 和 Anza 团队的共同努力，现在你可以直接在 Twitter 上与你最喜欢的加密 dApp 互动！&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1719496906961" title="" alt="Solana Action"&gt;&lt;/p&gt;

&lt;p&gt;在深入了解其工作原理之前，让我们先回顾一下使其成为可能的技术。&lt;/p&gt;
&lt;h2 id="Open Graph"&gt;Open Graph&lt;/h2&gt;
&lt;p&gt;Open Graph 是一种由 Facebook 开发的技术协议，允许 Web 开发人员控制其内容在社交媒体上的展示和分享方式。当网页实现 Open Graph 元标签时，它会向社交媒体平台提供结构化数据。这使得社交媒体平台在分享网页时能够生成一个视觉上吸引人且信息丰富的预览，通常包括标题、描述、缩略图和其他相关信息。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;现在，你可以为你最喜欢的加密 dApp 创建预览，享受相同的用户体验。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="Blinks 和 Solana Actions"&gt;Blinks 和 Solana Actions&lt;/h2&gt;
&lt;p&gt;基于与 Open Graph 标准相同的概念，Solana Actions 和 Blinks 为加密用户在 Twitter 网站内提供了自定义互动！&lt;/p&gt;

&lt;p&gt;让我们来探讨一下它们是什么：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Solana Actions&lt;/strong&gt;：符合规范的 API，返回 Solana 区块链上的交易，准备在各种上下文中预览、签名和发交易，包括 QR 码、按钮、小部件和网站。&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Blockchain Links (Blinks)&lt;/strong&gt;：这些将任何 Solana Action 转换为可分享的、元数据丰富的链接。Blinks 使得支持 Action 的客户端（如浏览器扩展钱包和机器人）能够为用户显示额外的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="它是如何工作的？"&gt;它是如何工作的？&lt;/h2&gt;&lt;h3 id="Actions"&gt;Actions&lt;/h3&gt;
&lt;p&gt;Actions 可以被视为将 Open Graph 标准与 Solana 连接起来的适配器，使得 Web 扩展能够读取正确的数据，正确显示，并传递可签名的交易（最终是可签名的消息）。&lt;/p&gt;

&lt;p&gt;Actions API 涉及对 Action 的 URL 端点进行简单的 GET 和 POST 请求，并处理符合 Actions 接口的响应。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;GET 请求&lt;/strong&gt;：返回元数据，为客户端提供关于此 URL 可用的操作的可读信息，以及一个可选的相关操作列表。&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;POST 请求&lt;/strong&gt;：返回一个可签名的交易或消息，客户端随后提示用户的钱包签名并在区块链或其他链下服务中执行。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1719496906979" title="" alt="Solana Actions"&gt;&lt;/p&gt;

&lt;p&gt;在实践中，与 Actions 互动与典型的 REST API 互动非常相似：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  客户端向 Action URL 发出初始 GET 请求，以获取关于可用 Actions 的元数据。&lt;/li&gt;
&lt;li&gt;  端点返回一个响应，其中包括关于端点的元数据（如应用程序的标题和图标）以及此端点的可用操作列表。&lt;/li&gt;
&lt;li&gt;  客户端应用程序（如移动钱包、聊天机器人或网站）显示一个 UI，供用户执行其中一个操作。&lt;/li&gt;
&lt;li&gt;  用户选择一个操作（通过点击按钮）后，客户端向端点发出 POST 请求，以获取用户签名的交易。&lt;/li&gt;
&lt;li&gt;  钱包促使用户签署交易，并最终将交易发送到区块链进行确认。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：Actions 在执行前还支持某种程度的无效化。GET 和 POST 请求可能会返回元数据，说明是否可以执行该操作（例如使用&lt;code&gt;disabled&lt;/code&gt;字段）。&lt;/p&gt;
&lt;h2 id="Blinks"&gt;Blinks&lt;/h2&gt;
&lt;p&gt;Blinks（区块链链接）是客户端应用程序，它们检查 Action API 并构建围绕互动和执行 Actions 的用户界面。支持 Blinks 的客户端应用程序只需检测 Action 兼容的 URL，解析它们，并允许用户在标准化的用户界面中与它们互动。&lt;/p&gt;
&lt;h2 id="渲染 Blinks"&gt;渲染 Blinks&lt;/h2&gt;
&lt;p&gt;Blinks 可以通过至少三种方式链接到 Actions：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;分享显式 Action URL&lt;/strong&gt;：例如，&lt;code&gt;solana-action:https://actions.alice.com/donate&lt;/code&gt;。只有支持的客户端可以渲染 blink；在不支持的客户端中不会有回退链接预览或可访问的网站。&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;分享链接到 Actions API 的网站链接&lt;/strong&gt;：例如，&lt;code&gt;https://alice.com/actions.json&lt;/code&gt;将&lt;code&gt;https://alice.com/donate&lt;/code&gt;映射到 API URL &lt;code&gt;https://actions.alice.com/donate&lt;/code&gt;，其中托管了捐赠给 Alice 的 Actions。&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;在“中间”网站 URL 中嵌入 Action URL&lt;/strong&gt;：例如，&lt;code&gt;https://blink.to/?action=&amp;lt;action_url&amp;gt;&lt;/code&gt;。支持 Blinks 的客户端应该能够采用上述任何格式，并正确渲染一个界面，以便在客户端中直接执行操作。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于不支持 Blinks 的客户端，应该有一个底层网站，使浏览器成为通用的回退。如果用户在客户端中点击任何非操作按钮或文本输入字段的地方，他们应该被带到底层网站。&lt;/p&gt;
&lt;h2 id="Blinks 用例"&gt;Blinks 用例&lt;/h2&gt;
&lt;p&gt;现在你知道了什么是 Blinks 和 Solana Actions，让我们来探讨一些潜在的用例：&lt;/p&gt;
&lt;h3 id="广告"&gt;广告&lt;/h3&gt;
&lt;p&gt;想象一下，你是一个像&lt;strong&gt;MarginFi&lt;/strong&gt;这样的协议，想要宣传他们的 USDC APY 目前是最好的。通过 Blinks 和 Solana Actions，你不仅可以在公司账户上高亮显示此信息，还可以在帖子中创建一个操作，让用户直接从他们的 Twitter feed 中存入和借出 USDC！&lt;/p&gt;
&lt;h3 id="游戏"&gt;游戏&lt;/h3&gt;
&lt;p&gt;想象一下，你是一个像&lt;strong&gt;Burger Games&lt;/strong&gt;这样的游戏。整个游戏的参与度和动力机制可以显著提高，潜在地使其成为更大的成功，因为减少了摩擦。实际上，通过 Actions，你无需关注新游戏的公告并通过 Discord 和游戏网站导航来连接你的钱包并开始游戏，你可以选择你的响应并直接在 Twitter 上开始游戏！&lt;/p&gt;
&lt;h3 id="打赏"&gt;打赏&lt;/h3&gt;
&lt;p&gt;想象一下在你的技术线程的最后一篇帖子中嵌入一个小打赏，你在这篇帖子中投入了大量精力。发现你的内容有趣且有价值的加密用户可以直接为你的工作打赏，甚至可能为你买一杯咖啡。不错吧？&lt;/p&gt;

&lt;p&gt;这为新消费者应用程序利用这项技术并迅速获得竞争优势打开了机会。无需切换上下文即可直接吸引和参与用户的能力可以创造一种新的方式来即时“奖励”用户，促使他们在看到相关信息出现在他们的 feed 中时立即采取行动。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;我是 &lt;a href="https://learnblockchain.cn/people/19584" rel="nofollow" target="_blank" title=""&gt;AI 翻译官&lt;/a&gt;，为大家转译优秀英文文章，如有翻译不通的地方，在&lt;a href="https://github.com/lbc-team/Pioneer/blob/master/translations/8527.md" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;修改，还请包涵～&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>Tiny</author>
      <pubDate>Fri, 28 Jun 2024 21:44:19 +0800</pubDate>
      <link>https://soldev.cn/topics/37</link>
      <guid>https://soldev.cn/topics/37</guid>
    </item>
    <item>
      <title>登链社区发布 Solana 知识架构图谱</title>
      <description>&lt;p&gt;通过一个图，系统了解  Solana 知识架构&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learnblockchain.cn/maps/Solana" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/maps/Solana&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/Tiny/438aecf5-537b-4906-a009-10d9ecda1b09.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Tue, 11 Jun 2024 20:41:20 +0800</pubDate>
      <link>https://soldev.cn/topics/27</link>
      <guid>https://soldev.cn/topics/27</guid>
    </item>
    <item>
      <title>转载：使用 React、Anchor、Rust 和 Phantom 进行全栈 Solana 开发</title>
      <description>&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;原文&lt;a href="https://dev.to/edge-and-node/the-complete-guide-to-full-stack-solana-development-with-react-anchor-rust-and-phantom-3291" rel="nofollow" target="_blank" title=""&gt;链接&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;译者：&lt;a href="https://learnblockchain.cn/people/19584" rel="nofollow" target="_blank" title=""&gt;登链 AI 翻译官&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;本文永久链接：&lt;a href="https://learnblockchain.cn/article/8280" rel="nofollow" target="_blank" title=""&gt;learnblockchain.cn/article…&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;在&lt;a href="https://learnblockchain.cn/article/2383" rel="nofollow" target="_blank" title=""&gt;全栈以太坊开发完全指南&lt;/a&gt;中，我深入探讨了如何在以太坊上构建全栈 dapp，这也适用于其他 EVM 兼容链，如 Polygon、Avalanche 和以太坊 Layer 2，比如 Arbitrum。&lt;/p&gt;

&lt;p&gt;在本指南中，我想深入研究 Solana，向你展示如何构建全栈 dapp。我还想向你介绍生态系统和开发工具，希望能帮助你开始构建自己的想法和应用程序。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;项目的代码位于&lt;a href="https://github.com/dabit3/complete-guide-to-full-stack-solana-development" rel="nofollow" target="_blank" title=""&gt;此处&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="Solana 开发者概览"&gt;Solana 开发者概览&lt;/h2&gt;
&lt;p&gt;作为学习过 Solidity 及其生态系统的人，我有点认为要开始并不会太难。我错了。&lt;/p&gt;

&lt;p&gt;开发工具的部分确实非常好和成熟（Solana CLI 和 Anchor），而生态系统的其他部分，甚至是 Anchor 的文档（公平地说，这是非常新的），还有相当多的不足之处。&lt;/p&gt;

&lt;p&gt;尽管如此，一旦你掌握了一切，很快就会更容易理解如何开始实现自己的想法并开始尝试。&lt;/p&gt;

&lt;p&gt;寻找答案的关键之一是要在 Google、Github 以及特别是各种 Discord 服务器中（&lt;a href="https://discord.gg/ZCHmqvXgDw" rel="nofollow" target="_blank" title=""&gt;Anchor&lt;/a&gt; 和 Solana）进行仔细搜索。这些频道中的开发人员非常乐于助人，尤其是 &lt;a href="https://twitter.com/armaniferrante" rel="nofollow" target="_blank" title=""&gt;Armani Ferrante&lt;/a&gt;，他创建了 Anchor 框架。熟悉搜索功能，你通常可以在 Discord 中的过去讨论中找到问题的答案。&lt;/p&gt;
&lt;h2 id="项目概述"&gt;项目概述&lt;/h2&gt;
&lt;p&gt;我们今天将使用的工具包括：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.solana.com/cli/install-solana-cli-tools" rel="nofollow" target="_blank" title=""&gt;Solana 工具套件&lt;/a&gt; - 这包括一个与 Solana 网络交互的非常成熟和文档完善的 CLI。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://project-serum.github.io/anchor/getting-started/introduction.html" rel="nofollow" target="_blank" title=""&gt;Anchor 框架&lt;/a&gt; - Anchor 对我来说实际上是一个救命稻草，我几乎可以肯定如果没有它，我将无法克服构建任何东西的难关。它是 Solana 开发的 &lt;a href="https://hardhat.org/" rel="nofollow" target="_blank" title=""&gt;Hardhat&lt;/a&gt;，我喜欢它。它还提供了一个 DSL，使你无需深入了解语言即可开始，尽管我仍在努力学习 Rust，因为即使使用 DSL，构建任何有意义的程序都可能需要会用 Rust。学习 Rust 的一个很好的免费地方是 &lt;a href="https://doc.rust-lang.org/book/title-page.html" rel="nofollow" target="_blank" title=""&gt;Rust 之书&lt;/a&gt; 。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://solana-labs.github.io/solana-web3.js/" rel="nofollow" target="_blank" title=""&gt;solana/web3.js&lt;/a&gt; - 一个 Solana 版本的 &lt;a href="https://web3js.readthedocs.io/" rel="nofollow" target="_blank" title=""&gt;web3.js&lt;/a&gt;，看起来运行得相当不错，但对我来说文档几乎无法使用。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/" rel="nofollow" target="_blank" title=""&gt;React&lt;/a&gt; - 客户端框架&lt;/p&gt;

&lt;p&gt;我将不会详细介绍 Solana 本身的工作原理，因为其他人可以比我更好地解释这一点。相反，我将尝试专注于构建一些东西并分享你需要了解的细节，以及我认为最重要的事项。&lt;/p&gt;

&lt;p&gt;如果你想了解更多关于 Solana 及其工作原理的信息，这里有一些不错的文章：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.solana.com/introduction" rel="nofollow" target="_blank" title=""&gt;Solana 文档介绍&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://2501babe.github.io/posts/solana101.html" rel="nofollow" target="_blank" title=""&gt;Solana 到底是怎么回事&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.notboring.co/p/solana-summer" rel="nofollow" target="_blank" title=""&gt;Solana Summer&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在本指南中，我们将主要关注项目设置、测试和前端客户端集成，以构建一些类型的应用程序，主要侧重于 CRUD 操作（当然不包括删除），我发现这方面的文档有些不完整（与客户端应用程序的集成）。&lt;/p&gt;

&lt;p&gt;我们还将学习如何使用 Solana CLI 向我们自己的开发账户空投代币，并将我们的应用程序部署到本地网络以及实时测试网络。&lt;/p&gt;

&lt;p&gt;在本指南中，我们不会专注于 NFT，但也许我会在未来的指南中专注于这一点。如果你有兴趣在 Solana 上构建 NFT 市场，我建议查看 &lt;a href="https://www.metaplex.com/" rel="nofollow" target="_blank" title=""&gt;Metaplex&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="预备条件"&gt;预备条件&lt;/h2&gt;
&lt;p&gt;本教程涵盖了如何在 Solana 上构建全栈应用程序，但不涉及如何安装所有单独的依赖项。&lt;/p&gt;

&lt;p&gt;相反，我将列出依赖项并链接到安装它们的文档，因为每个项目都能更好地解释和记录这些内容，也能保持其更新。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Node.js - 我建议使用 &lt;a href="https://github.com/nvm-sh/nvm" rel="nofollow" target="_blank" title=""&gt;nvm&lt;/a&gt; 或&lt;a href="https://github.com/Schniz/fnm" rel="nofollow" target="_blank" title=""&gt;fnm&lt;/a&gt;安装 Node&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solana 工具套件 - 你可以查看安装说明&lt;a href="https://docs.solana.com/cli/install-solana-cli-tools" rel="nofollow" target="_blank" title=""&gt;此处&lt;/a&gt; 。注意 - 如果在 M1 Mac 上安装 Solana 时遇到任何问题，请尝试&lt;a href="https://docs.solana.com/cli/install-solana-cli-tools#build-from-source" rel="nofollow" target="_blank" title=""&gt;从源代码构建&lt;/a&gt; ，并查看&lt;a href="https://dev.to/nickgarfield/how-to-install-solana-dev-tools-on-an-m1-mac-kfn" rel="nofollow" target="_blank" title=""&gt;此指南&lt;/a&gt; 。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anchor（包括 &lt;a href="https://project-serum.github.io/anchor/getting-started/installation.html#install-mocha" rel="nofollow" target="_blank" title=""&gt;Mocha&lt;/a&gt; 安装） - Anchor 的安装对我来说相当简单。你可以在&lt;a href="https://project-serum.github.io/anchor/getting-started/installation.html" rel="nofollow" target="_blank" title=""&gt;此处&lt;/a&gt;找到安装说明。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solana 浏览器钱包 - 我推荐使用 &lt;a href="https://phantom.app/" rel="nofollow" target="_blank" title=""&gt;Phantom&lt;/a&gt;，这是我用来测试此应用程序的钱包。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="开始"&gt;开始&lt;/h2&gt;
&lt;p&gt;在开始构建之前，让我们看一下 Solana CLI。&lt;/p&gt;
&lt;h3 id="Solana CLI"&gt;Solana CLI&lt;/h3&gt;
&lt;p&gt;我们将使用 Solana CLI 主要进行网络配置（在本地主机和开发者测试网络之间）以及向我们的钱包空投代币，其他几乎所有操作都将使用 Anchor CLI。&lt;/p&gt;

&lt;p&gt;例如，我们可以使用以下命令检查当前网络（和其他）配置：&lt;/p&gt;

&lt;p&gt;solana config get&lt;/p&gt;

&lt;p&gt;# 输出
    Config File: /Users/user/.config/solana/cli/config.yml
    RPC URL: &lt;a href="https://api.devnet.solana.com" rel="nofollow" target="_blank"&gt;https://api.devnet.solana.com&lt;/a&gt;
    WebSocket URL: wss://api.devnet.solana.com/ (computed)
    Keypair Path: /Users/user/.config/solana/id.json
    Commitment: confirmed&lt;/p&gt;

&lt;p&gt;进入全屏模式退出全屏模式&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;如果你没有&lt;code&gt;Keypair 路径&lt;/code&gt;，请按照&lt;a href="https://docs.solana.com/wallet-guide/paper-wallet#seed-phrase-generation" rel="nofollow" target="_blank" title=""&gt;此处&lt;/a&gt;的说明设置一个。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我们可以这样更改网络：&lt;/p&gt;

&lt;p&gt;# 设置为 localhost
    solana config set --url localhost&lt;/p&gt;

&lt;p&gt;# 设置为 devnet
    solana config set --url devnet&lt;/p&gt;

&lt;p&gt;进入全屏模式退出全屏模式&lt;/p&gt;

&lt;p&gt;这很重要，因为在构建、测试和部署程序时，你需要注意你正在使用的网络，你的钱包在测试时使用的网络必须与你的本地环境使用的网络相同，这是我将要介绍的内容。&lt;/p&gt;

&lt;p&gt;我们将从在&lt;code&gt;localhost&lt;/code&gt;网络上开发开始，然后切换到&lt;code&gt;devnet&lt;/code&gt;网络。我们还可以使用 CLI 查看当前本地钱包地址：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana address
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后获取关于一个账户的完整详情：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana account &amp;lt;address from above&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来让我们空投一些代币。为此，首先切换到本地网络，因为这是我们将要开始工作的地方：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--url&lt;/span&gt; localhost
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，启动本地网络。这将是一个本地的 Solana 节点，我们可以部署用于测试：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-test-validator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一旦本地网络运行起来，你可以向你的账户空投代币。在网络运行时，打开一个单独的窗口并运行以下命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana airdrop 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你可以检查钱包的余额：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana balance

&lt;span class="c"&gt;# 或者&lt;/span&gt;

solana balance &amp;lt;address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你现在应该在你的钱包中有 100 SOL 的余额。有了这个，我们可以开始构建。&lt;/p&gt;
&lt;h3 id="让我们开始构建"&gt;让我们开始构建&lt;/h3&gt;
&lt;p&gt;要开始，初始化一个新的 anchor 项目并切换到新目录：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor init mysolanaapp &lt;span class="nt"&gt;--javascript&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;mysolanaapp
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;请确保使用 Anchor 版本 0.16.0 或更高版本。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在这个项目中，你会看到四个主要文件夹（除了&lt;strong&gt;node_modules&lt;/strong&gt;）：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app&lt;/strong&gt; - 我们的前端代码将放在这里&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;programs&lt;/strong&gt; - 这是 Solana 程序的 Rust 代码所在的地方&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;test&lt;/strong&gt; - 程序的 JavaScript 测试所在的地方&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;migrations&lt;/strong&gt; - 一个基本的部署脚本&lt;/p&gt;

&lt;p&gt;让我们看一下为我们创建的程序。&lt;/p&gt;

&lt;p&gt;Anchor 使用并使我们能够编写一个 eDSL（ &lt;a href="https://en.wikipedia.org/wiki/Domain-specific_language#:~:text=embedded%20domain%2Dspecific%20language%20(eDSL,methods%2C%20macros%20etc.)." rel="nofollow" target="_blank" title=""&gt;嵌入式 DSL&lt;/a&gt;），它将许多你通常需要做的更复杂的低级操作抽象出来，如果你没有使用它，你将需要使用 Solana 和 Rust 进行更多的操作，这使得它对我来说更容易接近。&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// programs/src/lib.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;anchor_lang&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;declare_id!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[program]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;mysolanaapp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProgramResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Initialize&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这可能是你可以编写的最基本的程序。这里唯一发生的事情是我们定义了一个名为&lt;code&gt;initialize&lt;/code&gt;的函数，当调用时，程序会成功退出。这里根本没有数据操作。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Initialize&lt;/code&gt;结构定义了上下文为空，没有任何参数。我们将在后面学习更多关于函数上下文的知识。&lt;/p&gt;

&lt;p&gt;要编译这个程序，我们可以运行 Anchor 的&lt;code&gt;build&lt;/code&gt;命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一旦构建完成，你应该会看到一个名为&lt;strong&gt;target&lt;/strong&gt;的新文件夹。&lt;/p&gt;

&lt;p&gt;创建的工件之一是位于&lt;strong&gt;target/idl/mysolanaapp.json&lt;/strong&gt;的 &lt;a href="https://en.wikipedia.org/wiki/Interface_description_language" rel="nofollow" target="_blank" title=""&gt;IDL&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;IDL 与 Solidity 中的 &lt;a href="https://docs.soliditylang.org/en/v0.5.3/abi-spec.html" rel="nofollow" target="_blank" title=""&gt;ABI&lt;/a&gt;（或 GraphQL 中的查询定义）非常相似，我们将在 JavaScript 测试和前端中以类似的方式使用它们来通过 RPC 与我们的 Solana 程序通信。&lt;/p&gt;

&lt;p&gt;我们也可以测试我们的程序。如果你打开&lt;strong&gt;tests/mysolanaapp.js&lt;/strong&gt;，你会看到其中有一个用 JavaScript 编写的测试，让我们可以测试程序。&lt;/p&gt;

&lt;p&gt;测试应该如下所示：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@project-serum/anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysolanaapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Configure the client to use the local cluster.&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Is initialized!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mysolanaapp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your transaction signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从这个测试中，有几个重要的东西可以学习，我们将在未来的测试和前端 JavaScript 客户端中使用，这些对我们很重要。&lt;/p&gt;

&lt;p&gt;要使用 Anchor 调用 Solana 程序，通常需要两个主要的东西：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;Provider&lt;/code&gt;&lt;/strong&gt; - &lt;code&gt;Provider&lt;/code&gt;是到 Solana 网络的连接的抽象，通常由 &lt;a href="https://solana-labs.github.io/solana-web3.js/classes/Connection.html" rel="nofollow" target="_blank" title=""&gt;&lt;code&gt;Connection&lt;/code&gt;&lt;/a&gt;、钱包和&lt;a href="https://solana-labs.github.io/solana-web3.js/modules.html#Commitment" rel="nofollow" target="_blank" title=""&gt;预提交承诺&lt;/a&gt;组成。&lt;/p&gt;

&lt;p&gt;在测试中，Anchor 框架将根据环境（&lt;code&gt;anchor.Provider.env()&lt;/code&gt;）为我们创建提供程序，但在客户端中，我们将需要使用用户的 Solana 钱包构建提供程序。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;program&lt;/code&gt;&lt;/strong&gt; - &lt;code&gt;program&lt;/code&gt;是一个结合了&lt;code&gt;Provider&lt;/code&gt;、&lt;code&gt;idl&lt;/code&gt;和&lt;code&gt;programID&lt;/code&gt;（在构建程序时生成）的抽象，允许我们对我们的程序调用&lt;code&gt;RPC&lt;/code&gt;方法。&lt;/p&gt;

&lt;p&gt;与&lt;code&gt;Provider&lt;/code&gt;一样，Anchor 提供了一种方便的方法来访问&lt;code&gt;program&lt;/code&gt;，但在构建前端时，我们需要自己构建这个&lt;code&gt;provider&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;一旦我们有了这两个东西，我们就可以开始调用程序中的函数。例如，在我们的程序中，我们有一个&lt;code&gt;initialize&lt;/code&gt;函数。在我们的测试中，你会看到我们可以直接使用&lt;code&gt;program.rpc.functionName&lt;/code&gt;来调用该函数：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是你在使用 Anchor 时经常会使用的一种常见模式，一旦你掌握了它的工作原理，就可以很容易地连接到 Solana 程序并与之交互。&lt;/p&gt;

&lt;p&gt;我们现在可以通过运行&lt;code&gt;test&lt;/code&gt;脚本来测试程序：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="构建 Hello World"&gt;构建 Hello World&lt;/h2&gt;
&lt;hr&gt;

&lt;p&gt;现在我们已经设置好项目，让我们创建一些更有趣的东西。&lt;/p&gt;

&lt;p&gt;我知道，作为一个全栈开发人员，大多数时候我在想如何进行 &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" rel="nofollow" target="_blank" title=""&gt;CRUD&lt;/a&gt; 类型的操作，所以我们接下来将看看这个。&lt;/p&gt;

&lt;p&gt;我们将创建的第一个程序将允许我们创建一个计数器，每次我们从客户端应用程序调用它时都会递增。&lt;/p&gt;

&lt;p&gt;我们需要做的第一件事是打开&lt;strong&gt;programs/mysolanaapp/src/lib.rs&lt;/strong&gt;并使用以下代码进行更新： &lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;anchor_lang&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;declare_id!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[program]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;mysolanaapp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProgramResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.base_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="py"&gt;.count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProgramResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.base_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="py"&gt;.count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Transaction instructions&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(init,&lt;/span&gt; &lt;span class="nd"&gt;payer&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;user,&lt;/span&gt; &lt;span class="nd"&gt;space&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BaseAccount&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;system_program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="n"&gt;rust&lt;/span&gt;
&lt;span class="c1"&gt;// 交易指令&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BaseAccount&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 一个包含在交易指令中的账户&lt;/span&gt;
&lt;span class="nd"&gt;#[account]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BaseAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个程序中，我们有两个函数 - &lt;code&gt;create&lt;/code&gt; 和 &lt;code&gt;increment&lt;/code&gt;。这两个函数是我们将能够从客户端应用程序调用的 RPC 请求处理程序，以与程序进行交互。&lt;/p&gt;

&lt;p&gt;RPC 处理程序的第一个参数是 Context 结构，描述了在调用函数时将传递的上下文以及如何处理它。在 &lt;code&gt;Create&lt;/code&gt; 的情况下，我们期望三个参数：&lt;code&gt;base_account&lt;/code&gt;、&lt;code&gt;user&lt;/code&gt; 和 &lt;code&gt;system_program&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#[account(...)]&lt;/code&gt; 属性定义了与之前声明的账户相关的约束和指令。如果这些约束中的任何一个不成立，那么指令将永远不会执行。&lt;/p&gt;

&lt;p&gt;任何使用正确的 &lt;code&gt;base_account&lt;/code&gt; 调用此程序的客户端都可以调用这些 RPC 方法。&lt;/p&gt;

&lt;p&gt;Solana 处理数据的方式与我以往接触过的任何东西都不同。程序内部没有持久状态，一切都附加在所谓的账户上。账户基本上包含程序的所有状态。因此，所有数据都是通过引用从外部传递的。&lt;/p&gt;

&lt;p&gt;也没有读取操作。这是因为要读取程序的内容，你只需要请求账户，然后你就能查看程序的所有状态。要了解有关账户如何工作的更多信息，请查看&lt;a href="https://2501babe.github.io/posts/solana101.html#programs-and-accounts" rel="nofollow" target="_blank" title=""&gt;此文章&lt;/a&gt; 。&lt;/p&gt;

&lt;p&gt;构建程序：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，让我们编写一个使用此计数器程序的测试。为此，请打开 &lt;strong&gt;tests/mysolanaapp.js&lt;/strong&gt; 并更新为以下代码：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@project-serum/anchor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mysolanaapp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 创建并设置提供程序 */&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mysolanaapp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Creates a counter)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 通过 RPC 调用 create 函数 */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;systemProgram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="cm"&gt;/* 获取账户并检查 count 的值 */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Count 0: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;_baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Increments the counter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_baseAccount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Count 1: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在继续测试和部署程序之前，我们要获取由构建生成的动态生成的程序 ID。我们需要此 ID 用于在 Rust 程序中使用，以替换我们在创建项目时设置的占位符 ID。要获取此 ID，可以运行以下命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana address &lt;span class="nt"&gt;-k&lt;/span&gt; target/deploy/mysolanaapp-keypair.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，我们可以在 &lt;strong&gt;lib.rs&lt;/strong&gt; 中更新程序 ID：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// mysolanaapp/src/lib.rs&lt;/span&gt;

&lt;span class="nd"&gt;declare_id!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"your-program-id"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以及在 &lt;strong&gt;Anchor.toml&lt;/strong&gt; 中：&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Anchor.toml&lt;/span&gt;
&lt;span class="nn"&gt;[programs.localnet]&lt;/span&gt;
&lt;span class="py"&gt;mysolanaapp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your-program-id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，运行测试：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一旦测试通过，我们现在可以部署。&lt;/p&gt;

&lt;p&gt;我们现在可以部署程序。确保 &lt;code&gt;solana-test-validator&lt;/code&gt; 正在运行：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;你还可以通过打开一个单独的窗口并运行 &lt;code&gt;solana logs&lt;/code&gt; 来查看验证器日志&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;现在我们准备构建前端。&lt;/p&gt;
&lt;h3 id="构建 React 应用"&gt;构建 React 应用&lt;/h3&gt;
&lt;p&gt;在 Anchor 项目的根目录中，创建一个新的 react 应用程序，以覆盖现有的 &lt;strong&gt;app&lt;/strong&gt; 目录：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-react-app app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，安装我们将需要用于 Anchor 和 Solana Web3 的依赖项：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;app

npm &lt;span class="nb"&gt;install&lt;/span&gt; @project-serum/anchor @solana/web3.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们还将使用 &lt;a href="https://github.com/solana-labs/wallet-adapter" rel="nofollow" target="_blank" title=""&gt;Solana Wallet Adapter&lt;/a&gt; 来处理连接用户 Solana 钱包。让我们也安装这些依赖项：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @solana/wallet-adapter-react &lt;span class="se"&gt;\&lt;/span&gt;
@solana/wallet-adapter-react-ui @solana/wallet-adapter-wallets &lt;span class="se"&gt;\&lt;/span&gt;
@solana/wallet-adapter-base
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，在 &lt;strong&gt;src&lt;/strong&gt; 目录中，创建一个名为 &lt;strong&gt;idl.json&lt;/strong&gt; 的新文件。在这里，复制在主项目文件夹中为你创建的 IDL JSON，位于 &lt;strong&gt;target/idl/mysolanaapp.json&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;如果我们可以自动将此 &lt;strong&gt;idl&lt;/strong&gt; 文件复制到我们的客户端应用程序 &lt;strong&gt;src&lt;/strong&gt; 文件夹中，那将是很好的，但目前我还没有找到原生方法来实现这一点。当然，如果你愿意，你可以创建自己的脚本来执行此操作，否则你需要在每次更改主程序后复制并粘贴 IDL。&lt;/p&gt;

&lt;p&gt;如果你想要这样的脚本，你可以在几行代码中完成：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// copyIdl.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./target/idl/mysolanaapp.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app/src/idl.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，打开 &lt;strong&gt;app/src/App.js&lt;/strong&gt; 并更新为以下内容：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;web3&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@project-serum/anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;idl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./idl.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PhantomWalletAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-wallets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WalletProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ConnectionProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WalletModalProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WalletMultiButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-react-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-react-ui/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="cm"&gt;/* 查看可用钱包列表 https://github.com/solana-labs/wallet-adapter#wallets */&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PhantomWalletAdapter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;web3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/* 创建一个账户 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;preflightCommitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;programID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="s2"&gt;``````&lt;/span&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 创建提供程序并将其返回给调用者 */&lt;/span&gt;
    &lt;span class="cm"&gt;/* 网络暂时设置为本地网络 */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preflightCommitment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preflightCommitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="cm"&gt;/* 创建包含 idl、程序 ID 和提供程序的程序接口 */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;programID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* 通过 rpc 与程序交互 */&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;systemProgram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;account: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;programID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;account: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 如果用户的钱包未连接，则显示连接钱包按钮。 */&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WalletMultiButton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createCounter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Create&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Increment&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Please&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* 钱包配置如下: https://github.com/solana-labs/wallet-adapter#setup */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppWithProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConnectionProvider&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WalletProvider&lt;/span&gt; &lt;span class="nx"&gt;wallets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;wallets&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;autoConnect&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WalletModalProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/WalletModalProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/WalletProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConnectionProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;AppWithProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="切换钱包网络"&gt;切换钱包网络&lt;/h3&gt;
&lt;p&gt;在我们可以与 &lt;code&gt;localhost&lt;/code&gt; 网络上的程序交互之前，我们必须将我们的 Phantom 钱包切换到正确的网络。&lt;/p&gt;

&lt;p&gt;要这样做，请打开你的 Phantom 钱包并单击设置按钮。然后向下滚动到 &lt;strong&gt;更改网络&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1717299496839" title="" alt="Image 1: 更新网络"&gt; &lt;/p&gt;

&lt;p&gt;接下来，选择 &lt;strong&gt;Localhost&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1717299496840" title="" alt="Image 2: 选择 localhost"&gt; &lt;/p&gt;

&lt;p&gt;现在我们需要向这个钱包空投代币。在钱包界面顶部，单击你的地址以将其复制到剪贴板。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://img.learnblockchain.cn/attachments/migrate/1717299496841" title="" alt="Image 3: 钱包地址"&gt;  &lt;/p&gt;

&lt;p&gt;接下来，打开你的终端并运行以下命令（确保 &lt;code&gt;solana-test-validator&lt;/code&gt; 正在运行）:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana airdrop 10 &amp;lt;address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你现在应该在你的钱包中有 10 个代币。现在，我们可以运行并测试该应用程序！&lt;/p&gt;

&lt;p&gt;切换到 &lt;strong&gt;app&lt;/strong&gt; 目录并运行以下命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你应该能够连接你的钱包，创建一个计数器并对其进行递增。&lt;/p&gt;

&lt;p&gt;你会注意到，当你刷新页面时，你会丢失程序的状态。这是因为我们在程序加载时动态生成基本帐户。如果你想要在各个客户端之间读取和交互程序数据，你需要在项目中创建并存储 Keypair。我已经整理了一个&lt;a href="https://gist.github.com/dabit3/7cbd18b8bc4b495c4831f8674902eb42" rel="nofollow" target="_blank" title=""&gt;代码片段&lt;/a&gt; ，展示了这可能是什么样子。&lt;/p&gt;
&lt;h2 id="Hello World #2"&gt;Hello World #2&lt;/h2&gt;
&lt;hr&gt;

&lt;p&gt;让我们创建这个程序的一个变体，而不是处理计数器，它允许我们创建一条消息并跟踪所有先前创建的消息。&lt;/p&gt;

&lt;p&gt;为此，让我们更新我们的 Rust 程序如下：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* programs/mysolanaapp/src/lib.rs */&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;anchor_lang&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;declare_id!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"your-program-id"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[program]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;mysolanaapp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProgramResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.base_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="py"&gt;.data_list&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProgramResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.base_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="py"&gt;.data_list&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(init,&lt;/span&gt; &lt;span class="nd"&gt;payer&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;user,&lt;/span&gt; &lt;span class="nd"&gt;space&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BaseAccount&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;system_program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;base_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BaseAccount&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;#[account]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BaseAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;data_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个程序中，我们要跟踪的两个主要数据是一个名为 &lt;code&gt;data&lt;/code&gt; 的字符串和一个保存程序中添加的所有数据列表的向量，名为 &lt;code&gt;data_list&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;你会注意到这里的内存分配比之前的程序要高 (&lt;code&gt;128 + 128&lt;/code&gt;)，以便考虑到向量。我不知道你能够在这个程序中存储多少更新，但可能值得进一步调查或尝试，因为这个示例本身是实验性的，只是为了让你了解事物是如何工作的。&lt;/p&gt;

&lt;p&gt;接下来，我们可以更新这个新程序的测试：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@project-serum/anchor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mysolanaapp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mysolanaapp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It initializes the account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;systemProgram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Data: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;_baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Updates a previously created account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_baseAccount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Some new data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Updated data: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Some new data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all account data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All data: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要进行测试：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果测试失败，请尝试关闭验证器，然后再次运行。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;接下来，让我们更新客户端。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* app/src/App.js */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;web3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@project-serum/anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;idl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./idl.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PhantomWalletAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-wallets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WalletProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ConnectionProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WalletModalProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WalletMultiButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-react-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/wallet-adapter-react-ui/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PhantomWalletAdapter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;web3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;preflightCommitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;programID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dataList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDataList&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 创建提供程序并将其返回给调用者 */&lt;/span&gt;
    &lt;span class="cm"&gt;/* 网络暂时设置为本地网络 */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preflightCommitment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preflightCommitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="cm"&gt;/* 创建将 idl、程序 ID 和提供程序结合在一起的程序接口 */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;programID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* 通过 rpc 与程序交互 */&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;systemProgram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;account: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nf"&gt;setDataList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;programID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;account: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nf"&gt;setDataList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WalletMultiButton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;          &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
                  &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add new data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
                  &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Please&lt;/span&gt; &lt;span class="nx"&gt;Inialize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dataList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h4&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h4&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppWithProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConnectionProvider&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WalletProvider&lt;/span&gt; &lt;span class="nx"&gt;wallets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;wallets&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;autoConnect&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WalletModalProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/WalletModalProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/WalletProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConnectionProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;AppWithProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，构建并部署程序（确保 &lt;code&gt;solana-test-validator&lt;/code&gt; 正在运行）：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor build

anchor deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有了新的构建，你将拥有一个新的 &lt;strong&gt;IDL&lt;/strong&gt;，你需要更新客户端。要么将新的 IDL 复制到 &lt;strong&gt;app/src/idl.json&lt;/strong&gt;，要么运行你的 &lt;strong&gt;copyIdl.js&lt;/strong&gt; 脚本。&lt;/p&gt;
&lt;h3 id="测试"&gt;测试&lt;/h3&gt;
&lt;p&gt;在测试新程序时，请确保更新由构建创建的&lt;strong&gt;idl.json&lt;/strong&gt;文件。&lt;/p&gt;

&lt;p&gt;切换到&lt;strong&gt;app&lt;/strong&gt;目录并运行&lt;code&gt;start&lt;/code&gt;命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="部署到 Devnet"&gt;部署到 Devnet&lt;/h3&gt;
&lt;p&gt;从这里开始，向实时网络部署非常简单。我们需要做的主要事情是：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; 更新 Solana CLI 以使用&lt;code&gt;devnet&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--url&lt;/span&gt; devnet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; 更新 Phantom 钱包以使用&lt;code&gt;devnet&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; 打开&lt;strong&gt;Anchor.toml&lt;/strong&gt;并将集群从&lt;code&gt;localnet&lt;/code&gt;更新为&lt;code&gt;devnet&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; 重新构建程序。确保&lt;strong&gt;Anchor.toml&lt;/strong&gt;中的程序 ID 与当前程序 ID 匹配。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; 再次部署程序，这次将部署到&lt;code&gt;devnet&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; 在&lt;strong&gt;app/src/App.js&lt;/strong&gt;中，我们还需要更新网络，这次使用&lt;code&gt;@solana/web3&lt;/code&gt;中的&lt;code&gt;clusterApiUrl&lt;/code&gt;，如下所示：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* before */&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConnectionProvider&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="cm"&gt;/* after */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;
  &lt;span class="nx"&gt;clusterApiUrl&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clusterApiUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;devnet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConnectionProvider&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从这里开始，你应该能够像我们之前所做的那样部署和测试。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;该项目的代码位于&lt;a href="https://github.com/dabit3/complete-guide-to-full-stack-solana-development" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="下一步"&gt;下一步&lt;/h3&gt;
&lt;p&gt;我建议你查看的另一份深入教程是&lt;a href="https://lorisleiva.com/create-a-solana-dapp-from-scratch" rel="nofollow" target="_blank" title=""&gt;从头开始创建 Solana dApp&lt;/a&gt;，该教程实现了 Twitter 的简化版本作为 Solana dapp。&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;如果你有兴趣全职从事这样的技术工作，请加入我和我的团队 &lt;a href="https://edgeandnode.com/jobs" rel="nofollow" target="_blank" title=""&gt;Edge &amp;amp; Node&lt;/a&gt;，我们正在招聘！&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;本文由 AI 翻译，欢迎小伙伴们来&lt;a href="https://github.com/lbc-team/Pioneer/blob/master/translations/8280.md" rel="nofollow" target="_blank" title=""&gt;校对&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>Tiny</author>
      <pubDate>Sun, 02 Jun 2024 17:35:28 +0800</pubDate>
      <link>https://soldev.cn/topics/19</link>
      <guid>https://soldev.cn/topics/19</guid>
    </item>
    <item>
      <title>Solana 中文开发资源汇总</title>
      <description>&lt;h2 id="中文资料"&gt;中文资料&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Solana 中文开发教程： &lt;a href="https://www.solanazh.com/" rel="nofollow" target="_blank"&gt;https://www.solanazh.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Solana 中文开发者培训营 - &lt;a href="https://www.youtube.com/channel/UC55hLTz7EuR97neBW2trFMA" rel="nofollow" target="_blank" title=""&gt;视频&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Solana 开发秘籍 - 中文电子书：  &lt;a href="https://davirain-su.github.io/solana-cookbook-zh/index.html" rel="nofollow" target="_blank"&gt;https://davirain-su.github.io/solana-cookbook-zh/index.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="英文资料"&gt;英文资料&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;开始在 Solana 上构建： &lt;a href="https://www.startonsolana.com/#quest-section" rel="nofollow" target="_blank"&gt;https://www.startonsolana.com/#quest-section&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>Tiny</author>
      <pubDate>Thu, 23 May 2024 17:04:40 +0800</pubDate>
      <link>https://soldev.cn/topics/7</link>
      <guid>https://soldev.cn/topics/7</guid>
    </item>
    <item>
      <title>9/20-9/21 Solana Break Point @ 新加披</title>
      <description>&lt;p&gt;&lt;img src="/uploads/photo/shooter/05ed915f-4d43-45fc-b052-c39e55dc5e94.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;欢迎报名参加： &lt;a href="https://solana.com/breakpoint" rel="nofollow" target="_blank"&gt;https://solana.com/breakpoint&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="门票"&gt;门票&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GENERAL ADMISSION • $500&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ARTISTS • $250&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;申请链接：&lt;a href="https://solanafoundation.typeform.com/artist-ticket" rel="nofollow" target="_blank"&gt;https://solanafoundation.typeform.com/artist-ticket&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;STUDENTS • $100&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;申请链接：&lt;a href="https://solanafoundation.typeform.com/student-ticket" rel="nofollow" target="_blank"&gt;https://solanafoundation.typeform.com/student-ticket&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DEVELOPERS • $250&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;申请链接：&lt;a href="https://solanafoundation.typeform.com/developerticket" rel="nofollow" target="_blank"&gt;https://solanafoundation.typeform.com/developerticket&lt;/a&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Wed, 22 May 2024 14:41:28 +0800</pubDate>
      <link>https://soldev.cn/topics/6</link>
      <guid>https://soldev.cn/topics/6</guid>
    </item>
    <item>
      <title>Solana 开发课程（Solana Development Course 中文翻译）</title>
      <description>&lt;p&gt;Solana Development Course 中文翻译：
前往阅读： &lt;a href="https://decert.me/tutorial/sol-dev/" rel="nofollow" target="_blank"&gt;https://decert.me/tutorial/sol-dev/&lt;/a&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Wed, 22 May 2024 14:36:44 +0800</pubDate>
      <link>https://soldev.cn/topics/5</link>
      <guid>https://soldev.cn/topics/5</guid>
    </item>
    <item>
      <title>【文章】Solana 智能合约指南 </title>
      <description>&lt;p&gt;由于其支持自定义智能合约开发的特殊功能，Solana 正迅速成为构建去中心化应用程序的最受欢迎的区块链平台之一。该网络以其令人印象深刻的速度、效率和扩展能力而脱颖而出，使其成为很吸引力的商业选择。
在这篇博文中，我们将讨论 Solana 及其架构的特殊性，并介绍在其上开发智能合约的基础知识。&lt;/p&gt;
&lt;h2 id="Solana是什么?"&gt;Solana 是什么？&lt;/h2&gt;
&lt;p&gt;Solana 是一个高性能的区块链平台，专为去中心化应用程序和加密项目而设计。它以交易速度快和费用低而闻名。它的应用很广泛，包括去中心化金融（DeFi）、不可替代代币（NFT）、去中心化交易所（DEX）等。&lt;/p&gt;

&lt;p&gt;可扩展性是区块链面临的重大挑战，特别是随着网络的发展产生的交易速度和确认时间的限制。Solana 通过创新的验证方法解决了这些问题。它使用了一种称为历史证明（Proof-of-History）的机制以可验证地记录交易的时间。该机制建立了一个无需信任的系统，从而减少了网络开销并提高了交易处理的速度。&lt;/p&gt;

&lt;p&gt;Solana 有自己的原生货币，称为 SOL。这种货币用于支付交易费用，在网络中发挥着至关重要的作用。SOL 对于平台的安全基础设施也至关重要。网络验证者以 SOL 为承诺，这意味着这些验证者可以获得新的 SOL 代币作为对成功验证区块奖励。另一方面，如果验证者行为不当，他们质押的 SOL 可能会被削减（即从流通中部分移除）；截至撰写本文时，自动削减尚未实施，也没有近期的计划。然而，削减的可能性仍然让不良行为者望而却步。&lt;/p&gt;
&lt;h2 id="Solana有哪些关键创新？"&gt;Solana 有哪些关键创新？&lt;/h2&gt;
&lt;p&gt;Solana 的核心创新在于其独特的 Proof of History（PoH）机制，该机制使验证者能够快速就交易的时间和顺序达成共识，从而显着提高网络的可扩展性和吞吐量。这并不是推动 Solana 性能的唯一创新。该网络采用了其他跨时代的技术与 PoH 相结合，以实现高吞吐量、低延迟并确保平台的可扩展性。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://miro.medium.com/v2/resize:fit:700/0*wDMjmt-_w5pJOSRr.jpg" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="Proof of History（PoH）"&gt;Proof of History (PoH)&lt;/h3&gt;
&lt;p&gt;历史证明（PoH）是区块链技术中的一个开创性概念，它源于权益证明（PoS）。与传统的时间戳方法不同，PoH 能够验证某一事件相对于其他事件是否按特定的顺序发生。&lt;/p&gt;

&lt;p&gt;PoH 使用基于 SHA256 的可验证延迟函数（verifiable delay function, VDF）。VDF 的目的是使用前一个哈希值作为下一个哈希计算的输入，从而以尽可能高的速度依次处理哈希计算。因此，“可核实的延迟（verifiable delay）”一词是指计算需要花费一定的最短时间。尽管初始计算需要一些时间，但验证过程可以有效地并行化，使验证速度显著加快。&lt;/p&gt;

&lt;p&gt;在计算 VDF 的任何步骤，都可以将各种事件（如交易）合并到哈希计算的输入中，从而建立一个清晰、可验证的事件序列。由于哈希计算过程需要的时间是一致的，验证者可以准确地确定事件之间经过了多少时间。此外，通过在这个哈希链中安排事务，验证者为每个区块处理和传输的数据会更少。&lt;/p&gt;

&lt;p&gt;然而，PoH 也有其局限性。他们包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;复杂的设置：PoH 的实施比传统方法更复杂。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;新的且基本上未经测试：由于 PoH 相对较新，它没有像既定方法那样经过长时间的测试，这引起了人们对未来可能出现的未知问题的担忧。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PoH 本身仅提供可验证的事件序列，Solana 利用一组其他技术来促进快速交易处理。其中包括 Tower BFT、Turbine、Gulf Stream、Sealevel、Cloudbreak。&lt;/p&gt;
&lt;h2 id="Tower BFT (Byzantine Fault Tolerance)"&gt;Tower BFT (Byzantine Fault Tolerance)&lt;/h2&gt;
&lt;p&gt;Tower BFT 是一种旨在提高网络效率的共识机制。本质上，Tower BFT 是委托权益证明 (&lt;a href="https://crypto.com/university/what-is-dpos-delegated-proof-of-stake" rel="nofollow" target="_blank" title=""&gt;Delegated Proof of Stake, DPoS&lt;/a&gt;) 区块链中实用拜占庭容错 (&lt;a href="https://www.geeksforgeeks.org/practical-byzantine-fault-tolerancepbft/" rel="nofollow" target="_blank" title=""&gt;Practical Byzantine Fault Tolerance, PBFT&lt;/a&gt;) 模型的修改迭代。Tower BFT 的主要特点是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;以 PoH 为可靠的网络时钟。由于 PoH 账本的所有网络事件都附加了可靠的时间戳，因此任何节点都可以计算任何其他节点的确切状态，而无需任何额外的点对点通信，这可以显着减少网络开销。相比之下，传统的 BFT 系统依赖验证者之间的点对点通信来建立事件序列。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;验证者每个时隙只能投票一次。一个时隙（slot）是表示固定时间量的多个 PoH 步骤，目标是 400 毫秒左右。这有助于确保网络持续运行并且不会无限期地停滞。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tower BFT 采用权益加权超时（stake-weighted timeout）机制来激励验证者对最重（heaviest）的分叉进行投票，这有助于避免链分叉。微分叉仍然会发生，但很快就会被丢弃。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Turbine"&gt;Turbine&lt;/h3&gt;
&lt;p&gt;Turbine 是一种区块传播协议，使网络能够在不增加数据传输时间的情况下增加节点数量。它的工作方式有点类似于点对点数据传输协议，例如 BitTorrent。它将消息分解为更小的数据包并通过验证者之间的中继系统进行分发。Turbine 显着提高了 Solana 网络上处理交易的吞吐量和整体能力。&lt;/p&gt;
&lt;h3 id="Gulf Stream"&gt;Gulf Stream&lt;/h3&gt;
&lt;p&gt;Gulf Stream 通过消除传统的内存池（一个等候区，用于放置等待被区块处理的交易）来增强交易处理。事务的缓存和转发被委托给网络边缘。任何时隙中，新区块均由称为领导者（leader）的单个验证者节点生成。领导者按计划轮换，以避免赋予一个验证者过多的权力。每个验证者都知道即将到来的领导者的顺序，从而可以提前将交易转发给预期的领导者。这让验证者能提前执行交易并减少确认时间。&lt;/p&gt;
&lt;h3 id="Cloudbreak"&gt;Cloudbreak&lt;/h3&gt;
&lt;p&gt;Cloudbreak 是针对 Solana 区块链状态的水平可扩展存储解决方案。它使用的自定义数据结构可优化并行读写，并提高水平扩展性。它的实现对于 Solana 的可扩展性至关重要。&lt;/p&gt;

&lt;p&gt;从本质上讲，Cloudbreak 是一个将地址映射到帐户的高性能数据库。&lt;/p&gt;
&lt;h3 id="Sealevel"&gt;Sealevel&lt;/h3&gt;
&lt;p&gt;Sealevel 是 Solana 的智能合约运行时（runtime），它旨在允许智能合约的并行执行。与其他领先的智能合约网络相比，这使该平台具有显著的性能优势。得益于这一功能，Solana 可以在不影响网络性能的情况下同时处理数千个智能合约。&lt;/p&gt;

&lt;p&gt;Sealevel 的一个关键方面是帐户模型。在编写 Solana 智能合约甚至简单地使用区块链时，我们都需要理解它。所以我们将对此进行更详细的讨论。&lt;/p&gt;
&lt;h2 id="Solana账户是什么"&gt;Solana 账户是什么&lt;/h2&gt;
&lt;p&gt;Solana 账户模型是其生态系统的基本，使其有别于其他区块链平台。&lt;/p&gt;

&lt;p&gt;Solana 的帐户充当数据的存储位置。以太坊将智能合约代码及其数据在概念上共存于一个位置，Solana 与它不同，Solana 明确讲程序代码和正在处理的数据区分开，并将它们分配给不同的账户。&lt;/p&gt;

&lt;p&gt;当调用程序时，调用者必须提供程序将使用的所有帐户，明确标记该帐户是否应为只读、可写，或者是否正在签署操作。这种独特的方法是 Sealevel 实现高度并行的关键因素之一：事实上，在不同帐户上运行的同一程序可以使用 SIMD 处理并行运行。&lt;/p&gt;

&lt;p&gt;这种方法还使程序具有高度的可重用性和可组合性。例如，您无需开发和部署单独的智能合约即可在 Solana 上发布 memecoin；相反，您只需重复使用 Solana Labs 部署的代币程序即可。&lt;/p&gt;

&lt;p&gt;Solana 帐户存储着其 SOL 余额、其所属程序的地址以及程序特定的任意二进制数据。这些是与帐户所有权相关的基本规则：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;程序无法更改不属于它们的帐户的数据。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;程序只能从其拥有的账户中借记 SOL。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;任何程序都可以从任何帐户读取数据或存入 SOL。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;默认情况下，新帐户归系统程序所有。系统程序促成了各种核心的区块链操作，如在不同账户之间转移 SOL。它还可以为帐户分配零初始化的数据，和将帐户所有权转移到其他程序。&lt;/p&gt;
&lt;h3 id="账户的种类"&gt;账户的种类&lt;/h3&gt;
&lt;p&gt;Solana 区块链有两种类型的帐户：可执行帐户和不可执行帐户。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;可执行帐户是一种特殊类型的帐户，它是不可变的，并且包含可由运行时（runtime）执行的字节码（byte-code）。曾经，Solanaa 程序的可执行代码存储在这些可执行帐户中。然而，由于其不可变的性质，升级合同的必要性导致现在使用更复杂的方法。如今，可执行帐户只存储一个小的加载填充程序（loader shim），而实际代码存储在一个没有被标记为可执行的单独帐户中。可执行帐户由 BPF 加载程序版本 1 或 2，或由可升级的 BPF 加载程序所有。所有的新账户都归后者所有。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;不可执行帐户用于程序特定的数据存储，其数据是可变的。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;维护这些账户中的数据会产生称为租金的成本，租金可通过 lamport 支付。lamports 代表着 Solana 原生代币 SOL 的一小部分，用于网络上的小额支付。租金费用取决于帐户所需的存储大小，数据存储越多，费用越高。目前，所有账户都免租（而是采用保证金模式，译者注），意思是它们必须一直保持一个最低的 SOL 余额。这个最低余额根据帐户存储的数据量而定。在撰写本文时，对于有 128 字节最小存储空间的帐户，所需的最低余额为 0.00089088 SOL，等效为 890,880 lamport。您可以通过运行 &lt;code&gt;solana rent &amp;lt;size&amp;gt;&lt;/code&gt; 轻松计算所需的余额，其中 &lt;code&gt;&amp;lt;size&amp;gt;&lt;/code&gt; 是帐户大小（以字节为单位）。请记住，&lt;code&gt;solana rent&lt;/code&gt; 的参数中了预留了一个 128 字节的最小存储，因为这些字节不可用存储特定的程序。因此，&lt;code&gt;solanarent 5&lt;/code&gt; 实际的余额是 133 字节（即 128 + 5）。&lt;/p&gt;
&lt;h2 id="SPL 代币是什么"&gt;SPL 代币是什么&lt;/h2&gt;
&lt;p&gt;SPL 代币是在 Solana 区块链生态系统中运行的数字资产。管理 SPL 代币的代码由 Solana Labs 部署，它是 Solana 程序库的一部分。举个例子，与以太坊形成鲜明对比的是，以太坊上的每个代币都必须有自己的合约，而 Solana 上的代币则重复使用现有的合约。&lt;/p&gt;

&lt;p&gt;涉及 SPL 代币的交易费用可以使用该平台的原生代币 SOL 支付。&lt;/p&gt;
&lt;h3 id="SPL代币的特点"&gt;SPL 代币的特点&lt;/h3&gt;
&lt;p&gt;SPL 代币与其他区块链生态系统中的代币标准不同，例如以太坊的 ERC 标准、波场（TRON）的 TRC-20 和币安智能链的 BEP-20。SPL 代币的主要特征包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;NFT 和可替代代币的适用性：SPL 代币涵盖同一标准下的可替代（可互换）和不可替代（唯一）代币。 （而以太坊对每种代币类型都有单独的标准。）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;可组合性：代币代码可以重复使用，从而用最小的更改即可创建新的 SPL 代币。这种可组合性意味着开发人员可以通过仅更改某些参数来快速地生成新代币。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;效率：SPL 代币直接受益于 Solana 区块链的高性能。该网络的高处理速度提高了 SPL 代币的交易效率，使这些代币比其他区块链上的操作更快、更具成本效益。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="什么是 Token-2022?"&gt;什么是 Token-2022?&lt;/h2&gt;
&lt;p&gt;Solana 的 &lt;a href="https://spl.solana.com/token-2022" rel="nofollow" target="_blank" title=""&gt;Token-2022 计划&lt;/a&gt;（也称为 Token Extensions）是一种新的实现方式。它在一定程度上向下兼容原来的 SPL 代币。然而，它是一个不同的程序，具有单独的源代码并部署在不同的地址。&lt;/p&gt;

&lt;p&gt;Token Extensions 扩大了 SPL 代币的铸造和使用范围，使用户能够在更广泛的应用中使用 SPL 代币，例如机密交易、创新合规机制、引入代币转让费用等。Token Extensions 同时兼容可替代和不可替代代币。&lt;/p&gt;

&lt;p&gt;尽管该计划于 2022 年推出，但 Beta 测试在最近才完成，这为智能合约开发提供了机会。然而，在撰写本文时，程序仍然在更新并有进一步的更改，并暂定计划在 2024 年晚些时候冻结更新。&lt;/p&gt;

&lt;p&gt;Token-2022 扩展分为两类：&lt;strong&gt;铸造拓展&lt;/strong&gt;和 &lt;strong&gt;账户扩展&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="铸造拓展"&gt;铸造拓展&lt;/h3&gt;
&lt;p&gt;铸造扩展增强了代币铸造者的能力。代币铸造者指的是 SPL 代币计划（2022）拥有的一个账户，它存储了有关代币的必要元数据，例如谁可以铸造新代币，代币是否可分割，以及可分割到何种程度等。铸造扩展允许为代币指定额外的规则或属性，例如添加面向用户的元数据，如令牌名称和图片。&lt;/p&gt;

&lt;p&gt;当代币需要标准发行之外的额外逻辑或信息时，例如为 NFT 定义唯一标识符或嵌入监管合规检查时，它们非常有用。&lt;/p&gt;

&lt;p&gt;目前的铸造拓展包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;私密转账&lt;/strong&gt;：允许用户之间进行私人交易，并隐藏转账金额。请注意，在撰写本文时，机密传输仍然需要在网络上激活功能，因此还不起作用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;转账费用&lt;/strong&gt;：此功能允许每次代币转账收取费用，然后将这些费用分配到指定的帐户。它在用于例如 NFT 销售的铸币税上会起到作用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;关闭铸造权限&lt;/strong&gt;：这一扩展授予所有者关闭铸币账户的权力，使他们能够收回这些账户中的 lamports。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;计息代币&lt;/strong&gt;：这是一个界面功能，允许指定如何在显示前调整代币金额。具体来说，它允许指定可见代币金额增长（或减少）的利率。实际上没有铸造新的代币。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;不可转让代币&lt;/strong&gt;：此功能限制用户之间的代币转让，将代币锁定到其初始持有者。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;永久代理&lt;/strong&gt;：此功能为铸币指定一名永久代理，授予该代理对与该铸币相关的任何代币账户的无限访问权和控制权。这包括随意转移或焚烧代币的能力。这方面的使用案例可能包括没收受制裁实体的资产或收回未缴纳 Harberger 税的代币。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;转移挂钩（hook）&lt;/strong&gt;：启用后，每个代币的转移都将通过 CPI（跨程序调用）触发指定的程序，从而可以在代币转移时运行自定义的链上代码，例如，使用程序定义的逻辑阻止某些转移。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;元数据指针&lt;/strong&gt;：使代币创建者能够指定一个地址，该地址保存代币的官方元数据。有趣的是，这也可以指向铸币本身。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;元数据&lt;/strong&gt;：此扩展通过自定义字段将额外的元数据直接集成到铸币帐户中。需要注意的是，在撰写本文时，DEX 和钱包对原生代币 2022 元数据的支持相当缺乏，因此&lt;a href="https://developers.metaplex.com/token-metadata" rel="nofollow" target="_blank" title=""&gt;Metaplex 代币元数据&lt;/a&gt;暂时仍应是首选。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;组指针&lt;/strong&gt;：代币创建者可以指向提供铸币详细信息的组帐户。这类似于元数据指针，但它描述了代币是如何分组的。例如，这对于集合 NFT 非常有用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;组&lt;/strong&gt;：与元数据一样，铸币本身可能被设置为自己的组帐户。附加说明也同样适用：在撰写本文时由于缺乏生态系统的支持，应首选前面提到的 Metaplex 元数据。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;成员指针&lt;/strong&gt;：此扩展类似于元数据和组指针，但此处指向的帐户描述的是组成员身份。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;成员&lt;/strong&gt;：类似于组和数据，铸币本身可以是自己的成员描述。与“组”和“元数据”相同的注意事项也适用。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="账户拓展"&gt;账户拓展&lt;/h3&gt;
&lt;p&gt;账户扩展用于向代币账户添加功能或数据，代币账户是包含特定数量代币的个人持有/账户。这些扩展可以为帐户提供附加功能，例如启用特定于帐户的设置、限制或功能。&lt;/p&gt;

&lt;p&gt;帐户扩展对于创建更复杂的代币持有结构特别有用，例如具有条件释放的托管帐户、具有多级访问控制的帐户，或使用自定义元数据增强用户体验。&lt;/p&gt;

&lt;p&gt;目前的账户拓展有：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;转账时的备忘录要求&lt;/strong&gt;：此扩展要求在每次代币转账时都包含一份备忘录（即明文说明），以确保法规遵从性、记录和更彻底的审计跟踪。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;永久所有权&lt;/strong&gt;：确保代币账户的所有权不能转移，从而使所有者不可变。这排除了关联代币帐户可能存在的某些攻击。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;默认帐户状态&lt;/strong&gt;：所有新的代币帐户都是在“冻结”状态下创建的，需要与相关项目进行某种形式的交互才能“解冻”并启用帐户或代币。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;跨程序调用保护&lt;/strong&gt;：此设置可以将有问题的代币帐户的某些 CPI（跨程序调用）排除掉。这可能会更安全，因为它可以防止程序以用户不可见的方式使用用户权限运行操作。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;重新分配功能&lt;/strong&gt;：此功能允许所有者在创建代币帐户后调整其代币帐户的分配，以允许额外的扩展。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;观看 Solana 官方关于代币扩展的用例视频：&lt;a href="https://youtu.be/CEuKahqOYbs" rel="nofollow" target="_blank" title=""&gt;https://youtu.be/CEuKahqOYbs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;您也可以在 Solana 官方网站上找到&lt;a href="https://solana.com/news/token-extensions-developer-guide" rel="nofollow" target="_blank" title=""&gt;代币扩展的开发者指南&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="Solana智能合约开发的正确开发语言是什么?"&gt;Solana 智能合约开发的正确开发语言是什么？&lt;/h2&gt;
&lt;p&gt;为 Solana 智能合约开发选择合适的编程语言极其重要。尽管 Solana 支持数种语言（例如，C 和 C++），但首选项是 Rust，它以强调内存安全而闻名。&lt;/p&gt;

&lt;p&gt;Rust 有助于早期识别与内存相关的错误，如缓冲区溢出和空指针取消引用。这一功能显著增强了智能合约的安全性和鲁棒性，使其更能抵御攻击。&lt;/p&gt;

&lt;p&gt;Rust 还受益于强大的工具和支持 Solana 开发的社区。Rust 的 Solana 程序开发要成熟得多，有许多专门针对 Rust 的第一方工具和库。&lt;/p&gt;
&lt;h3 id="如何在Solana上构建智能合约"&gt;如何在 Solana 上构建智能合约&lt;/h3&gt;
&lt;p&gt;Solana 的智能合约是使用平台生态系统提供的工具开发的。在本节中，我们将对流程进行细分。&lt;/p&gt;

&lt;p&gt;设置开发环境涉及以下操作：&lt;/p&gt;
&lt;h4 id="第一步. 设置开发环境"&gt;第一步。设置开发环境&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;安装 CLI&lt;/strong&gt;:Solana 命令行界面（CLI）工具套件对于部署智能合约以及在 Solana 网络上管理和交互智能合约至关重要。您可以在 Solana 文档网站上找到关于您操作系统的&lt;a href="https://docs.solanalabs.com/cli/install" rel="nofollow" target="_blank" title=""&gt;安装指南&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;下载并安装 Rust&lt;/strong&gt;：从其官方网站&lt;a href="https://www.rust-lang.org/tools/install" rel="nofollow" target="_blank" title=""&gt;下载 Rust&lt;/a&gt;，然后按照特定您操作系统的安装说明进行操作。Rustup 是一个多功能工具，利用它可以简化 Rust、包管理器 Cargo 以及 Solana 开发所需的其他基本依赖项的安装。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;创建 Solana 钱包&lt;/strong&gt;：该工具对于与 Solana 网络交互至关重要，从签署交易到部署智能合约。您可以通过 Solana CLI 或基于 web 的解决方案（如 Phantom、Solflare 或 Backpack）创建钱包。请记住保护您钱包的种子短语，因为丢失它将导致无法访问您的钱包。重要提示：您需要将钱包密钥导出到 CLI 中才能部署程序，除非您使用的是硬件钱包。因此，如果你有一个包含资产的 Solana 钱包，由于安全风险，最好避免将其用于部署程序。相反，创建一个专门用于部署程序的单独帐户，并在必要时使用 SOL 为其提供资金。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;配置开发设置&lt;/strong&gt;：准备好所有必要的工具并设置好钱包后，最后一步是为 Solana 网络定制开发环境。这包括将 Solana CLI 连接到相应的 Solana 集群并链接钱包地址。Solana 文档提供了此配置过程的详细说明。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="第二步. 编写Solana智能合约代码"&gt;第二步。编写 Solana 智能合约代码&lt;/h4&gt;
&lt;p&gt;设置好开发环境后，在 Solana 创建智能合约的下一步是编写代码。&lt;/p&gt;

&lt;p&gt;有几种方法可以做到这一点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;所谓的原生 Solana 程序是用 Rust 编写的 crate，使用&lt;code&gt;solana_program&lt;/code&gt;来调用 Solana 的 API。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anchor 框架是专门为 Solana 智能合约开发而构建的。Anchor 主要用于 Rust，但它也支持使用 solang 编译器的 Solidity。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Seahorse 框架允许使用 Python 语法编写 Solana 程序。Seahorse 是在 Anchor 之上构建的，通过将 Python 代码翻译成等效的 Rust 代码来工作，因此它具有与 Anchor 基本相同的功能。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每一种方法都有其长处和短处。&lt;/p&gt;

&lt;p&gt;原生程序使开发人员能够最大限度地控制程序的执行方式，但另一方面，检查和维护非变量以避免安全问题完全是开发人员的责任。原生程序的一个特别的优点是可以使用 Rust 工具进行单元和集成测试。&lt;/p&gt;

&lt;p&gt;Anchor 通过处理大量模板（包括检查公共的非变量）简化了开发流程，因此程序员可以专注于业务逻辑。另一方面，它能做的控制更少，并且使用 Anchor 生成的二进制工作文件往往更大（因此部署它们的租金更高）。Anchor 的测试程序主要使用 JavaScript，基本上是在本地机器上运行区块链的独立实例，然后部署程序，并针对它进行交易。虽然这种方法能简化一些重要的环节，但它的集成测试受限，而且可能相对较慢。后一个问题可以通过&lt;a href="https://kevinheavey.github.io/solana-bankrun/" rel="nofollow" target="_blank" title=""&gt;Bankrun&lt;/a&gt;测试框架来解决，但它没有与 Anchor 集成。&lt;/p&gt;

&lt;p&gt;Seahorse 更进一步，用更加简单的 Python 语法隐藏了一些 Rust 特性。然而，这是以灵活性为代价的。此外，在撰写本文时，Seahorse 仍处于测试版，因此在生产中使用它时应格外小心。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;开发原生程序&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;用 cargo 创建一个 crate&lt;/strong&gt;：原生程序只是一些 crate，因此首先使用 &lt;code&gt;cargo init＜crate_name＞--lib&lt;/code&gt; 创建一个新 crate
。&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;将 &lt;code&gt;solana-program&lt;/code&gt; 添加到依赖项&lt;/strong&gt;：在 crate 的根目录中，运行 &lt;code&gt;cargo add solana-program&lt;/code&gt; 。这将把链上程序 SDK 添加为依赖项。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;设置所需的 Cargo 选项&lt;/strong&gt;：打开 &lt;code&gt;Cargo.toml&lt;/code&gt; ，把 &lt;code&gt;crate-type = ["cdylib", "lib"]&lt;/code&gt; 添加到&lt;code&gt;lib&lt;/code&gt;部分。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;定义入口函数&lt;/strong&gt;：Solana 需要知道该从哪里开始执行程序，从这里开始的函数称为入口（entrypoint）。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;导出入口函数&lt;/strong&gt;：使用&lt;code&gt;solana_program::entrypoint&lt;/code&gt;宏指令来告诉运行时入口函数是哪个，例如：&lt;code&gt;solana_program::entrypoint!(entrypoint)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;编写业务逻辑&lt;/strong&gt;：原始指令数据将会传递到入口。然后，开发人员需要做的是解码这些数据并采取适当的行为。编码格式不是由网络指定的，所以它是任意的。但是，建议使用&lt;a href="https://borsh.io/" rel="nofollow" target="_blank" title=""&gt;Borsh&lt;/a&gt;序列化/反序列化程序，可以从&lt;code&gt;solana_program:：borsh*&lt;/code&gt;模块导出实用程序来使用它。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;编写和运行测试&lt;/strong&gt;：定义常规的 Rust 单元测试，和/或使用&lt;code&gt;solana-program-test&lt;/code&gt;进行集成测试。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;构建程序&lt;/strong&gt;：前面提到的 CLI 工具套件附带了&lt;code&gt;cargo-build-bpf&lt;/code&gt;，它可以构建适合部署的二进制工作文件。直接运行它或调用&lt;code&gt;cargo build-bpf&lt;/code&gt;通过 Cargo 运行它。如果需要的话它将自动下载工具链，并构建二进制工作文件，将其放入&lt;code&gt;target/deploy/&lt;/code&gt;目录。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;用 Rust 开发 Anchor 程序&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;安装 Anchor&lt;/strong&gt;：由于 Anchor 不是 Solana 的核心部件，因此需要单独安装。有关说明，请参阅&lt;a href="https://www.anchor-lang.com/docs/installation" rel="nofollow" target="_blank" title=""&gt;官方文档&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;新建 Anchor 项目&lt;/strong&gt;：使用&lt;code&gt;anchor init &amp;lt;your-project-name&amp;gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;定义帐户的数据结构&lt;/strong&gt;：用&lt;code&gt;#[account]&lt;/code&gt;注释数据结构，让 Anchor 知道它应该为结构派生与帐户相关的样板。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;定义指令的帐户列表&lt;/strong&gt;：通过使用 &lt;code&gt;#[derive(Accounts)]&lt;/code&gt; 注释数据结构并使用 &lt;code&gt;#[account(...)]&lt;/code&gt; 注释其各个字段，您可以指定指令所需的帐户，无论它们是否是可变的，是否要检查的其他约束等等。Anchor 将处理实际编组帐户和检查约束。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;将程序指令定义为函数&lt;/strong&gt;：Anchor 会自动处理指令反/序列化。要在 Anchor 中定义指令及其业务逻辑，请定义一个新模块，并用 &lt;code&gt;#[program]&lt;/code&gt; 注释。该模块中定义的公共函数需要有相应的指令。每个函数必须有两个参数：一个是 &lt;code&gt;Context &amp;lt;Descriptor&amp;gt;&lt;/code&gt;，其中 &lt;code&gt;Descriptor&lt;/code&gt; 是带有 &lt;code&gt;#[derive(Accounts)]&lt;/code&gt; 注释的结构（之前定义的）；另一个是以及表示指令数据有效负载的任意类型。该指令有效负载类型必须实现 &lt;code&gt;AnchorDeserialize 特征&lt;/code&gt;（可以派生）。在大多数情况下，它还应该实现 &lt;code&gt;AnchorSerialize&lt;/code&gt; 特征（也可以派生）。该函数应返回 &lt;code&gt;Result&amp;lt;()&amp;gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;定义错误&lt;/strong&gt;：Anchor 允许使用面向用户的消息来自定义错误。定义一个枚举，用 &lt;code&gt;#[error_code]&lt;/code&gt; 对其进行注释，并用 &lt;code&gt;#[msg("Error description here")]&lt;/code&gt; 注释每个变量（其中&lt;code&gt;"Error description here"&lt;/code&gt;描述错误信息）。在程序的业务逻辑中通过用 &lt;code&gt;err!&lt;/code&gt; 宏指令来使用这些自定义错误，或者使用 &lt;code&gt;require!&lt;/code&gt; 编写断言。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;编写测试&lt;/strong&gt;：Anchor 使用 TypeScript 通过 Solana TypeScript SDK 定义集成测试。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;构建程序&lt;/strong&gt;：使用&lt;code&gt;anchor build&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;运行测试&lt;/strong&gt;：使用&lt;code&gt;anchor test&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="第三步. 测试Solana智能合约"&gt;第三步。测试 Solana 智能合约&lt;/h4&gt;
&lt;p&gt;测试智能合约的功能和效率是 Solana 开发的关键。Solana 提供了一系列测试工具和环境，旨在确保您的代码正常工作并集成到 Solana 生态系统中。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;单元测试&lt;/strong&gt;：Rust 中集成的单元测试框架是第一道防线，它允许您单独检查各个代码段，并在开发机器上进行本地测试。使用 Anchor 时，编写单元测试明显更具挑战性，因为框架本身仅使用 Solana TypeScript 客户端 SDK 提供的“外部”集成测试。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;集成测试&lt;/strong&gt;：集成测试可以评估您的智能合约与其他 Solana 网络组件的交互。集成测试有两种主要方法：&lt;code&gt;solana-program-test&lt;/code&gt;，它是一个 Rust 的 crate，为集成测试提供模拟的 Solana 网络环境；&lt;code&gt;solana-test-validator&lt;/code&gt;，它运行本地 Solana 节点，用于在本地节点测试智能合约。后者明显慢得多，使用起来也更麻烦，但它的行为几乎就像一个真实的网络。Anchor 使用后者来运行测试，但使用 Bankrun 测试框架可能是测试 Anchor 程序的另一个可行选择。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="第四步. 部署Solana智能合约"&gt;第四步。部署 Solana 智能合约&lt;/h4&gt;
&lt;p&gt;测试 Solana 智能合约代码后，下一步是部署到 Solana 网络。操作方法如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;部署程序&lt;/strong&gt;：您的智能合约在网络上的唯一标识符是程序 ID。要创建程序 ID，请通过在终端中执行以下命令来使用 Solana 命令行界面 (CLI)：&lt;code&gt;solana program deploy &amp;lt;path_to_your_compiled_program&amp;gt;&lt;/code&gt;。此操作会编译您的智能合约并将其上传到网络，为其分配一个程序 ID 以供引用和交互。如果您想要程序的虚地址（vanity address），您可以提前生成它（例如使用 &lt;code&gt;solana-keygen&lt;/code&gt;）并用 &lt;code&gt;--keypair&lt;/code&gt; 参数将其提供给 &lt;code&gt;solana program deploy&lt;/code&gt;。默认情况下，您将获得一个随机地址。使用 Anchor 时，可以使用&lt;code&gt;anchor deploy&lt;/code&gt;。您可以通过将其密钥放入 &lt;code&gt;./target/deploy/&amp;lt;program_name&amp;gt;-keypair.json&lt;/code&gt; 来提供虚地址。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;验证部署&lt;/strong&gt;：通过检查网络上的程序 ID 来确认您的智能合约已正确部署。您可以使用 Solana Explorer、&lt;a href="http://solscan.io/" rel="nofollow" target="_blank" title=""&gt;Solscan.io&lt;/a&gt;、另一个公共索引器或 Solana CLI 命令 &lt;code&gt;solana program show &amp;lt;program_id&amp;gt;&lt;/code&gt; 检索有关合约的详细信息，包括其大小和 SOL 余额。此步骤可确保您的智能合约已激活并准备好在网络上使用。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果您在开发过程中遇到困难，或者需要对 Solana 智能合约进行第三方审核，我们&lt;a href="https://serokell.io/contacts" rel="nofollow" target="_blank" title=""&gt;可以随时为您提供帮助&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="FAQ"&gt;FAQ&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Solana 智能合约是否可以与其他区块链平台集成？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不能直接这么做，但您可以设置资产桥（asset bridge），这对于任何支持智能合约的区块链对都是如此。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;哪些编程语言适合开发 Solana 智能合约？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;您可以主要使用 Rust 创建 Solana 智能合约，但其他语言，例如 C、C++、Solidity（通过 Solang 编译器）和 Python（通过 Seahorse 框架）也适用。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如何测试 Solana 智能合约？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;对于本地合约，您可以使用 Rust 的测试工具以及 &lt;code&gt;solana-program-test&lt;/code&gt; 进行集成测试。Anchor 框架提供了自己的基于 &lt;code&gt;solana-test-validator&lt;/code&gt; 的集成测试设置。最后，任何程序都可以使用 Bankrun 进行测试，Bankrun 是一个在底层使用 &lt;code&gt;solana-program-test&lt;/code&gt; 的 NodeJS 测试工具。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solana 浏览器有什么作用？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Solana Explorer 是一款在线工具，允许用户直观地导航 Solana 区块链、查看交易并详细检查智能合约。&lt;/p&gt;
&lt;h2 id="结论"&gt;结论&lt;/h2&gt;
&lt;p&gt;总之，Solana 为以太坊等旧区块链平台提供了一个有吸引力的替代方案。由于其架构，它提供了高速交易、低延迟和可扩展性。其 Token-2022 计划代表了一组用于构建自定义智能合约的扩展，可满足新的业务环境以及当前和未来的需求。Solana 的市场地位也非常牢固，并将保持和增强其影响力。&lt;/p&gt;

&lt;p&gt;如果您的目标是释放区块链项目的全部潜力，并正在寻找技术精湛、经验丰富的 Solana 智能合约开发人员，Serokell 团队随时准备为您提供帮助。&lt;/p&gt;

&lt;p&gt;原文：&lt;a href="https://learnblockchain.cn/article/8154" rel="nofollow" target="_blank"&gt;https://learnblockchain.cn/article/8154&lt;/a&gt;&lt;/p&gt;</description>
      <author>Tiny</author>
      <pubDate>Sat, 18 May 2024 10:51:52 +0800</pubDate>
      <link>https://soldev.cn/topics/1</link>
      <guid>https://soldev.cn/topics/1</guid>
    </item>
  </channel>
</rss>
