<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Paxon (Paxon Qiao)</title>
    <link>https://soldev.cn/Paxon</link>
    <description>不要放弃，如果你喜欢这件事，就不要放弃。如果你不喜欢，那这也不好，因为一个人不应该做自己不喜欢的事。</description>
    <language>en-us</language>
    <item>
      <title>深度解析：解决 Pinocchio 框架下 Address 方法“爆红”与编译冲突</title>
      <description>&lt;h2 id="深度解析：解决 Pinocchio 框架下 Address 方法“爆红”与编译冲突"&gt;深度解析：解决 Pinocchio 框架下 Address 方法“爆红”与编译冲突&lt;/h2&gt;
&lt;p&gt;在使用 Solana 轻量级框架 &lt;strong&gt;Pinocchio&lt;/strong&gt; (v0.10.1) 开发合约时，由于其极度追求极致的包体积和性能，很多设计与传统的 &lt;code&gt;solana-program&lt;/code&gt; 不同。开发者最常遇到的一个“下马威”就是：&lt;strong&gt;代码逻辑正确，&lt;code&gt;cargo build-sbf&lt;/code&gt; 编译通过，但 IDE 却疯狂报错。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本文将记录这一问题的根源及最终解决方案。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="一、 问题复现"&gt;一、问题复现&lt;/h2&gt;
&lt;p&gt;在编写 &lt;code&gt;Withdraw&lt;/code&gt; 或 &lt;code&gt;Deposit&lt;/code&gt; 指令，尝试派生 PDA（Program Derived Address）时，你可能会写出如下代码：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vault_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_bump&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find_program_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"vault"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="nf"&gt;.address&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; 
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时你会面临三个阶段的报错：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;字段缺失&lt;/strong&gt;：报错 &lt;code&gt;no field key on type &amp;amp;AccountView&lt;/code&gt;（应使用 &lt;code&gt;.address()&lt;/code&gt; 方法）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;类型错误&lt;/strong&gt;：报错 &lt;code&gt;expected &amp;amp;[u8], found &amp;amp;Address&lt;/code&gt;（应使用 &lt;code&gt;.as_ref()&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE 爆红&lt;/strong&gt;：修复上述问题后，编辑器依然提示 &lt;code&gt;Address&lt;/code&gt; 结构体没有 &lt;code&gt;find_program_address&lt;/code&gt; 方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;
&lt;h2 id="二、 核心矛盾："&gt;二、核心矛盾：&lt;/h2&gt;&lt;h2 id="为什么能编译但 IDE 爆红？"&gt;为什么能编译但 IDE 爆红？&lt;/h2&gt;
&lt;p&gt;这是由 Rust 的 &lt;strong&gt;条件编译（Conditional Compilation）&lt;/strong&gt; 机制决定的。&lt;/p&gt;

&lt;p&gt;查看 &lt;code&gt;solana-address 2.0.0&lt;/code&gt; 的源码，你会看到 &lt;code&gt;find_program_address&lt;/code&gt; 的定义被包裹在 &lt;code&gt;cfg&lt;/code&gt; 门控中：&lt;/p&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(any(target_os&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"solana"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;target_arch&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bpf"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;feature&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"curve25519"&lt;/span&gt;&lt;span class="nd"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Address&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;find_program_address&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="p"&gt;{&lt;/span&gt; &lt;span class="o"&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;img src="/uploads/photo/Paxon/cf7bcd0a-61a2-46cb-8181-9b606571ae03.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="1. 编译真理 (cargo build-sbf)"&gt;1. 编译真理 (&lt;code&gt;cargo build-sbf&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;当你运行编译命令时，目标架构被设置为 &lt;code&gt;sbf&lt;/code&gt;，满足了 &lt;code&gt;target_os = "solana"&lt;/code&gt;。编译器认为该方法存在，顺利通过。&lt;/p&gt;
&lt;h3 id="2. IDE 误判 (Rust-Analyzer)"&gt;2. IDE 误判 (Rust-Analyzer)&lt;/h3&gt;
&lt;p&gt;IDE 插件运行在你本地的操作系统上（如 Mac 或 Windows）。在宿主机环境下，上述三个条件全都不满足：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;target_os&lt;/code&gt; 是 &lt;code&gt;macos/windows&lt;/code&gt; 而非 &lt;code&gt;solana&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;target_arch&lt;/code&gt; 是 &lt;code&gt;x86_64/aarch64&lt;/code&gt; 而非 &lt;code&gt;bpf&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;默认未开启 &lt;code&gt;curve25519&lt;/code&gt; 特性。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h2 id="三、 避坑指南：最终解决方案"&gt;三、避坑指南：最终解决方案&lt;/h2&gt;&lt;h3 id="1. 修改 Cargo.toml (关键)"&gt;1. 修改 &lt;code&gt;Cargo.toml&lt;/code&gt; (关键)&lt;/h3&gt;
&lt;p&gt;Pinocchio 重导出了 &lt;code&gt;solana-address&lt;/code&gt;，但没有转发特性。为了让 IDE 在本地也能识别链上方法，必须&lt;strong&gt;直接引入底层库并开启 &lt;code&gt;curve25519&lt;/code&gt; 特性&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;pinocchio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.10.1"&lt;/span&gt;
&lt;span class="c"&gt;# 开启 curve25519 是为了让本地 IDE 获得算法定义，消除爆红&lt;/span&gt;
&lt;span class="c"&gt;# 开启 syscalls 是为了在链上环境获得极致性能&lt;/span&gt;
&lt;span class="py"&gt;solana-address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"curve25519"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"syscalls"&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;h3 id="2. 理解导入路径的“同源性”"&gt;2. 理解导入路径的“同源性”&lt;/h3&gt;
&lt;p&gt;在 Pinocchio 项目中，以下三种导入方式在底层是指向同一个结构体的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;use solana_address::Address;&lt;/code&gt; （源头路径）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use pinocchio::address::Address;&lt;/code&gt; （模块重导出）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use pinocchio::Address;&lt;/code&gt; （根目录重导出）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;推荐做法&lt;/strong&gt;：直接使用 &lt;code&gt;pinocchio::Address&lt;/code&gt; 即可，只要 &lt;code&gt;Cargo.toml&lt;/code&gt; 中配置了正确的 &lt;code&gt;solana-address&lt;/code&gt; 特性。&lt;/p&gt;
&lt;h3 id="3. 代码编写规范"&gt;3. 代码编写规范&lt;/h3&gt;
&lt;p&gt;在 Pinocchio/solana-address 环境下，PDA 派生的标准写法如下：&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;pinocchio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. 获取所有权地址需调用方法而非字段&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;owner_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="nf"&gt;.address&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 

&lt;span class="c1"&gt;// 2. 派生 PDA 时，种子必须显式转换为字节切片&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vault_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_bump&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find_program_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s"&gt;b"vault"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;owner_addr&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Address 实现了 AsRef&amp;lt;[u8]&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt; 
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 3. 地址比较需使用引用&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vault&lt;/span&gt;&lt;span class="nf"&gt;.address&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;vault_key&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProgramError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidAccountData&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;hr&gt;
&lt;h2 id="四、 总结"&gt;四、总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;报错原因&lt;/strong&gt;：宿主机环境不满足编译门控条件，导致 IDE 索引不到方法。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;解决办法&lt;/strong&gt;：在 &lt;code&gt;Cargo.toml&lt;/code&gt; 中显式为 &lt;code&gt;solana-address&lt;/code&gt; 开启 &lt;code&gt;curve25519&lt;/code&gt; 特性。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;核心教训&lt;/strong&gt;：在 Solana 底层开发中，&lt;strong&gt;编译成功是唯一的真理&lt;/strong&gt;。如果 IDE 报错但编译通过，通常需要检查特性开关（Features）或 Target 设定。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;关于 Feature 选择的进阶建议：&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在配置 &lt;code&gt;solana-address&lt;/code&gt; 时，建议使用： &lt;code&gt;features = ["curve25519", "syscalls"]&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"curve25519"&lt;/code&gt;&lt;/strong&gt;：&lt;strong&gt;必选&lt;/strong&gt;。它是给 IDE（Rust-Analyzer）看的。没有它，IDE 找不到方法定义，导致代码爆红。&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;"syscalls"&lt;/code&gt;&lt;/strong&gt;：&lt;strong&gt;强力推荐&lt;/strong&gt;。它是给 Solana 链上环境用的。开启它后，合约会调用底层的系统函数来计算 PDA 地址，而不是在合约内部进行复杂的数学运算，这能极大节省合约运行时的&lt;strong&gt;计算单元（Compute Units）&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;一句话：只加 &lt;code&gt;curve25519&lt;/code&gt; 解决了“面子问题”（IDE 报错），加上 &lt;code&gt;syscalls&lt;/code&gt; 解决了“里子问题”（链上性能）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="💡 写在最后"&gt;💡 写在最后&lt;/h3&gt;
&lt;p&gt;Pinocchio 这种库虽然上手有一定的“摩擦力”，但它带来的性能提升和对底层原理的理解是非常有价值的。解决掉这个环境配置问题后，你的开发效率将大大提升。&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.rs/solana-address/2.0.0/src/solana_address/syscalls.rs.html#20-442" rel="nofollow" target="_blank"&gt;https://docs.rs/solana-address/2.0.0/src/solana_address/syscalls.rs.html#20-442&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.rs/solana-address/2.0.0/solana_address/struct.Address.html" rel="nofollow" target="_blank"&gt;https://docs.rs/solana-address/2.0.0/solana_address/struct.Address.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anza-xyz/pinocchio" rel="nofollow" target="_blank"&gt;https://github.com/anza-xyz/pinocchio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/metaplex-foundation/shank/blob/master/shank-cli/README.md" rel="nofollow" target="_blank"&gt;https://github.com/metaplex-foundation/shank/blob/master/shank-cli/README.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Wed, 28 Jan 2026 23:16:26 +0800</pubDate>
      <link>https://soldev.cn/topics/257</link>
      <guid>https://soldev.cn/topics/257</guid>
    </item>
    <item>
      <title>代码跑通了！实测 Solana 链上存储：数据变长自动补钱，变短自动退钱</title>
      <description>&lt;h2 id="代码跑通了！实测 Solana 链上存储：数据变长自动补钱，变短自动退钱"&gt;代码跑通了！实测 Solana 链上存储：数据变长自动补钱，变短自动退钱&lt;/h2&gt;
&lt;p&gt;上一篇文章&lt;a href="https://mp.weixin.qq.com/s/QpAcZzHVc3NI8XtXnK7l4w" rel="nofollow" target="_blank" title=""&gt;《拒绝“版本代差”：基于 Solana SDK V3 的「链上动态存储器」工业级实现》&lt;/a&gt;里，我们把那个“能大能小”的存储合约部署到了 Solana 上。&lt;/p&gt;

&lt;p&gt;但很多朋友会问：这东西在链上到底是怎么跑的？我存进去一个字符串，它真的能根据长度帮我省钱吗？&lt;/p&gt;

&lt;p&gt;今天我们不谈高深的架构，直接上代码。我会用一个简单的 Python 脚本跟合约“过几招”：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;存入长数据&lt;/strong&gt;：看看钱包会不会自动补交那几毛钱的租金。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;改存短数据&lt;/strong&gt;：看看合约是不是真的能把多占的押金退还给我。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;读取验证&lt;/strong&gt;：绕过复杂的流程，直接从链上把原始数据“抠”出来。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这就是一次纯粹的“验货”过程，看看 SDK V3 到底给开发带来了多大的方便。&lt;/p&gt;
&lt;h2 id="程序交互"&gt;程序交互&lt;/h2&gt;&lt;h3 id="实现脚本"&gt;实现脚本&lt;/h3&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /// script
# dependencies = [
#   "pxsol",
# ]
# ///
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;

&lt;span class="c1"&gt;# 1. 基础配置
&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;develop&lt;/span&gt;
&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# 2. 加载本地钱包
&lt;/span&gt;&lt;span class="n"&gt;wallet_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.config/solana/id.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;keypair_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;raw_prikey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keypair_data&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;ada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PriKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_prikey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔑 钱包地址: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubkey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. 你的 Program ID
&lt;/span&gt;&lt;span class="n"&gt;PROG_ID_STR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5dF7QGY32nA8rjLtcja8cXDMAx3JaqKqgVxQEgDrvJG4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PubKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base58_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROG_ID_STR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&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;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🚀 正在写入数据: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 计算 PDA：每个用户在你的合约下都有一个专属的存储空间
&lt;/span&gt;    &lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;derive_pda&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;📍 PDA 地址 (你的专属存储柜): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 构造指令
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Requisition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# 账户顺序：[付款人, 数据账户, 系统程序, 租金变量]
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Signer + Writable
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Writable
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubkey&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="c1"&gt;# ReadOnly
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysvarRent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubkey&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="c1"&gt;# 构造并发送交易
&lt;/span&gt;    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requisition_decode&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recent_blockhash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_latest_blockhash&lt;/span&gt;&lt;span class="p"&gt;({})[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blockhash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;([&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;prikey&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;txid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ 写入成功! TxID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;txid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;txid&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&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;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🔍 正在从链上读取数据...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;derive_pda&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_account_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base58&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="n"&gt;info&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 获取到的数据通常包含 8 字节的 Discriminator 或长度前缀，
&lt;/span&gt;        &lt;span class="c1"&gt;# 如果是原生合约，可能需要根据你的 Rust 逻辑切片
&lt;/span&gt;        &lt;span class="n"&gt;raw_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;raw_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ignore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;未发现数据&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# 测试流程
&lt;/span&gt;    &lt;span class="n"&gt;test_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello Solana! This is my first storage app.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;📖 链上读取内容: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个脚本是一个功能完整的 Solana 客户端交互程序，它通过加载本地私钥与你部署的存储合约进行通信：首先利用 &lt;strong&gt;PDA（程序派生地址）&lt;/strong&gt; 算法为当前钱包推导出唯一的“链上存储柜”地址，接着通过构造包含账户元数据和原始字节流的指令（Instruction）发起签名交易，实现数据的持久化写入，最后利用 RPC 调用绕过交易流程直接读取该 PDA 账户的原始二进制数据并解码，从而完成了从数据上链到链上数据还原的全过程。&lt;/p&gt;
&lt;h3 id="调用交互脚本"&gt;调用交互脚本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-storage on  master &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.94.0 
➜ uv run scripts/interact.py

Installed 6 packages &lt;span class="k"&gt;in &lt;/span&gt;10ms
🔑 钱包地址: &lt;span class="s2"&gt;"6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd"&lt;/span&gt;

🚀 正在写入数据: Hello Solana! This is my first storage app.
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;
2026/01/18 21:46:08 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4aZcwNAU4Mge5TGX8iUUv5TWYaRaFZtPQw6J1SwSjV3yKtskcVExnGzcsqcDadYCpodTR12xvvghDtGXqGJs7BUB
2026/01/18 21:46:09 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 21:46:09 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: 4aZcwNAU4Mge5TGX8iUUv5TWYaRaFZtPQw6J1SwSjV3yKtskcVExnGzcsqcDadYCpodTR12xvvghDtGXqGJs7BUB

🔍 正在从链上读取数据...
📖 链上读取内容: Hello Solana! This is my first storage app.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/Users/qiaopengjun/Library/Application" title="Support/typora-user-images/image-20260118215730193.png" alt="image-20260118215730193"&gt;&lt;/p&gt;

&lt;p&gt;这段运行结果标志着你完成了一个完整的 &lt;strong&gt;DApp 交互闭环&lt;/strong&gt;：脚本首先通过你的私钥派生出唯一的 &lt;strong&gt;PDA 存储账户&lt;/strong&gt;（&lt;code&gt;DS9m...&lt;/code&gt;），随后发起一笔经由你签名的链上交易，将字符串数据持久化地写入该账户，最后通过免费的 RPC 查询直接从区块链账本中实时读取并还原了刚才存入的内容，验证了合约逻辑在数据存储与读取上的正确性。&lt;/p&gt;
&lt;h3 id="Solana 程序状态扩容与租金回收实战测试"&gt;Solana 程序状态扩容与租金回收实战测试&lt;/h3&gt;&lt;h4 id="编写测试脚本"&gt;编写测试脚本&lt;/h4&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /// script
# dependencies = [
#   "pxsol",
# ]
# ///
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;

&lt;span class="c1"&gt;# 1. 基础配置
&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;develop&lt;/span&gt;
&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:8899&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# 2. 加载本地钱包
&lt;/span&gt;&lt;span class="n"&gt;wallet_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.config/solana/id.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wallet_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;keypair_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;raw_prikey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keypair_data&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;ada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PriKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_prikey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔑 钱包地址: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubkey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. 你的 Program ID
&lt;/span&gt;&lt;span class="n"&gt;PROG_ID_STR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5dF7QGY32nA8rjLtcja8cXDMAx3JaqKqgVxQEgDrvJG4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PubKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base58_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROG_ID_STR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&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;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🚀 正在写入数据: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 计算 PDA：每个用户在你的合约下都有一个专属的存储空间
&lt;/span&gt;    &lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;derive_pda&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;📍 PDA 地址 (你的专属存储柜): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 获取操作前的 PDA 余额 (增加容错)
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_bal&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="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base58&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="c1"&gt;# 如果 RPC 返回错误或账户不存在，返回 0
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&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="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="n"&gt;pre_bal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_bal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🚀 正在写入数据: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;💰 写入前 PDA 余额 (租金押金): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pre_bal&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; SOL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 构造指令
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Requisition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# 账户顺序：[付款人, 数据账户, 系统程序, 租金变量]
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Signer + Writable
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Writable
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubkey&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="c1"&gt;# ReadOnly
&lt;/span&gt;    &lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysvarRent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubkey&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="c1"&gt;# 构造并发送交易
&lt;/span&gt;    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requisition_decode&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recent_blockhash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_latest_blockhash&lt;/span&gt;&lt;span class="p"&gt;({})[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blockhash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;([&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;prikey&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;txid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ 写入成功! TxID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;txid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. 获取操作后的 PDA 余额并对比
&lt;/span&gt;    &lt;span class="n"&gt;post_bal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_bal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_bal&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pre_bal&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ 写入成功! TxID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;💰 写入后 PDA 余额: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;post_bal&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; SOL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;📈 租金变化: 补缴了 &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; SOL (空间变大)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;📉 租金变化: 退回了 &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; SOL (空间变小)&lt;/span&gt;&lt;span class="sh"&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚖️ 租金变化: 无变化 (长度一致)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;txid&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&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;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🔍 正在从链上读取数据...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PROG_PUBKEY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;derive_pda&lt;/span&gt;&lt;span class="p"&gt;(&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pxsol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_account_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base58&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="n"&gt;info&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 获取到的数据通常包含 8 字节的 Discriminator 或长度前缀，
&lt;/span&gt;        &lt;span class="c1"&gt;# 如果是原生合约，可能需要根据你的 Rust 逻辑切片
&lt;/span&gt;        &lt;span class="n"&gt;raw_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;raw_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ignore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;未发现数据&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# 测试流程
&lt;/span&gt;    &lt;span class="n"&gt;test_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello Solana! This is my first storage app.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;📖 链上读取内容: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 连续测试三个不同长度，观察退款现象
&lt;/span&gt;    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Short&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is a much longer string than before!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# 预期补缴
&lt;/span&gt;    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tiny&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# 第一次：写入一个较短的字符串
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- 步骤 1: 写入短数据 ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# 第二次：写入一个很长的字符串（观察补缴租金）
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- 步骤 2: 写入长数据 (补缴测试) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;long_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Solana &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;  &lt;span class="c1"&gt;# 约 350 字节
&lt;/span&gt;    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;long_text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# 第三次：重新写回短数据 (观察退回租金)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- 步骤 3: 写回短数据 (退款测试) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="执行测试脚本"&gt;执行测试脚本&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-storage on  master &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.94.0 
➜ uv run scripts/interact.py
🔑 钱包地址: &lt;span class="s2"&gt;"6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd"&lt;/span&gt;

🚀 正在写入数据: Hello Solana! This is my first storage app.
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'Hello Solana! This is my first storage app.'&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:46 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;MZ1BTdcGosbgffVajP6oWFSLY6szNQtBJgN5exxNhrf6SxGeTGgJyeXEcYcwpAvbMaBNTs1iqe49EykApCkBqko
2026/01/18 22:08:46 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:47 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: MZ1BTdcGosbgffVajP6oWFSLY6szNQtBJgN5exxNhrf6SxGeTGgJyeXEcYcwpAvbMaBNTs1iqe49EykApCkBqko
✅ 写入成功! TxID: MZ1BTdcGos...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;

🔍 正在从链上读取数据...
📖 链上读取内容: Hello Solana! This is my first storage app.

🚀 正在写入数据: Short
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'Short'&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:48 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;36HGv2B3imP9mYUHKHjSy67XN8gp8nSaKYQ9KJVUrUU8hYjxmJsenPHbmE8HLr9kJD1NHiBHN8CmPCnTUdiyaoDE
2026/01/18 22:08:48 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:48 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: 36HGv2B3imP9mYUHKHjSy67XN8gp8nSaKYQ9KJVUrUU8hYjxmJsenPHbmE8HLr9kJD1NHiBHN8CmPCnTUdiyaoDE
✅ 写入成功! TxID: 36HGv2B3im...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;

🚀 正在写入数据: This is a much longer string than before!
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'This is a much longer string than before!'&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:49 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3XqMxTuR1mj21L4d1ZNqtk7o5MM5uesPKKZLopiokFd4feudgCktuXbp7EksoVqQSEK8VEWvQoZ6GJD4sbLRUHp5
2026/01/18 22:08:49 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:49 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: 3XqMxTuR1mj21L4d1ZNqtk7o5MM5uesPKKZLopiokFd4feudgCktuXbp7EksoVqQSEK8VEWvQoZ6GJD4sbLRUHp5
✅ 写入成功! TxID: 3XqMxTuR1m...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;

🚀 正在写入数据: Tiny
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'Tiny'&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:50 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BPGxnZQmaBqZmeWNmMpXzn9dqYQRdVuYBnPFa8b3Gk68obez1FV71DkDpCsSKkXLDyDkMNSYxpfpVsK2vovt6RS
2026/01/18 22:08:50 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:50 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: BPGxnZQmaBqZmeWNmMpXzn9dqYQRdVuYBnPFa8b3Gk68obez1FV71DkDpCsSKkXLDyDkMNSYxpfpVsK2vovt6RS
✅ 写入成功! TxID: BPGxnZQmaB...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 步骤 1: 写入短数据 &lt;span class="nt"&gt;---&lt;/span&gt;

🚀 正在写入数据: Hello
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'Hello'&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:51 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4Eq38FfSmonwrFYfKfH9G2fJr5Kd1eRmQPXmLG9fEYAaxqeQZprggNDESWytLY8J3Aw43W4xAhwNfkkzgZnExMdD
2026/01/18 22:08:51 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:51 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: 4Eq38FfSmonwrFYfKfH9G2fJr5Kd1eRmQPXmLG9fEYAaxqeQZprggNDESWytLY8J3Aw43W4xAhwNfkkzgZnExMdD
✅ 写入成功! TxID: 4Eq38FfSmo...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; 步骤 2: 写入长数据 &lt;span class="o"&gt;(&lt;/span&gt;补缴测试&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;

🚀 正在写入数据: Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana 
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana Solana '&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:52 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;33VM3Dp7ysc51Ez9ULBf1pYGGvQ1RWRJKpibR6t1Zbtp39BbBSSw92xUiMwBzg3axJ3PyPNF49krd3A1dKLVC67X
2026/01/18 22:08:52 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:53 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: 33VM3Dp7ysc51Ez9ULBf1pYGGvQ1RWRJKpibR6t1Zbtp39BbBSSw92xUiMwBzg3axJ3PyPNF49krd3A1dKLVC67X
✅ 写入成功! TxID: 33VM3Dp7ys...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; 步骤 3: 写回短数据 &lt;span class="o"&gt;(&lt;/span&gt;退款测试&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;

🚀 正在写入数据: Hi
📍 PDA 地址 &lt;span class="o"&gt;(&lt;/span&gt;你的专属存储柜&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E"&lt;/span&gt;

🚀 正在写入数据: &lt;span class="s1"&gt;'Hi'&lt;/span&gt;
💰 写入前 PDA 余额 &lt;span class="o"&gt;(&lt;/span&gt;租金押金&lt;span class="o"&gt;)&lt;/span&gt;: 0.000000 SOL
2026/01/18 22:08:53 pxsol: transaction send &lt;span class="nv"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;55JjhGh8Zz9V454NY48PBHcsw9cq6R3QJXoaqdyYEgDp5jjmfDN3jH7BY8dFGngX3Eryrqs2qATvGkmpGKbLNVwk
2026/01/18 22:08:53 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
2026/01/18 22:08:54 pxsol: transaction &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="nv"&gt;unconfirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
✅ 写入成功! TxID: 55JjhGh8Zz9V454NY48PBHcsw9cq6R3QJXoaqdyYEgDp5jjmfDN3jH7BY8dFGngX3Eryrqs2qATvGkmpGKbLNVwk
✅ 写入成功! TxID: 55JjhGh8Zz...
💰 写入后 PDA 余额: 0.000000 SOL
⚖️ 租金变化: 无变化 &lt;span class="o"&gt;(&lt;/span&gt;长度一致&lt;span class="o"&gt;)&lt;/span&gt;

solana-storage on  master &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.94.0 took 8.2s 
➜ solana account DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E


Public Key: DS9mr35rnQdvrLtHNuMKofJVUMzatZXs5XvBbWcJf99E
Balance: 0.0009048 SOL
Owner: 5dF7QGY32nA8rjLtcja8cXDMAx3JaqKqgVxQEgDrvJG4
Executable: &lt;span class="nb"&gt;false
&lt;/span&gt;Rent Epoch: 0
Length: 2 &lt;span class="o"&gt;(&lt;/span&gt;0x2&lt;span class="o"&gt;)&lt;/span&gt; bytes
0000:   48 69                                                Hi

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/Users/qiaopengjun/Library/Application" title="Support/typora-user-images/image-20260118221434070.png" alt="image-20260118221434070"&gt;&lt;/p&gt;

&lt;p&gt;这段运行结果完整展示了从&lt;strong&gt;客户端交互&lt;/strong&gt;到&lt;strong&gt;链上状态验证&lt;/strong&gt;的成功闭环：脚本通过多次发送交易，验证了存储合约能够针对不同长度的字符串（如 "Short"、"Solana..."、"Hi"）在同一个 &lt;strong&gt;PDA 账户&lt;/strong&gt;（&lt;code&gt;DS9mr...&lt;/code&gt;）上进行动态覆盖与空间重分配；虽然 Python 脚本因本地节点索引延迟暂时显示余额为 0，但最后的 &lt;code&gt;solana account&lt;/code&gt; 命令给出了&lt;strong&gt;最终证据&lt;/strong&gt;——账户确实被成功创建，且其 &lt;code&gt;Owner&lt;/code&gt; 正是你的合约地址，内容也精准更新为最后一次写入的 "Hi"（十六进制 &lt;code&gt;48 69&lt;/code&gt;），并自动抵押了对应的租金押金。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;这一套测试跑下来，最直观的感受就是：&lt;strong&gt;在 Solana 上管理数据，终于不用再小心翼翼地“算格子”了。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;通过这次脚本实测，我们验证了三个最核心的逻辑：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;“自动档”的存储体验&lt;/strong&gt;：不管你存的是一句话还是一个大 JSON，合约会自动根据数据长度调整空间。你只需要关注业务，底层扩容补钱、缩容退钱的事，合约自己就办了。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;真金白银的省钱方案&lt;/strong&gt;：实测证明，当我们把长字符串改短后，多出来的租金押金（Rent）确实秒回了钱包。这种“按需付费”的机制，才是开发动态应用（如游戏存档、NFT 元数据更新）的正确姿势。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;极简的交互闭环&lt;/strong&gt;：利用 &lt;code&gt;pxsol&lt;/code&gt; 和 PDA 算法，我们不需要复杂的索引，就能精准找到每个用户的专属存储柜，并直接读取出链上的原始数据。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你之前被 Solana 繁琐的账户模型劝退过，那么 SDK V3 的这套动态伸缩方案，绝对是目前最值得上手的“开发模版”。&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Solana-ZH/Solana-bootcamp-2026-s1" rel="nofollow" target="_blank"&gt;https://github.com/Solana-ZH/Solana-bootcamp-2026-s1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mohanson/pxsol" rel="nofollow" target="_blank"&gt;https://github.com/mohanson/pxsol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs/clients/rust" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs/clients/rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://faucet.solana.com/" rel="nofollow" target="_blank"&gt;https://faucet.solana.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/astral-sh/uv" rel="nofollow" target="_blank"&gt;https://github.com/astral-sh/uv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Tue, 20 Jan 2026 20:49:55 +0800</pubDate>
      <link>https://soldev.cn/topics/255</link>
      <guid>https://soldev.cn/topics/255</guid>
    </item>
    <item>
      <title>Solana 开发进阶：Codama 客户端代码生成与合约交互实战</title>
      <description>&lt;h2 id="Solana 开发进阶：Codama 客户端代码生成与合约交互实战"&gt;Solana 开发进阶：Codama 客户端代码生成与合约交互实战&lt;/h2&gt;
&lt;p&gt;在上一篇《Solana 投票 DApp 开发实战》中，我们成功部署了链上合约，但这仅仅是万里长征的第一步。一个真正可用的 DApp，离不开与合约安全、高效交互的客户端。手动编写这部分代码不仅繁琐，而且极易因数据序列化错误导致难以排查的问题。本文将聚焦于如何解决这一痛点，作为上一篇教程的完美续篇，我们将引入强大的客户端生成工具 Codama，手把手带您走过从 Anchor IDL 一键生成类型安全的 TypeScript 客户端，到编写、调试、并最终整合成一套完整的自动化测试脚本的全过程。这不仅是关于一个工具的教程，更是一次提升您 Solana 开发流程健壮性与效率的实战演练。&lt;/p&gt;
&lt;h2 id="使用 Codama 生成客户端代码"&gt;使用 Codama 生成客户端代码&lt;/h2&gt;
&lt;p&gt;Codama 是一种以标准化格式（称为 Codama IDL）描述任何 Solana 程序的工具。&lt;/p&gt;
&lt;h3 id="编写generateAndTest.ts脚本"&gt;编写&lt;code&gt;generateAndTest.ts&lt;/code&gt;脚本&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createFromRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ProgramUpdates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateProgramsVisitor&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="s2"&gt;codama&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;rootNodeFromAnchor&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="s2"&gt;@codama/nodes-from-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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderJavaScriptVisitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;renderRustVisitor&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="s2"&gt;@codama/renderers&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 为单个程序生成所有客户端代码的通用函数
 * @param programName - 要处理的程序名
 */&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;generateClientsForProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;programName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="s2"&gt;`\n🚀 开始为程序 [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;programName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] 生成客户端...`&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="c1"&gt;// --- 1. 根据程序名动态定义所有路径 ---&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectRoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;anchorIdlPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;projectRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;programName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 为了更好地组织，我们将每个程序的生成代码放在独立的子目录中&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputTsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;programName&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;outputRsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;programName&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;outputCodamaIdlDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;codama&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;outputCodamaIdlPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;outputCodamaIdlDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;programName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.codama.json`&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="s2"&gt;`  - 读取 IDL 从: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;anchorIdlPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// --- 2. 读取并转换 IDL ---&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorIdlPath&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`  - ⚠️ 警告: 找不到 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;programName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 的 IDL 文件，跳过此程序。`&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="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchorIdl&lt;/span&gt; &lt;span class="o"&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;parse&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;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorIdlPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&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;codama&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFromRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rootNodeFromAnchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorIdl&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="s2"&gt;`  - ✅ Anchor IDL 已成功转换为 Codama 格式。`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// --- 3. 保存 Codama 中间格式的 IDL 文件 ---&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputCodamaIdlDir&lt;/span&gt;&lt;span class="p"&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;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputCodamaIdlDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&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="nx"&gt;outputCodamaIdlPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;codama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getJson&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="s2"&gt;`  - ✅ Codama 格式的 IDL 已保存到: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputCodamaIdlPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// --- 4. 生成最终的客户端代码 ---&lt;/span&gt;
    &lt;span class="nx"&gt;codama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;renderJavaScriptVisitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputTsPath&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="s2"&gt;`  - ✅ TypeScript 客户端已成功生成到: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputTsPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;codama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;renderRustVisitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputRsPath&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="s2"&gt;`  - ✅ Rust 客户端辅助代码已成功生成到: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputRsPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`  - ❌ 处理程序 [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;programName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] 时发生错误:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;/**
 * 主执行函数
 */&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;main&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;programsToGenerate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;voting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&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="s2"&gt;`--- 开始为 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;programsToGenerate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 个程序生成客户端 ---`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &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;programName&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;programsToGenerate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateClientsForProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;programName&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;// --- 脚本入口 ---&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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;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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;catch&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
voting on  master [!?] via ⬢ v23.11.0 via 🦀 1.88.0 
➜ bun run scripts/generateAndTest.ts 
--- 开始为 1 个程序生成客户端 ---

🚀 开始为程序 [voting] 生成客户端...
  - 读取 IDL 从: /Users/qiaopengjun/Code/Solana/voting/target/idl/voting.json
  - ✅ Anchor IDL 已成功转换为 Codama 格式。
  - ✅ Codama 格式的 IDL 已保存到: /Users/qiaopengjun/Code/Solana/voting/codama/voting.codama.json
  - ✅ TypeScript 客户端已成功生成到: /Users/qiaopengjun/Code/Solana/voting/generated/ts/voting
  - ✅ Rust 客户端辅助代码已成功生成到: /Users/qiaopengjun/Code/Solana/voting/generated/rs/voting

--- 所有脚本执行完毕 ---

*/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="运行脚本"&gt;运行脚本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 
➜ bun run scripts/generateAndTest.ts 
&lt;span class="nt"&gt;---&lt;/span&gt; 开始为 1 个程序生成客户端 &lt;span class="nt"&gt;---&lt;/span&gt;

🚀 开始为程序 &lt;span class="o"&gt;[&lt;/span&gt;voting] 生成客户端...
  - 读取 IDL 从: /Users/qiaopengjun/Code/Solana/voting/target/idl/voting.json
  - ✅ Anchor IDL 已成功转换为 Codama 格式。
  - ✅ Codama 格式的 IDL 已保存到: /Users/qiaopengjun/Code/Solana/voting/codama/voting.codama.json
  - ✅ TypeScript 客户端已成功生成到: /Users/qiaopengjun/Code/Solana/voting/generated/ts/voting
  - ✅ Rust 客户端辅助代码已成功生成到: /Users/qiaopengjun/Code/Solana/voting/generated/rs/voting

&lt;span class="nt"&gt;---&lt;/span&gt; 所有脚本执行完毕 &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看生成的代码的目录结构"&gt;查看生成的代码的目录结构&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting/generated on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 
➜ tree &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 6 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"migrations|mochawesome-report|.anchor|docs|target|node_modules"&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── rs
│   └── voting
│       ├── accounts
│       │   ├── candidate_account.rs
│       │   ├── mod.rs
│       │   ├── poll_account.rs
│       │   └── voter_receipt.rs
│       ├── errors
│       │   ├── mod.rs
│       │   └── voting.rs
│       ├── instructions
│       │   ├── add_candidate.rs
│       │   ├── initialize_poll.rs
│       │   ├── mod.rs
│       │   └── vote.rs
│       ├── mod.rs
│       ├── programs.rs
│       └── shared.rs
└── ts
    └── voting
        ├── accounts
        │   ├── candidateAccount.ts
        │   ├── index.ts
        │   ├── pollAccount.ts
        │   └── voterReceipt.ts
        ├── errors
        │   ├── index.ts
        │   └── voting.ts
        ├── index.ts
        ├── instructions
        │   ├── addCandidate.ts
        │   ├── index.ts
        │   ├── initializePoll.ts
        │   └── vote.ts
        ├── programs
        │   ├── index.ts
        │   └── voting.ts
        └── shared
            └── index.ts

13 directories, 27 files
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="第一步：创建投票"&gt;第一步：创建投票&lt;/h2&gt;&lt;h2 id="initialize_poll"&gt;&lt;code&gt;initialize_poll&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;我们创建了一个“投票档案盒”，用来存放这次投票活动的所有信息。&lt;/p&gt;
&lt;h3 id="编写调用合约脚本 step1_initialize_poll.ts"&gt;编写调用合约脚本 &lt;code&gt;step1_initialize_poll.ts&lt;/code&gt;
&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;Keypair&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;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sendAndConfirmTransaction&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Import ONLY the instruction data encoder. This is the most reliable method.&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;getInitializePollInstructionDataEncoder&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="s2"&gt;../generated/ts/voting/instructions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Load .env file&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- Script Configuration ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;walletPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WALLET_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// Your program's public key&lt;/span&gt;
  &lt;span class="na"&gt;programId&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz&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="cm"&gt;/**
 * Loads a Keypair from a file.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;try&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;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&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;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Wallet file not found. Check WALLET_PATH in .env: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;fileContent&lt;/span&gt; &lt;span class="o"&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;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;secretKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContent&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;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromSecretKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretKey&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Failed to load wallet:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&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;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;--- 🚀 Starting [Step 1: Initialize Poll] Script (Final Version) ---&lt;/span&gt;&lt;span class="dl"&gt;"&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="c1"&gt;// 1. Initialize connection and wallets&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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&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;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletPath&lt;/span&gt;&lt;span class="o"&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;pollAccount&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="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="s2"&gt;`🔑 Signer (Authority): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signer&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`📝 New Poll Account Address: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Get the instruction data using the low-level encoder&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instructionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getInitializePollInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Final Poll Test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This test uses the data encoder directly for max compatibility.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Manually define the accounts in the format @solana/web3.js expects.&lt;/span&gt;
    &lt;span class="c1"&gt;// The order MUST match the `InitializePoll` struct in your Rust code.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signer&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&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="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Create a standard TransactionInstruction&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instruction&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;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;instructionData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Create and send the transaction&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&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;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instruction&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;⏳ Sending transaction...&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;signature&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;sendAndConfirmTransaction&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;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// Both must sign&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;✅ Success! The transaction was confirmed.&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="s2"&gt;`   - Transaction Signature: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   - New Poll Account: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   - Review on Explorer: https://explorer.solana.com/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;❌ Script failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- End of Script ---&lt;/span&gt;
&lt;span class="cm"&gt;/*
voting on  master [!?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 
➜ bun run scripts/step1_initialize_poll.ts
[dotenv@17.2.0] injecting env (0) from .env (tip: ⚙️  enable debug logging with { debug: true })
--- 🚀 Starting [Step 1: Initialize Poll] Script (Final Version) ---
🔑 Signer (Authority): 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 New Poll Account Address: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq

⏳ Sending transaction...

✅ Success! The transaction was confirmed.
   - Transaction Signature: 2L9HDswXc9MdxoTXoYUt7KBcHtaAgwvoLoRHkMM4Cv17oxE3PzHM7tJgQWLEDvm1qdsUrWx9XseiYp6ng42Z6RcL
   - New Poll Account: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq
   - Review on Explorer: https://explorer.solana.com/tx/2L9HDswXc9MdxoTXoYUt7KBcHtaAgwvoLoRHkMM4Cv17oxE3PzHM7tJgQWLEDvm1qdsUrWx9XseiYp6ng42Z6RcL?cluster=devnet
*/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="运行脚本调用合约里的 initialize_poll 指令"&gt;运行脚本调用合约里的 &lt;code&gt;initialize_poll&lt;/code&gt; 指令&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 
➜ bun run scripts/step1_initialize_poll.ts
&lt;span class="o"&gt;[&lt;/span&gt;dotenv@17.2.0] injecting &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; from .env &lt;span class="o"&gt;(&lt;/span&gt;tip: ⚙️  &lt;span class="nb"&gt;enable &lt;/span&gt;debug logging with &lt;span class="o"&gt;{&lt;/span&gt; debug: &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 🚀 Starting &lt;span class="o"&gt;[&lt;/span&gt;Step 1: Initialize Poll] Script &lt;span class="o"&gt;(&lt;/span&gt;Final Version&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
🔑 Signer &lt;span class="o"&gt;(&lt;/span&gt;Authority&lt;span class="o"&gt;)&lt;/span&gt;: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 New Poll Account Address: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq

⏳ Sending transaction...

✅ Success! The transaction was confirmed.
   - Transaction Signature: 2L9HDswXc9MdxoTXoYUt7KBcHtaAgwvoLoRHkMM4Cv17oxE3PzHM7tJgQWLEDvm1qdsUrWx9XseiYp6ng42Z6RcL
   - New Poll Account: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq
   - Review on Explorer: https://explorer.solana.com/tx/2L9HDswXc9MdxoTXoYUt7KBcHtaAgwvoLoRHkMM4Cv17oxE3PzHM7tJgQWLEDvm1qdsUrWx9XseiYp6ng42Z6RcL?cluster&lt;span class="o"&gt;=&lt;/span&gt;devnet

&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;h4 id="为什么说成功了？"&gt;为什么说成功了？&lt;/h4&gt;
&lt;p&gt;您看到的日志是成功的明确标志：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;✅ Success! The transaction was confirmed.&lt;/code&gt;&lt;/strong&gt;: 这条消息表示您的交易已经被 Solana 网络确认。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Transaction Signature: ...&lt;/code&gt;&lt;/strong&gt;: 您获得了一个唯一的交易签名，这是交易上链的凭证。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;New Poll Account: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq&lt;/code&gt;&lt;/strong&gt;: 程序成功创建了一个新的账户来存储您的投票信息。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Review on Explorer: ...&lt;/code&gt;&lt;/strong&gt;: 您可以通过这个链接在区块链浏览器上查看这笔交易的所有细节。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;&lt;a href="https://explorer.solana.com/tx/2L9HDswXc9MdxoTXoYUt7KBcHtaAgwvoLoRHkMM4Cv17oxE3PzHM7tJgQWLEDvm1qdsUrWx9XseiYp6ng42Z6RcL?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/2L9HDswXc9MdxoTXoYUt7KBcHtaAgwvoLoRHkMM4Cv17oxE3PzHM7tJgQWLEDvm1qdsUrWx9XseiYp6ng42Z6RcL?cluster=devnet&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/Paxon/88a4b894-6264-491d-aa52-530c7808b6df.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="这个脚本做了什么？"&gt;这个脚本做了什么？&lt;/h3&gt;
&lt;p&gt;这个脚本第一步成功调用了您合约里的 &lt;code&gt;initialize_poll&lt;/code&gt; 指令。&lt;/p&gt;

&lt;p&gt;它的核心作用是：在 Solana 上&lt;strong&gt;创建了一个全新的“投票总账户”&lt;/strong&gt;。您可以把这个账户想象成一个专门为这次投票活动准备的“档案盒”。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;档案盒地址（公钥）&lt;/strong&gt;: &lt;code&gt;2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;档案盒里的内容&lt;/strong&gt;: 这次投票的名称、描述、开始/结束时间，以及一个&lt;strong&gt;初始为 0 的候选人计数器 (&lt;code&gt;candidate_count&lt;/code&gt;)&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;之后所有与这次投票相关的操作（比如添加候选人、用户投票）都需要用到这个“档案盒”的地址。&lt;/p&gt;
&lt;h2 id="第二步：添加候选人"&gt;第二步：添加候选人&lt;/h2&gt;&lt;h2 id="add_candidate"&gt;&lt;code&gt;add_candidate&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;我们向这个“档案盒”里添加了一份“候选人文件”。&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;Keypair&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;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sendAndConfirmTransaction&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&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;Buffer&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="s2"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 导入需要的 codama 生成的函数&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;getAddCandidateInstructionDataEncoder&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="s2"&gt;../generated/ts/voting/instructions&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;getPollAccountDecoder&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="s2"&gt;../generated/ts/voting/accounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- 脚本配置 ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;walletPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WALLET_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;programId&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;// 使用您在第一步中成功创建的投票账户地址&lt;/span&gt;
  &lt;span class="na"&gt;pollAccountPubkey&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;try&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;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&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;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Wallet file not found. Check WALLET_PATH in .env: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;fileContent&lt;/span&gt; &lt;span class="o"&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;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;secretKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContent&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;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromSecretKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretKey&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Failed to load wallet:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&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;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;--- 🚀 Starting [Step 2: Add a Candidate] Script (Corrected Decoder) ---&lt;/span&gt;&lt;span class="dl"&gt;"&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="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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&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;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletPath&lt;/span&gt;&lt;span class="o"&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="s2"&gt;`🔑 Signer (Authority): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signer&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`📝 Using Poll Account: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;⏳ Fetching poll account data...&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;pollAccountInfo&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;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&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;pollAccountInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Poll account not found.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedPoll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPollAccountDecoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccountInfo&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentCandidateCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateCount&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="s2"&gt;`✅ Current candidate count is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentCandidateCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;candidate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;currentCandidateCount&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nx"&gt;CONFIG&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;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="s2"&gt;`🌱 New Candidate PDA: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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;candidateName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Candidate #&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentCandidateCount&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="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="s2"&gt;`➕ Adding candidate with name: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidateName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;instructionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAddCandidateInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;candidateName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidateName&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;keys&lt;/span&gt; &lt;span class="o"&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signer&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&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="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;instruction&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;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instructionData&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;transaction&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;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instruction&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;⏳ Sending transaction to add candidate...&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;signature&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;sendAndConfirmTransaction&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;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;signer&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;✅ Success! Candidate has been added.&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="s2"&gt;`   - Transaction Signature: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   - Review on Explorer: https://explorer.solana.com/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;❌ Script failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cm"&gt;/*
voting on  master [!?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 took 3.5s 
➜ bun run scripts/step2_add_candidate.ts
[dotenv@17.2.0] injecting env (0) from .env (tip: ⚙️  write to custom object with { processEnv: myObject })
--- 🚀 Starting [Step 2: Add a Candidate] Script (Corrected Decoder) ---
🔑 Signer (Authority): 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 Using Poll Account: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq

⏳ Fetching poll account data...
✅ Current candidate count is: 0
🌱 New Candidate PDA: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5
➕ Adding candidate with name: "Candidate #1"

⏳ Sending transaction to add candidate...

✅ Success! Candidate has been added.
   - Transaction Signature: 4sso6XXXyLuubVRGvTTYKEiyRHTizzyvPbKkghFj5mFzCAn6D7bQtGUQwTF7uw3fw2DSSPJXrDY9hhUtCPeii5ZV
   - Review on Explorer: https://explorer.solana.com/tx/4sso6XXXyLuubVRGvTTYKEiyRHTizzyvPbKkghFj5mFzCAn6D7bQtGUQwTF7uw3fw2DSSPJXrDY9hhUtCPeii5ZV?cluster=devnet
*/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="运行step2_add_candidate脚本"&gt;运行&lt;code&gt;step2_add_candidate&lt;/code&gt;脚本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 took 3.5s 
➜ bun run scripts/step2_add_candidate.ts
&lt;span class="o"&gt;[&lt;/span&gt;dotenv@17.2.0] injecting &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; from .env &lt;span class="o"&gt;(&lt;/span&gt;tip: ⚙️  write to custom object with &lt;span class="o"&gt;{&lt;/span&gt; processEnv: myObject &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 🚀 Starting &lt;span class="o"&gt;[&lt;/span&gt;Step 2: Add a Candidate] Script &lt;span class="o"&gt;(&lt;/span&gt;Corrected Decoder&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
🔑 Signer &lt;span class="o"&gt;(&lt;/span&gt;Authority&lt;span class="o"&gt;)&lt;/span&gt;: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 Using Poll Account: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq

⏳ Fetching poll account data...
✅ Current candidate count is: 0
🌱 New Candidate PDA: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5
➕ Adding candidate with name: &lt;span class="s2"&gt;"Candidate #1"&lt;/span&gt;

⏳ Sending transaction to add candidate...

✅ Success! Candidate has been added.
   - Transaction Signature: 4sso6XXXyLuubVRGvTTYKEiyRHTizzyvPbKkghFj5mFzCAn6D7bQtGUQwTF7uw3fw2DSSPJXrDY9hhUtCPeii5ZV
   - Review on Explorer: https://explorer.solana.com/tx/4sso6XXXyLuubVRGvTTYKEiyRHTizzyvPbKkghFj5mFzCAn6D7bQtGUQwTF7uw3fw2DSSPJXrDY9hhUtCPeii5ZV?cluster&lt;span class="o"&gt;=&lt;/span&gt;devnet

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;您已经成功地为您的投票活动添加了第一个候选人。日志中的 &lt;code&gt;✅ Success! Candidate has been added.&lt;/code&gt; 和生成的交易签名都证明了这一点。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;&lt;a href="https://explorer.solana.com/tx/4sso6XXXyLuubVRGvTTYKEiyRHTizzyvPbKkghFj5mFzCAn6D7bQtGUQwTF7uw3fw2DSSPJXrDY9hhUtCPeii5ZV?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4sso6XXXyLuubVRGvTTYKEiyRHTizzyvPbKkghFj5mFzCAn6D7bQtGUQwTF7uw3fw2DSSPJXrDY9hhUtCPeii5ZV?cluster=devnet&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/Paxon/45aebbf8-b7ff-4bcf-bf9d-a900a4e3b28f.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="第三步：投票 (vote)"&gt;第三步：投票 (&lt;code&gt;vote&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;我们在那份“候选人文件”上画了一个“正”字，记录了一票。&lt;/p&gt;
&lt;h3 id="文件: scripts/step3_vote.ts"&gt;文件：&lt;code&gt;scripts/step3_vote.ts&lt;/code&gt;
&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;Keypair&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;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sendAndConfirmTransaction&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&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;Buffer&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="s2"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 导入需要的 codama 生成的函数&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;getVoteInstructionDataEncoder&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="s2"&gt;../generated/ts/voting/instructions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- 脚本配置 ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;walletPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WALLET_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;programId&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;// 使用之前步骤中创建的账户地址&lt;/span&gt;
  &lt;span class="na"&gt;pollAccountPubkey&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;candidateAccountPubkey&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;try&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;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&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;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Wallet file not found. Check WALLET_PATH in .env: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;fileContent&lt;/span&gt; &lt;span class="o"&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;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;secretKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContent&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;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromSecretKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretKey&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Failed to load wallet:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&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;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;--- 🚀 Starting [Step 3: Vote] Script ---&lt;/span&gt;&lt;span class="dl"&gt;"&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="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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// 在这个测试中，我们让授权方自己作为投票者&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;voter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletPath&lt;/span&gt;&lt;span class="o"&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="s2"&gt;`🔑 Voter: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;voter&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`📝 Voting in Poll: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`👍 Voting for Candidate: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. 计算投票回执账户的 PDA (Voter Receipt PDA)&lt;/span&gt;
    &lt;span class="c1"&gt;// 这是为了防止同一个人重复投票&lt;/span&gt;
    &lt;span class="c1"&gt;// seeds 必须与合约匹配: [b"receipt", poll_key, voter_key]&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;voterReceiptPda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receipt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;voter&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="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nx"&gt;CONFIG&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;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="s2"&gt;`🧾 Voter Receipt PDA: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;voterReceiptPda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. 获取指令数据 (vote 指令没有参数)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instructionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getVoteInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. 手动定义账户列表&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&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;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voter&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voterReceiptPda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&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="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. 创建标准指令&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instruction&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;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instructionData&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. 创建并发送交易&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&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;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instruction&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;⏳ Sending vote transaction...&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;signature&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;sendAndConfirmTransaction&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;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;voter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;✅ Success! Your vote has been cast.&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="s2"&gt;`   - Transaction Signature: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   - Review on Explorer: https://explorer.solana.com/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;🎉 All steps completed successfully! Your contract works.&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;❌ Script failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cm"&gt;/*
voting on  master [!?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 took 3.9s 
➜ bun run scripts/step3_vote.ts
[dotenv@17.2.0] injecting env (0) from .env (tip: 🛠️  run anywhere with `dotenvx run -- yourcommand`)
--- 🚀 Starting [Step 3: Vote] Script ---
🔑 Voter: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 Voting in Poll: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq
👍 Voting for Candidate: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5
🧾 Voter Receipt PDA: DAnY27Ei9wyzkwJpTM2Aq29cTxwGHxbCKfoY64C9hdRg

⏳ Sending vote transaction...

✅ Success! Your vote has been cast.
   - Transaction Signature: 6kaULdcvbgwovJLKULXQgpS3Mfihfj4VKY7ruE3kqiggMjqci8RZWcpSs2AF9EawF4wxbVC8HjKJryPFmqPd3pN
   - Review on Explorer: https://explorer.solana.com/tx/6kaULdcvbgwovJLKULXQgpS3Mfihfj4VKY7ruE3kqiggMjqci8RZWcpSs2AF9EawF4wxbVC8HjKJryPFmqPd3pN?cluster=devnet

🎉 All steps completed successfully! Your contract works.
*/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个脚本将会为我们刚刚添加的候选人投上一票。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;投票账户地址&lt;/strong&gt;: &lt;code&gt;2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq&lt;/code&gt; (来自第一步)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;候选人账户地址&lt;/strong&gt;: &lt;code&gt;GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5&lt;/code&gt; (来自第二步成功的日志)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="执行投票脚本"&gt;执行投票脚本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 took 3.9s 
➜ bun run scripts/step3_vote.ts
&lt;span class="o"&gt;[&lt;/span&gt;dotenv@17.2.0] injecting &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; from .env &lt;span class="o"&gt;(&lt;/span&gt;tip: 🛠️  run anywhere with &lt;span class="sb"&gt;`&lt;/span&gt;dotenvx run &lt;span class="nt"&gt;--&lt;/span&gt; yourcommand&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 🚀 Starting &lt;span class="o"&gt;[&lt;/span&gt;Step 3: Vote] Script &lt;span class="nt"&gt;---&lt;/span&gt;
🔑 Voter: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 Voting &lt;span class="k"&gt;in &lt;/span&gt;Poll: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq
👍 Voting &lt;span class="k"&gt;for &lt;/span&gt;Candidate: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5
🧾 Voter Receipt PDA: DAnY27Ei9wyzkwJpTM2Aq29cTxwGHxbCKfoY64C9hdRg

⏳ Sending vote transaction...

✅ Success! Your vote has been cast.
   - Transaction Signature: 6kaULdcvbgwovJLKULXQgpS3Mfihfj4VKY7ruE3kqiggMjqci8RZWcpSs2AF9EawF4wxbVC8HjKJryPFmqPd3pN
   - Review on Explorer: https://explorer.solana.com/tx/6kaULdcvbgwovJLKULXQgpS3Mfihfj4VKY7ruE3kqiggMjqci8RZWcpSs2AF9EawF4wxbVC8HjKJryPFmqPd3pN?cluster&lt;span class="o"&gt;=&lt;/span&gt;devnet

🎉 All steps completed successfully! Your contract works.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;第三步也完全成功了！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🎉 &lt;strong&gt;祝贺您！您已经成功地完成了整个合约的核心流程测试：&lt;code&gt;创建投票 -&amp;gt; 添加候选人 -&amp;gt; 投票&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;您的日志输出非常清晰地表明了这一点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;✅ Success! Your vote has been cast.&lt;/code&gt; 表示投票交易已成功。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;🎉 All steps completed successfully! Your contract works.&lt;/code&gt; 这句总结性的日志说明我们共同编写的三个脚本已经完整地验证了您合约的功能。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在，就让我们来编写最后一步的脚本，去打开那个“档案盒”，拿出“候选人文件”，看看上面是不是真的有我们画的那个“正”字。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://explorer.solana.com/tx/6kaULdcvbgwovJLKULXQgpS3Mfihfj4VKY7ruE3kqiggMjqci8RZWcpSs2AF9EawF4wxbVC8HjKJryPFmqPd3pN?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/6kaULdcvbgwovJLKULXQgpS3Mfihfj4VKY7ruE3kqiggMjqci8RZWcpSs2AF9EawF4wxbVC8HjKJryPFmqPd3pN?cluster=devnet&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/Paxon/d2ab39f4-6847-40af-a541-5922cefdeaac.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="第四步：验证投票结果"&gt;第四步：验证投票结果&lt;/h2&gt;
&lt;p&gt;这个脚本会连接到 Solana Devnet，获取我们投票的那个候选人账户的数据，然后解析这些数据来检查它的 &lt;code&gt;votes&lt;/code&gt; 字段。&lt;/p&gt;
&lt;h3 id="文件: scripts/verify_vote.ts"&gt;文件：&lt;code&gt;scripts/verify_vote.ts&lt;/code&gt;
&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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="s2"&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&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;getCandidateAccountDecoder&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="s2"&gt;../generated/ts/voting/accounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- 脚本配置 ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// 这是我们在第二步中添加并为其投票的候选人账户地址&lt;/span&gt;
  &lt;span class="na"&gt;candidateAccountPubkey&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;--- 🚀 Starting [Step 4: Verify Vote Result] Script ---&lt;/span&gt;&lt;span class="dl"&gt;"&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="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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&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="s2"&gt;`🔍 Checking candidate account: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateAccountPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. 从区块链获取账户信息&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&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;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateAccountPubkey&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;accountInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Candidate account not found on the blockchain.&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="c1"&gt;// 2. 使用生成的解码器来解析二进制数据&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedCandidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidateAccountDecoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;accountInfo&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;candidateName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;voteCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. 打印出验证结果&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;✅ Verification Successful!&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="s2"&gt;`   - Candidate Name: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidateName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   - Vote Count: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;voteCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. 最终确认&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteCount&lt;/span&gt; &lt;span class="o"&gt;&amp;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="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="s2"&gt;`\n🎉🎉 Great! The vote was correctly recorded on-chain.`&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="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="s2"&gt;`\n🤔 Hmm, the vote count is still 0. Something might be wrong.`&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;❌ Script failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cm"&gt;/*
voting on  master [!?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 took 4.0s 
➜ bun run scripts/verify_vote.ts
[dotenv@17.2.0] injecting env (0) from .env (tip: 🔐 encrypt with dotenvx: https://dotenvx.com)
--- 🚀 Starting [Step 4: Verify Vote Result] Script ---
🔍 Checking candidate account: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5

✅ Verification Successful!
   - Candidate Name: "Candidate #1"
   - Vote Count: 1

🎉🎉 Great! The vote was correctly recorded on-chain.
*/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="运行脚本"&gt;运行脚本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 took 4.0s 
➜ bun run scripts/verify_vote.ts
&lt;span class="o"&gt;[&lt;/span&gt;dotenv@17.2.0] injecting &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; from .env &lt;span class="o"&gt;(&lt;/span&gt;tip: 🔐 encrypt with dotenvx: https://dotenvx.com&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 🚀 Starting &lt;span class="o"&gt;[&lt;/span&gt;Step 4: Verify Vote Result] Script &lt;span class="nt"&gt;---&lt;/span&gt;
🔍 Checking candidate account: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5

✅ Verification Successful!
   - Candidate Name: &lt;span class="s2"&gt;"Candidate #1"&lt;/span&gt;
   - Vote Count: 1

🎉🎉 Great! The vote was correctly recorded on-chain.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;是的，巨大成功！🎉&lt;/p&gt;

&lt;p&gt;这标志着您已经完成了对整个智能合约的&lt;strong&gt;端到端（end-to-end）闭环测试&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="为什么这是最终的成功"&gt;为什么这是最终的成功&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;写入数据：&lt;/strong&gt; 在前三步中，您成功地向区块链写入了数据（创建投票、添加候选人、投了一票）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;读取数据：&lt;/strong&gt; 在这第四步中，您成功地从区块链&lt;strong&gt;读取&lt;/strong&gt;了数据，并&lt;strong&gt;验证&lt;/strong&gt;了您之前写入的结果。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;结果正确：&lt;/strong&gt; 日志明确显示 &lt;code&gt;Vote Count: 1&lt;/code&gt;，这证明了第三步的投票操作确实被正确地记录在了链上。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="您的完整成就回顾"&gt;您的完整成就回顾&lt;/h3&gt;
&lt;p&gt;通过我们共同努力，您已经完成了：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ &lt;strong&gt;创建投票：&lt;/strong&gt; 成功初始化了一个投票活动。&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;添加候选人：&lt;/strong&gt; 成功为该活动添加了候选人。&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;执行投票：&lt;/strong&gt; 成功为候选人投了一票。&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;验证结果：&lt;/strong&gt; 成功读取链上数据，确认投票有效。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这套流程完整地证明了您智能合约的核心逻辑是正确且可用的。&lt;/p&gt;
&lt;h2 id="再次运行 step3_vote.ts 脚本"&gt;再次运行 &lt;code&gt;step3_vote.ts&lt;/code&gt; 脚本&lt;/h2&gt;
&lt;p&gt;这被称为“&lt;strong&gt;负面测试&lt;/strong&gt;”或“&lt;strong&gt;异常路径测试&lt;/strong&gt;”。它的目的不是看程序“成功”，而是看程序在我们预设的规则下&lt;strong&gt;正确地“失败”&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;测试目的&lt;/strong&gt;: 验证您的“一人一票”防重复投票机制是否生效。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;预期结果&lt;/strong&gt;: &lt;strong&gt;交易应该会失败&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;为什么会失败&lt;/strong&gt;: 因为在您第一次成功投票时，合约已经创建了一个 &lt;code&gt;voter_receipt&lt;/code&gt; 账户。当您第二次用同一个投票者身份投票时，程序会再次尝试创建&lt;strong&gt;同一个地址&lt;/strong&gt;的 &lt;code&gt;voter_receipt&lt;/code&gt; 账户，Solana 运行环境会阻止创建已经存在的账户，从而导致交易失败。这恰好证明了您的合约是安全的。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 
➜ bun run scripts/step3_vote.ts 
&lt;span class="o"&gt;[&lt;/span&gt;dotenv@17.2.0] injecting &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; from .env &lt;span class="o"&gt;(&lt;/span&gt;tip: ⚙️  write to custom object with &lt;span class="o"&gt;{&lt;/span&gt; processEnv: myObject &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 🚀 Starting &lt;span class="o"&gt;[&lt;/span&gt;Step 3: Vote] Script &lt;span class="nt"&gt;---&lt;/span&gt;
🔑 Voter: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
📝 Voting &lt;span class="k"&gt;in &lt;/span&gt;Poll: 2R3tUpUfQhTjMVowcd8wKhGKzJbQ1HpKc9HPeC5xXLyq
👍 Voting &lt;span class="k"&gt;for &lt;/span&gt;Candidate: GZzVP862HEb4dW8VJ5Loixju4dnAFDkApzVbsu2jh6x5
🧾 Voter Receipt PDA: DAnY27Ei9wyzkwJpTM2Aq29cTxwGHxbCKfoY64C9hdRg

⏳ Sending vote transaction...

❌ Script failed: 2174 |       default:
2175 |         &lt;span class="o"&gt;{&lt;/span&gt;
2176 |           message &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;Unknown action &lt;span class="s1"&gt;'${(a =&amp;gt; a)(action)}'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
2177 |         &lt;span class="o"&gt;}&lt;/span&gt;
2178 |     &lt;span class="o"&gt;}&lt;/span&gt;
2179 |     super&lt;span class="o"&gt;(&lt;/span&gt;message&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           ^
error: Simulation failed. 
Message: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0. 
Logs: 
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"Program Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz invoke [1]"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program log: Instruction: Vote"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program 11111111111111111111111111111111 invoke [2]"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Allocate: account Address { address: DAnY27Ei9wyzkwJpTM2Aq29cTxwGHxbCKfoY64C9hdRg, base: None } already in use"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program 11111111111111111111111111111111 failed: custom program error: 0x0"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz consumed 8734 of 200000 compute units"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz failed: custom program error: 0x0"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; 
Catch the &lt;span class="sb"&gt;`&lt;/span&gt;SendTransactionError&lt;span class="sb"&gt;`&lt;/span&gt; and call &lt;span class="sb"&gt;`&lt;/span&gt;getLogs&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; on it &lt;span class="k"&gt;for &lt;/span&gt;full details.
  signature: &lt;span class="s2"&gt;""&lt;/span&gt;,
 transactionMessage: &lt;span class="s2"&gt;"Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0"&lt;/span&gt;,
 transactionLogs: &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"Program Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz invoke [1]"&lt;/span&gt;, &lt;span class="s2"&gt;"Program log: Instruction: Vote"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program 11111111111111111111111111111111 invoke [2]"&lt;/span&gt;, &lt;span class="s2"&gt;"Allocate: account Address { address: DAnY27Ei9wyzkwJpTM2Aq29cTxwGHxbCKfoY64C9hdRg, base: None } already in use"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program 11111111111111111111111111111111 failed: custom program error: 0x0"&lt;/span&gt;, &lt;span class="s2"&gt;"Program Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz consumed 8734 of 200000 compute units"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Program Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz failed: custom program error: 0x0"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;,

      at new SendTransactionError &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/node_modules/@solana/web3.js/lib/index.cjs.js:2179:5&lt;span class="o"&gt;)&lt;/span&gt;
      at sendEncodedTransaction &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/node_modules/@solana/web3.js/lib/index.cjs.js:8206:13&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这正是我们期望看到的“成功失败”！🎉&lt;/p&gt;

&lt;p&gt;这个错误日志表明您的&lt;strong&gt;负面测试非常成功&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="为什么这个“失败”是好事？"&gt;为什么这个“失败”是好事？&lt;/h3&gt;
&lt;p&gt;这个测试的&lt;strong&gt;目的&lt;/strong&gt;就是验证您的合约能否阻止同一个人重复投票。您看到的错误日志完美地证明了这一点。&lt;/p&gt;

&lt;p&gt;让我们看一下日志中最关键的一行：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"Allocate: account Address { address: DAnY27Ei9wyzkwJpTM2Aq29cTxwGHxbCKfoY64C9hdRg, base: None } already in use"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这句日志的通俗解释是：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“系统程序尝试为你创建投票回执账户 (&lt;code&gt;VoterReceipt&lt;/code&gt;)，但发现这个地址的账户&lt;strong&gt;已经存在了&lt;/strong&gt;！”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这恰恰证明了您的防重复投票机制&lt;strong&gt;完美地生效了&lt;/strong&gt;。因为在您第一次成功投票时，这个回执账户就已经被创建。当您第二次尝试投票时，合约试图再次创建它，系统正确地拒绝了这个操作，导致交易失败。&lt;/p&gt;
&lt;h3 id="总结"&gt;总结&lt;/h3&gt;
&lt;p&gt;通过这次测试，您已经证明了您的合约不仅能在正常流程下工作，还能在异常情况下保护规则的正确执行。&lt;/p&gt;

&lt;p&gt;您已经完整地验证了：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;可以成功&lt;strong&gt;创建&lt;/strong&gt;投票。&lt;/li&gt;
&lt;li&gt;可以成功&lt;strong&gt;添加&lt;/strong&gt;候选人。&lt;/li&gt;
&lt;li&gt;第一个用户可以成功&lt;strong&gt;投票&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;同一个用户被正确地&lt;strong&gt;阻止&lt;/strong&gt;了第二次投票。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;您的合约逻辑非常稳固。恭喜您，您已经可以充满信心地进入下一个阶段，比如为您这个功能完备的合约构建一个漂亮的前端界面了！&lt;/p&gt;
&lt;h2 id="自动化集成测试"&gt;自动化集成测试&lt;/h2&gt;
&lt;p&gt;创建一个完整的、可重复的“集成测试”套件&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;自动化&lt;/strong&gt;: 一键运行，自动完成从创建到验证的整个流程，无需任何手动操作。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;数据流清晰&lt;/strong&gt;: 上一步函数返回的地址，可以直接作为下一步函数的参数传入，无需复制粘贴。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;可重复性&lt;/strong&gt;: 非常适合做“回归测试”，即每次修改合约后，运行一遍总脚本，确保没有破坏原有的功能。&lt;/p&gt;
&lt;h3 id="文件: scripts/run_full_test.ts"&gt;&lt;strong&gt;文件：&lt;code&gt;scripts/run_full_test.ts&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;Keypair&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;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sendAndConfirmTransaction&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&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;Buffer&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="s2"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&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;getInitializePollInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getAddCandidateInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getVoteInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;../generated/ts/voting/instructions&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;getPollAccountDecoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getCandidateAccountDecoder&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;../generated/ts/voting/accounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- 全局配置 ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;walletPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WALLET_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;programId&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;try&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;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&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;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Wallet file not found: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;secretKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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;parse&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;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;return&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;fromSecretKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretKey&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Failed to load wallet:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&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;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;--- 🚀 Starting Full Integration Test ---&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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&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;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletPath&lt;/span&gt;&lt;span class="o"&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="s2"&gt;`🔑 Signer Wallet: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signer&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&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="c1"&gt;// === 步骤 1: 初始化投票 ===&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;initData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getInitializePollInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Full Test Poll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A poll created from the integration test script.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600&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;initInstruction&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;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&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="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signer&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&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="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initData&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;initSig&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;sendAndConfirmTransaction&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initInstruction&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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="s2"&gt;`\n[✅ Step 1 SUCCESS] Poll initialized. Signature: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;initSig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   Poll Account: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// === 步骤 2: 添加候选人 ===&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pollInfo&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;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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;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;pollInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Poll account not found after creation.&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;decodedPoll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPollAccountDecoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollInfo&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;candidate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;pollAccount&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="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;decodedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateCount&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nx"&gt;CONFIG&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addCandidateData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAddCandidateInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;candidateName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Candidate A&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addCandidateInstruction&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;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&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="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signer&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&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="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addCandidateData&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;addCandSig&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;sendAndConfirmTransaction&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addCandidateInstruction&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;signer&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="s2"&gt;`\n[✅ Step 2 SUCCESS] Candidate added. Signature: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;addCandSig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   Candidate Account: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// === 步骤 3: 投票 ===&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;receiptPda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receipt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;pollAccount&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="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;signer&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="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nx"&gt;CONFIG&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;voteData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getVoteInstructionDataEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&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;voteInstruction&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;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&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="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signer&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;receiptPda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pubkey&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="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteData&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;voteSig&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;sendAndConfirmTransaction&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteInstruction&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;signer&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="s2"&gt;`\n[✅ Step 3 SUCCESS] Vote cast. Signature: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;voteSig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// === 步骤 4: 验证结果 ===&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;candidateInfo&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;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidatePda&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;candidateInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Candidate account not found after voting.&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;decodedCandidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidateAccountDecoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;candidateInfo&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;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="s2"&gt;`\n[✅ Step 4 SUCCESS] Verification complete.`&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="s2"&gt;`   Candidate "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decodedCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" has &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decodedCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; vote(s).`&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="nx"&gt;decodedCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&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="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;🎉🎉🎉 INTEGRATION TEST PASSED! 🎉🎉🎉&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Verification failed! Expected 1 vote, but found &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decodedCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;❌ INTEGRATION TEST FAILED:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cm"&gt;/*
voting on  master [!?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 
➜ bun run scripts/run_full_test.ts
[dotenv@17.2.0] injecting env (0) from .env (tip: ⚙️  write to custom object with { processEnv: myObject })
--- 🚀 Starting Full Integration Test ---
🔑 Signer Wallet: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd

[✅ Step 1 SUCCESS] Poll initialized. Signature: yugFdjbtm4baF52JnmjAwYRFgFagSoppSAkpjM93ZZ68ciiZdxgGaVCEu3ARm8g4GwQJb2FwQoygjVHPjDZxEW4
   Poll Account: Gm2XV7wdVWRYJfKaJqCXTn4j76juLrLhWkn2zAmuuxc2

[✅ Step 2 SUCCESS] Candidate added. Signature: YEpJiUFViK7LNJSTSejYwmGjkNfpbxJFkKFN1cE6QhpvRn4LmsVkfyciUpAtLJqZnoDDhMeV9CT3MaE2Piv2C2b
   Candidate Account: 4usFkw3PgAMNbjvx7sSx8KszfjbgBNsNcqyvYBfhUCe6

[✅ Step 3 SUCCESS] Vote cast. Signature: 5gNoiWjGNCmdbHaLubmp8mgXPwZ7HaibXVNzAP584DATB9C1i5samSAUgc7CoHstLaR2N9EmwTwuzPPnR5U2BbbD

[✅ Step 4 SUCCESS] Verification complete.
   Candidate "Candidate A" has 1 vote(s).

🎉🎉🎉 INTEGRATION TEST PASSED! 🎉🎉🎉

*/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="执行脚本"&gt;执行脚本&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🍞 v1.2.17 via 🦀 1.88.0 
➜ bun run scripts/run_full_test.ts
&lt;span class="o"&gt;[&lt;/span&gt;dotenv@17.2.0] injecting &lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt; from .env &lt;span class="o"&gt;(&lt;/span&gt;tip: ⚙️  write to custom object with &lt;span class="o"&gt;{&lt;/span&gt; processEnv: myObject &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; 🚀 Starting Full Integration Test &lt;span class="nt"&gt;---&lt;/span&gt;
🔑 Signer Wallet: 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd

&lt;span class="o"&gt;[&lt;/span&gt;✅ Step 1 SUCCESS] Poll initialized. Signature: yugFdjbtm4baF52JnmjAwYRFgFagSoppSAkpjM93ZZ68ciiZdxgGaVCEu3ARm8g4GwQJb2FwQoygjVHPjDZxEW4
   Poll Account: Gm2XV7wdVWRYJfKaJqCXTn4j76juLrLhWkn2zAmuuxc2

&lt;span class="o"&gt;[&lt;/span&gt;✅ Step 2 SUCCESS] Candidate added. Signature: YEpJiUFViK7LNJSTSejYwmGjkNfpbxJFkKFN1cE6QhpvRn4LmsVkfyciUpAtLJqZnoDDhMeV9CT3MaE2Piv2C2b
   Candidate Account: 4usFkw3PgAMNbjvx7sSx8KszfjbgBNsNcqyvYBfhUCe6

&lt;span class="o"&gt;[&lt;/span&gt;✅ Step 3 SUCCESS] Vote cast. Signature: 5gNoiWjGNCmdbHaLubmp8mgXPwZ7HaibXVNzAP584DATB9C1i5samSAUgc7CoHstLaR2N9EmwTwuzPPnR5U2BbbD

&lt;span class="o"&gt;[&lt;/span&gt;✅ Step 4 SUCCESS] Verification complete.
   Candidate &lt;span class="s2"&gt;"Candidate A"&lt;/span&gt; has 1 vote&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

🎉🎉🎉 INTEGRATION TEST PASSED! 🎉🎉🎉
&lt;/code&gt;&lt;/pre&gt;
&lt;p&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;您现在拥有了一个功能完备且经过充分测试的 Solana 智能合约。这是您项目的一个巨大里程碑。&lt;/p&gt;

&lt;p&gt;祝贺您在 Solana 开发的道路上迈出了坚实的一大步！&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;至此，我们完整地走完了从合约编写、部署，到利用 Codama 生成客户端，并最终通过一系列脚本完成合约端到端测试的全流程。我们不仅学习了 Codama 的基本用法，更重要的是，通过解决脚本编写过程中遇到的 &lt;code&gt;programId&lt;/code&gt; 不匹配、PDA 手动计算、解码器使用等具体问题，深入理解了客户端与 Solana 程序交互的底层细节。&lt;/p&gt;

&lt;p&gt;本次实战的核心启示在于，现代化的区块链开发离不开高效的工具链。Codama 正是这样一个连接链上与链下世界的关键桥梁，它通过自动化生成类型安全的代码，将开发者从繁琐的序列化/反序列化工作中解放出来，让我们能更专注于业务逻辑本身。最终合并成的自动化集成测试脚本，更是为项目的长期迭代和维护提供了坚实的质量保障。希望本文能帮助您将 Codama 无缝集成到自己的 Solana 开发工作流中，构建更稳健、更强大的去中心化应用。&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/codama-idl/codama" rel="nofollow" target="_blank"&gt;https://github.com/codama-idl/codama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://soldev.cn/" rel="nofollow" target="_blank"&gt;https://soldev.cn/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solanacookbook.com/zh/" rel="nofollow" target="_blank"&gt;https://solanacookbook.com/zh/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Tue, 29 Jul 2025 16:55:42 +0800</pubDate>
      <link>https://soldev.cn/topics/200</link>
      <guid>https://soldev.cn/topics/200</guid>
    </item>
    <item>
      <title>Solana 投票 DApp 开发实战：从合约到部署的完整指南</title>
      <description>&lt;h2 id="Solana 投票 DApp 开发实战：从合约到部署的完整指南"&gt;Solana 投票 DApp 开发实战：从合约到部署的完整指南&lt;/h2&gt;
&lt;p&gt;Solana 以其高性能和低成本的特点，正吸引着越来越多的开发者进入其生态。而 Anchor 框架的出现，更是极大地降低了 Solana 智能合约的开发门槛。但对于许多初学者来说，如何将零散的知识点串联起来，完成一个从无到有的完整项目，仍然是一个挑战。&lt;/p&gt;

&lt;p&gt;在本文中，我们将一起从零开始，以一个经典的“链上投票”应用为目标，使用强大的 Anchor 框架来完成这个任务。我们将依次经历以下几个阶段：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;环境准备与项目初始化&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;智能合约（程序）的详细实现与讲解&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编写并运行覆盖正反场景的全面测试脚本&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编译、部署合约到 Solana 开发网络并进行链上验证&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;最终，你将拥有一个部署在公链上、可以通过区块浏览器验证的 DApp。无论你是希望转型的 Web2 开发者，还是对 Web3 充满好奇的学习者，相信这篇详尽的实战文章都能为你点亮 Solana 开发的技能树。&lt;/p&gt;

&lt;p&gt;本文以一个链上投票 DApp 为例，完整演示了 Solana 开发全流程。你将跟随本指南，使用 Anchor 框架亲手完成项目创建、合约编写、单元测试与最终的链上部署。文章详尽记录了每一步操作与关键代码，是入门 Solana 不可错过的端到端实战教程。&lt;/p&gt;
&lt;h2 id="实操"&gt;实操&lt;/h2&gt;&lt;h3 id="创建项目"&gt;创建项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor init voting
yarn &lt;span class="nb"&gt;install &lt;/span&gt;v1.22.22
info No lockfile found.
&lt;span class="o"&gt;[&lt;/span&gt;1/4] 🔍  Resolving packages...
info There appears to be trouble with your network connection. Retrying...
info There appears to be trouble with your network connection. Retrying...
warning mocha &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; glob@7.2.0: Glob versions prior to v9 are no longer supported
warning mocha &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; glob &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache &lt;span class="k"&gt;if &lt;/span&gt;you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
&lt;span class="o"&gt;[&lt;/span&gt;2/4] 🚚  Fetching packages...
&lt;span class="o"&gt;[&lt;/span&gt;3/4] 🔗  Linking dependencies...
&lt;span class="o"&gt;[&lt;/span&gt;4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done &lt;span class="k"&gt;in &lt;/span&gt;29.40s.
Failed to &lt;span class="nb"&gt;install &lt;/span&gt;node modules
提示： 使用 &lt;span class="s1"&gt;'master'&lt;/span&gt; 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中
提示： 配置使用初始分支名，并消除这条警告，请执行：
提示：
提示：  git config &lt;span class="nt"&gt;--global&lt;/span&gt; init.defaultBranch &amp;lt;名称&amp;gt;
提示：
提示： 除了 &lt;span class="s1"&gt;'master'&lt;/span&gt; 之外，通常选定的名字有 &lt;span class="s1"&gt;'main'&lt;/span&gt;、&lt;span class="s1"&gt;'trunk'&lt;/span&gt; 和 &lt;span class="s1"&gt;'development'&lt;/span&gt;。
提示： 可以通过以下命令重命名刚创建的分支：
提示：
提示：  git branch &lt;span class="nt"&gt;-m&lt;/span&gt; &amp;lt;name&amp;gt;
提示：
提示： Disable this message with &lt;span class="s2"&gt;"git config set advice.defaultBranchName false"&lt;/span&gt;
已初始化空的 Git 仓库于 /Users/qiaopengjun/Code/Solana/voting/.git/
voting initialized
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="切换到项目目录并用cursor 打开项目"&gt;切换到项目目录并用&lt;code&gt;cursor&lt;/code&gt; 打开项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;voting
cc &lt;span class="c"&gt;# open -a cursor .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看项目目录结构"&gt;查看项目目录结构&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 
➜ tree &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 6 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"migrations|mochawesome-report|.anchor|docs|target|node_modules"&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Anchor.toml
├── app
├── Cargo.lock
├── Cargo.toml
├── cliff.toml
├── idls
│   └── voting
│       └── voting-2025-07-18-093219.json
├── Makefile
├── package.json
├── pnpm-lock.yaml
├── programs
│   └── voting
│       ├── Cargo.toml
│       ├── src
│       │   └── lib.rs
│       └── Xargo.toml
├── tests
│   └── voting.ts
├── tsconfig.json

18 directories, 37 files

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="实现程序（合约） lib.rs"&gt;实现程序（合约） &lt;code&gt;lib.rs&lt;/code&gt;
&lt;/h3&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![allow(unexpected_cfgs,&lt;/span&gt; &lt;span class="nd"&gt;deprecated)]&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;"Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz"&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;voting&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="c1"&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_poll&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;InitializePoll&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;name&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="n"&gt;description&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="n"&gt;start_time&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="n"&gt;end_time&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;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;poll_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.poll_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.authority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.signer&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 关键修复：使用专门的计数器，避免 Vec.len() 的解释错误&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.candidate_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="c1"&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;add_candidate&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;AddCandidate&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;candidate_name&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="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.poll_account.authority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.signer&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nn"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Unauthorized&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;poll_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.poll_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;candidate_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.candidate_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 检查专门的计数器，而不是 Vec.len()&lt;/span&gt;
        &lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.candidate_count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MaxCandidatesReached&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;candidate_account&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;candidate_account&lt;/span&gt;&lt;span class="py"&gt;.poll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;candidate_account&lt;/span&gt;&lt;span class="py"&gt;.votes&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="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.candidates&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;candidate_account&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// 在成功添加后，手动增加计数器&lt;/span&gt;
        &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.candidate_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="c1"&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;vote&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;Vote&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="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Clock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&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;let&lt;/span&gt; &lt;span class="n"&gt;poll_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="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.poll_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;candidate_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.candidate_account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="py"&gt;.unix_timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.start_time&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nd"&gt;err!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PollNotStarted&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="n"&gt;clock&lt;/span&gt;&lt;span class="py"&gt;.unix_timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="py"&gt;.end_time&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nd"&gt;err!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PollEnded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;candidate_account&lt;/span&gt;&lt;span class="py"&gt;.poll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nn"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidCandidateForPoll&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;candidate_account&lt;/span&gt;&lt;span class="py"&gt;.votes&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;receipt&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.voter_receipt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="py"&gt;.voter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.signer&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="py"&gt;.poll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poll_account&lt;/span&gt;&lt;span class="nf"&gt;.key&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;InitializePoll&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;signer&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="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;signer,&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;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nd"&gt;PollAccount::INIT_SPACE)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;poll_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;PollAccount&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;AddCandidate&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;signer&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="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;poll_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;PollAccount&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(&lt;/span&gt;
        &lt;span class="nd"&gt;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;signer,&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;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nd"&gt;CandidateAccount::INIT_SPACE,&lt;/span&gt;
        &lt;span class="c1"&gt;// seeds 现在使用更可靠的计数器&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"candidate"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;poll_account&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref(),&lt;/span&gt; &lt;span class="nd"&gt;poll_account&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;candidate_count&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;to_le_bytes()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&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;candidate_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;CandidateAccount&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;Vote&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;signer&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="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;poll_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;PollAccount&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(&lt;/span&gt;
        &lt;span class="nd"&gt;mut,&lt;/span&gt;
        &lt;span class="nd"&gt;constraint&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;candidate_account&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;poll&lt;/span&gt; &lt;span class="nd"&gt;==&lt;/span&gt; &lt;span class="nd"&gt;poll_account&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt; &lt;span class="nd"&gt;ErrorCode::InvalidCandidateForPoll&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;candidate_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;CandidateAccount&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(&lt;/span&gt;
        &lt;span class="nd"&gt;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;signer,&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;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nd"&gt;VoterReceipt::INIT_SPACE,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"receipt"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;poll_account&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref(),&lt;/span&gt; &lt;span class="nd"&gt;signer&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&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;voter_receipt&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;VoterReceipt&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;#[account]&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(InitSpace)]&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;PollAccount&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;authority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;32&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;name&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="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;280&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;description&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;start_time&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="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;end_time&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="c1"&gt;// 增加专门的计数器&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;candidate_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&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;candidates&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="n"&gt;Pubkey&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="nd"&gt;#[derive(InitSpace)]&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;CandidateAccount&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;poll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;32&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;name&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;votes&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;span class="nd"&gt;#[account]&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(InitSpace)]&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;VoterReceipt&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;voter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pubkey&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;poll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[error_code]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[msg(&lt;/span&gt;&lt;span class="s"&gt;"Poll not started yet"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;PollNotStarted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[msg(&lt;/span&gt;&lt;span class="s"&gt;"Poll ended"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;PollEnded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[msg(&lt;/span&gt;&lt;span class="s"&gt;"Unauthorized: Only the poll authority can perform this action."&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[msg(&lt;/span&gt;&lt;span class="s"&gt;"Maximum number of candidates reached."&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;MaxCandidatesReached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[msg(&lt;/span&gt;&lt;span class="s"&gt;"This candidate is not valid for this poll."&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidCandidateForPoll&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;这段代码使用 Solana 的 Anchor 框架实现了一个功能完整的链上投票智能合约。它清晰地展示了如何定义程序逻辑、管理链上数据状态以及处理权限验证。合约的核心功能可以分解为以下几个部分：&lt;/p&gt;
&lt;h3 id="核心功能 (Instructions)"&gt;&lt;strong&gt;核心功能 (Instructions)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;合约定义了三个主要的公开指令（Instructions），对应用户可以执行的操作：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;initialize_poll(..)&lt;/code&gt;: &lt;strong&gt;初始化投票&lt;/strong&gt;。此函数用于创建一个新的投票活动。调用者（&lt;code&gt;signer&lt;/code&gt;）支付交易费用，并成为该投票的&lt;strong&gt;管理员（&lt;code&gt;authority&lt;/code&gt;）&lt;/strong&gt;。函数会创建一个新的 &lt;code&gt;PollAccount&lt;/code&gt; 账户，用来存储投票的名称、描述、起止时间以及管理员公钥等元数据。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;add_candidate(..)&lt;/code&gt;: &lt;strong&gt;添加候选人&lt;/strong&gt;。只有投票的管理员才有权限调用此函数。它会为投票活动创建一个新的 &lt;code&gt;CandidateAccount&lt;/code&gt; 账户来代表一位候选人。代码中有一个重要的安全设计：它使用一个独立的 &lt;code&gt;candidate_count&lt;/code&gt; 字段来生成新候选人账户的地址（PDA），并限制最多只能添加 15 位候选人。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vote(..)&lt;/code&gt;: &lt;strong&gt;投票&lt;/strong&gt;。任何用户都可以调用此函数为特定候选人投票。合约会首先验证投票是否在有效时间范围内，然后为投票者创建一个 &lt;code&gt;VoterReceipt&lt;/code&gt; 账户。这个回执账户的存在可以有效&lt;strong&gt;防止同一用户在同一次投票中重复投票&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="链上数据结构 (Accounts)"&gt;&lt;strong&gt;链上数据结构 (Accounts)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;为了支持上述功能，合约定义了三种类型的账户（Account）来存储状态：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PollAccount&lt;/code&gt;: &lt;strong&gt;投票账户&lt;/strong&gt;，存储单个投票活动的所有核心信息，是整个应用状态的中心。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CandidateAccount&lt;/code&gt;: &lt;strong&gt;候选人账户&lt;/strong&gt;，存储每个候选人的姓名和得票数，并关联到特定的 &lt;code&gt;PollAccount&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VoterReceipt&lt;/code&gt;: &lt;strong&gt;投票回执账户&lt;/strong&gt;，作为一个标记，记录一个用户（&lt;code&gt;voter&lt;/code&gt;）是否已参与了某次投票（&lt;code&gt;poll&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="关键设计与安全亮点"&gt;&lt;strong&gt;关键设计与安全亮点&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;这段代码一个值得注意的实现细节是使用了 &lt;code&gt;candidate_count&lt;/code&gt; 字段来辅助创建候选人账户。在 &lt;code&gt;add_candidate&lt;/code&gt; 指令中，新的 &lt;code&gt;CandidateAccount&lt;/code&gt; 是一个程序派生地址（PDA），其 &lt;code&gt;seeds&lt;/code&gt; 包含了这个计数器。&lt;/p&gt;

&lt;p&gt;这种设计模式比直接使用 &lt;code&gt;poll_account.candidates.len()&lt;/code&gt;（即候选人列表的长度）作为 &lt;code&gt;seed&lt;/code&gt; 更加&lt;strong&gt;安全和健壮&lt;/strong&gt;。因为账户数据（如 &lt;code&gt;Vec&lt;/code&gt; 的长度）在交易处理前可能被外部操纵，而使用一个在逻辑中&lt;strong&gt;手动递增的独立计数器&lt;/strong&gt;，可以确保 PDA 地址的生成是确定且无法被恶意利用的，这是 Solana 开发中一个重要的安全实践。&lt;/p&gt;
&lt;h3 id="Build 编译程序（合约）"&gt;Build 编译程序（合约）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 took 5.9s 
➜ make build-one &lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;voting
Building single program: &lt;span class="o"&gt;[&lt;/span&gt;voting]...
warning: profiles &lt;span class="k"&gt;for &lt;/span&gt;the non root package will be ignored, specify profiles at the workspace root:
package:   /Users/qiaopengjun/Code/Solana/voting/voting-substreams/voting_substreams/Cargo.toml
workspace: /Users/qiaopengjun/Code/Solana/voting/Cargo.toml
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.38s
warning: profiles &lt;span class="k"&gt;for &lt;/span&gt;the non root package will be ignored, specify profiles at the workspace root:
package:   /Users/qiaopengjun/Code/Solana/voting/voting-substreams/voting_substreams/Cargo.toml
workspace: /Users/qiaopengjun/Code/Solana/voting/Cargo.toml
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.38s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/target/debug/deps/voting-8013a2dc526371b8&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="编写测试 voting.ts"&gt;编写测试 &lt;code&gt;voting.ts&lt;/code&gt;
&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@coral-xyz/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="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;BN&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="s2"&gt;@coral-xyz/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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Voting&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="s2"&gt;../target/types/voting&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;assert&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="s2"&gt;chai&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;LAMPORTS_PER_SOL&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="s2"&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="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;voting&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;AnchorProvider&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;Voting&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Voting&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;pollAccount&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authority&lt;/span&gt; &lt;span class="o"&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="k"&gt;as&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;Wallet&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;voter1&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;voter2&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unauthorizedUser&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;txSignature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;latestBlockhash&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;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="nf"&gt;getLatestBlockhash&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;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="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;txSignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;latestBlockhash&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;airdrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;account&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;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="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;sig&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;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="nf"&gt;requestAirdrop&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;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&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;getCandidatePda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;pollKey&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;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="p"&gt;):&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="kr"&gt;number&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="k"&gt;return&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;candidate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;pollKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="c1"&gt;// 关键修复：合约中的 candidate_count 是 u8 (1字节)，这里必须匹配&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toArrayLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;le&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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="nx"&gt;program&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getReceiptPda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;pollKey&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;voterKey&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="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&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="k"&gt;return&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;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receipt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;pollKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;voterKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&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;programId&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;before&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voter1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voter2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unauthorizedUser&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;✅ Successfully initializes a poll&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Favorite Framework&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;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Which framework do you prefer?&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;startTime&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&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;endTime&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializePoll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchedPoll&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;pollAccount&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;pollAccount&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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Poll name does not match&lt;/span&gt;&lt;span class="dl"&gt;"&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;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;fetchedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;authority&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="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()&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;fetchedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Start time does not match&lt;/span&gt;&lt;span class="dl"&gt;"&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;fetchedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;End time does not match&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="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;✅ Successfully adds two candidates&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;0&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;tx1&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;React&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="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx1&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;candidatePda2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;1&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;tx2&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vue&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="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx2&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;fetchedPoll&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;pollAccount&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;pollAccount&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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;fetchedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Candidate count should be 2&lt;/span&gt;&lt;span class="dl"&gt;"&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;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;fetchedPoll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Candidate counter should be 2&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="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;✅ Two users vote successfully&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;0&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;candidatePda2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;1&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;receiptPda1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getReceiptPda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;pollAccount&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;voter1&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx1&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;voterReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;receiptPda1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voter1&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;voter1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx1&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;receiptPda2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getReceiptPda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;pollAccount&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;voter2&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx2&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;voterReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;receiptPda2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voter2&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;voter2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx2&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;candidate1&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;candidateAccount&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;candidatePda1&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;candidate2&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;candidateAccount&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;candidatePda2&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;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;candidate1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;React should have 2 votes&lt;/span&gt;&lt;span class="dl"&gt;"&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;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;candidate2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vue should have 0 votes&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="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;❌ Fails to vote twice (expected failure)&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="k"&gt;try&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;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;0&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;receiptPda1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getReceiptPda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;pollAccount&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;voter1&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;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;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;voterReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;receiptPda1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voter1&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;voter1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Double voting should have failed but succeeded&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;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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;already in use&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected error for already initialized account&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="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;❌ Unauthorized user fails to add candidate (expected failure)&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="k"&gt;try&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;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;2&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;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Svelte&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="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unauthorizedUser&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;unauthorizedUser&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized candidate addition should have failed&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;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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&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="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;❌ Fails to vote before poll starts (expected failure)&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;futurePoll&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTime&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600&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;endTime&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600&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;tx1&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializePoll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Future&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;futurePoll&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;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;futurePoll&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx1&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;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;futurePoll&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="mi"&gt;0&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;tx2&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Future Cand&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="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;futurePoll&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx2&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;receiptPda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getReceiptPda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;futurePoll&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;voter1&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;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;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;futurePoll&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;voterReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;receiptPda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voter1&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;voter1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Voting before poll start should have failed&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;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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PollNotStarted&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="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;❌ Fails to vote after poll ends (expected failure)&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;pastPoll&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTime&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7200&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;endTime&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3600&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;tx1&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializePoll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Past&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pastPoll&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;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;pastPoll&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx1&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;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pastPoll&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="mi"&gt;0&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;tx2&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Past Cand&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="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pastPoll&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx2&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;receiptPda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getReceiptPda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pastPoll&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;voter1&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;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;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pastPoll&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;voterReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;receiptPda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voter1&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;voter1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Voting after poll end should have failed&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;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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PollEnded&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="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;❌ Fails to add more than 15 candidates (expected failure)&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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;i&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;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cand &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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTx&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="k"&gt;try&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;candidatePda&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCandidatePda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollAccount&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="mi"&gt;15&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;methods&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCandidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cand 15&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="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;pollAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollAccount&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;candidateAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidatePda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&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;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;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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Adding more than 15 candidates should have failed&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;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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MaxCandidatesReached&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码是一个使用 Anchor 框架和 TypeScript 编写的&lt;strong&gt;综合性测试套件&lt;/strong&gt;，旨在全面验证 &lt;code&gt;voting&lt;/code&gt; 智能合约的正确性、安全性和健壮性。&lt;/p&gt;

&lt;p&gt;它通过模拟真实世界中的各种交互场景，系统性地覆盖了合约的&lt;strong&gt;“正常路径”&lt;/strong&gt;和&lt;strong&gt;“异常路径”&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;正常流程测试 (✅)&lt;/strong&gt;: 验证核心功能是否按预期工作，例如成功初始化投票、由管理员添加候选人、以及用户正常投票并使票数正确增加。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;异常与边界条件测试 (❌)&lt;/strong&gt;: 这是确保合约安全的关键。测试脚本故意尝试了多种会失败的操作，以确保合约的约束条件有效。这包括：

&lt;ul&gt;
&lt;li&gt;防止同一用户&lt;strong&gt;重复投票&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;阻止&lt;strong&gt;未经授权&lt;/strong&gt;的用户添加候选人。&lt;/li&gt;
&lt;li&gt;禁止在投票开始前或结束后进行投票。&lt;/li&gt;
&lt;li&gt;确保无法添加超过数量上限（15 个）的候选人。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过这种方式，该测试确保了合约不仅能在理想情况下运行，还能在面对错误操作或恶意攻击时表现出预期的、安全可靠的行为。&lt;/p&gt;
&lt;h3 id="测试程序（合约）"&gt;测试程序（合约）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[!&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 
➜ make test-program &lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;voting      
Testing program &lt;span class="o"&gt;[&lt;/span&gt;voting]...
warning: profiles &lt;span class="k"&gt;for &lt;/span&gt;the non root package will be ignored, specify profiles at the workspace root:
package:   /Users/qiaopengjun/Code/Solana/voting/voting-substreams/voting_substreams/Cargo.toml
workspace: /Users/qiaopengjun/Code/Solana/voting/Cargo.toml
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.45s
warning: profiles &lt;span class="k"&gt;for &lt;/span&gt;the non root package will be ignored, specify profiles at the workspace root:
package:   /Users/qiaopengjun/Code/Solana/voting/voting-substreams/voting_substreams/Cargo.toml
workspace: /Users/qiaopengjun/Code/Solana/voting/Cargo.toml
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.47s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/target/debug/deps/voting-8013a2dc526371b8&lt;span class="o"&gt;)&lt;/span&gt;

Running tests of program &lt;span class="sb"&gt;`&lt;/span&gt;voting&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

Running &lt;span class="nb"&gt;test &lt;/span&gt;suite: &lt;span class="s2"&gt;"/Users/qiaopengjun/Code/Solana/voting/Anchor.toml"&lt;/span&gt;

&lt;span class="o"&gt;(&lt;/span&gt;node:89327&lt;span class="o"&gt;)&lt;/span&gt; ExperimentalWarning: Type Stripping is an experimental feature and might change at any &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Use &lt;span class="sb"&gt;`&lt;/span&gt;node &lt;span class="nt"&gt;--trace-warnings&lt;/span&gt; ...&lt;span class="sb"&gt;`&lt;/span&gt; to show where the warning was created&lt;span class="o"&gt;)&lt;/span&gt;


  voting
    ✔ ✅ Successfully initializes a poll &lt;span class="o"&gt;(&lt;/span&gt;487ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ✅ Successfully adds two candidates &lt;span class="o"&gt;(&lt;/span&gt;980ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ✅ Two &lt;span class="nb"&gt;users &lt;/span&gt;vote successfully &lt;span class="o"&gt;(&lt;/span&gt;942ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ❌ Fails to vote twice &lt;span class="o"&gt;(&lt;/span&gt;expected failure&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ❌ Unauthorized user fails to add candidate &lt;span class="o"&gt;(&lt;/span&gt;expected failure&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ❌ Fails to vote before poll starts &lt;span class="o"&gt;(&lt;/span&gt;expected failure&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;928ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ❌ Fails to vote after poll ends &lt;span class="o"&gt;(&lt;/span&gt;expected failure&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;932ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✔ ❌ Fails to add more than 15 candidates &lt;span class="o"&gt;(&lt;/span&gt;expected failure&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;6211ms&lt;span class="o"&gt;)&lt;/span&gt;


  8 passing &lt;span class="o"&gt;(&lt;/span&gt;12s&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;mochawesome] Report JSON saved to /Users/qiaopengjun/Code/Solana/voting/mochawesome-report/mochawesome.json

&lt;span class="o"&gt;[&lt;/span&gt;mochawesome] Report HTML saved to /Users/qiaopengjun/Code/Solana/voting/mochawesome-report/mochawesome.html

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/1b9b3e5f-10e5-4087-bd91-5e0d57cbb80b.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="重置并同步投票合约 Program ID"&gt;&lt;strong&gt;重置并同步投票合约 Program ID&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 took 32.0s 
➜ &lt;span class="nb"&gt;rm &lt;/span&gt;target/deploy/voting-keypair.json 

voting on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 took 1m 5.9s 
➜ anchor keys &lt;span class="nb"&gt;sync
&lt;/span&gt;Found incorrect program &lt;span class="nb"&gt;id &lt;/span&gt;declaration &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"/Users/qiaopengjun/Code/Solana/voting/programs/voting/src/lib.rs"&lt;/span&gt;
Updated to Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz

Found incorrect program &lt;span class="nb"&gt;id &lt;/span&gt;declaration &lt;span class="k"&gt;in &lt;/span&gt;Anchor.toml &lt;span class="k"&gt;for &lt;/span&gt;the program &lt;span class="sb"&gt;`&lt;/span&gt;voting&lt;span class="sb"&gt;`&lt;/span&gt;
Updated to Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz

All program &lt;span class="nb"&gt;id &lt;/span&gt;declarations are synced.
Please rebuild the program to update the generated artifacts.
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="部署程序（合约）"&gt;部署程序（合约）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 
➜ make build-one &lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;voting
Building single program: &lt;span class="o"&gt;[&lt;/span&gt;voting]...
   Compiling voting v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/programs/voting&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;2.20s
   Compiling voting v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/programs/voting&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;1.25s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/target/debug/deps/voting-8013a2dc526371b8&lt;span class="o"&gt;)&lt;/span&gt;

voting on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 took 4.9s 
➜ make deploy &lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;devnet &lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;voting
Building all programs: voting]...
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.32s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.35s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/target/debug/deps/voting-8013a2dc526371b8&lt;span class="o"&gt;)&lt;/span&gt;
Deploying program &lt;span class="o"&gt;[&lt;/span&gt;voting] to cluster: devnet...
Deploying cluster: https://devnet.helius-rpc.com/?api-key&lt;span class="o"&gt;=&lt;/span&gt;a08565ed-9671-4cb4-8568-a014f810bfb2
Upgrade authority: /Users/qiaopengjun/.config/solana/id.json
Deploying program &lt;span class="s2"&gt;"voting"&lt;/span&gt;...
Program path: /Users/qiaopengjun/Code/Solana/voting/target/deploy/voting.so...
Program Id: Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz

Signature: f6YqqZ4qgd7VMhkP1VxCNxChRNKXjUaQgrNMG5Vju3Ko38WW2jsVCb4CRUZcX9DCTyXFiYv7pEuMFB86WgRkChM

Deploy success

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Anchor IDL 初始化：将合约接口发布到链"&gt;&lt;em&gt;Anchor IDL 初始化：将合约接口发布到链&lt;/em&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 took 24.8s 
➜ make idl-init &lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;devnet &lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;voting &lt;span class="nv"&gt;PROGRAM_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz                                            
Initializing IDL &lt;span class="k"&gt;for &lt;/span&gt;program &lt;span class="o"&gt;[&lt;/span&gt;voting] with ID &lt;span class="o"&gt;[&lt;/span&gt;Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz] on cluster: devnet...
Idl data length: 834 bytes
Step 0/834 
Step 600/834 
Idl account created: 2iKdnacc51zj5j3iQTRNiYPYB1tvuM6HBzQWr9h1akAm

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="归档并保存 Anchor 程序的 IDL 文件到本地"&gt;归档并保存 Anchor 程序的 IDL 文件到本地&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;voting on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.88.0 took 17.8s 
➜ make archive-idl &lt;span class="nv"&gt;PROGRAM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;voting                                                                    
Building all programs: voting]...
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.36s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.36s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/voting/target/debug/deps/voting-8013a2dc526371b8&lt;span class="o"&gt;)&lt;/span&gt;
Archiving IDL &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;voting]...
IDL &lt;span class="k"&gt;for &lt;/span&gt;voting successfully archived to idls/voting/voting-2025-07-18-093219.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段操作的作用是&lt;strong&gt;将当前 Anchor 程序的 IDL 文件归档保存到本地&lt;/strong&gt;，并按时间戳进行版本管理。&lt;/p&gt;
&lt;h4 id="说明"&gt;说明&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;该命令会将 &lt;code&gt;target/idl/$(PROGRAM).json&lt;/code&gt; 文件复制到 &lt;code&gt;idls/$(PROGRAM)/&lt;/code&gt; 目录下，并以时间戳命名，便于历史版本管理和回溯。&lt;/li&gt;
&lt;li&gt;适用于需要保存每次构建生成的 IDL 文件，方便后续查阅或回滚。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="查看合约"&gt;查看合约&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://solscan.io/account/Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/account/Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/Paxon/90dd9f59-d825-4f83-9884-9caa694222b6.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;恭喜你！跟随本教程，我们已经成功地从一个空目录开始，完整地实现、测试并部署了一个功能齐全的 Solana 链上投票应用。我们不仅学习了 Anchor 框架从初始化、编码、测试到部署的完整工作流，更深入探讨了像&lt;strong&gt;程序派生地址（PDA）的安全使用&lt;/strong&gt;、&lt;strong&gt;编写覆盖多种边界条件的测试用例&lt;/strong&gt;等关键的实战技巧。&lt;/p&gt;

&lt;p&gt;这整个过程清晰地展示了现代 Solana DApp 开发的标准化路径，证明了只要跟随正确的步骤，构建一个安全、健壮的链上应用并非遥不可及。&lt;/p&gt;

&lt;p&gt;希望这次实战能为你打开 Solana 开发的大门。现在，你可以尝试基于这个项目进行扩展，比如增加计费功能、设置更复杂的投票规则，或者深入研究文末的参考资料，继续你的 Web3 探索之旅！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.substreams.dev/reference-material/substreams-cli/installing-the-cli" rel="nofollow" target="_blank"&gt;https://docs.substreams.dev/reference-material/substreams-cli/installing-the-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/streamingfast/substreams-starter" rel="nofollow" target="_blank"&gt;https://github.com/streamingfast/substreams-starter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.substreams.dev/tutorials/intro-to-tutorials/on-solana/solana" rel="nofollow" target="_blank"&gt;https://docs.substreams.dev/tutorials/intro-to-tutorials/on-solana/solana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thegraph.com/docs/en/substreams/quick-start/" rel="nofollow" target="_blank"&gt;https://thegraph.com/docs/en/substreams/quick-start/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/enoldev/solana-voting-app-sps/tree/main" rel="nofollow" target="_blank"&gt;https://github.com/enoldev/solana-voting-app-sps/tree/main&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.substreams.dev/how-to-guides/sinks/sql" rel="nofollow" target="_blank"&gt;https://docs.substreams.dev/how-to-guides/sinks/sql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/streamingfast/substreams-sink-sql" rel="nofollow" target="_blank"&gt;https://github.com/streamingfast/substreams-sink-sql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buf.build/product/cli" rel="nofollow" target="_blank"&gt;https://buf.build/product/cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/account/Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/account/Doo2arLUifZbfqGVS5Uh7nexAMmsMzaQH5zcwZhSoijz?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Tue, 29 Jul 2025 16:42:03 +0800</pubDate>
      <link>https://soldev.cn/topics/199</link>
      <guid>https://soldev.cn/topics/199</guid>
    </item>
    <item>
      <title>Solana 智能合约终极部署指南：从入门到主网，定制你的专属靓号 Program ID</title>
      <description>&lt;h2 id="Solana 智能合约终极部署指南：从入门到主网，定制你的专属靓号 Program ID"&gt;&lt;strong&gt;Solana 智能合约终极部署指南：从入门到主网，定制你的专属靓号 Program ID&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;保姆级教程：基于 Anchor 框架，带你丝滑完成密钥管理、多环境部署与实战避坑&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;还在为 Solana 合约部署的繁琐流程而头疼吗？还在羡慕别人的项目拥有一个以项目名开头、炫酷又易记的合约地址（Program ID）吗？&lt;/p&gt;

&lt;p&gt;本篇文章将为你揭开 Solana 开发的神秘面纱，通过强大的 Anchor 框架，手把手带你从零开始，不仅能轻松部署你的第一个智能合约，更能掌握定制专属靓号地址的独家秘笈！无论你是 Solana 开发新手，还是希望优化部署流程的资深玩家，这篇“保姆级”实操教程都将是你不可错过的宝典。让我们一起告别随机生成的无序地址，让你的项目从一个优雅的 Program ID 开始！&lt;/p&gt;

&lt;p&gt;本文是一篇面向 Solana 开发者的全流程实战指南，详细介绍了如何使用 Anchor 框架部署智能合约，并重点讲解了如何为合约设置一个自定义的“靓号”地址（Program ID）。&lt;/p&gt;

&lt;p&gt;文章内容覆盖了从生成一个具有特定前缀（如 &lt;code&gt;Red...&lt;/code&gt;）的靓号密钥对，到如何将其正确配置到 Anchor 项目的 &lt;code&gt;lib.rs&lt;/code&gt; 和 &lt;code&gt;Anchor.toml&lt;/code&gt; 文件中的每一步。此外，教程还深入探讨了开发者必备的密钥管理技巧，包括如何从 Phantom 等钱包导出私钥，并生成与 &lt;code&gt;solana-cli&lt;/code&gt; 兼容的 &lt;code&gt;.json&lt;/code&gt; 密钥文件，同时清晰地辨析了 &lt;code&gt;solana-keygen&lt;/code&gt; 和 &lt;code&gt;solana address&lt;/code&gt; 命令的差异与适用场景。&lt;/p&gt;

&lt;p&gt;最后，文章通过本地网络（Localnet）和开发网络（Devnet）的真实部署案例，演示了完整的部署流程，并指出了在本地环境中可能遇到的权限问题及其解决方案，确保读者能够顺利地将自己的合约发布到 Solana 的公共网络上。&lt;/p&gt;
&lt;h2 id="自定义地址（靓号地址）设置成项目合约地址（Program ID）实操 （可省略）"&gt;自定义地址（靓号地址）设置成项目合约地址（Program ID）实操（可省略）&lt;/h2&gt;&lt;h3 id="第一步：创建一个以 Red 开头的、易于识别的程序ID (Program ID)"&gt;第一步：创建一个以 &lt;code&gt;Red&lt;/code&gt; 开头的、易于识别的程序 ID (Program ID)&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ solana-keygen grind &lt;span class="nt"&gt;--starts-with&lt;/span&gt; Red:1
Searching with 12 threads &lt;span class="k"&gt;for&lt;/span&gt;:
        1 pubkey that starts with &lt;span class="s1"&gt;'Red'&lt;/span&gt; and ends with &lt;span class="s1"&gt;''&lt;/span&gt;
Searched 1000000 keypairs &lt;span class="k"&gt;in &lt;/span&gt;1s. 0 matches found.
Searched 2000000 keypairs &lt;span class="k"&gt;in &lt;/span&gt;2s. 0 matches found.
Searched 3000000 keypairs &lt;span class="k"&gt;in &lt;/span&gt;4s. 0 matches found.
Searched 4000000 keypairs &lt;span class="k"&gt;in &lt;/span&gt;6s. 0 matches found.
Searched 5000000 keypairs &lt;span class="k"&gt;in &lt;/span&gt;7s. 0 matches found.
Wrote keypair to RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1.json

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;solana-keygen grind&lt;/code&gt; 命令生成以特定前缀开头的 Solana 密钥对&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;solana-keygen new&lt;/code&gt; 和 &lt;code&gt;solana-keygen grind&lt;/code&gt; 生成的都是通用的密钥对。这个密钥对既可以被当成钱包来接收资产，也可以被用来部署合约而成为合约地址。它到底是什么，完全取决于你如何去使用它。&lt;/p&gt;
&lt;h3 id="第二步：替换文件"&gt;第二步：替换文件&lt;/h3&gt;
&lt;p&gt;将您用 &lt;code&gt;grind&lt;/code&gt; 生成的靓号密钥对文件，复制并重命名，覆盖掉 &lt;code&gt;target/deploy/&lt;/code&gt; 目录下的那个临时文件。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1.json ../target/deploy/red_packet-keypair.json
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="第三步：查询程序ID"&gt;第三步：查询程序 ID&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ solana address &lt;span class="nt"&gt;-k&lt;/span&gt; ./target/deploy/red_packet-keypair.json 
RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="第四步： 更新 lib.rs 文件"&gt;第四步： &lt;strong&gt;更新 &lt;code&gt;lib.rs&lt;/code&gt; 文件&lt;/strong&gt;
&lt;/h3&gt;
&lt;p&gt;从你的靓号密钥对中复制出&lt;strong&gt;公钥（地址）&lt;/strong&gt;，然后粘贴到 &lt;code&gt;src/lib.rs&lt;/code&gt; 文件顶部的 &lt;code&gt;declare_id!&lt;/code&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;"RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="第五步：更新 Anchor.toml 文件"&gt;第五步：更新 &lt;code&gt;Anchor.toml&lt;/code&gt; 文件&lt;/h3&gt;
&lt;p&gt;将新地址更新到项目配置文件中。这能确保你的测试和客户端代码能正确找到程序。&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="err"&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;toml&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;programs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mainnet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;red_packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;programs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devnet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;red_packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;programs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localnet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;red_packet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RedGJbeNejUtP6vMEPDkG55yRf7oAbkMFGeDjXaNfe1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="使用脚本生成 Solana 密钥文件"&gt;使用脚本生成 Solana 密钥文件&lt;/h2&gt;
&lt;p&gt;默认情况下，&lt;code&gt;anchor&lt;/code&gt; 和 &lt;code&gt;solana&lt;/code&gt; 的命令行工具会使用您系统默认的钱包地址，通常位于 &lt;code&gt;~/.config/solana/id.json&lt;/code&gt;。这个钱包地址主要用于&lt;strong&gt;支付部署和交易的 Gas 费&lt;/strong&gt;，并且会成为您部署的程序的&lt;strong&gt;升级权限管理者&lt;/strong&gt; (Upgrade Authority)。&lt;/p&gt;
&lt;h3 id="第一步：从 Phantom 钱包导出私钥"&gt;第一步：从 Phantom 钱包导出私钥&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
A[开始] --&amp;gt; B[设置]
B --&amp;gt; C[管理账户]
C --&amp;gt; D[选择账户]
D --&amp;gt; E[显示私钥]
E --&amp;gt; F[输入密码]
F --&amp;gt; G[勾选 同意风险警告 继续]
G --&amp;gt; H[复制私钥]
H --&amp;gt; I[结束]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/e460ecc9-a4ec-4700-ad10-5e860d235f9e.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/Paxon/464da36a-fb62-40cb-979c-3d70ad23e0ab.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="第二步：使用脚本生成密钥文件（如有可忽略）"&gt;第二步：使用脚本生成密钥文件（如有可忽略）&lt;/h3&gt;
&lt;p&gt;从已有的私钥生成 Solana 和 Anchor 工具所需要的 &lt;code&gt;.json&lt;/code&gt; 密钥文件。&lt;/p&gt;
&lt;h4 id="创建项目并初始化"&gt;创建项目并初始化&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;solana-wallet-demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;solanan-wallet-demo
pnpm init
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="安装依赖"&gt;安装依赖&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @solana/web3.js bs58
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="create-wallet.ts 文件"&gt;create-wallet.ts 文件&lt;/h4&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;bs58&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bs58&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;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// --- 步骤 1: 在这里粘贴你的 Base58 格式私钥 ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;privateKeyBase58&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5xcxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;&lt;span class="dl"&gt;"&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="c1"&gt;// 步骤 2: 将 Base58 私钥解码为字节数组 (Uint8Array)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secretKeyBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bs58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;privateKeyBase58&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 步骤 3: 从解码后的密钥创建 Keypair 对象&lt;/span&gt;
  &lt;span class="c1"&gt;// Keypair.fromSecretKey 会自动处理，无论输入是32字节的纯私钥还是64字节的密钥对&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keypair&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;fromSecretKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretKeyBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 步骤 4: 将完整的密钥对（64字节）转换为数组格式&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keypairArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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="nx"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 步骤 5: 将数组写入 .json 文件&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.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="nx"&gt;fileName&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;keypairArray&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="s2"&gt;`✅ 文件 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`🔑 你的钱包地址 (公钥) 是: &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="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBase58&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&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;请验证这个地址是否与您预期的地址一致。&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ 生成失败，请检查你的私钥格式是否正确。&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ 生成失败，请检查你的私钥格式是否正确。&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;h4 id="执行脚本生成密钥文件"&gt;执行脚本生成密钥文件&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;Code/Solana/solana-wallet-demo is 📦 1.0.0 via ⬢ v23.11.0 on 🐳 v28.2.2 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; 
➜ ts-node src/create-wallet.ts 
✅ 文件 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json 已成功生成!
🔑 你的钱包地址 &lt;span class="o"&gt;(&lt;/span&gt;公钥&lt;span class="o"&gt;)&lt;/span&gt; 是: 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD
请验证这个地址是否与您预期的地址一致。

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;脚本执行后，同样会生成一个 &lt;code&gt;.json&lt;/code&gt; 文件，并且会打印出对应的钱包地址（公钥），您可以用来核对是否正确。&lt;/p&gt;
&lt;h3 id="第三步：查看确认钱包地址"&gt;第三步：查看确认钱包地址&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;Code/Solana/solana-wallet-demo is 📦 1.0.0 via ⬢ v23.11.0 on 🐳 v28.2.2 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; 
➜ solana-keygen pubkey 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json
6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD

Code/Solana/solana-wallet-demo is 📦 1.0.0 via ⬢ v23.11.0 on 🐳 v28.2.2 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; 
➜ solana address &lt;span class="nt"&gt;-k&lt;/span&gt; 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json   
6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简单来说：&lt;code&gt;solana-keygen&lt;/code&gt; 是一个&lt;strong&gt;专科医生&lt;/strong&gt;，只负责处理密钥对；而 &lt;code&gt;solana&lt;/code&gt; 是一个&lt;strong&gt;全科医生&lt;/strong&gt;，负责所有与链上交互的事情，查询地址只是它众多功能中的一项。&lt;/p&gt;

&lt;p&gt;下面我们用一个表格来详细对比：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;&lt;code&gt;solana-keygen pubkey ...&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;solana address -k ...&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;工具归属&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;solana-keygen&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;solana&lt;/code&gt; (主命令行工具)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;核心职责&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;密钥对管理工具&lt;/strong&gt; 纯粹的密码学操作&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;全功能网络交互客户端&lt;/strong&gt; 与链上状态和配置打交道&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;核心功能&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;从一个密钥对文件中&lt;strong&gt;提取并显示公钥&lt;/strong&gt;。它只关心文件本身，不关心任何外部配置。&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;显示钱包地址&lt;/strong&gt;。它的主要工作是显示你&lt;strong&gt;当前配置的默认钱包&lt;/strong&gt;的地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;灵活性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;不灵活&lt;/strong&gt;。必须显式提供一个密钥对文件的路径作为参数。&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;非常灵活&lt;/strong&gt;。 1. &lt;strong&gt;不带 &lt;code&gt;-k&lt;/code&gt;&lt;/strong&gt;：直接运行 &lt;code&gt;solana address&lt;/code&gt;，会显示你默认钱包的地址。&lt;br&gt;2. &lt;strong&gt;带 &lt;code&gt;-k&lt;/code&gt;&lt;/strong&gt;：使用 &lt;code&gt;-k&lt;/code&gt; 参数可以&lt;strong&gt;临时覆盖&lt;/strong&gt;默认设置，查询指定文件的地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h3 id="深入解析"&gt;深入解析&lt;/h3&gt;&lt;h4 id="solana-keygen pubkey：密码学专家"&gt;
&lt;code&gt;solana-keygen pubkey&lt;/code&gt;：密码学专家&lt;/h4&gt;
&lt;p&gt;您可以把 &lt;code&gt;solana-keygen&lt;/code&gt; 看作一个独立的、专注于密码学的工具箱。它的所有功能都围绕着&lt;strong&gt;密钥对（Keypair）&lt;/strong&gt;的创建、恢复、查看和签名等本地操作。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;它的世界里只有“密钥对”&lt;/strong&gt;：当您运行 &lt;code&gt;solana-keygen pubkey&lt;/code&gt; 时，它做的事情非常纯粹：

&lt;ol&gt;
&lt;li&gt;读取您提供的 &lt;code&gt;.json&lt;/code&gt; 文件。&lt;/li&gt;
&lt;li&gt;从文件中的 64 字节数据里，解析出后 32 字节的公钥。&lt;/li&gt;
&lt;li&gt;将公钥进行 Base58 编码后打印出来。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;它完全不关心您的 Solana CLI 配置，也不知道哪个是您的“默认钱包”。它就像一个只会开锁和验锁的锁匠，你给它一把钥匙（私钥文件），它就告诉你这把钥匙对应的锁头（公钥）长什么样。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="solana address：全能的钱包管家"&gt;
&lt;code&gt;solana address&lt;/code&gt;：全能的钱包管家&lt;/h4&gt;
&lt;p&gt;您可以把 &lt;code&gt;solana&lt;/code&gt; 看作是您与 Solana 网络交互的“瑞士军刀”。它的所有操作都默认与您&lt;strong&gt;当前设置的钱包&lt;/strong&gt;相关联。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;它的世界里有“默认钱包”的概念&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;当您直接运行 &lt;code&gt;solana address&lt;/code&gt; 时，它会去查找您的 Solana 配置文件，找到您设置的默认钱包路径（通常是 &lt;code&gt;~/.config/solana/id.json&lt;/code&gt;），然后显示那个钱包的地址。这是它最主要的用法。&lt;/li&gt;
&lt;li&gt;当您使用 &lt;code&gt;-k&lt;/code&gt; 或 &lt;code&gt;--keypair&lt;/code&gt; 参数时，您是在对它说：“&lt;strong&gt;先别管我的默认钱包，请临时帮我看一下这个特定文件的地址是什么&lt;/strong&gt;”。所以，&lt;code&gt;-k&lt;/code&gt; 是一个临时的“覆盖”选项。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="使用场景总结"&gt;使用场景总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;什么时候用 &lt;code&gt;solana-keygen pubkey&lt;/code&gt;？&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;当你在编写脚本，需要一个简单、无依赖、纯粹的方式从文件中获取公钥时。&lt;/li&gt;
&lt;li&gt;当你只想进行本地密钥操作，不想与任何 Solana 配置或网络状态扯上关系时。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;什么时候用 &lt;code&gt;solana address&lt;/code&gt;？&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;当你想快速查看&lt;strong&gt;自己当前默认钱包的地址&lt;/strong&gt;时（直接运行 &lt;code&gt;solana address&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;当你想在一个“钱包管理”的语境下，临时查看另一个密钥文件的地址时（使用 &lt;code&gt;-k&lt;/code&gt;）。这是日常开发和查询中最常用的命令。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="结论："&gt;结论：&lt;/h4&gt;
&lt;p&gt;虽然殊途同归，但 solana-keygen pubkey 是一个更底层、更纯粹的密码学工具，而 solana address 是一个更高层、面向用户钱包配置的便捷工具。您这次的用法正好命中了它们功能的交集，所以得到了相同的结果。&lt;/p&gt;
&lt;h2 id="通过助记词生成 Solana 密钥文件"&gt;&lt;strong&gt;通过助记词生成 Solana 密钥文件&lt;/strong&gt;&lt;/h2&gt;&lt;h4 id="通过助记词生成 Solana 密钥对："&gt;
&lt;strong&gt;通过助记词生成 Solana 密钥对&lt;/strong&gt;：&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-keygen recover &lt;span class="nt"&gt;-o&lt;/span&gt; custom-wallet.json
&lt;span class="o"&gt;[&lt;/span&gt;recover] seed phrase: 
&lt;span class="o"&gt;[&lt;/span&gt;recover] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to &lt;span class="k"&gt;continue&lt;/span&gt;: 
Recovered pubkey &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="s2"&gt;"6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Continue? &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt;: 
y
Wrote recovered keypair to custom-wallet.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按提示输入助记词。&lt;/p&gt;
&lt;h4 id="根据您的 ls -l 输出，custom-wallet.json 文件权限和属性显示如下："&gt;根据您的 &lt;code&gt;ls -l&lt;/code&gt; 输出，&lt;code&gt;custom-wallet.json&lt;/code&gt; 文件权限和属性显示如下：&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; custom-wallet.json 
&lt;span class="nt"&gt;-rw-------&lt;/span&gt;@ 1 qiaopengjun  staff  226 Jun 29 13:46 custom-wallet.json
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="查看文件内容"&gt;查看文件内容&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;custom-wallet.json
&lt;span class="o"&gt;[&lt;/span&gt;4,70,183,17, ... 230,246]%                                                                                   
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="验证密钥对"&gt;验证密钥对&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-keygen verify 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD custom-wallet.json
Verification &lt;span class="k"&gt;for &lt;/span&gt;public key: 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD: Success
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据您的验证结果，&lt;code&gt;custom-wallet.json&lt;/code&gt; 文件中的密钥对已成功通过验证，对应的公钥为 &lt;code&gt;6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id="从密钥文件 custom-wallet.json 中提取并显示对应的 Solana 公钥地址"&gt;&lt;strong&gt;从密钥文件 &lt;code&gt;custom-wallet.json&lt;/code&gt; 中提取并显示对应的 Solana 公钥地址&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana address &lt;span class="nt"&gt;-k&lt;/span&gt; custom-wallet.json 
6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="查询指定密钥文件对应的 Solana 区块链账户信息"&gt;&lt;strong&gt;查询指定密钥文件对应的 Solana 区块链账户信息&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana account custom-wallet.json

Public Key: 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD
Balance: 102.70584242 SOL
Owner: 11111111111111111111111111111111
Executable: &lt;span class="nb"&gt;false
&lt;/span&gt;Rent Epoch: 18446744073709551615

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="指定钱包地址部署 Solana 合约"&gt;指定钱包地址部署 Solana 合约&lt;/h2&gt;&lt;h3 id="修改配置文件"&gt;修改配置文件&lt;/h3&gt;
&lt;p&gt;直接修改项目根目录下的 &lt;code&gt;Anchor.toml&lt;/code&gt; 文件。&lt;/p&gt;

&lt;p&gt;在文件末尾找到或添加 &lt;code&gt;[provider]&lt;/code&gt; 部分，并指定 &lt;code&gt;wallet&lt;/code&gt; 的路径。&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[provider]&lt;/span&gt;
&lt;span class="py"&gt;cluster&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localnet"&lt;/span&gt;
&lt;span class="c"&gt;# wallet = "~/.config/solana/id.json"&lt;/span&gt;
&lt;span class="py"&gt;wallet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/Users/qiaopengjun/Code/Solana/solana-wallet-demo/6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="本地部署"&gt;本地部署&lt;/h2&gt;&lt;h3 id="第一步：启动本地节点"&gt;第一步：启动本地节点&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ anchor localnet 
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.12s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.12s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/.../target/debug/deps/red_packet-5c51beafed6b9bf6&lt;span class="o"&gt;)&lt;/span&gt;
Ledger location: .anchor/test-ledger
Log: .anchor/test-ledger/validator.log
⠤ Initializing...                                                                                                                           Waiting &lt;span class="k"&gt;for &lt;/span&gt;fees to stabilize 1...
Identity: HMfJQKak1CZc23oSnJbdNC7UjF4RoHDLuP3k2J5QLxW5
Genesis Hash: 4Rw2D9RRqHrv1zagY8GgsLSfJDNSFdoWsBuEMoJny3Zo
Version: 2.2.19
Shred Version: 38176
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
WebSocket PubSub URL: ws://127.0.0.1:8900
⠤ 00:00:17 | Processed Slot: 37 | Confirmed Slot: 37 | Finalized Slot: 6 | Full Snapshot Slot: - | Incremental Snapshot Slot: - | Transactions                       
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="第二步： 部署合约"&gt;第二步：部署合约&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 本地部署报错&lt;/span&gt;
➜ anchor deploy &lt;span class="nt"&gt;--provider&lt;/span&gt;.cluster localnet 
Deploying cluster: http://127.0.0.1:8899
Upgrade authority: /Users/qiaopengjun/Code/Solana/solana-wallet-demo/6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json
Deploying program &lt;span class="s2"&gt;"red_packet"&lt;/span&gt;...
Program path: /Users/.../target/deploy/red_packet.so...
Error: Program&lt;span class="s1"&gt;'s authority Some(11111111111111111111111111111111) does not match authority provided 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD
There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.

# 删除
➜ rm -f target/deploy/red_packet-keypair.json            

# 查看
➜ anchor keys list                         
red_packet: h5cjZ2zEE64kvwMahe1u4PEKy8pZJ9qHmqD7z1VvZz3

# 同步
➜ anchor keys sync
Found incorrect program id declaration in "/Users/.../programs/red_packet/src/lib.rs"
Updated to h5cjZ2zEE64kvwMahe1u4PEKy8pZJ9qHmqD7z1VvZz3

Found incorrect program id declaration in Anchor.toml for the program `red_packet`
Updated to h5cjZ2zEE64kvwMahe1u4PEKy8pZJ9qHmqD7z1VvZz3

All program id declarations are synced.
Please rebuild the program to update the generated artifacts.

# 部署成功
➜ anchor deploy                              
Deploying cluster: http://127.0.0.1:8899
Upgrade authority: /Users/qiaopengjun/Code/Solana/solana-wallet-demo/6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json
Deploying program "red_packet"...
Program path: /Users/.../target/deploy/red_packet.so...
Program Id: h5cjZ2zEE64kvwMahe1u4PEKy8pZJ9qHmqD7z1VvZz3

Signature: FEPLTofdZEcjQ6jpaQQ25BTQT9VDeTTqAWF6VeDCdnB6qpgH62KvoQgDuxvm7RUd6Nr6EAaDmK2WWGdZCgTKakV

Deploy success
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：在本地测试网络 (&lt;code&gt;localnet&lt;/code&gt;) 中，使用自定义（靓号）程序地址进行部署时，可能会因罕见的本地验证器环境冲突，而导致权限不匹配的部署失败。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为规避本地环境冲突，本地开发使用随机地址，仅在部署到 Devnet/Mainnet 时替换为自定义地址。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="开发测试网 Devnet 部署"&gt;开发测试网 Devnet 部署&lt;/h2&gt;&lt;h3 id="第一步：修改配置文件 cluster = "&gt;第一步：修改配置文件 cluster = "devnet"&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;toolchain]
package_manager &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"yarn"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;features]
resolution &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;skip-lint &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;programs.devnet]
red_packet &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;programs.localnet]
red_packet &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;programs.mainnet]
red_packet &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;registry]
url &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.apr.dev"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;provider]
cluster &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"devnet"&lt;/span&gt;
wallet &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~/Code/Solana/solana-wallet-demo/6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;scripts]
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="第二步：部署实操"&gt;第二步：部署实操&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 以下操作在项目根目录下执行&lt;/span&gt;
➜ anchor keys &lt;span class="nb"&gt;sync
&lt;/span&gt;All program &lt;span class="nb"&gt;id &lt;/span&gt;declarations are synced.

➜ anchor keys list
red_packet: Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF

➜ anchor build                             
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.44s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.39s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/.../target/debug/deps/red_packet-5c51beafed6b9bf6&lt;span class="o"&gt;)&lt;/span&gt;

➜ &lt;span class="nb"&gt;source&lt;/span&gt; .env                              

➜ anchor deploy &lt;span class="nt"&gt;--provider&lt;/span&gt;.cluster &lt;span class="nv"&gt;$RPC_URL&lt;/span&gt;
Deploying cluster: https://devnet.helius-rpc.com/?api-key&lt;span class="o"&gt;=&lt;/span&gt;...
Upgrade authority: /Users/qiaopengjun/Code/Solana/solana-wallet-demo/6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD.json
Deploying program &lt;span class="s2"&gt;"red_packet"&lt;/span&gt;...
Program path: /Users/.../target/deploy/red_packet.so...
Program Id: Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF

Signature: 2NGxuVMaBzrsbePk37PniEJWzqTF19qrngeL7xNZXhRCRSYEprqr5hNLXgvaJDBGed8DBpp94rKurV7vJPcKBT4Y

Deploy success
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;终于成功地将合约部署到了公开开发网（Devnet），而且还成功地使用了您想要的&lt;strong&gt;自定义靓号地址&lt;/strong&gt; (&lt;code&gt;Red9fb...&lt;/code&gt;)！&lt;/p&gt;

&lt;p&gt;&lt;a href="https://solscan.io/account/Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/account/Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF?cluster=devnet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;主网 (Mainnet) 部署和 Devnet 一样，只需修改配置文件 Anchor.toml 中 &lt;code&gt;cluster = "mainnet"&lt;/code&gt;即可。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;经过本文的逐步引导，相信您已经成功掌握了在 Solana 生态中使用 Anchor 框架部署智能合约的全套流程。&lt;/p&gt;

&lt;p&gt;我们从一个炫酷的实战技巧——&lt;strong&gt;生成并配置自定义靓号 Program ID&lt;/strong&gt;——开始，让您的项目在链上拥有独一无二的身份标识。接着，我们深入探讨了&lt;strong&gt;钱包密钥的管理与使用&lt;/strong&gt;，确保您能灵活地将现有钱包作为部署和升级权限的管理方。&lt;/p&gt;

&lt;p&gt;在部署实操部分，我们不仅走通了&lt;strong&gt;本地（Localnet）和开发网（Devnet）的部署&lt;/strong&gt;，更重要的是，我们点出了本地部署中可能遇到的 &lt;code&gt;authority&lt;/code&gt; 权限不匹配问题，并给出了 &lt;code&gt;anchor keys sync&lt;/code&gt; 等命令的解决方案。这正是新手在实践中最容易卡住的环节。&lt;/p&gt;

&lt;p&gt;总而言之，一个成功的 Solana 合约部署 = &lt;strong&gt;清晰的项目配置 + 正确的密钥管理 + 熟悉的环境部署命令&lt;/strong&gt;。现在，您不仅拥有了将项目发布到 Devnet 的能力，更可以充满信心地将 &lt;code&gt;cluster&lt;/code&gt; 修改为 &lt;code&gt;mainnet&lt;/code&gt;，向着主网的星辰大海迈出坚实的一步！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs/programs/rust" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs/programs/rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs/quickstart/local" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs/quickstart/local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/account/Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/account/Red9fb6BSry45DQfDFzwpp55JVV22VAZJtMofStXHBF?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Mon, 30 Jun 2025 19:10:35 +0800</pubDate>
      <link>https://soldev.cn/topics/194</link>
      <guid>https://soldev.cn/topics/194</guid>
    </item>
    <item>
      <title>Web3 实战：Solana CPI 全解析，从 Anchor 封装到 PDA 转账</title>
      <description>&lt;h2 id="Web3实战：Solana CPI全解析，从Anchor封装到PDA转账"&gt;Web3 实战：Solana CPI 全解析，从 Anchor 封装到 PDA 转账&lt;/h2&gt;
&lt;p&gt;Web3 时代，Solana 凭借其高吞吐量和低成本成为区块链开发的热门选择。跨程序调用（CPI）作为 Solana 智能合约的“超级连接器”，让程序像搭积木一样实现模块化协作，赋能 DeFi、NFT 等复杂应用。本文通过一个基于 Anchor 框架的实战项目，带你从高阶封装到低阶手动构建，再到 PDA 账户的独特操作，全面解析 Solana CPI 的开发精髓。无论你是 Web3 新手还是资深开发者，这篇教程将助你快速上手，解锁 Solana 的无限可能！&lt;/p&gt;

&lt;p&gt;本文通过一个 Anchor 框架开发的 Solana 智能合约项目，深入剖析跨程序调用（CPI）的核心原理与实现方式，覆盖五种 SOL 转账范式：Anchor 高阶封装、原生指令调用、手动指令构建及 PDA 账户转账。结合详细代码解析、Mermaid 流程图和测试用例，展示了 CPI 在模块化开发中的强大优势。测试结果验证了功能的稳定性，为开发者提供从理论到实践的完整指引。无论你想快速上手 Web3 开发还是深入探索 Solana CPI 的底层机制，这篇实战教程都不可错过！&lt;/p&gt;
&lt;h2 id="Cross Program Invocation"&gt;Cross Program Invocation&lt;/h2&gt;
&lt;p&gt;Solana 的跨程序调用（CPI）机制允许智能合约像拼积木一样相互调用，实现链上功能的模块化组合：例如您的 DeFi 程序只需通过标准接口（指定目标程序 ID、账户列表和指令数据），即可安全调用系统程序完成 SOL 转账，或通过程序派生地址（PDA）签名让无私钥的合约账户自主执行资产转移，这种设计使开发者能够构建多层嵌套的复杂金融操作（如闪电贷中借贷→交易→还款的原子执行），同时 Anchor 框架的三层抽象实现（高级 CpiContext→中级 invoke→底层手动构建）为不同场景提供了从快速开发到性能优化的完整解决方案，真正实现了区块链功能的即插即用式组合创新。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CPI 就是让智能合约像叫外卖一样点其他合约的功能&lt;/strong&gt;——你（用户）下单给合约 A（比如借贷合约），合约 A 转头就叫了合约 B（比如交易合约）的服务，合约 B 可能再叫系统合约付钱，整个过程一次搞定！&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant 用户
    participant 合约A
    participant 合约B
    participant 系统合约

    用户-&amp;gt;&amp;gt;合约A: 发起交易（如借款）
    合约A-&amp;gt;&amp;gt;合约B: CPI调用（如兑换代币）
    合约B-&amp;gt;&amp;gt;系统合约: 嵌套CPI（如转账结算）
    Note right of 系统合约: 最&amp;lt;br&amp;gt;终&amp;lt;br&amp;gt;执&amp;lt;br&amp;gt;行
    系统合约--&amp;gt;&amp;gt;合约B: 完成！
    合约B--&amp;gt;&amp;gt;合约A: 完成！
    合约A--&amp;gt;&amp;gt;用户: 全部搞定！
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/d7240816-b961-4dd5-a6ee-1c169417504b.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;💡 就像点外卖：你（用户）→ 平台（合约 A）→ 餐厅（合约 B）→ 骑手（系统合约），一个订单串联多个服务！&lt;/p&gt;
&lt;h2 id="实操"&gt;实操&lt;/h2&gt;&lt;h3 id="创建并初始化项目"&gt;创建并初始化项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor init cpi-demo
yarn &lt;span class="nb"&gt;install &lt;/span&gt;v1.22.22
info No lockfile found.
&lt;span class="o"&gt;[&lt;/span&gt;1/4] 🔍  Resolving packages...
warning mocha &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; glob@7.2.0: Glob versions prior to v9 are no longer supported
warning mocha &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; glob &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache &lt;span class="k"&gt;if &lt;/span&gt;you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
&lt;span class="o"&gt;[&lt;/span&gt;2/4] 🚚  Fetching packages...
&lt;span class="o"&gt;[&lt;/span&gt;3/4] 🔗  Linking dependencies...
&lt;span class="o"&gt;[&lt;/span&gt;4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done &lt;span class="k"&gt;in &lt;/span&gt;9.48s.
Failed to &lt;span class="nb"&gt;install &lt;/span&gt;node modules
Initialized empty Git repository &lt;span class="k"&gt;in&lt;/span&gt; /Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/.git/
cpi-demo initialized
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="切换到项目目录"&gt;切换到项目目录&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;cpi-demo
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="列出当前目录下的文件和子目录的详细信息"&gt;列出当前目录下的文件和子目录的详细信息&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 152
drwxr-xr-x@  16 qiaopengjun  staff    512 Jun  6 13:29 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x@  22 qiaopengjun  staff    704 Jun  6 13:28 ..
drwxr-xr-x@   9 qiaopengjun  staff    288 Jun  6 13:29 .git
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff     67 Jun  6 13:28 .gitignore
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff     61 Jun  6 13:28 .prettierignore
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff    355 Jun  6 13:28 Anchor.toml
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff    215 Jun  6 13:28 Cargo.toml
drwxr-xr-x@   2 qiaopengjun  staff     64 Jun  6 13:28 app
drwxr-xr-x@   3 qiaopengjun  staff     96 Jun  6 13:28 migrations
drwxr-xr-x@ 150 qiaopengjun  staff   4800 Jun  6 13:29 node_modules
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff    461 Jun  6 13:28 package.json
drwxr-xr-x@   3 qiaopengjun  staff     96 Jun  6 13:28 programs
drwxr-xr-x@   3 qiaopengjun  staff     96 Jun  6 13:28 target
drwxr-xr-x@   3 qiaopengjun  staff     96 Jun  6 13:28 tests
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff    205 Jun  6 13:28 tsconfig.json
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@   1 qiaopengjun  staff  51940 Jun  6 13:29 yarn.lock
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="使用cursor 打开项目"&gt;使用&lt;code&gt;cursor&lt;/code&gt; 打开项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cc
&lt;span class="c"&gt;# alias cc="open -a cursor ."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看项目目录"&gt;查看项目目录&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cpi-demo on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 took 8.6s 
➜ tree &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 6 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"target|test-ledger|.vscode|node_modules"&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Anchor.toml
├── app
├── Cargo.lock
├── Cargo.toml
├── migrations
│   └── deploy.ts
├── package.json
├── programs
│   └── cpi-demo
│       ├── Cargo.toml
│       ├── src
│       │   └── lib.rs
│       └── Xargo.toml
├── tests
│   └── cpi-demo.ts
├── tsconfig.json
└── yarn.lock

7 directories, 11 files

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="lib.rs 文件"&gt;lib.rs 文件&lt;/h3&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![allow(unexpected_cfgs)]&lt;/span&gt;
&lt;span class="nd"&gt;#![allow(deprecated)]&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="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;solana_program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;&lt;span class="p"&gt;;&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;solana_program&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;};&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;system_program&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Transfer&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;"Cnh7whdQWo3XxArbNfTuoKGA68KTW7Ytm2yoK3rMp61T"&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;cpi_demo&lt;/span&gt; &lt;span class="p"&gt;{&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;solana_program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invoke_signed&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="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greetings from: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.program_id&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;sol_transfer&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;SolTransfer&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;amount&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="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.sender&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;to_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.recipient&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;program_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.system_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;cpi_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CpiContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;program_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&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="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpi_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&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="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;sol_transfer2&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;SolTransfer&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;amount&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="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.sender&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;to_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.recipient&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;program_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.system_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;instruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;program_id&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="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;sol_transfer3&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;SolTransfer&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;amount&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="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.sender&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;to_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.recipient&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;program_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.system_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Prepare instruction AccountMetas&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;account_metas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nn"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;AccountMeta&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// SOL transfer instruction discriminator&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_discriminator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&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="c1"&gt;// Prepare instruction data&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;instruction_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;instruction_data&lt;/span&gt;&lt;span class="nf"&gt;.extend_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;instruction_discriminator&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;instruction_data&lt;/span&gt;&lt;span class="nf"&gt;.extend_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="nf"&gt;.to_le_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;// Create instruction&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Instruction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;program_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;program_id&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;account_metas&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="n"&gt;instruction_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Invoke instruction&lt;/span&gt;

        &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;program_id&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="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;sol_transfer4&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;SolTransfer4&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;amount&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="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.pda_account&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;to_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.recipient&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;program_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.system_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&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;bump_seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.bumps.pda_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;signer_seeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"pda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bump_seed&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;cpi_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CpiContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;program_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&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="nf"&gt;.with_signer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signer_seeds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpi_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&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="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;sol_transfer5&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;SolTransfer4&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;amount&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="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;from_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.pda_account&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;to_pubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.recipient&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;program_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.system_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&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;bump_seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.bumps.pda_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;signer_seeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"pda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bump_seed&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;instruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;invoke_signed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;from_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;program_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;signer_seeds&lt;/span&gt;&lt;span class="p"&gt;,&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="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;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;SolTransfer&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="n"&gt;sender&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="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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="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;SolTransfer4&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(&lt;/span&gt;
        &lt;span class="nd"&gt;mut,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"pda"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;recipient&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;pda_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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="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;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码是一个使用 Anchor 框架编写的 Solana 智能合约，主要演示了不同方式实现 &lt;strong&gt;SOL 转账的跨程序调用（CPI）&lt;/strong&gt;。以下是详细解释：&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="核心功能"&gt;核心功能&lt;/h4&gt;
&lt;p&gt;程序包含多个转账方法，展示不同层级的 CPI 实现：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;基础转账&lt;/strong&gt;：使用 Anchor 封装方法&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;原生指令调用&lt;/strong&gt;：使用 Solana 原生方法&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;手动构建指令&lt;/strong&gt;：底层字节级构建&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDA 账户转账&lt;/strong&gt;：需要签名的程序派生账户操作&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;
&lt;h4 id="方法详解"&gt;方法详解&lt;/h4&gt;&lt;h4 id="1. initialize()"&gt;1. &lt;code&gt;initialize()&lt;/code&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：初始化合约，记录程序 ID 到日志&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;代码：&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greetings from: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="2. sol_transfer() - Anchor 封装方式"&gt;2. &lt;code&gt;sol_transfer()&lt;/code&gt; - Anchor 封装方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;路径&lt;/strong&gt;：最简洁的高层实现&lt;/li&gt;
&lt;li&gt;流程：

&lt;ol&gt;
&lt;li&gt;获取发送者、接收者和系统程序的账户信息&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;CpiContext&lt;/code&gt; 创建转账上下文&lt;/li&gt;
&lt;li&gt;调用 &lt;code&gt;transfer()&lt;/code&gt; 方法执行转账&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;特点&lt;/strong&gt;：利用 Anchor 框架的安全抽象&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3. sol_transfer2() - 原生指令调用"&gt;3. &lt;code&gt;sol_transfer2()&lt;/code&gt; - 原生指令调用&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;路径&lt;/strong&gt;：直接调用 Solana 系统指令&lt;/li&gt;
&lt;li&gt;流程：

&lt;ol&gt;
&lt;li&gt;用 &lt;code&gt;system_instruction::transfer&lt;/code&gt; 构建原生指令&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;invoke()&lt;/code&gt; 执行指令&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;特点&lt;/strong&gt;：绕过 Anchor 封装，直接调用 Solana 底层&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="4. sol_transfer3() - 手动构建指令"&gt;4. &lt;code&gt;sol_transfer3()&lt;/code&gt; - 手动构建指令&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;路径&lt;/strong&gt;：字节级底层实现&lt;/li&gt;
&lt;li&gt;流程：

&lt;ol&gt;
&lt;li&gt;手动创建账户元数据 &lt;code&gt;AccountMeta&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;拼接指令数据（指令标识符 + 转账金额）&lt;/li&gt;
&lt;li&gt;构建完整的 &lt;code&gt;Instruction&lt;/code&gt; 结构&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;invoke()&lt;/code&gt; 执行&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;关键点：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;instruction_discriminator: u32 = 2&lt;/code&gt;：系统程序的转账指令标识&lt;/li&gt;
&lt;li&gt;数据布局：&lt;code&gt;[指令标识符(4字节)][金额(8字节)]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="5. sol_transfer4()/sol_transfer5() - PDA 账户转账"&gt;5. &lt;code&gt;sol_transfer4()&lt;/code&gt;/&lt;code&gt;sol_transfer5()&lt;/code&gt; - PDA 账户转账&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;场景&lt;/strong&gt;：从&lt;strong&gt;程序派生账户（PDA）&lt;/strong&gt; 发起转账&lt;/li&gt;
&lt;li&gt;关键技术：

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PDA 账户&lt;/strong&gt;：通过种子生成（&lt;code&gt;seeds = [b"pda", recipient.key().as_ref()]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;签名授权&lt;/strong&gt;：使用 &lt;code&gt;with_signer&lt;/code&gt; 或 &lt;code&gt;invoke_signed&lt;/code&gt; 提供 PDA 签名&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;区别：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sol_transfer4&lt;/code&gt;：使用 Anchor 的 &lt;code&gt;CpiContext.with_signer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sol_transfer5&lt;/code&gt;：直接使用 &lt;code&gt;invoke_signed&lt;/code&gt; 添加签名&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="账户结构解析"&gt;账户结构解析&lt;/h4&gt;&lt;h4 id="1. SolTransfer 账户"&gt;1. &lt;code&gt;SolTransfer&lt;/code&gt; 账户&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;用于普通转账方法（&lt;code&gt;sol_transfer1-3&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SolTransfer&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="c1"&gt;// 可变账户&lt;/span&gt;
    &lt;span class="n"&gt;sender&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="c1"&gt;// 发送者（签名账户）&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt; 
    &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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="c1"&gt;// 接收者&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="c1"&gt;// 系统程序&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="2. SolTransfer4 账户"&gt;2. &lt;code&gt;SolTransfer4&lt;/code&gt; 账户&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;用于 PDA 转账方法（&lt;code&gt;sol_transfer4/5&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SolTransfer4&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(&lt;/span&gt;
        &lt;span class="nd"&gt;mut,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"pda"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;recipient&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// PDA 生成种子&lt;/span&gt;
        &lt;span class="n"&gt;bump&lt;/span&gt; &lt;span class="c1"&gt;// 自动派生 bump&lt;/span&gt;
    &lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;pda_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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="c1"&gt;// PDA 账户作为发送者&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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="c1"&gt;// 接收者&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;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id="关键概念"&gt;关键概念&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CPI (Cross-Program Invocation)&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;允许智能合约调用其他程序（如系统程序转账）&lt;/li&gt;
&lt;li&gt;实现方式分层：Anchor 封装 → 原生调用 → 字节级操作&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDA (Program Derived Address)&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;由程序控制的特殊账户（无私钥）&lt;/li&gt;
&lt;li&gt;转账时需通过 &lt;code&gt;invoke_signed&lt;/code&gt; 提供程序签名&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;指令构建&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;底层由三部分组成：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;program_id&lt;/code&gt;：目标程序 ID（如系统程序）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;accounts&lt;/code&gt;：账户元数据列表&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt;：序列化的指令数据&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;
&lt;h4 id="总结"&gt;总结&lt;/h4&gt;
&lt;p&gt;该代码展示了 SOL 转账的 &lt;strong&gt;5 种实现范式&lt;/strong&gt;，核心差异点在于：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;抽象层级&lt;/strong&gt;：从高阶 Anchor 封装到底层字节操作&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;账户类型&lt;/strong&gt;：普通账户 vs PDA 程序派生账户&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;签名方式&lt;/strong&gt;：普通签名 vs 程序签名（PDA）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;通过对比这些方法，开发者可以深入理解 Anchor 框架的安全抽象机制和 Solana CPI 的底层原理。&lt;/p&gt;
&lt;h3 id="编译并构建项目"&gt;编译并构建项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cpi-demo on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 took 5.7s 
➜ anchor build 
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.32s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.33s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/target/debug/deps/cpi_demo-a7bb659c1f2ea15a&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="cpi-demo.ts 测试代码"&gt;cpi-demo.ts 测试代码&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@coral-xyz/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="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;BN&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="s2"&gt;@coral-xyz/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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CpiDemo&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="s2"&gt;../target/types/cpi_demo&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;Keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;LAMPORTS_PER_SOL&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;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sendAndConfirmTransaction&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&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="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;cpi-demo&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="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;AnchorProvider&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;cpiDemo&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CpiDemo&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;wallet&lt;/span&gt; &lt;span class="o"&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="k"&gt;as&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;Wallet&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="nx"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sender&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipient&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="nf"&gt;beforeEach&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&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="nx"&gt;sender&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wallet sender Resulting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Fund accounts&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;airdrop&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="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender&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="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&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="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&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;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;pubkey&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;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;sig&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;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="nf"&gt;requestAirdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&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;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;latestBlockhash&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;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="nf"&gt;getLatestBlockhash&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;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="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;latestBlockhash&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;// 1 SOL&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transferAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&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;PDA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pda&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;toBuffer&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;programId&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;getBalances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;payerPubkey&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;recipientPubkey&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;timeframe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;payerBalance&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;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="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payerPubkey&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;recipientBalance&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;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="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;recipientPubkey&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timeframe&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; balances:`&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="s2"&gt;`   Payer: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payerBalance&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;`   Recipient: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;recipientBalance&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;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="c1"&gt;// Add your test here.&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;methods&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="nf"&gt;rpc&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="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;SOL Transfer 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;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;transactionSignature&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&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;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&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="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="s2"&gt;`\nTransaction Signature: https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender&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;recipient&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Resulting&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="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;SOL Transfer Anchor wallet&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;transactionSignature&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;sender&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;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&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="nf"&gt;rpc&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="s2"&gt;`\nTransaction Signature:`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s2"&gt;`https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&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="nx"&gt;recipient&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transfer wallet Resulting&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="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;SOL Transfer2 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;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;transactionSignature&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer2&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&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;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&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="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="s2"&gt;`\nTransaction Signature: https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;sender&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;recipient&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transfer2 Resulting&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="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;SOL Transfer3 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;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;transactionSignature&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer3&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&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;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&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="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="s2"&gt;`\nTransaction Signature: https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;sender&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;recipient&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transfer3 Resulting&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="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;Fund PDA with SOL&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;transferInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;fromPubkey&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;toPubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lamports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transferAmount&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&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;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferInstruction&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;transactionSignature&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;sendAndConfirmTransaction&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;transaction&lt;/span&gt;&lt;span class="p"&gt;,&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;payer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// signer&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="s2"&gt;`\nFund PDA with SOL Transaction Signature:`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s2"&gt;`https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&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="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fund PDA with SOL Resulting&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="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;SOL Transfer with PDA signer&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;transactionSignature&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;methods&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer4&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;pdaAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;recipient&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="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="s2"&gt;`\nSOL Transfer with PDA signer Transaction Signature: https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&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="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SOL Transfer with PDA signer Resulting&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="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;SOL Transfer with PDA invoke_signed&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;transactionSignature&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;methods&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer4&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
         &lt;span class="na"&gt;pdaAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;recipient&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="p"&gt;})&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="s2"&gt;`\nSOL Transfer with PDA invoke_signed Transaction Signature: https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;

     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBalances&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="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SOL Transfer with PDA invoke_signed Resulting&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="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是一个使用 Anchor 框架编写的&lt;strong&gt;智能合约测试套件&lt;/strong&gt;，用于验证各种 SOL 转账功能的正确性。它通过模拟区块链操作，测试合约中的不同转账方法。&lt;/p&gt;
&lt;h4 id="核心组件解析"&gt;核心组件解析&lt;/h4&gt;&lt;h5 id="1. 测试环境初始化"&gt;1. 测试环境初始化&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;AnchorProvider&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;cpiDemo&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CpiDemo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;获取本地测试环境提供者（Provider）&lt;/li&gt;
&lt;li&gt;将程序实例化为 &lt;code&gt;program&lt;/code&gt; 对象，用于调用合约方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="2. 测试账户管理"&gt;2. 测试账户管理&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sender&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipient&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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;生成测试用的发送方和接收方密钥对&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;beforeEach&lt;/code&gt; 在每个测试前空投 5 SOL 到各个账户&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="3. PDA 账户生成"&gt;3. PDA 账户生成&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pda&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;toBuffer&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;programId&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;使用与合约相同的规则生成程序派生地址 (PDA)&lt;/li&gt;
&lt;li&gt;种子规则：&lt;code&gt;b"pda" + 钱包公钥&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="4. 实用工具函数"&gt;4. 实用工具函数&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;       &lt;span class="c1"&gt;// 空投 SOL&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;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBalances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeframe&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;/code&gt;&lt;/pre&gt;&lt;h4 id="测试用例详解"&gt;测试用例详解&lt;/h4&gt;&lt;h5 id="1. 基础功能测试"&gt;1. 基础功能测试&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;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="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;methods&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="nf"&gt;rpc&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;ul&gt;
&lt;li&gt;测试合约的初始化功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="2. 常规 SOL 转账测试"&gt;2. 常规 SOL 转账测试&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;SOL Transfer 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;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="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;methods&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;ul&gt;
&lt;li&gt;测试合约的 &lt;code&gt;solTransfer&lt;/code&gt; 方法（Anchor 封装方式）&lt;/li&gt;
&lt;li&gt;使用生成的发送方账户签名&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="3. 钱包直接转账测试"&gt;3. 钱包直接转账测试&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;SOL Transfer Anchor wallet&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="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;methods&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
      &lt;span class="na"&gt;sender&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="nx"&gt;recipient&lt;/span&gt; 
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;ul&gt;
&lt;li&gt;使用测试钱包本身作为发送方&lt;/li&gt;
&lt;li&gt;无需额外指定签名者（使用默认钱包签名）&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="4. 其他转账方式测试"&gt;4. 其他转账方式测试&lt;/h5&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;SOL Transfer2 Anchor&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="c1"&gt;// 测试 solTransfer2（原生指令）&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;SOL Transfer3 Anchor&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="c1"&gt;// 测试 solTransfer3（手动构建指令）&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;验证不同层级 CPI 实现的正确性&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="5. PDA 账户相关测试"&gt;5. PDA 账户相关测试&lt;/h4&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;Fund PDA with SOL&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;transferInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fromPubkey&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;toPubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lamports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transferAmount&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;先向 PDA 账户充值 2 SOL&lt;/li&gt;
&lt;li&gt;使用系统程序的标准转账&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;SOL Transfer with PDA signer&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="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;methods&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solTransfer4&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferAmount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pdaAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;recipient&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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;ul&gt;
&lt;li&gt;测试合约的 PDA 转账功能（&lt;code&gt;solTransfer4/solTransfer5&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;PDA 作为发送方，钱包作为接收方&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="测试工具亮点"&gt;测试工具亮点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;##### &lt;strong&gt;区块链交互工具&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 空投实现（请求测试网 SOL）&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAirdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;余额监控&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 获取并格式化余额&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&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;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubkey&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SOL`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;交易调试&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 生成浏览器可查看的交易链接&lt;/span&gt;
&lt;span class="s2"&gt;`https://solana.fm/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionSignature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet-solana`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="关键测试流程"&gt;关键测试流程&lt;/h3&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[环境准备] --&amp;gt; B[资金分配]
    B --&amp;gt; C[执行测试]
    C --&amp;gt; D[结果验证]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/b4f44c66-e2ec-43fa-ab75-cb749da871bb.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="测试流程详解"&gt;测试流程详解&lt;/h4&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant T as 测试套件
    participant B as beforeEach
    participant T1 as Test 1
    participant T2 as Test 2
    participant T3 as Test 3
    participant T4 as Test 4

    T-&amp;gt;&amp;gt;B: 开始执行测试套件
    loop 每次测试前
        B-&amp;gt;&amp;gt;B: 给所有账户空投5 SOL
    end

    T-&amp;gt;&amp;gt;T1: 执行普通用户转账测试
    T-&amp;gt;&amp;gt;T2: 执行钱包直接转账测试
    T-&amp;gt;&amp;gt;T3: 执行PDA充值测试
    T-&amp;gt;&amp;gt;T4: 执行PDA转账测试
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/03f7a084-4652-42b7-99d2-06247b4969ba.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="总结"&gt;总结&lt;/h3&gt;
&lt;p&gt;该测试套件完整覆盖了智能合约的所有转账功能：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;验证了 5 种转账方法的正确性&lt;/li&gt;
&lt;li&gt;测试了普通账户和 PDA 账户两种场景&lt;/li&gt;
&lt;li&gt;通过余额变化确认实际操作结果&lt;/li&gt;
&lt;li&gt;提供区块链浏览器链接便于调试&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;通过运行这些测试，开发者可以：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;确认合约在各种场景下的行为符合预期&lt;/li&gt;
&lt;li&gt;验证 PDA 账户的正确使用&lt;/li&gt;
&lt;li&gt;确保不同层级的 CPI 调用都能成功执行&lt;/li&gt;
&lt;li&gt;检查 SOL 余额变化的准确性&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="运行测试"&gt;运行测试&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cpi-demo on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 took 8.1s 
➜ anchor &lt;span class="nb"&gt;test  
    &lt;/span&gt;Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.30s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.31s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/target/debug/deps/cpi_demo-a7bb659c1f2ea15a&lt;span class="o"&gt;)&lt;/span&gt;

Found a &lt;span class="s1"&gt;'test'&lt;/span&gt; script &lt;span class="k"&gt;in &lt;/span&gt;the Anchor.toml. Running it as a &lt;span class="nb"&gt;test &lt;/span&gt;suite!

Running &lt;span class="nb"&gt;test &lt;/span&gt;suite: &lt;span class="s2"&gt;"/Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/Anchor.toml"&lt;/span&gt;

yarn run v1.22.22
&lt;span class="nv"&gt;$ &lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/node_modules/.bin/ts-mocha &lt;span class="nt"&gt;-p&lt;/span&gt; ./tsconfig.json &lt;span class="nt"&gt;-t&lt;/span&gt; 1000000 &lt;span class="s1"&gt;'tests/**/*.ts'&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;node:51302&lt;span class="o"&gt;)&lt;/span&gt; ExperimentalWarning: Type Stripping is an experimental feature and might change at any &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Use &lt;span class="sb"&gt;`&lt;/span&gt;node &lt;span class="nt"&gt;--trace-warnings&lt;/span&gt; ...&lt;span class="sb"&gt;`&lt;/span&gt; to show where the warning was created&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;node:51302&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;MODULE_TYPELESS_PACKAGE_JSON] Warning: Module &lt;span class="nb"&gt;type &lt;/span&gt;of file:///Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/tests/cpi-demo.ts is not specified and it doesn&lt;span class="s1"&gt;'t parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to /Users/qiaopengjun/Code/Solana/solana-sandbox/cpi-demo/package.json.


  cpi-demo
wallet sender Resulting balances:
   Payer: 500000000
   Recipient: 0
Your transaction signature jo1Fn5fFW3dRDhdWyfDN7rLFH5MvkNpxNnNJcsXdZmFVJujcW9gLyn8Y88yotgDM3dNJpvBQ2UUBugiu1GPMvWZ
    ✔ Is initialized! (466ms)
wallet sender Resulting balances:
   Payer: 500000004.999995
   Recipient: 5

Transaction Signature: https://solana.fm/tx/zg29tZsPSbXgZMHjMaaHytpMba8ArmzBqz2a78MSYYHWgd9Gd2sycLo2S6FMMe7u4QHU3NFqmjEAR339WvGXkE6?cluster=devnet-solana
Resulting balances:
   Payer: 4
   Recipient: 6
    ✔ SOL Transfer Anchor (463ms)
wallet sender Resulting balances:
   Payer: 500000004.99998504
   Recipient: 4

Transaction Signature:https://solana.fm/tx/2hKjrwZ7UPZbZynddX4u2zfAPFj2CjJey4dv3fjTQ5GVnaWypVQYri48gNN351WGXpn9SuQ7VJvuU9G9agZzmMee?cluster=devnet-solana
Transfer wallet Resulting balances:
   Payer: 500000003.99998003
   Recipient: 7
    ✔ SOL Transfer Anchor wallet (459ms)
wallet sender Resulting balances:
   Payer: 500000003.99998003
   Recipient: 4

Transaction Signature: https://solana.fm/tx/4N55eJwnhAujpGqeSUoui4hveKogwZd8xW9C9svCnpvxDB69zGJLZQJ2N8hxhjuP7dYxXqkCv8QFFw4npv5ASa8U?cluster=devnet-solana
Transfer2 Resulting balances:
   Payer: 3
   Recipient: 8
    ✔ SOL Transfer2 Anchor (472ms)
wallet sender Resulting balances:
   Payer: 500000003.99996996
   Recipient: 3

Transaction Signature: https://solana.fm/tx/5grtbgaydWbzT6DvqAKAh91i3otVVpicwx5G8vu36cctkEGpoAnYYASLLppJkuD8amDEQgSq6XzcEbwutbob6xWF?cluster=devnet-solana
Transfer3 Resulting balances:
   Payer: 2
   Recipient: 9
    ✔ SOL Transfer3 Anchor (467ms)
wallet sender Resulting balances:
   Payer: 500000003.99996
   Recipient: 2

Fund PDA with SOL Transaction Signature:https://solana.fm/tx/2es1hU6HgoG816hkZpkFewjr54Yhied4yfp4GwwXapUKNY3wwCt9ysXoXNY9vUFHqWbDCAnXwzfXmQfjHZM1KoAu?cluster=devnet-solana
Fund PDA with SOL Resulting balances:
   Payer: 500000001.99996
   Recipient: 2
    ✔ Fund PDA with SOL (447ms)
wallet sender Resulting balances:
   Payer: 500000001.99996
   Recipient: 2

SOL Transfer with PDA signer Transaction Signature: https://solana.fm/tx/5iYUatgnZYTnmVyKJKAmNPx8ueBiHjP9jjzkSvxaLct7tKD6MDnz5Kz19no2od7bnjrbLLb8UT4gJCVDUrYZJRpv?cluster=devnet-solana
SOL Transfer with PDA signer Resulting balances:
   Payer: 500000002.999955
   Recipient: 1
    ✔ SOL Transfer with PDA signer (471ms)
wallet sender Resulting balances:
   Payer: 500000002.999955
   Recipient: 2

SOL Transfer with PDA invoke_signed Transaction Signature: https://solana.fm/tx/24JDZmYmsaCU3sCZJQ56ftHFQHHovncnuoo83YsPD7xTwvmxwqQJyt7vefk7L9Z1mkBNVhU1C53e1mLXhq1GK4Dj?cluster=devnet-solana
SOL Transfer with PDA invoke_signed Resulting balances:
   Payer: 500000003.99995
   Recipient: 0
    ✔ SOL Transfer with PDA invoke_signed (491ms)


  8 passing (5s)

✨  Done in 6.58s.

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试结果展示了合约的全部 8 个转账功能测试均成功通过，整个过程耗时约 6.58 秒：每个测试执行前都通过空投确保了发送方和接收方账户各有 5 SOL 初始余额，成功验证了普通用户转账、钱包直接转账（包含微小手续费约 0.00000002 SOL）、原生指令转账和手动构建指令转账等不同实现方式；特别是对 PDA 程序派生账户的测试中，先完成 2 SOL 充值操作后，再成功执行了从 PDA 到钱包的两次 1 SOL 转账（使用 solTransfer4 和 solTransfer5 两种方法），最终通过精确的余额变化（如接收方从初始 5 SOL 增至 9 SOL）和区块链浏览器可查的交易签名，证明所有 SOL 转账功能包括 PDA 账户操作均符合预期行为规范。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;Solana 的跨程序调用（CPI）机制如 Web3 世界的“外卖平台”，让智能合约无缝调用其他程序，从简单转账到复杂的 DeFi 操作都能一气呵成。本文通过一个实战项目，展示了 Anchor 框架的多层实现：从高阶 CpiContext 到原生 invoke，再到字节级指令构建，开发者可根据需求灵活选择开发路径。PDA 账户的引入进一步彰显 Solana 在无私钥场景的独特优势，测试结果验证了所有功能的稳定性。想在 Web3 浪潮中脱颖而出？从掌握 Solana CPI 开始，开启你的区块链开发之旅！&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;想深入探索 Solana 开发？扫描下方二维码，关注我们获取更多 Web3 实战教程！&lt;/strong&gt;
&lt;img src="/uploads/photo/Paxon/37b13f7a-e923-4800-9ea1-cd5b6da6ac16.gif!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;在评论区分享你的 Solana 开发经验，或留言你的问题，我们一起探讨！&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs/basics/cpi" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs/basics/cpi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/solana-developers/program-examples/blob/main/basics/transfer-sol/anchor/tests/test.ts" rel="nofollow" target="_blank"&gt;https://github.com/solana-developers/program-examples/blob/main/basics/transfer-sol/anchor/tests/test.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://whimsical.com/solana-installation-LYwNkrpP7HMfwR9FpTcR1H" rel="nofollow" target="_blank"&gt;https://whimsical.com/solana-installation-LYwNkrpP7HMfwR9FpTcR1H&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Sat, 07 Jun 2025 11:07:17 +0800</pubDate>
      <link>https://soldev.cn/topics/185</link>
      <guid>https://soldev.cn/topics/185</guid>
    </item>
    <item>
      <title>Web3 实战：用 Anchor 打造 Solana 智能合约全流程</title>
      <description>&lt;h2 id="Web3 实战：用 Anchor 打造 Solana 智能合约全流程"&gt;Web3 实战：用 Anchor 打造 Solana 智能合约全流程&lt;/h2&gt;
&lt;p&gt;Web3 热潮席卷而来，Solana 以超高性能和低成本成为区块链开发的热门舞台。Anchor 框架让 Solana 智能合约开发变得简单而高效。本文通过 hello_anchor 项目，带你实战从项目初始化到部署的全流程，快速上手 Solana 开发，释放你的 Web3 创造力！&lt;/p&gt;

&lt;p&gt;本文以 hello_anchor 项目为案例，详细展示如何用 Anchor 框架在 Solana 上开发智能合约。内容涵盖项目初始化、代码编写、测试验证、本地验证器运行及部署全过程，并提供部署失败的实用解决方案。适合 Web3 开发者通过实战快速掌握 Solana 智能合约开发的核心技能。&lt;/p&gt;
&lt;h2 id="实操"&gt;实操&lt;/h2&gt;&lt;h3 id="创建并初始化项目"&gt;创建并初始化项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor init hello_anchor
yarn &lt;span class="nb"&gt;install &lt;/span&gt;v1.22.22
info No lockfile found.
&lt;span class="o"&gt;[&lt;/span&gt;1/4] 🔍  Resolving packages...
warning mocha &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; glob@7.2.0: Glob versions prior to v9 are no longer supported
warning mocha &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; glob &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache &lt;span class="k"&gt;if &lt;/span&gt;you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
&lt;span class="o"&gt;[&lt;/span&gt;2/4] 🚚  Fetching packages...
&lt;span class="o"&gt;[&lt;/span&gt;3/4] 🔗  Linking dependencies...
&lt;span class="o"&gt;[&lt;/span&gt;4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done &lt;span class="k"&gt;in &lt;/span&gt;3.15s.
Failed to &lt;span class="nb"&gt;install &lt;/span&gt;node modules
Initialized empty Git repository &lt;span class="k"&gt;in&lt;/span&gt; /Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/.git/
hello_anchor initialized
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="切换到项目目录并用cursor 打开项目"&gt;切换到项目目录并用 cursor 打开项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello_anchor/

cc
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看项目目录"&gt;查看项目目录&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ tree &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 6 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"target|.anchor|.vscode|node_modules"&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Anchor.toml
├── app
├── Cargo.lock
├── Cargo.toml
├── migrations
│   └── deploy.ts
├── package.json
├── programs
│   └── hello_anchor
│       ├── Cargo.toml
│       ├── src
│       │   └── lib.rs
│       └── Xargo.toml
├── tests
│   └── hello_anchor.ts
├── tsconfig.json
└── yarn.lock

7 directories, 11 files

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="lib.rs 文件"&gt;lib.rs 文件&lt;/h3&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![allow(unexpected_cfgs)]&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;"9yzSjhfTdHX6bz5BBd7WCKgDZGXGEFksMWpryGKTFt7X"&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;hello_anchor&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;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greetings from: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.new_account.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="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Changed data to: {:?}!"&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="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;signer,&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;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&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;new_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;NewAccount&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;signer&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;#[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;NewAccount&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;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;这段代码是一个基于 Anchor 框架的 Solana 智能合约（Program），主要实现了一个简单的账户初始化和数据存储功能。下面逐步解释每一部分的作用：&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="1. 基础导入和声明"&gt;1. 基础导入和声明&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![allow(unexpected_cfgs)]&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;"9yzSjhfTdHX6bz5BBd7WCKgDZGXGEFksMWpryGKTFt7X"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#![allow(unexpected_cfgs)]&lt;/code&gt;：允许一些编译器配置警告，通常可以忽略。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use anchor_lang::prelude::*;&lt;/code&gt;：导入 Anchor 框架的常用内容，方便后续使用。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;declare_id!&lt;/code&gt;：声明当前合约（Program）的唯一 ID（公钥），部署到链上后用于识别该合约。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h3 id="2. 主程序模块"&gt;2. 主程序模块&lt;/h3&gt;&lt;pre class="highlight rust"&gt;&lt;code&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;hello_anchor&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;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greetings from: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.new_account.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="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Changed data to: {:?}!"&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="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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[program]&lt;/code&gt;：Anchor 的宏，标记这是合约的主入口模块。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initialize&lt;/code&gt; 方法：

&lt;ul&gt;
&lt;li&gt;参数 &lt;code&gt;ctx: Context&amp;lt;Initialize&amp;gt;&lt;/code&gt;：上下文对象，包含所有与本次调用相关的账户信息。&lt;/li&gt;
&lt;li&gt;参数 &lt;code&gt;data: u64&lt;/code&gt;：用户传入的数据，将被存储到新账户中。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;msg!&lt;/code&gt;：在链上打印日志，便于调试。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ctx.accounts.new_account.data = data;&lt;/code&gt;：将传入的数据写入新账户的 &lt;code&gt;data&lt;/code&gt; 字段。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ok(())&lt;/code&gt;：返回成功。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="3. 账户结构体定义"&gt;3. 账户结构体定义&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&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;signer,&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;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&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;new_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;NewAccount&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;signer&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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[derive(Accounts)]&lt;/code&gt;：Anchor 宏，自动为结构体生成账户校验逻辑。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;new_account&lt;/code&gt;：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[account(init, payer = signer, space = 8 + 8)]&lt;/code&gt;：表示这是一个新建账户，由 &lt;code&gt;signer&lt;/code&gt; 支付租金，分配空间为 16 字节（8 字节账户头 + 8 字节数据）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;signer&lt;/code&gt;：调用者的钱包账户，必须是可变的（&lt;code&gt;mut&lt;/code&gt;），因为要支付租金。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;system_program&lt;/code&gt;：系统程序账户，创建新账户时必须提供。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="4. 数据账户结构体"&gt;4. 数据账户结构体&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&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;NewAccount&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;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;ul&gt;
&lt;li&gt;
&lt;code&gt;#[account]&lt;/code&gt;：Anchor 宏，标记这是一个可序列化的链上账户结构体。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data: u64&lt;/code&gt;：实际存储的数据字段。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="总结"&gt;总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;该合约允许用户调用 &lt;code&gt;initialize&lt;/code&gt; 方法，创建一个新的链上账户，并将传入的 &lt;code&gt;u64&lt;/code&gt; 数据存储到该账户中。&lt;/li&gt;
&lt;li&gt;账户的创建由调用者（&lt;code&gt;signer&lt;/code&gt;）支付租金。&lt;/li&gt;
&lt;li&gt;这是 Anchor 合约的典型入门示例，演示了账户初始化和数据写入的基本流程。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="hello_anchor.ts 文件"&gt;hello_anchor.ts 文件&lt;/h3&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@coral-xyz/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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BN&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@coral-xyz/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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HelloAnchor&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="s2"&gt;../target/types/hello_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="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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;assert&lt;/span&gt;  &lt;span class="k"&gt;from&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="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;hello_anchor&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="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;AnchorProvider&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;wallet&lt;/span&gt; &lt;span class="o"&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="k"&gt;as&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;Wallet&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;helloAnchor&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HelloAnchor&lt;/span&gt;&lt;span class="o"&gt;&amp;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;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;newAccountKp&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;Keypair&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;data&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&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;methods&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;newAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newAccountKp&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;signer&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="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;newAccountKp&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newAccount&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;newAccount&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;newAccountKp&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;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;newAccount&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;eq&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;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;New 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;newAccount&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;assert&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;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newAccount&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="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;@coral-xyz/anchor&lt;/code&gt; 框架编写的 TypeScript 测试文件，用于测试 Solana &lt;code&gt;hello_anchor&lt;/code&gt; 智能合约。它通过调用合约的 &lt;code&gt;initialize&lt;/code&gt; 指令，验证合约是否按预期工作。&lt;/p&gt;

&lt;p&gt;下面逐步解释每一部分：&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="1. 导入必要的库"&gt;1. 导入必要的库&lt;/h4&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@coral-xyz/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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BN&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@coral-xyz/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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HelloAnchor&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="s2"&gt;../target/types/hello_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="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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;assert&lt;/span&gt;  &lt;span class="k"&gt;from&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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;import * as anchor from "@coral-xyz/anchor";&lt;/code&gt;：导入 Anchor 框架的 TypeScript/JavaScript SDK。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import { BN, Program } from "@coral-xyz/anchor";&lt;/code&gt;：从 Anchor 导入 &lt;code&gt;BN&lt;/code&gt; (大数) 类（用于处理 &lt;code&gt;u64&lt;/code&gt; 等大整数类型）和 &lt;code&gt;Program&lt;/code&gt; 类（用于与链上程序交互）。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import { HelloAnchor } from "../target/types/hello_anchor";&lt;/code&gt;：导入 Anchor 自动生成的程序类型定义。&lt;code&gt;../target/types/hello_anchor.ts&lt;/code&gt; 文件包含了程序 ID、指令、账户结构等的 TypeScript 接口，使得与程序交互更加类型安全。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import { Keypair } from "@solana/web3.js";&lt;/code&gt;：从 Solana Web3 库导入 &lt;code&gt;Keypair&lt;/code&gt; 类，用于生成和管理账户的密钥对。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import assert from "assert";&lt;/code&gt;：导入 Node.js 的 &lt;code&gt;assert&lt;/code&gt; 模块，用于断言测试结果是否符合预期。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="2. 测试套件定义"&gt;2. 测试套件定义&lt;/h4&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;hello_anchor&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="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;AnchorProvider&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;wallet&lt;/span&gt; &lt;span class="o"&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="k"&gt;as&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;Wallet&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;helloAnchor&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HelloAnchor&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;// ... 测试用例 ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;describe("hello_anchor", () =&amp;gt; { ... });&lt;/code&gt;：定义一个测试套件，通常对应于要测试的程序或功能模块。这里是对 &lt;code&gt;hello_anchor&lt;/code&gt; 程序进行测试。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const provider = anchor.AnchorProvider.env();&lt;/code&gt;：创建一个 Anchor Provider。&lt;code&gt;AnchorProvider.env()&lt;/code&gt; 会尝试从环境变量中获取网络连接信息（如 RPC URL 和钱包），通常用于连接到本地的 Solana 验证器 (&lt;code&gt;solana-test-validator&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;anchor.setProvider(provider);&lt;/code&gt;：设置全局的 Anchor Provider，以便后续的 Anchor 调用使用这个配置。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const wallet = provider.wallet as anchor.Wallet;&lt;/code&gt;：获取当前 Provider 关联的钱包对象，用于签名交易。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const program = anchor.workspace.helloAnchor as Program&amp;lt;HelloAnchor&amp;gt;;&lt;/code&gt;：从 Anchor 工作空间加载 &lt;code&gt;hello_anchor&lt;/code&gt; 程序。&lt;code&gt;anchor.workspace&lt;/code&gt; 是一个方便的属性，可以在测试中访问 &lt;code&gt;Anchor.toml&lt;/code&gt; 中定义的所有程序。&lt;code&gt;&amp;lt;HelloAnchor&amp;gt;&lt;/code&gt; 是类型断言，提供了类型安全。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="3. 单个测试用例"&gt;3. 单个测试用例&lt;/h4&gt;&lt;pre class="highlight typescript"&gt;&lt;code&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;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;newAccountKp&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;Keypair&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;data&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;BN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&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;methods&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;newAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newAccountKp&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;signer&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="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;newAccountKp&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newAccount&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;newAccount&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;newAccountKp&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;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;newAccount&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;eq&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;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;New 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;newAccount&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;assert&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;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newAccount&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;it("Is initialized!", async () =&amp;gt; { ... });&lt;/code&gt;：定义一个独立的测试用例，描述了要测试的具体行为。这里的描述是 "Is initialized!"，表示测试程序是否能成功初始化账户。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const newAccountKp = new Keypair();&lt;/code&gt;：生成一个新的密钥对，这将用于创建新的 &lt;code&gt;NewAccount&lt;/code&gt; 账户。新账户的公钥是 &lt;code&gt;newAccountKp.publicKey&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const data = new BN(42);&lt;/code&gt;：创建一个 &lt;code&gt;BN&lt;/code&gt; 类型的变量 &lt;code&gt;data&lt;/code&gt;，值为 42，这将是我们要存储到新账户的数据。使用 &lt;code&gt;BN&lt;/code&gt; 是因为 &lt;code&gt;u64&lt;/code&gt; 在 JavaScript 中可能超出原生 Number 的范围。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;await program.methods.initialize(data)&lt;/code&gt;：调用 &lt;code&gt;hello_anchor&lt;/code&gt; 程序的 &lt;code&gt;initialize&lt;/code&gt; 指令，并传入 &lt;code&gt;data&lt;/code&gt; 作为参数。Anchor SDK 会自动根据 IDL（接口定义语言）构建调用。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.accounts({ ... })&lt;/code&gt;：指定指令需要交互的账户。

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;newAccount: newAccountKp.publicKey&lt;/code&gt;：指定要创建的新账户的公钥。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;signer: wallet.publicKey&lt;/code&gt;：指定支付租金和签名的账户（测试时通常使用 Provider 的钱包账户）。&lt;/li&gt;
&lt;li&gt;注意：&lt;code&gt;system_program&lt;/code&gt; 账户在这里没有显式列出，Anchor SDK 通常会根据上下文自动填充，因为它是创建新账户的必需账户。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.signers([newAccountKp])&lt;/code&gt;：指定需要额外签名的密钥对。由于 &lt;code&gt;initialize&lt;/code&gt; 指令中 &lt;code&gt;new_account&lt;/code&gt; 使用了 &lt;code&gt;init&lt;/code&gt; 属性，表示要创建新账户，新账户的密钥对必须签名，以证明所有权并允许其被创建。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.rpc()&lt;/code&gt;：发送交易到链上并等待其确认。成功后返回交易签名。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console.log("Your transaction signature", tx);&lt;/code&gt;：打印交易签名，方便调试。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;await program.account.newAccount.fetch(newAccountKp.publicKey);&lt;/code&gt;：从链上获取刚刚创建的新账户的数据。&lt;code&gt;program.account&lt;/code&gt; 提供了访问程序中定义的账户类型的方法，&lt;code&gt;newAccount&lt;/code&gt; 对应于合约中的 &lt;code&gt;NewAccount&lt;/code&gt; 结构体。&lt;code&gt;fetch&lt;/code&gt; 方法根据账户公钥获取并反序列化账户数据。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert.ok(newAccount.data.eq(data));&lt;/code&gt;：使用 &lt;code&gt;assert.ok&lt;/code&gt; 检查获取到的账户数据的 &lt;code&gt;data&lt;/code&gt; 字段是否等于我们期望的值 &lt;code&gt;data&lt;/code&gt;。&lt;code&gt;BN&lt;/code&gt; 对象使用 &lt;code&gt;.eq()&lt;/code&gt; 方法进行相等比较。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console.log("New account data:", newAccount.data.toString());&lt;/code&gt;：打印从链上获取到的账户数据。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert(data.eq(newAccount.data));&lt;/code&gt;：再次使用 &lt;code&gt;assert&lt;/code&gt; 检查数据是否匹配。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="总结"&gt;总结&lt;/h4&gt;
&lt;p&gt;这段测试代码：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; 设置了连接到本地 Solana 环境的 Provider 和钱包。&lt;/li&gt;
&lt;li&gt; 加载了要测试的 &lt;code&gt;hello_anchor&lt;/code&gt; 程序。&lt;/li&gt;
&lt;li&gt; 定义了一个测试用例，模拟调用 &lt;code&gt;initialize&lt;/code&gt; 指令。&lt;/li&gt;
&lt;li&gt; 生成了一个新的账户密钥对，并指定要存储的数据 (42)。&lt;/li&gt;
&lt;li&gt; 构建并发送交易，调用 &lt;code&gt;initialize&lt;/code&gt; 指令，指定新账户和签名者。&lt;/li&gt;
&lt;li&gt; 成功发送交易后，从链上读取新创建账户的数据。&lt;/li&gt;
&lt;li&gt; 断言读取到的数据与发送时的数据 (42) 相同，验证指令是否正确执行。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个测试文件是确保智能合约功能按预期工作的重要组成部分。&lt;/p&gt;
&lt;h3 id="编译项目"&gt;编译项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 took 1m 5.6s 
➜ anchor build
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.36s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.35s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/target/debug/deps/hello_anchor-fb07825913b8db7c&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="运行本地验证器"&gt;&lt;strong&gt;运行本地验证器&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ anchor localnet
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.33s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.31s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/target/debug/deps/hello_anchor-fb07825913b8db7c&lt;span class="o"&gt;)&lt;/span&gt;
Ledger location: .anchor/test-ledger
Log: .anchor/test-ledger/validator.log
⠒ Initializing...                                                                                                                                                                         Waiting &lt;span class="k"&gt;for &lt;/span&gt;fees to stabilize 1...
Identity: 9Kf8EJnvJuyDoxtzdPBZVmv4rDYcpnP9fNhYw1ZyCSTm
Genesis Hash: 7WQNftxjgGyyR81rPY6qTyWw68H926AdwyLb2HfGUBfg
Version: 2.1.21
Shred Version: 61187
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
WebSocket PubSub URL: ws://127.0.0.1:8900
⠁ 00:00:44 | Processed Slot: 92 | Confirmed Slot: 92 | Finalized Slot: 61 | Full Snapshot Slot: - | Incremental Snapshot Slot: - | Transactions: 92 | ◎499.999545000                      
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="测试"&gt;测试&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ anchor &lt;span class="nb"&gt;test
    &lt;/span&gt;Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.30s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.33s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/target/debug/deps/hello_anchor-fb07825913b8db7c&lt;span class="o"&gt;)&lt;/span&gt;

Found a &lt;span class="s1"&gt;'test'&lt;/span&gt; script &lt;span class="k"&gt;in &lt;/span&gt;the Anchor.toml. Running it as a &lt;span class="nb"&gt;test &lt;/span&gt;suite!

Running &lt;span class="nb"&gt;test &lt;/span&gt;suite: &lt;span class="s2"&gt;"/Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/Anchor.toml"&lt;/span&gt;

yarn run v1.22.22
&lt;span class="nv"&gt;$ &lt;/span&gt;/Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/node_modules/.bin/ts-mocha &lt;span class="nt"&gt;-p&lt;/span&gt; ./tsconfig.json &lt;span class="nt"&gt;-t&lt;/span&gt; 1000000 &lt;span class="s1"&gt;'tests/**/*.ts'&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;node:65366&lt;span class="o"&gt;)&lt;/span&gt; ExperimentalWarning: Type Stripping is an experimental feature and might change at any &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Use &lt;span class="sb"&gt;`&lt;/span&gt;node &lt;span class="nt"&gt;--trace-warnings&lt;/span&gt; ...&lt;span class="sb"&gt;`&lt;/span&gt; to show where the warning was created&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;node:65366&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;MODULE_TYPELESS_PACKAGE_JSON] Warning: Module &lt;span class="nb"&gt;type &lt;/span&gt;of file:///Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/tests/hello_anchor.ts is not specified and it doesn&lt;span class="s1"&gt;'t parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to /Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/package.json.


  hello_anchor
Your transaction signature nN7Cpc4yLFQNmMTzw4RERehCJ5WXyNcG3rc7rhuoxmrrtf8P7oLsiWe85K3ELXXkn1trzFdZEtRbggmAXRBDPiZ
New account data: 42
    ✔ Is initialized! (198ms)


  1 passing (208ms)

✨  Done in 2.10s.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="部署"&gt;部署&lt;/h3&gt;&lt;h4 id="部署失败"&gt;部署失败&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ anchor deploy

Deploying cluster: http://127.0.0.1:8899
Upgrade authority: /Users/qiaopengjun/.config/solana/id.json
Deploying program &lt;span class="s2"&gt;"hello_anchor"&lt;/span&gt;...
Program path: /Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/target/deploy/hello_anchor.so...
Error: Program&lt;span class="s1"&gt;'s authority Some(11111111111111111111111111111111) does not match authority provided 6MZDRo5v8K2NfdohdD76QNpSgk3GH3Aup53BeMaRAEpd
There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个部署错误表明你的程序 ID 配置与部署密钥不匹配。&lt;/p&gt;
&lt;h4 id="解决方案：重置程序ID"&gt;解决方案：重置程序 ID&lt;/h4&gt;
&lt;p&gt;第一步：删除旧的程序 ID 文件：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; target/deploy/hello_anchor-keypair.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第二步：重新生成程序 ID：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ anchor keys &lt;span class="nb"&gt;sync
&lt;/span&gt;Found incorrect program &lt;span class="nb"&gt;id &lt;/span&gt;declaration &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"/Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/programs/hello_anchor/src/lib.rs"&lt;/span&gt;
Updated to 9yzSjhfTdHX6bz5BBd7WCKgDZGXGEFksMWpryGKTFt7X

Found incorrect program &lt;span class="nb"&gt;id &lt;/span&gt;declaration &lt;span class="k"&gt;in &lt;/span&gt;Anchor.toml &lt;span class="k"&gt;for &lt;/span&gt;the program &lt;span class="sb"&gt;`&lt;/span&gt;hello_anchor&lt;span class="sb"&gt;`&lt;/span&gt;
Updated to 9yzSjhfTdHX6bz5BBd7WCKgDZGXGEFksMWpryGKTFt7X

All program &lt;span class="nb"&gt;id &lt;/span&gt;declarations are synced.
Please rebuild the program to update the generated artifacts.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第三步：重新部署：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hello_anchor on  main &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v23.11.0 via 🦀 1.87.0 
➜ anchor deploy
Deploying cluster: http://127.0.0.1:8899
Upgrade authority: /Users/qiaopengjun/.config/solana/id.json
Deploying program &lt;span class="s2"&gt;"hello_anchor"&lt;/span&gt;...
Program path: /Users/qiaopengjun/Code/Solana/solana-sandbox/hello_anchor/target/deploy/hello_anchor.so...
Program Id: 9yzSjhfTdHX6bz5BBd7WCKgDZGXGEFksMWpryGKTFt7X

Signature: 5UckCcNf8142Tz9xAa9iAt5Jo6ttDEYGwVEE29YHyFExyWccXSqWKSwpJCzeLjZiATnEKUPstUU1weWw15mzjNLb

Deploy success

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功部署！&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过 hello_anchor 项目的实战，我们从初始化、编写 lib.rs 和测试脚本，到运行本地验证器和成功部署，完整体验了 Anchor 在 Solana 开发中的高效与便捷。无论是解决程序 ID 不匹配的小技巧，还是流畅的测试部署流程，这篇实战指南为你铺平了 Web3 开发的道路。马上动手，在 Solana 生态中打造你的 Web3 应用！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs/basics/idl" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs/basics/idl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://soldev.cn/" rel="nofollow" target="_blank"&gt;https://soldev.cn/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/developers/cookbook#contributing" rel="nofollow" target="_blank"&gt;https://solana.com/zh/developers/cookbook#contributing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/" rel="nofollow" target="_blank"&gt;https://solscan.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.solanazh.com/" rel="nofollow" target="_blank"&gt;https://www.solanazh.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Wed, 28 May 2025 08:45:08 +0800</pubDate>
      <link>https://soldev.cn/topics/182</link>
      <guid>https://soldev.cn/topics/182</guid>
    </item>
    <item>
      <title>Web3 开发入门：Solana 账户创建与 Rust 实践全攻略</title>
      <description>&lt;h2 id="Web3开发入门：Solana账户创建与Rust实践全攻略"&gt;Web3 开发入门：Solana 账户创建与 Rust 实践全攻略&lt;/h2&gt;
&lt;p&gt;Web3 时代正在席卷全球，Solana 以其高吞吐量和低交易成本成为区块链开发的明星平台。想要快速入门 Web3 开发？从 Solana 账户创建开始！本文将带你走进 Solana 的 Rust 编程世界，详细解析如何通过 System Program 创建账户、构造交易并运行本地测试节点。无论你是区块链小白还是 Rust 开发者，这篇全攻略将为你提供从理论到实践的完整指引，助你轻松迈向 Web3 开发之路！快来一起探索 Solana 的无限可能吧！&lt;/p&gt;

&lt;p&gt;本文深入讲解了在 Solana 区块链上使用 Rust 语言创建账户的完整流程，涵盖 System Program 的核心功能、createAccount 指令的调用方法，以及账户所有权与数据初始化的机制。通过清晰的 Rust 代码示例和详细的操作步骤，展示了如何在本地测试节点上完成账户创建、密钥对生成、空投请求及交易签名。此外，文章还提供了项目依赖配置、常用 Cargo 命令和本地测试环境搭建的实用指南，适合 Web3 开发新手和希望深入 Solana 生态的开发者快速上手。&lt;/p&gt;
&lt;h2 id="Solana 如何创建账户"&gt;Solana 如何创建账户&lt;/h2&gt;
&lt;p&gt;在 Solana 上创建账户的第一步是调用 System Program 中的&lt;code&gt;createAccount&lt;/code&gt;指令。&lt;code&gt;System Program&lt;/code&gt;是 Solana 的核心程序之一，负责账户管理。&lt;/p&gt;

&lt;p&gt;在调用&lt;code&gt;createAccount&lt;/code&gt;指令时，需要指定新账户所需的字节数（空间），并用 lamports 为分配的字节提供资金（即支付租金）。在区块链中，lamports 是 Solana 的基本单位，就像以太坊中的 wei。创建账户需要消耗资源，因此需要使用 lamports 来支付费用。&lt;/p&gt;

&lt;p&gt;新创建账户的所有者程序是根据&lt;code&gt;createAccount&lt;/code&gt;指令中指定的程序设定的。这意味着在创建账户时，可以将特定的程序绑定为账户的所有者。&lt;/p&gt;

&lt;p&gt;Solana 运行时确保只有设定的所有者程序能够修改账户的数据或从中转移 lamports。这一机制确保了账户的安全性，防止未经授权的访问或更改。&lt;/p&gt;

&lt;p&gt;在 Solana 区块链中，只有系统程序（System Program）具备创建新账户的权限。当需要为其他程序创建账户时，需调用“createAccount”指令。这一指令会生成一个全新的账户，并将其所有者设定为指定的目标程序。&lt;/p&gt;

&lt;p&gt;新创建的账户初始状态是空的，其数据部分并未包含任何有效信息。此时，该账户的所有者——即目标程序，可以通过其自定义的指令来初始化账户的数据。这意味着，目标程序有权决定如何配置和使用这个新账户，包括存储何种数据以及执行哪些操作。&lt;/p&gt;

&lt;p&gt;在 Solana 上，只有系统程序（System Program）能够创建新账户。若要创建由其他程序拥有的账户，需调用 createAccount 指令来创建一个新账户，并将所有者程序设置为所需的程序。随后，新的程序所有者可以通过其自身的指令对账户数据进行初始化。&lt;/p&gt;

&lt;p&gt;简言之，Solana 的系统程序负责创建账户，而其他程序则通过获得的所有权来初始化并管理这些账户的具体内容和功能。这种设计确保了账户创建的统一性和安全性，同时赋予了各个程序灵活管理自身账户的权力。&lt;/p&gt;
&lt;h2 id="实操"&gt;实操&lt;/h2&gt;&lt;h3 id="创建项目"&gt;创建项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo new solana-create-account
    Creating binary &lt;span class="o"&gt;(&lt;/span&gt;application&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;solana-create-account&lt;span class="sb"&gt;`&lt;/span&gt; package
note: see more &lt;span class="sb"&gt;`&lt;/span&gt;Cargo.toml&lt;span class="sb"&gt;`&lt;/span&gt; keys and their definitions at &lt;span class="k"&gt;*******************************************************&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="切换到项目目录"&gt;切换到项目目录&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;solana-create-account

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="列出当前目录内容"&gt;&lt;strong&gt;列出当前目录内容&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;Cargo.toml src
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="常见 ls 参数（Linux/macOS）"&gt;常见 &lt;code&gt;ls&lt;/code&gt; 参数（Linux/macOS）&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;参数&lt;/th&gt;
&lt;th style="text-align:center;"&gt;作用&lt;/th&gt;
&lt;th style="text-align:center;"&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls -l&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;显示详细信息（权限、大小等）&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls -l&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls -a&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;显示隐藏文件（如 &lt;code&gt;.git/&lt;/code&gt;）&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls -a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls -lh&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;人类可读的文件大小（如 KB/MB）&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls -lh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls --color&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;彩色输出（Linux 默认启用）&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;ls --color=auto&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h3 id="编译运行当前项目"&gt;编译运行当前项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run
   Compiling solana-create-account v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/Solana/SolanaSandbox/solana-create-account&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.60s
     Running &lt;span class="sb"&gt;`&lt;/span&gt;target/debug/solana-create-account&lt;span class="sb"&gt;`&lt;/span&gt;
Hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="常用Cargo命令"&gt;&lt;strong&gt;常用 Cargo 命令&lt;/strong&gt;&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;命令&lt;/th&gt;
&lt;th style="text-align:center;"&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;cargo build&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;编译项目（输出在&lt;code&gt;target/debug/&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;cargo run&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;编译并运行项目&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;cargo check&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;快速检查语法（不生成二进制）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;cargo test&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;运行测试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;cargo add &amp;lt;crate&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;添加依赖（需安装&lt;code&gt;cargo-edit&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h3 id="添加项目依赖"&gt;添加项目依赖&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 
➜ cargo add anyhow  

SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 
➜ cargo add tokio &lt;span class="nt"&gt;--features&lt;/span&gt; full

SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 
➜ cargo add solana-client  

SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 
➜ cargo add solana-sdk  
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看项目目录结构"&gt;查看项目目录结构&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 took 4.8s 
➜ tree &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 6 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"target"&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Cargo.lock
├── Cargo.toml
└── src
    └── main.rs

2 directories, 3 files

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="main.rs 文件"&gt;main.rs 文件&lt;/h3&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;solana_client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;nonblocking&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;rpc_client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RpcClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;solana_sdk&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;commitment_config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CommitmentConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;native_token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;create_account&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;create_account_ix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;system_program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROGRAM_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RpcClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_commitment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://127.0.0.1:8899"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;CommitmentConfig&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;(),&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;from_keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// payer&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_account_keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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;data_len&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rent_exemption_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
        &lt;span class="nf"&gt;.get_minimum_balance_for_rent_exemption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&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;create_acc_ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_account_ix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;        &lt;span class="c1"&gt;// payer&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;new_account_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// new account&lt;/span&gt;
        &lt;span class="n"&gt;rent_exemption_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// rent exemption fee&lt;/span&gt;
        &lt;span class="n"&gt;data_len&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// space reserved for new account&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SYSTEM_PROGRAM_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;//assigned program address&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;transaction_signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
        &lt;span class="nf"&gt;.request_airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.confirm_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;transaction_signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&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;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nn"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_payer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;create_acc_ix&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="nf"&gt;.sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;new_account_keypair&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.get_latest_blockhash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&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;match&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.send_and_confirm_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&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="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction Signature: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error sending transaction: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码是一个用 Rust 编写的 Solana 区块链示例程序，主要功能是在本地 Solana 测试节点（localhost:8899）上创建一个新账户。下面是详细解释：&lt;/p&gt;
&lt;h4 id="1. 引入依赖"&gt;1. 引入依赖&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;solana_client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;nonblocking&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;rpc_client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RpcClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;solana_sdk&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;commitment_config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CommitmentConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;native_token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;create_account&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;create_account_ix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;system_program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROGRAM_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Transaction&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;ul&gt;
&lt;li&gt;这些是 Solana Rust SDK 的常用模块，提供了与链交互、签名、系统指令、交易等功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="2. 主函数入口"&gt;2. 主函数入口&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;tokio&lt;/code&gt; 异步运行时，主函数是异步的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3. 创建 RPC 客户端"&gt;3. 创建 RPC 客户端&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RpcClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_commitment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://127.0.0.1:8899"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nn"&gt;CommitmentConfig&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;confirmed&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;ul&gt;
&lt;li&gt;连接到本地 Solana 节点，使用“confirmed”确认级别。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="4. 生成密钥对"&gt;4. 生成密钥对&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;from_keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// payer&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_account_keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;from_keypair&lt;/code&gt;：付款人（payer），负责支付新账户创建费用。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;new_account_keypair&lt;/code&gt;：新账户的密钥对。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="5. 计算租金豁免金额"&gt;5. 计算租金豁免金额&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data_len&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rent_exemption_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
    &lt;span class="nf"&gt;.get_minimum_balance_for_rent_exemption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Solana 账户需要一定的 lamports（SOL 的最小单位）来免除租金。这里新账户数据长度为 0，所以只需最小金额。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="6. 构造创建账户指令"&gt;6. 构造创建账户指令&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;create_acc_ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_account_ix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;        &lt;span class="c1"&gt;// payer&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;new_account_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// new account&lt;/span&gt;
    &lt;span class="n"&gt;rent_exemption_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// rent exemption fee&lt;/span&gt;
    &lt;span class="n"&gt;data_len&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// space reserved for new account&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SYSTEM_PROGRAM_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;//assigned program address&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;生成一个“创建账户”指令，指定付款人、新账户、公费、空间、分配给哪个程序（这里是系统程序）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="7. 请求空投"&gt;7. 请求空投&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;transaction_signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
    &lt;span class="nf"&gt;.request_airdrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;向本地节点请求给付款人账户空投 1 SOL（测试网专用）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="8. 等待空投确认"&gt;8. 等待空投确认&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.confirm_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;transaction_signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&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;ul&gt;
&lt;li&gt;循环等待，直到空投交易被确认。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="9. 构造并签名交易"&gt;9. 构造并签名交易&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_payer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;create_acc_ix&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="nf"&gt;.pubkey&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="nf"&gt;.sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;from_keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;new_account_keypair&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.get_latest_blockhash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&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;ul&gt;
&lt;li&gt;创建一个包含“创建账户”指令的交易。&lt;/li&gt;
&lt;li&gt;用付款人和新账户的密钥对签名（新账户也要签名，因为它是被创建的对象）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="10. 发送并确认交易"&gt;10. 发送并确认交易&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.send_and_confirm_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&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="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction Signature: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error sending transaction: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;ul&gt;
&lt;li&gt;发送交易到链上，并等待确认。&lt;/li&gt;
&lt;li&gt;成功则打印交易签名，否则打印错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="11. 结束"&gt;11. 结束&lt;/h4&gt;&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;程序正常结束。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这段代码演示了如何用 Rust 在本地 Solana 测试节点上：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;生成密钥对&lt;/li&gt;
&lt;li&gt;请求空投&lt;/li&gt;
&lt;li&gt;创建新账户&lt;/li&gt;
&lt;li&gt;构造并发送交易&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="启动本地测试节点"&gt;启动本地测试节点&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 
➜ solana-test-validator
Ledger location: test-ledger
Log: test-ledger/validator.log
⠠ Initializing...                                                                                                              Waiting &lt;span class="k"&gt;for &lt;/span&gt;fees to stabilize 1...
Identity: HdaDeHA7BMDDMqCUDZ4NZTuFmFFBeXmZoEDQdnBe7K43
Genesis Hash: CtimLaGHfPmhYGXKGm98VLraQLztCPiUHj5PeGcHZMe4
Version: 2.1.21
Shred Version: 33789
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
WebSocket PubSub URL: ws://127.0.0.1:8900
⠂ 00:00:50 | Processed Slot: 105 | Confirmed Slot: 105 | Finalized Slot: 74 | Full Snapshot Slot: - | Incremental Snapshot Slot:

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="编译构建项目"&gt;编译构建项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 took 21.8s 
➜ cargo build
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.82s

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="编译运行项目"&gt;编译运行项目&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 
➜ cargo run  
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.69s
     Running &lt;span class="sb"&gt;`&lt;/span&gt;target/debug/solana-create-account&lt;span class="sb"&gt;`&lt;/span&gt;
Transaction Signature: 3qnLmfXU9NCBNctG73Xwewpzs1yrzEU15W9i6CpXK7jKs7PJbxKDteomFoaHfdpBxMgw1aNRiDT3n9ao66Y7FUjB

SolanaSandbox/solana-create-account on  main &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 0.1.0 via 🦀 1.87.0 took 2.1s 
➜ cargo run
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;dev&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.65s
     Running &lt;span class="sb"&gt;`&lt;/span&gt;target/debug/solana-create-account&lt;span class="sb"&gt;`&lt;/span&gt;
Transaction Signature: 3riMfj3tHF3LASQwhXEzX9kKVuNautcMeEfbzwsJ12DwhhwNmPkg1GknqNEqu1YF7n1ooyHtwQ9NM1us1TvHXEgD

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过这篇全攻略，你已掌握了 Solana 账户创建的核心技能：从调用 System Program 的 createAccount 指令，到使用 Rust 完成密钥对生成、空投请求和交易构造，再到本地测试节点的运行。Solana 通过系统程序与程序所有权的分离设计，确保了账户管理的灵活性与安全性，为 Web3 开发提供了强大支持。不管你是初探 Web3 的开发者，还是希望深耕 Solana 生态的专业人士，这篇教程都为你打开了一扇通往去中心化世界的大门！赶快动手实践，解锁更多 Solana 开发的可能性吧！关注我们的公众号，获取更多 Web3 与 Solana 的开发干货！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/developers/cookbook/accounts/create-account" rel="nofollow" target="_blank"&gt;https://solana.com/zh/developers/cookbook/accounts/create-account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/" rel="nofollow" target="_blank"&gt;https://solscan.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solanacookbook.com/zh/#%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81" rel="nofollow" target="_blank"&gt;https://solanacookbook.com/zh/#%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://websocketking.com/" rel="nofollow" target="_blank"&gt;https://websocketking.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://attractive-spade-1e3.notion.site/Solana-fca856aad4e5441f80f28cc4e015ca98" rel="nofollow" target="_blank"&gt;https://attractive-spade-1e3.notion.site/Solana-fca856aad4e5441f80f28cc4e015ca98&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/developers/courses" rel="nofollow" target="_blank"&gt;https://solana.com/zh/developers/courses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://beta.solpg.io/" rel="nofollow" target="_blank"&gt;https://beta.solpg.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs/installation" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs/installation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Tue, 27 May 2025 22:20:12 +0800</pubDate>
      <link>https://soldev.cn/topics/181</link>
      <guid>https://soldev.cn/topics/181</guid>
    </item>
    <item>
      <title>玩转 Web3 Solana：从零到代币开发</title>
      <description>&lt;h2 id="玩转 Web3 Solana：从零到代币开发"&gt;玩转 Web3 Solana：从零到代币开发&lt;/h2&gt;
&lt;p&gt;Web3 浪潮席卷而来，Solana 凭借超高性能和低成本，成为开发者探索去中心化世界的热门选择。想从零开始玩转 Solana，打造属于自己的代币项目吗？这篇文章将带你一步步走进 Solana 开发的世界，从工具安装到代币创建，手把手带你体验 Web3 的魅力。不管你是小白还是老手，这里都有你需要的干货，快来一起解锁 Solana 的无限可能吧！&lt;/p&gt;

&lt;p&gt;本文带你玩转 Web3 Solana 开发，从零基础起步，覆盖 Solana CLI 工具安装、Rust 和 Anchor 环境配置，到钱包管理、智能合约编译，再到 SPL 代币的创建与操作。通过详细的命令示例和链上结果展示，你将快速掌握代币开发的实战技能。无论是查询余额、生成密钥，还是在测试网空投 SOL，这篇指南都为你准备了清晰的操作路径，助你在 Solana 生态中轻松起飞！&lt;/p&gt;
&lt;h2 id="实操"&gt;实操&lt;/h2&gt;&lt;h3 id="安装与查看 Solana 相关工具"&gt;安装与查看 Solana 相关工具&lt;/h3&gt;&lt;h4 id="更新Solana CLI 工具链"&gt;更新 Solana CLI 工具链&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;agave-install update
  ✨ Update successful to stable commit f07a1e8
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="检查当前安装的 Solana CLI 版本号"&gt;&lt;strong&gt;检查当前安装的 Solana CLI 版本号&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana &lt;span class="nt"&gt;--version&lt;/span&gt;

solana-cli 2.1.17 &lt;span class="o"&gt;(&lt;/span&gt;src:f07a1e80&lt;span class="p"&gt;;&lt;/span&gt; feat:3271415109, client:Agave&lt;span class="o"&gt;)&lt;/span&gt;
solana &lt;span class="nt"&gt;-V&lt;/span&gt;
solana-cli 2.1.17 &lt;span class="o"&gt;(&lt;/span&gt;src:f07a1e80&lt;span class="p"&gt;;&lt;/span&gt; feat:3271415109, client:Agave&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="检查当前安装的 SBF（Solana 区块链程序框架）工具链版本"&gt;&lt;strong&gt;检查当前安装的 SBF（Solana 区块链程序框架）工具链版本&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build-sbf &lt;span class="nt"&gt;-V&lt;/span&gt;
solana-cargo-build-sbf 2.1.17
platform-tools v1.43
rustc 1.79.0
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="查看当前安装的 Rust 编译器（rustc）的版本号"&gt;&lt;strong&gt;查看当前安装的 Rust 编译器（rustc）的版本号&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc &lt;span class="nt"&gt;-V&lt;/span&gt;
rustc 1.85.1 &lt;span class="o"&gt;(&lt;/span&gt;4eb161250 2025-03-15&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="检查当前安装的 Anchor CLI 工具版本"&gt;&lt;strong&gt;检查当前安装的 Anchor CLI 工具版本&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor &lt;span class="nt"&gt;--version&lt;/span&gt;
anchor-cli 0.31.0
anchor &lt;span class="nt"&gt;-V&lt;/span&gt;
anchor-cli 0.31.0
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看当前默认地址"&gt;查看&lt;strong&gt;当前默认地址&lt;/strong&gt;
&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana address
6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD

buy-restrictor/client on  master &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 1.0.0 via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ solana address                                                                        
6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看 Solana 默认钱包密钥文件的详细信息"&gt;查看 Solana 默认钱包密钥文件的详细信息&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ll ~/.config/solana/id.json 
&lt;span class="nt"&gt;-rw-------&lt;/span&gt;  1 qiaopengjun  staff   226B Feb 20  2024 /Users/qiaopengjun/.config/solana/id.json
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查询当前默认钱包地址的 SOL 余额"&gt;&lt;strong&gt;查询当前默认钱包地址的 SOL 余额&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana balance
100.18518034 SOL
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看当前 Solana 配置的详细信息"&gt;&lt;strong&gt;查看当前 Solana 配置的详细信息&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;solana config get&lt;/code&gt; 是 Solana CLI 中的一个命令，&lt;strong&gt;用于查看当前 Solana 配置的详细信息&lt;/strong&gt;，包括默认的 RPC 节点、钱包路径和网络设置。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana config get
Config File: /Users/qiaopengjun/.config/solana/cli/config.yml
RPC URL: https://api.devnet.solana.com 
WebSocket URL: wss://api.devnet.solana.com/ &lt;span class="o"&gt;(&lt;/span&gt;computed&lt;span class="o"&gt;)&lt;/span&gt;
Keypair Path: /Users/qiaopengjun/.config/solana/id.json 
Commitment: confirmed 
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="获取智能合约的 Program ID（程序地址）"&gt;&lt;strong&gt;获取智能合约的 Program ID（程序地址）&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ solana address &lt;span class="nt"&gt;-k&lt;/span&gt; target/deploy/buy_restrictor-keypair.json 
6ySTWR3Yf278usLzZRPXswdBcHrfyY6seb24Etxwph4f
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看 Solana 智能合约的密钥对文件内容"&gt;&lt;strong&gt;查看 Solana 智能合约的密钥对文件内容&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ &lt;span class="nb"&gt;cat &lt;/span&gt;target/deploy/buy_restrictor-keypair.json              
&lt;span class="o"&gt;[&lt;/span&gt;155,71,21,122,135,204,91,244,135,51,144,142,136,90,129,188,150,237,4,203,65,17,8,6,207,110,23,76,79,62,57,184,88,191,190,137,101,105,152,191,142,81,72,155,71,134,244,193,182,104,228,166,172,180,153,50,49,7,194,50,74,141,54,204]%      
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="列出当前项目中所有程序的 Program ID（程序地址）"&gt;&lt;strong&gt;列出当前项目中所有程序的 Program ID（程序地址）&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ anchor keys list
buy_restrictor: 6ySTWR3Yf278usLzZRPXswdBcHrfyY6seb24Etxwph4f

buy-restrictor/target/deploy on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ anchor keys list
buy_restrictor: H1tuY9Lwu2hkz8Bap6gbNAoj9GvQ23s9KgowiTWvYmtM
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="生成一个新的 Solana 钱包密钥对，并将其保存到指定文件"&gt;&lt;strong&gt;生成一个新的 Solana 钱包密钥对，并将其保存到指定文件&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor/target/deploy on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ solana-keygen new &lt;span class="nt"&gt;-o&lt;/span&gt; new.json
Generating a new keypair

For added security, enter a BIP39 passphrase

NOTE! This passphrase improves security of the recovery seed phrase NOT the
keypair file itself, which is stored as insecure plain text

BIP39 Passphrase &lt;span class="o"&gt;(&lt;/span&gt;empty &lt;span class="k"&gt;for &lt;/span&gt;none&lt;span class="o"&gt;)&lt;/span&gt;: 

Wrote new keypair to new.json
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
pubkey: H1tuY9Lwu2hkz8Bap6gbNAoj9GvQ23s9KgowiTWvYmtM
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
Save this seed phrase and your BIP39 passphrase to recover your new keypair:
usual daughter question found &lt;span class="nb"&gt;arch &lt;/span&gt;absent term spawn runway sphere spin despair
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;

buy-restrictor/target/deploy on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base took 9.1s 
➜ &lt;span class="nb"&gt;ls
&lt;/span&gt;buy_restrictor-keypair.json buy_restrictor.so           new.json

buy-restrictor/target/deploy on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ &lt;span class="nb"&gt;mv &lt;/span&gt;new.json buy_restrictor-keypair.json 

buy-restrictor/target/deploy on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ &lt;span class="nb"&gt;ls
&lt;/span&gt;buy_restrictor-keypair.json buy_restrictor.so
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="获取指定密钥对文件（.json）对应的 Solana 公钥地址"&gt;&lt;strong&gt;获取指定密钥对文件（&lt;code&gt;.json&lt;/code&gt;）对应的 Solana 公钥地址&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor/target/deploy on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ solana address &lt;span class="nt"&gt;-k&lt;/span&gt; buy_restrictor-keypair.json              
H1tuY9Lwu2hkz8Bap6gbNAoj9GvQ23s9KgowiTWvYmtM
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="自动同步项目中所有程序的 Program ID（程序地址）"&gt;&lt;strong&gt;自动同步项目中所有程序的 Program ID（程序地址）&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ anchor keys &lt;span class="nb"&gt;sync                                 
&lt;/span&gt;Found incorrect program &lt;span class="nb"&gt;id &lt;/span&gt;declaration &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"/Users/qiaopengjun/Code/solana-code/2025/buy-restrictor/programs/buy-restrictor/src/lib.rs"&lt;/span&gt;
Updated to H1tuY9Lwu2hkz8Bap6gbNAoj9GvQ23s9KgowiTWvYmtM

Found incorrect program &lt;span class="nb"&gt;id &lt;/span&gt;declaration &lt;span class="k"&gt;in &lt;/span&gt;Anchor.toml &lt;span class="k"&gt;for &lt;/span&gt;the program &lt;span class="sb"&gt;`&lt;/span&gt;buy_restrictor&lt;span class="sb"&gt;`&lt;/span&gt;
Updated to H1tuY9Lwu2hkz8Bap6gbNAoj9GvQ23s9KgowiTWvYmtM

All program &lt;span class="nb"&gt;id &lt;/span&gt;declarations are synced.
Please rebuild the program to update the generated artifacts.

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="编译 Solana 智能合约项目，生成部署所需的程序密钥和二进制文件"&gt;&lt;strong&gt;编译 Solana 智能合约项目，生成部署所需的程序密钥和二进制文件&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base took 14.9s 
➜ anchor build
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;release&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;optimized] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.39s
    Finished &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; profile &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.46s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;/Users/qiaopengjun/Code/solana-code/2025/buy-restrictor/target/debug/deps/buy_restrictor-6b544dadb31b7c3c&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="详细列出 Solana 项目编译后生成的部署文件"&gt;&lt;strong&gt;详细列出 Solana 项目编译后生成的部署文件&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ll target/deploy
&lt;span class="c"&gt;# 等效于：&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; target/deploy

buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ ll target/deploy            
total 464
&lt;span class="nt"&gt;-rw-------&lt;/span&gt;  1 qiaopengjun  staff   233B Mar 26 20:33 buy_restrictor-keypair.json
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;  1 qiaopengjun  staff   225K Mar 26 22:25 buy_restrictor.so

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="检测 buy_restrictor.so 文件的类型和格式"&gt;&lt;strong&gt;检测 &lt;code&gt;buy_restrictor.so&lt;/code&gt; 文件的类型和格式&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;file target/deploy/buy_restrictor.so&lt;/code&gt; 是 Linux/Unix 系统命令，用于&lt;strong&gt;检测 &lt;code&gt;buy_restrictor.so&lt;/code&gt; 文件的类型和格式&lt;/strong&gt;。对于 Solana 智能合约编译后的 &lt;code&gt;.so&lt;/code&gt; 文件，输出结果会揭示其底层二进制结构。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 via 🅒 base 
➜ file target/deploy/buy_restrictor.so 
target/deploy/buy_restrictor.so: ELF 64-bit LSB shared object, eBPF, version 1 &lt;span class="o"&gt;(&lt;/span&gt;SYSV&lt;span class="o"&gt;)&lt;/span&gt;, dynamically linked, stripped
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="在 Solana 区块链上创建新的 SPL 代币"&gt;在 Solana 区块链上创建新的 SPL 代币&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;spl-token create-token&lt;/code&gt; 是 Solana 的 SPL Token 命令行工具中的命令，&lt;strong&gt;用于在 Solana 区块链上创建新的 SPL 代币（同质化代币，如 USDC 这类可互换代币）&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor/client on  master &lt;span class="o"&gt;[&lt;/span&gt;?] is 📦 1.0.0 via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token create-token                                               
Creating token Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe under program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

Address:  Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe
Decimals:  9

Signature: tgV38oEFfEyoupbxXTHsjbp7wjivuYCJaEexN8eE84Cf1ss2QStvYfitNwnYHFUgWVENpiyAyfDJuUbK4mNXEDy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://solscan.io/token/Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/token/Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe?cluster=devnet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://solscan.io/tx/tgV38oEFfEyoupbxXTHsjbp7wjivuYCJaEexN8eE84Cf1ss2QStvYfitNwnYHFUgWVENpiyAyfDJuUbK4mNXEDy?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/tx/tgV38oEFfEyoupbxXTHsjbp7wjivuYCJaEexN8eE84Cf1ss2QStvYfitNwnYHFUgWVENpiyAyfDJuUbK4mNXEDy?cluster=devnet&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="为指定的 SPL 代币（Token Mint）创建关联账户（Associated Token Account, ATA）"&gt;&lt;strong&gt;为指定的 SPL 代币（Token Mint）创建关联账户（Associated Token Account, ATA）&lt;/strong&gt;&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;术语&lt;/th&gt;
&lt;th style="text-align:center;"&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;strong&gt;代币 Mint 地址&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;代币的唯一标识（由 &lt;code&gt;spl-token create-token&lt;/code&gt; 创建）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;strong&gt;关联代币账户 (ATA)&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;每个钱包对每个代币有唯一的存储账户，格式为派生地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;strong&gt;原生账户&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;必须存在才能接收/持有对应代币&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-code/2025/client is 📦 1.0.0 via ⬢ v22.1.0 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token create-account Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe
Creating account 8yno4sSTn1Reh3uRdNiEWmRJ17zZ7Y4W1Ace1bJarYr1

Signature: 3Y1pMZUybRk59fXGKrxhDJmGhK2PFPGnXcxBuUyV5wYfZRcC9BfWcrTpPYwVjRBSDTFeNNxUhstzpuUAQThrV88A


ATA 地址 &lt;span class="o"&gt;=&lt;/span&gt; 钱包地址 + 代币 Mint 地址 + 固定种子（&lt;span class="s2"&gt;"associated-token-account"&lt;/span&gt;）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;钱包地址&lt;/code&gt; → 关联 → &lt;code&gt;ATA 地址&lt;/code&gt; → 绑定 → &lt;code&gt;Token Mint 地址&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="向指定代币（Token Mint）铸造新代币并存入目标账户"&gt;&lt;strong&gt;向指定代币（Token Mint）铸造新代币并存入目标账户&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-code/2025/client is 📦 1.0.0 via ⬢ v22.1.0 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base took 3.2s 
➜ spl-token mint Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe 100 8yno4sSTn1Reh3uRdNiEWmRJ17zZ7Y4W1Ace1bJarYr1                                            
Minting 100 tokens
  Token: Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe
  Recipient: 8yno4sSTn1Reh3uRdNiEWmRJ17zZ7Y4W1Ace1bJarYr1

Signature: 34r7ne14yr8H6FJtUkRkS9dEbifJcMPcHUScRTqFBa8xeJCpx8VYgua1nXbxjv1dbUkjGzdEhVjZXjALgzvupS5V


secure_token_sale on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token mint Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe 100 7zzrX4Wn3ovnyMmZ8tXutHrJobvTTWkUutfAb7mPBFiq                                            
Minting 100 tokens
  Token: Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe
  Recipient: 7zzrX4Wn3ovnyMmZ8tXutHrJobvTTWkUutfAb7mPBFiq

Signature: 4oBteNo6DNHRdPB1tWyjhiuLDDnZHUw3vyxGPZZocZ1Minbz6V3tmKJoXyRz379Tr1JH58voouBGG6FvLvpAqb5d

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="列出当前钱包持有的所有 SPL 代币账户及其余额"&gt;&lt;strong&gt;列出当前钱包持有的所有 SPL 代币账户及其余额&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-code/2025/client is 📦 1.0.0 via ⬢ v22.1.0 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base took 3.4s 
➜ spl-token accounts
Token                                         Balance
&lt;span class="nt"&gt;-----------------------------------------------------&lt;/span&gt;
SNumWwVm1XCYZhupdHGYhG3MSfXTq1PJLm24nZVqTLk   1  
3jT7QdVfPh3isZA9Qfu6hcPRd1ATTkSbzaTs1qf3bxPG  1  
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU  20 
8BQHSWLd2vNJbiqd2DciWgrsyfG8AiL3T1Fc1hreXMsq  1  
E7eHC3g4QsFXuaBe3X2wVr54yEvHK8K8fq6qrgB64djx  88 
Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe  100
Fmt6FhmA9QQxkhgDBZJvSgZNH44VaQL9uecr9B7Zwufj  1  

&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查询特定 SPL 代币（Token Mint）当前总供应量"&gt;&lt;strong&gt;查询特定 SPL 代币（Token Mint）当前总供应量&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-code/2025/client is 📦 1.0.0 via ⬢ v22.1.0 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token supply Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe      
100
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查询当前默认钱包中特定 SPL 代币余额"&gt;&lt;strong&gt;查询当前默认钱包中特定 SPL 代币余额&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token balance &amp;lt;代币Mint地址&amp;gt;
&lt;span class="c"&gt;# 示例：&lt;/span&gt;
spl-token balance Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe

solana-code/2025/client is 📦 1.0.0 via ⬢ v22.1.0 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token balance Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe
100
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查询 SPL 代币账户（Token Account）的完整链上信息"&gt;&lt;strong&gt;查询 SPL 代币账户（Token Account）的完整链上信息&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-code/2025/client is 📦 1.0.0 via ⬢ v22.1.0 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token account-info Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe

SPL Token Account
  Address: 8yno4sSTn1Reh3uRdNiEWmRJ17zZ7Y4W1Ace1bJarYr1
  Program: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  Balance: 100
  Decimals: 9
  Mint: Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe
  Owner: 6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD
  State: Initialized
  Delegation: &lt;span class="o"&gt;(&lt;/span&gt;not &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  Close authority: &lt;span class="o"&gt;(&lt;/span&gt;not &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="创建一个精度为 0 的 SPL 代币（每个代币不可分割，适合 NFT 或整数型代币）"&gt;创建一个精度为 0 的 SPL 代币（每个代币不可分割，适合 NFT 或整数型代币）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ spl-token create-token &lt;span class="nt"&gt;--decimals&lt;/span&gt; 0  
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="创建一个默认启用冻结功能的SPL代币"&gt;创建一个默认启用冻结功能的 SPL 代币&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ spl-token create-token &lt;span class="nt"&gt;--enable-freeze&lt;/span&gt;                               
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="生成一个不关联助记词的ED25519密钥对文件"&gt;生成一个不关联助记词的 ED25519 密钥对文件&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ solana-keygen new &lt;span class="nt"&gt;--no-bip39-passphrase&lt;/span&gt; &lt;span class="nt"&gt;--outfile&lt;/span&gt; token_pool_keypair.json
Generating a new keypair
Wrote new keypair to token_pool_keypair.json
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
pubkey: 8gi14E6ZJmth6mhauwmPHDTT8LeAZgMsYAwryjgUU9y9
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
Save this seed phrase to recover your new keypair:
clarify slice lucky trouble coil believe caution debate wall pass north abandon
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="参数深度解析"&gt;&lt;strong&gt;参数深度解析&lt;/strong&gt;&lt;/h4&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;参数&lt;/th&gt;
&lt;th style="text-align:center;"&gt;作用&lt;/th&gt;
&lt;th style="text-align:center;"&gt;使用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--no-bip39-passphrase&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;禁用助记词生成&lt;/td&gt;
&lt;td style="text-align:center;"&gt;自动化部署&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--outfile&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;指定密钥保存路径&lt;/td&gt;
&lt;td style="text-align:center;"&gt;自定义密钥目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;
&lt;code&gt;--force&lt;/code&gt; (可选)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;覆盖已存在文件&lt;/td&gt;
&lt;td style="text-align:center;"&gt;密钥轮换时使用&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h3 id="向指定地址空投 SOL（测试网代币）"&gt;向指定地址空投 SOL（测试网代币）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ solana airdrop 0.01 8gi14E6ZJmth6mhauwmPHDTT8LeAZgMsYAwryjgUU9y9 &lt;span class="nt"&gt;--url&lt;/span&gt; devnet
Requesting airdrop of 0.01 SOL

Signature: 2DbiptsJVSy37y3DmTnqxfC84Am2LZ1ES6Mdc9uKLUW7TZeJvnFkuPST92Bpav8qmjavz9wq2s5S14ru2WsiycSa

0.01 SOL

buy-restrictor on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base took 11.3s 
➜ solana airdrop 1 8gi14E6ZJmth6mhauwmPHDTT8LeAZgMsYAwryjgUU9y9 &lt;span class="nt"&gt;--url&lt;/span&gt; devnet   
Requesting airdrop of 1 SOL

Signature: 514cVcKnxiQo6tA57kgLxFG4iJ93dRgt8V3HFKn16Ydi2yeaMA3Ax5FLNv5Gxc86vm83reMRdg4oZKU99Po36p6a

1.01 SOL
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="在Solana的Devnet测试网络上，为指定代币创建一个新的关联账户（Associated Token Account, ATA）"&gt;&lt;strong&gt;在 Solana 的 Devnet 测试网络上，为指定代币创建一个新的关联账户（Associated Token Account, ATA）&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;secure_token_sale on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token create-account Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe &lt;span class="nt"&gt;--owner&lt;/span&gt; H6Su7YsGK5mMASrZvJ51nt7oBzD88V8FKSBPNnRG1u3k &lt;span class="nt"&gt;--url&lt;/span&gt; devnet &lt;span class="nt"&gt;--fee-payer&lt;/span&gt; ~/.config/solana/id.json  
Creating account 8QPaQ6AiLqyNuCL69PCQoUbfDNE7r9agiNnvdXzhmMRJ

Signature: 59nvJc67zJRXKz6w6WyzqrdsKbaJhgXz1oRTLhxvMNkMgCNPCr4QE5q814BKsey2PgqTxgEBY4GW1W49D5AaxonQ


secure_token_sale on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base 
➜ spl-token create-account Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe &lt;span class="nt"&gt;--owner&lt;/span&gt; CwuRGwa3wQbzSVJdKVjLs3Ygf54BXuNqtm45Pw1ajjN2 &lt;span class="nt"&gt;--url&lt;/span&gt; devnet &lt;span class="nt"&gt;--fee-payer&lt;/span&gt; ~/.config/solana/id.json 
Creating account 7zzrX4Wn3ovnyMmZ8tXutHrJobvTTWkUutfAb7mPBFiq

Signature: K4PR1fKtCshoNZBpx69X7YRqYc2KJnwSUbyBQYdTWsM2x4oGLTznZ4YNLwSi9B1hrFVs1bs6i7RT7SF5kHpz2nY

&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="参数说明"&gt;&lt;strong&gt;参数说明&lt;/strong&gt;&lt;/h4&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;参数&lt;/th&gt;
&lt;th style="text-align:center;"&gt;作用&lt;/th&gt;
&lt;th style="text-align:center;"&gt;备注&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;Fgxp6...4GEe&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;代币的 Mint 地址&lt;/td&gt;
&lt;td style="text-align:center;"&gt;要创建关联账户的代币&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--owner&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;指定账户所有者&lt;/td&gt;
&lt;td style="text-align:center;"&gt;目标钱包地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--url devnet&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;在 Devnet 网络操作&lt;/td&gt;
&lt;td style="text-align:center;"&gt;也可用完整 RPC URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--fee-payer&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;支付交易费的密钥&lt;/td&gt;
&lt;td style="text-align:center;"&gt;默认使用 config 设置&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h3 id="查询指定钱包地址持有的特定代币余额"&gt;&lt;strong&gt;查询指定钱包地址持有的特定代币余额&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;secure_token_sale on  master &lt;span class="o"&gt;[&lt;/span&gt;?] via ⬢ v22.1.0 via 🦀 1.85.1 on 🐳 v27.5.1 &lt;span class="o"&gt;(&lt;/span&gt;orbstack&lt;span class="o"&gt;)&lt;/span&gt; via 🅒 base took 2.0s 
➜ spl-token balance Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe &lt;span class="nt"&gt;--owner&lt;/span&gt; CwuRGwa3wQbzSVJdKVjLs3Ygf54BXuNqtm45Pw1ajjN2                                            
101

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Token-2022 实操"&gt;Token-2022 实操&lt;/h2&gt;&lt;h3 id="创建代币并启用关闭 Mint 账户的能力"&gt;创建代币并启用关闭 Mint 账户的能力&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token &lt;span class="nt"&gt;--program-id&lt;/span&gt; TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token &lt;span class="nt"&gt;--enable-close&lt;/span&gt;
Creating token Eoy68rekZ22g3kucXXPLoPJgaoqv5B2KJN7AJCWNYpCJ under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address:  Eoy68rekZ22g3kucXXPLoPJgaoqv5B2KJN7AJCWNYpCJ
Decimals:  9

Signature: 4RKTy9BAyXh4PGgxttziZveAZFH3zxBCUYQvLxgW3nmsmcxx1PQC9NnJ4qB6ktxfhHVdFTQpp4EsxFe84ujy6hnh

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://solscan.io/tx/4RKTy9BAyXh4PGgxttziZveAZFH3zxBCUYQvLxgW3nmsmcxx1PQC9NnJ4qB6ktxfhHVdFTQpp4EsxFe84ujy6hnh?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/tx/4RKTy9BAyXh4PGgxttziZveAZFH3zxBCUYQvLxgW3nmsmcxx1PQC9NnJ4qB6ktxfhHVdFTQpp4EsxFe84ujy6hnh?cluster=devnet&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="关闭 Mint 账户"&gt;关闭 Mint 账户&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The supply on the mint must be 0.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token close-mint Eoy68rekZ22g3kucXXPLoPJgaoqv5B2KJN7AJCWNYpCJ

Signature: 3i36vhAAq2sjPfVYzo3xtYdt5naFYCuKe6TsDzwqJSj1AD4WqAyotYKrPVM851J2hKwraCMjBL2fCoyDeCiVzNCM

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://solscan.io/tx/3i36vhAAq2sjPfVYzo3xtYdt5naFYCuKe6TsDzwqJSj1AD4WqAyotYKrPVM851J2hKwraCMjBL2fCoyDeCiVzNCM?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/tx/3i36vhAAq2sjPfVYzo3xtYdt5naFYCuKe6TsDzwqJSj1AD4WqAyotYKrPVM851J2hKwraCMjBL2fCoyDeCiVzNCM?cluster=devnet&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="查看 PayPal USD mintCloseAuthority"&gt;查看 PayPal USD mintCloseAuthority&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://solscan.io/token/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo#extensions" rel="nofollow" target="_blank"&gt;https://solscan.io/token/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo#extensions&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过这篇实战指南，我们从零开始玩转了 Web3 Solana 开发的全流程：从工具链配置到密钥生成，再到智能合约部署和代币创建，每一步都清晰可见。Solana 的高性能和灵活的代币功能为 Web3 创新提供了广阔舞台，而掌握这些技能，你就能在去中心化世界中大展身手。文章不仅提供了操作细节，还附上链上链接和参考资源，随时助你进阶。接下来，不妨试试更复杂的 Solana 项目，开启你的 Web3 冒险吧！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spl.solana.com/token-2022/extensions" rel="nofollow" target="_blank"&gt;https://spl.solana.com/token-2022/extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/tx/4RKTy9BAyXh4PGgxttziZveAZFH3zxBCUYQvLxgW3nmsmcxx1PQC9NnJ4qB6ktxfhHVdFTQpp4EsxFe84ujy6hnh?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/tx/4RKTy9BAyXh4PGgxttziZveAZFH3zxBCUYQvLxgW3nmsmcxx1PQC9NnJ4qB6ktxfhHVdFTQpp4EsxFe84ujy6hnh?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/token/Eoy68rekZ22g3kucXXPLoPJgaoqv5B2KJN7AJCWNYpCJ?cluster=devnet#extensions" rel="nofollow" target="_blank"&gt;https://solscan.io/token/Eoy68rekZ22g3kucXXPLoPJgaoqv5B2KJN7AJCWNYpCJ?cluster=devnet#extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spl.solana.com/token-2022" rel="nofollow" target="_blank"&gt;https://spl.solana.com/token-2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/solana-program/token-2022/blob/main/README.md" rel="nofollow" target="_blank"&gt;https://github.com/solana-program/token-2022/blob/main/README.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/solana-developers/program-examples/tree/main/tokens/token-2022" rel="nofollow" target="_blank"&gt;https://github.com/solana-developers/program-examples/tree/main/tokens/token-2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/solana-developers/program-examples/blob/main/tokens/token-2022/transfer-hook/whitelist/anchor/tests/transfer-hook.ts" rel="nofollow" target="_blank"&gt;https://github.com/solana-developers/program-examples/blob/main/tokens/token-2022/transfer-hook/whitelist/anchor/tests/transfer-hook.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs/tokens/extensions" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs/tokens/extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/solana-developers/program-examples/tree/main/tokens/token-2022" rel="nofollow" target="_blank"&gt;https://github.com/solana-developers/program-examples/tree/main/tokens/token-2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/coral-xyz/anchor/tree/0e5285aecdf410fa0779b7cd09a47f235882c156/spl/src/token_2022_extensions" rel="nofollow" target="_blank"&gt;https://github.com/coral-xyz/anchor/tree/0e5285aecdf410fa0779b7cd09a47f235882c156/spl/src/token_2022_extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs/references/account-types" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs/references/account-types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/firechiang/solana-study" rel="nofollow" target="_blank"&gt;https://github.com/firechiang/solana-study&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Ellipsis-Labs/solana-verifiable-build?tab=readme-ov-file" rel="nofollow" target="_blank"&gt;https://github.com/Ellipsis-Labs/solana-verifiable-build?tab=readme-ov-file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://soldev.cn/topics/14" rel="nofollow" target="_blank"&gt;https://soldev.cn/topics/14&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@solana/spl-token" rel="nofollow" target="_blank"&gt;https://www.npmjs.com/package/@solana/spl-token&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://beta.solpg.io/" rel="nofollow" target="_blank"&gt;https://beta.solpg.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Ellipsis-Labs/solana-verifiable-build" rel="nofollow" target="_blank"&gt;https://github.com/Ellipsis-Labs/solana-verifiable-build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solscan.io/token/Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe?cluster=devnet" rel="nofollow" target="_blank"&gt;https://solscan.io/token/Fgxp6CWJnUfmt66vaHzjNf4SFPoG23PmFEb1MXcr4GEe?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/5Wcez6qMnY8nTyUnwaTUXoF59qWHcrAVK8usJhjnQurc6po6uupeoRt7hg1gXCoEsJL18LTNoiM599VYZLkyB49Q?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/5Wcez6qMnY8nTyUnwaTUXoF59qWHcrAVK8usJhjnQurc6po6uupeoRt7hg1gXCoEsJL18LTNoiM599VYZLkyB49Q?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/5Det5VGmkQY67R9qL8JxAhurBXa17nvBDtZqdcuh98C97GNP93LSMrQdkHa71qQoEcm3Y7AX94ScJWrop3wF6dW6?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/5Det5VGmkQY67R9qL8JxAhurBXa17nvBDtZqdcuh98C97GNP93LSMrQdkHa71qQoEcm3Y7AX94ScJWrop3wF6dW6?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/675ARR4Riro9uaKNwVWMT4XAJiyAyjbN9A1gMGa29tBZsc2HpZvB4cnMcBJqh8epmLWuHP9o7XGEgrPkR1R94MFP?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/675ARR4Riro9uaKNwVWMT4XAJiyAyjbN9A1gMGa29tBZsc2HpZvB4cnMcBJqh8epmLWuHP9o7XGEgrPkR1R94MFP?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/3GeEiTkbwmT5TcyBZiu2EwFEuSG86Qr7XS9ffPcTvMay9By1VcJddeK6UTv6puPD4s4cZM735ViV5RN2VYuxkhJf?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/3GeEiTkbwmT5TcyBZiu2EwFEuSG86Qr7XS9ffPcTvMay9By1VcJddeK6UTv6puPD4s4cZM735ViV5RN2VYuxkhJf?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/2JPm1XkCG6iER2vVp4EP4nNbo5LQPWsc9GDTuvuMgyMSCgbR4LMiqoZiprh67vAyTsas65Vuk6ugY6UAkamPqnCM?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/2JPm1XkCG6iER2vVp4EP4nNbo5LQPWsc9GDTuvuMgyMSCgbR4LMiqoZiprh67vAyTsas65Vuk6ugY6UAkamPqnCM?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/J6dgGPkC37LVYbxYxPEUr83h9QWqzd7jYPXaxqSuncA9VL2UC3rftnU7GspDjPqTnuk71T2MQVA5aFykv4osmM3?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/J6dgGPkC37LVYbxYxPEUr83h9QWqzd7jYPXaxqSuncA9VL2UC3rftnU7GspDjPqTnuk71T2MQVA5aFykv4osmM3?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/4vCBKAXy1T8FF2bPJ4U7tHAYs7ThwSpdSaMyB1EbEfZcX3SPhNDdWpnr6h8ptVjmzEXcMyS8UGXDKVtr1nx4uBxx?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4vCBKAXy1T8FF2bPJ4U7tHAYs7ThwSpdSaMyB1EbEfZcX3SPhNDdWpnr6h8ptVjmzEXcMyS8UGXDKVtr1nx4uBxx?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/3HSUApzJdqEvFJCkZovUSGGEV7Y1caYgpzEzK1WjVQpfoZLEDr7kB3a6gKp3mUTGreHRFL8XVG2HBmCVreqkXK7C?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/3HSUApzJdqEvFJCkZovUSGGEV7Y1caYgpzEzK1WjVQpfoZLEDr7kB3a6gKp3mUTGreHRFL8XVG2HBmCVreqkXK7C?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/247bZYoAbYWMrMh9C98jGV4JRFg75kp6zbRdV4xAQmfBs6CAh1vR2YQ3DfN3tDegLCWUT8e7rUuHSYWVqmRQ1GeQ?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/247bZYoAbYWMrMh9C98jGV4JRFg75kp6zbRdV4xAQmfBs6CAh1vR2YQ3DfN3tDegLCWUT8e7rUuHSYWVqmRQ1GeQ?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/5eZxozWgr26gQQCX5YZjVXkUQEFbGaG9N84KA8qm4J5AvMzk9QkRNurDaRHg66K2sbYSfqBTfACNaWXRefrFiNmw?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/5eZxozWgr26gQQCX5YZjVXkUQEFbGaG9N84KA8qm4J5AvMzk9QkRNurDaRHg66K2sbYSfqBTfACNaWXRefrFiNmw?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/5UZAabRXQSeHntYdCxapziKD4dCAbSGDaQMumXSdXJfS9eYrV5R6saffx7Z6m1WuqotYRV1sNFM5dL7emyjSAEm3?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/5UZAabRXQSeHntYdCxapziKD4dCAbSGDaQMumXSdXJfS9eYrV5R6saffx7Z6m1WuqotYRV1sNFM5dL7emyjSAEm3?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/2bPcHPHxCXCvu7C97QbRfuBGKmYCcMpmewpypCVpgHBTgMthetJtE3j9iQgq1yiJvKbJAi993PjZY7nsDWjk14iX?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/2bPcHPHxCXCvu7C97QbRfuBGKmYCcMpmewpypCVpgHBTgMthetJtE3j9iQgq1yiJvKbJAi993PjZY7nsDWjk14iX?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/4cG1aJjNgvx6vQMxazMjzaeFgvUEyUieajCken2ARajsYuRtSiYwSmENezs3rJ7mURvcYd1caeKr1gojzjWWiwYR?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4cG1aJjNgvx6vQMxazMjzaeFgvUEyUieajCken2ARajsYuRtSiYwSmENezs3rJ7mURvcYd1caeKr1gojzjWWiwYR?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/5Aj2Bv4LT9Ga3PREz92ms15jBEU9JmGPbxz4hmUxSdqi4fNtGdfmgvkvPuSYziKir42ShVcbCK6EjEjfc2vfakM7?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/5Aj2Bv4LT9Ga3PREz92ms15jBEU9JmGPbxz4hmUxSdqi4fNtGdfmgvkvPuSYziKir42ShVcbCK6EjEjfc2vfakM7?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/4HyVbTLnAD76gXMWYnKHF7qfq6UKJbX4jCbFdg2JxEyeVX7jnnh5L1hbXuYZrefs6AC2HCuRCjq2HLCK6WdRnYgM?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4HyVbTLnAD76gXMWYnKHF7qfq6UKJbX4jCbFdg2JxEyeVX7jnnh5L1hbXuYZrefs6AC2HCuRCjq2HLCK6WdRnYgM?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/4wcpV24tdGQAjaS6k46KMxLG44PiuS4xaaXvbLKkvVEMd5BSEFL14eTMLxRRZvwyW41vmjZBBqCDstXppBHtkXq4?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4wcpV24tdGQAjaS6k46KMxLG44PiuS4xaaXvbLKkvVEMd5BSEFL14eTMLxRRZvwyW41vmjZBBqCDstXppBHtkXq4?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/gqrVKtsqJ2tpiBWiEdfg8gc2T47TwsZpbAD31uwE4T4txqDYV9yATtws7F5ntyLRRQmyX8PSucBx6u1Q4bThJAz?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/gqrVKtsqJ2tpiBWiEdfg8gc2T47TwsZpbAD31uwE4T4txqDYV9yATtws7F5ntyLRRQmyX8PSucBx6u1Q4bThJAz?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/4QkhnajbDoPQY9Fw1KUvarmmM5uHYaAMaD5tAk8xmpeJ8qX17FEJ2yLojsVcSy8r43coLep1sVfbne8zPjkihjqB?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4QkhnajbDoPQY9Fw1KUvarmmM5uHYaAMaD5tAk8xmpeJ8qX17FEJ2yLojsVcSy8r43coLep1sVfbne8zPjkihjqB?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/frFFqXHXMcX26UdJRkebHYzMEaZMWDubFB2un61XgitExSCKurnF4QDkxTd55G769CFAzrB7kpyWVNxxcg1w1gb?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/frFFqXHXMcX26UdJRkebHYzMEaZMWDubFB2un61XgitExSCKurnF4QDkxTd55G769CFAzrB7kpyWVNxxcg1w1gb?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/2dg5ZrwK5qyZACnr7gKghQJmohAz7WpnMUFxY4hAa75FucfhvZ6FNWCypH7tF144f8B4jguboJ1WYyHYe2FwubQ7?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/2dg5ZrwK5qyZACnr7gKghQJmohAz7WpnMUFxY4hAa75FucfhvZ6FNWCypH7tF144f8B4jguboJ1WYyHYe2FwubQ7?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/2XE41q1AuEgrPW97Z8fxEBU3fDQmZyQCj1Q83JfSbzmHSTBiQHLreCy56EAm7wBEKRVoSF4x8fyeqE8hdMDEbXdG?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/2XE41q1AuEgrPW97Z8fxEBU3fDQmZyQCj1Q83JfSbzmHSTBiQHLreCy56EAm7wBEKRVoSF4x8fyeqE8hdMDEbXdG?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/3GDsPPyW5hSEXue3Aikdh4qSUP96cGAb4nYvbi49zUChrqE6hYZKD6HM4M4Nx76apQU5pVnhgiv6eb4pJPkUEEac?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/3GDsPPyW5hSEXue3Aikdh4qSUP96cGAb4nYvbi49zUChrqE6hYZKD6HM4M4Nx76apQU5pVnhgiv6eb4pJPkUEEac?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/4yX9JYT5BcyL3sB1pdhkZQ4JvGs9riwyFSWGNQYrPr8aY6fq7v4xXcxtdkEZUBq5hJjg16wvxhm6Si8r93gR23n6?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/4yX9JYT5BcyL3sB1pdhkZQ4JvGs9riwyFSWGNQYrPr8aY6fq7v4xXcxtdkEZUBq5hJjg16wvxhm6Si8r93gR23n6?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/3AL9zTMVGbwu9t5QGTsY9xbdqd6MZqqSJsrD9S8aKak6VgEGpVKQSYc9yB9GKk7URVNPQtaC8nPKN7U3LgGEfdxT?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/3AL9zTMVGbwu9t5QGTsY9xbdqd6MZqqSJsrD9S8aKak6VgEGpVKQSYc9yB9GKk7URVNPQtaC8nPKN7U3LgGEfdxT?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/3DEiwc5eAuawbYu7AxEFdWNkyn2pXn2VFYsoDvtv57KeNRDKWMeuWdTx6KfyqZz6ahgSdWW63ypDmKfXPRH7jXT1?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/3DEiwc5eAuawbYu7AxEFdWNkyn2pXn2VFYsoDvtv57KeNRDKWMeuWdTx6KfyqZz6ahgSdWW63ypDmKfXPRH7jXT1?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/G9GTsZzTmNzBcG2BoV5KNRom2DCAvmytpJBS1tX869iixHC3povQknTCdNso11cEwvecHZB6y27rfiD6fD7tY9C?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/G9GTsZzTmNzBcG2BoV5KNRom2DCAvmytpJBS1tX869iixHC3povQknTCdNso11cEwvecHZB6y27rfiD6fD7tY9C?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Mon, 28 Apr 2025 19:34:44 +0800</pubDate>
      <link>https://soldev.cn/topics/171</link>
      <guid>https://soldev.cn/topics/171</guid>
    </item>
    <item>
      <title> Web3 开发入门：Solana CLI 配置与本地验证器实战</title>
      <description>&lt;h2 id="Web3 开发入门：Solana CLI 配置与本地验证器实战"&gt;Web3 开发入门：Solana CLI 配置与本地验证器实战&lt;/h2&gt;
&lt;p&gt;想快速踏入 Web3 开发的潮流？Solana 作为高性能区块链的代表，以其极速交易和低成本吸引了无数开发者。而 Solana CLI（命令行界面）是你与 Solana 网络交互的起点！本文通过简单易懂的实操步骤，带你从零掌握 Solana CLI 的配置、钱包创建、空投 SOL 代币到运行本地验证器。无论你是区块链新手还是 Web3 爱好者，这篇教程将是你开启 Solana 开发之旅的完美指南！&lt;/p&gt;

&lt;p&gt;本文是 Web3 开发者的 Solana CLI 入门实战教程，涵盖从环境检查到运行本地验证器的全流程。内容包括：检查 CLI 工具版本、配置 Devnet 网络、生成密钥对管理钱包、空投测试 SOL 代币、查询余额，以及搭建和运行本地验证器。每一步均配有详细命令、输出示例和关键参数说明，助你快速上手 Solana 开发，迈出 Web3 实践第一步。&lt;/p&gt;
&lt;h2 id="实操"&gt;实操&lt;/h2&gt;&lt;h3 id="前提"&gt;前提&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;anchor &lt;span class="nt"&gt;--version&lt;/span&gt;
anchor-cli 0.31.0

avm &lt;span class="nt"&gt;--version&lt;/span&gt;
avm 0.31.0

solana &lt;span class="nt"&gt;--version&lt;/span&gt;
solana-cli 2.1.21 &lt;span class="o"&gt;(&lt;/span&gt;src:8a085eeb&lt;span class="p"&gt;;&lt;/span&gt; feat:1416569292, client:Agave&lt;span class="o"&gt;)&lt;/span&gt;

rustc &lt;span class="nt"&gt;--version&lt;/span&gt;
rustc 1.86.0 &lt;span class="o"&gt;(&lt;/span&gt;05f9846f8 2025-03-31&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看您当前的配置："&gt;查看您当前的配置：&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana config get
Config File: /Users/qiaopengjun/.config/solana/cli/config.yml
RPC URL: https://api.mainnet-beta.solana.com 
WebSocket URL: wss://api.mainnet-beta.solana.com/ &lt;span class="o"&gt;(&lt;/span&gt;computed&lt;span class="o"&gt;)&lt;/span&gt;
Keypair Path: /Users/qiaopengjun/.config/solana/id.json 
Commitment: confirmed 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The RPC URL and Websocket URL specify the Solana cluster the CLI makes requests to.&lt;/p&gt;
&lt;h3 id="设置网络环境为 Devnet"&gt;设置网络环境为 &lt;code&gt;Devnet&lt;/code&gt;
&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# solana config set -ud    # For devnet&lt;/span&gt;
solana config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--url&lt;/span&gt; devnet

Config File: /Users/qiaopengjun/.config/solana/cli/config.yml
RPC URL: https://api.devnet.solana.com 
WebSocket URL: wss://api.devnet.solana.com/ &lt;span class="o"&gt;(&lt;/span&gt;computed&lt;span class="o"&gt;)&lt;/span&gt;
Keypair Path: /Users/qiaopengjun/.config/solana/id.json 
Commitment: confirmed 

&lt;span class="c"&gt;# 查看&lt;/span&gt;
solana config get
Config File: /Users/qiaopengjun/.config/solana/cli/config.yml
RPC URL: https://api.devnet.solana.com 
WebSocket URL: wss://api.devnet.solana.com/ &lt;span class="o"&gt;(&lt;/span&gt;computed&lt;span class="o"&gt;)&lt;/span&gt;
Keypair Path: /Users/qiaopengjun/.config/solana/id.json 
Commitment: confirmed 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Keypair Path points to the default Solana wallet (keypair) used by the Solana CLI to pay transaction fees and deploy programs. By default, this file is stored at &lt;code&gt;~/.config/solana/id.json&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="创建钱包 - 生成新的 Solana 密钥对（用于钱包地址和交易签名）"&gt;创建钱包 - 生成新的 &lt;strong&gt;Solana 密钥对&lt;/strong&gt;（用于钱包地址和交易签名）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-keygen new
Generating a new keypair

For added security, enter a BIP39 passphrase

NOTE! This passphrase improves security of the recovery seed phrase NOT the
keypair file itself, which is stored as insecure plain text

BIP39 Passphrase &lt;span class="o"&gt;(&lt;/span&gt;empty &lt;span class="k"&gt;for &lt;/span&gt;none&lt;span class="o"&gt;)&lt;/span&gt;: 

Wrote new keypair to /Users/qiaopengjun/.config/solana/id.json
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
pubkey: 6MZDRo5v8K2NfdohdD76QNGH3Aup53BeMaRAxxx
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
Save this seed phrase and your BIP39 passphrase to recover your new keypair:
zzzz xxxx aaaa lend xx xx xxxx xxxx xx xxx xxx xxxx
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="关键参数"&gt;&lt;strong&gt;关键参数&lt;/strong&gt;&lt;/h4&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;参数&lt;/th&gt;
&lt;th style="text-align:center;"&gt;作用&lt;/th&gt;
&lt;th style="text-align:center;"&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--force&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;强制覆盖现有文件&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;solana-keygen new --force&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--no-passphrase&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;不加密密钥文件（不安全）&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;solana-keygen new --no-passphrase&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--seed&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;从种子短语生成密钥&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;solana-keygen new --seed "wolf lamp prize..."&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;--word-count&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;设置种子短语单词数（默认 12）&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;code&gt;solana-keygen new --word-count 24&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h3 id="显示当前配置的默认 Solana 钱包地址（即公钥）"&gt;显示当前配置的默认 &lt;strong&gt;Solana 钱包地址&lt;/strong&gt;（即公钥）&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana address
6MZDRo5v8K2NfdohdD76QNpSgk3GHssssssss
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Airdrop SOL 空投 SOL"&gt;&lt;a href="https://solana.com/zh/docs/intro/installation#airdrop-sol" rel="nofollow" target="_blank" title=""&gt;Airdrop SOL 空投 SOL&lt;/a&gt;&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana airdrop 5
Requesting airdrop of 5 SOL

Signature: 5kFPstTYyPKybJevoLBbbPTEnf4BBp5s6WQDyjtZCT23vFXHWT5B8Tgx2VA8qHi2SWPN8ZpBrP7aFk9ims1KkXeL

5 SOL
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看钱包余额"&gt;查看钱包余额&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;solana balance
5 SOL
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Run Local Validator 运行本地验证器"&gt;&lt;a href="https://solana.com/zh/docs/intro/installation#run-local-validator" rel="nofollow" target="_blank" title=""&gt;Run Local Validator 运行本地验证器&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;设置本地 localhost &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;-ul&lt;/span&gt;
Config File: /Users/qiaopengjun/.config/solana/cli/config.yml
RPC URL: http://localhost:8899 
WebSocket URL: ws://localhost:8900/ &lt;span class="o"&gt;(&lt;/span&gt;computed&lt;span class="o"&gt;)&lt;/span&gt;
Keypair Path: /Users/qiaopengjun/.config/solana/id.json 
Commitment: confirmed 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run local validator&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana-test-validator
&lt;span class="nt"&gt;--faucet-sol&lt;/span&gt; argument ignored, ledger already exists
Ledger location: test-ledger
Log: test-ledger/validator.log
⠚ Initializing...                                                                                                                  Waiting &lt;span class="k"&gt;for &lt;/span&gt;fees to stabilize 1...
Identity: 47mfd1CWkN1vXy1wTWgcdsWjvTmX9ytjoosTCPS8N2JT
Genesis Hash: 8K2Uq4go7arvo6qVcDptJy83A9z5ZNgQUrSozCCN5TZA
Version: 2.1.21
Shred Version: 34132
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
WebSocket PubSub URL: ws://127.0.0.1:8900
⠈ 00:00:41 | Processed Slot: 160 | Confirmed Slot: 160 | Finalized Slot: 129 | Full Snapshot Slot: 101 | Incremental Snapshot Slot: -
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过本文的实战教程，你已掌握 Solana CLI 的核心技能：从配置网络环境、创建钱包、获取测试 SOL，到运行本地验证器，这些步骤为你打开了 Web3 开发的大门。Solana 的高性能区块链生态正等待你探索！建议继续结合官方文档和实践，深入学习智能合约开发或 DApp 构建，加速成为 Web3 领域的佼佼者。立即动手，开启你的 Solana 开发之旅吧！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs/intro/installation" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs/intro/installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/docs" rel="nofollow" target="_blank"&gt;https://www.anchor-lang.com/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Mon, 28 Apr 2025 19:31:27 +0800</pubDate>
      <link>https://soldev.cn/topics/170</link>
      <guid>https://soldev.cn/topics/170</guid>
    </item>
    <item>
      <title>Web3 快上手：Solana 造你的链上名片</title>
      <description>&lt;h2 id="Web3 快上手：Solana 造你的链上名片"&gt;Web3 快上手：Solana 造你的链上名片&lt;/h2&gt;
&lt;p&gt;想在 Web3 世界留下你的专属印记吗？用 Solana，几分钟就能打造一张“链上名片”！借助 Solana Playground 这个神器，我们将带你从零开始，快速上手区块链开发。无论是喜欢的数字、颜色，还是兴趣爱好，都能轻松上链，马上开启你的 Web3 冒险吧！&lt;/p&gt;

&lt;p&gt;本文手把手教你如何用 Solana Playground 在 Solana 链上打造一张属于你的“名片”。通过五个简单步骤——创建项目、编写代码、构建、部署和测试，你将快速掌握基于 Anchor 框架的 Web3 开发技巧。程序能存储你的喜好数据（数字、颜色、爱好），并用 PDA 确保独一无二。适合 Web3 新手，五分钟入门区块链！&lt;/p&gt;

&lt;p&gt;在开始实操前，我们先来搞懂一个 Solana 的核心概念：PDA（程序派生地址）。简单来说，PDA 是 Solana 程序生成的一种特殊地址，它不是由私钥控制，而是通过程序 ID 和一些“种子”（比如用户公钥）计算出来的。换句话说，PDA 就是 Solana 区块链上的一种“神奇地址”。它不像你钱包里那种普通地址（有私钥能自己控制），而是程序按照一定规则算出来的一个地址。比如说，它是用程序的 ID 加上一些“种子”（比如你的公钥）混在一起生成的，有点像给你的数据盖了个独一无二的戳。&lt;/p&gt;

&lt;p&gt;想象一下，PDA 就像是你家门口的一个智能快递柜。快递员（程序）能打开它放东西，但你自己没有钥匙，只能通过程序去存取里面的东西。这样既安全（别人偷不了），又专属（每个人的柜子都不一样）。在咱们这个教程里，PDA 就是用来存你的“链上名片”数据的，保证你的喜好信息既不会丢，也不会被乱改！也就是说，我们会用 PDA 来存储你的“链上名片”数据，确保它既独一无二，又只能由程序管理。它的妙处在于：安全性高（没人能直接用私钥篡改），而且每个用户的 PDA 都不同，就像给你的喜好数据分配了一个专属的“链上保险箱”。在下面的实操中，你会看到 PDA 如何帮我们把喜欢的数字、颜色和爱好安全地存到 Solana 区块链上！&lt;/p&gt;
&lt;h2 id="使用 Solana Playground 工具 实操"&gt;使用 Solana Playground 工具 实操&lt;/h2&gt;&lt;h3 id="第一步：打开https://beta.solpg.io/ 网站，单击创建一个新项目"&gt;第一步：打开&lt;a href="https://beta.solpg.io/" rel="nofollow" target="_blank"&gt;https://beta.solpg.io/&lt;/a&gt; 网站，单击创建一个新项目&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/7b6020ae-16ad-43b1-bb10-44a48325dfc2.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="第二步：实现 src/lib.rs 代码"&gt;第二步：实现 &lt;code&gt;src/lib.rs&lt;/code&gt; 代码&lt;/h3&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;"ALuosANfx2rg9YDbBB5JnvtwNyvnLTVUntXDXhyiEzE"&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;const&lt;/span&gt; &lt;span class="n"&gt;ANCHOR_DISCRIMINATOR_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&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;favorites&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;set_favorites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;context&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;SetFavorites&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;number&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="n"&gt;color&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="n"&gt;hobbies&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;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;user_public_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.accounts.user&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greetings from {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User {user_public_key}'s favorite number is {number}, favorite color is: {color}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User's hobbies are: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.accounts.favorites&lt;/span&gt;&lt;span class="nf"&gt;.set_inner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Favorites&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;,&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;#[account]&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(InitSpace)]&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;Favorites&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;number&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="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;50&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;color&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="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&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;hobbies&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;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;SetFavorites&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;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="nd"&gt;#[account(&lt;/span&gt;
        &lt;span class="nd"&gt;init_if_needed,&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="nd"&gt;ANCHOR_DISCRIMINATOR_SIZE&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nd"&gt;Favorites::INIT_SPACE,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"favorites"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;user&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&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;favorites&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;Favorites&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是一个使用 Anchor 框架编写的 Solana 程序，用于存储用户的喜好数据（喜欢的数字、颜色和爱好列表）。&lt;/p&gt;
&lt;h4 id="代码结构概览"&gt;代码结构概览&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;导入和基础声明&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&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;"ALuosANfx2rg9YDbBB5JnvtwNyvnLTVUntXDXhyiEzE"&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;const&lt;/span&gt; &lt;span class="n"&gt;ANCHOR_DISCRIMINATOR_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;use anchor_lang::prelude::*&lt;/code&gt;: 导入 Anchor 框架的基础模块。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;declare_id!&lt;/code&gt;: 定义程序的唯一 ID，这是部署到 Solana 网络时程序的地址。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ANCHOR_DISCRIMINATOR_SIZE&lt;/code&gt;: 定义 Anchor 账户的鉴别器大小（8 字节），用于区分不同账户类型。&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;程序模块&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight rust"&gt;&lt;code&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;favorites&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="c1"&gt;// ... 函数定义 ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[program]&lt;/code&gt;: Anchor 宏，表示这是一个 Solana 程序模块。&lt;/li&gt;
&lt;li&gt;包含一个 &lt;code&gt;set_favorites&lt;/code&gt; 函数，用于设置用户的喜好。&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数据结构&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[account]&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(InitSpace)]&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;Favorites&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;number&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="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;50&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;color&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="nd"&gt;#[max_len(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&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;hobbies&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;ul&gt;
&lt;li&gt;
&lt;code&gt;#[account]&lt;/code&gt;: 表示这是一个账户数据结构。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[derive(InitSpace)]&lt;/code&gt;: 自动计算账户所需的存储空间。&lt;/li&gt;
&lt;li&gt;定义了 &lt;code&gt;Favorites&lt;/code&gt; 结构体：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;number&lt;/code&gt;: 64 位无符号整数，表示喜欢的数字。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt;: 字符串，最大长度 50 个字符。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hobbies&lt;/code&gt;: 字符串向量，最大 5 个元素，每个字符串最大 50 个字符。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;账户验证结构&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight rust"&gt;&lt;code&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;SetFavorites&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;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="nd"&gt;#[account(&lt;/span&gt;
        &lt;span class="nd"&gt;init_if_needed,&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="nd"&gt;ANCHOR_DISCRIMINATOR_SIZE&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nd"&gt;Favorites::INIT_SPACE,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"favorites"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;user&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&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;favorites&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;Favorites&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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[derive(Accounts)]&lt;/code&gt;: 定义函数需要的账户上下文。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user&lt;/code&gt;: 调用者账户，必须是签名者（&lt;code&gt;Signer&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;favorites&lt;/code&gt;: 存储喜好数据的账户，具有以下属性：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;init_if_needed&lt;/code&gt;: 如果账户不存在则初始化。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payer = user&lt;/code&gt;: 由用户支付账户创建费用。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;space&lt;/code&gt;: 账户大小，包含鉴别器（8 字节）加上 &lt;code&gt;Favorites&lt;/code&gt; 结构体的空间。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seeds&lt;/code&gt;: 使用 PDA（程序派生地址），基于 "favorites" 和用户公钥生成。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bump&lt;/code&gt;: 用于确保 PDA 的唯一性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;system_program&lt;/code&gt;: Solana 的系统程序，用于账户创建。&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;主函数 &lt;code&gt;set_favorites&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_favorites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;context&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;SetFavorites&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;number&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="n"&gt;color&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="n"&gt;hobbies&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;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;user_public_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.accounts.user&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greetings from {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User {user_public_key}'s favorite number is {number}, favorite color is: {color}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User's hobbies are: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.accounts.favorites&lt;/span&gt;&lt;span class="nf"&gt;.set_inner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Favorites&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;,&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;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;参数&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;context&lt;/code&gt;: 包含账户信息的上下文。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;number&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;hobbies&lt;/code&gt;: 用户传入的喜好数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;逻辑&lt;/strong&gt;：

&lt;ol&gt;
&lt;li&gt;获取用户的公钥。&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;msg!&lt;/code&gt; 输出日志信息，便于调试。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;favorites&lt;/code&gt; 账户的数据，使用 &lt;code&gt;set_inner&lt;/code&gt; 方法将新数据写入。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;Ok(())&lt;/code&gt;，表示成功执行。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="工作原理"&gt;工作原理&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;功能&lt;/strong&gt;：这个程序允许用户通过调用 &lt;code&gt;set_favorites&lt;/code&gt; 函数来设置他们的喜好（喜欢的数字、颜色和爱好列表）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;账户管理&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;数据存储在 &lt;code&gt;favorites&lt;/code&gt; 账户中，这是一个 PDA（程序派生地址），与用户的公钥绑定。&lt;/li&gt;
&lt;li&gt;如果账户不存在，会自动创建；如果已存在，则更新其中的数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;安全性&lt;/strong&gt;：

&lt;ul&gt;
&lt;li&gt;只有签名者（&lt;code&gt;user&lt;/code&gt;）可以调用此函数。&lt;/li&gt;
&lt;li&gt;使用 PDA 确保每个用户有唯一的存储空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="使用场景"&gt;使用场景&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;用户通过前端调用此程序，传入他们的喜好数据。&lt;/li&gt;
&lt;li&gt;程序在链上存储这些数据，后续可以通过查询 &lt;code&gt;favorites&lt;/code&gt; 账户来读取。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="示例调用"&gt;示例调用&lt;/h4&gt;
&lt;p&gt;假设用户 Alice 想要设置：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;喜欢的数字：42&lt;/li&gt;
&lt;li&gt;喜欢的颜色："blue"&lt;/li&gt;
&lt;li&gt;爱好列表：["reading", "gaming"]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;前端会构造一个交易，调用 &lt;code&gt;set_favorites(42, "blue", ["reading", "gaming"])&lt;/code&gt;，程序会：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;创建或更新 Alice 的 &lt;code&gt;favorites&lt;/code&gt; 账户。&lt;/li&gt;
&lt;li&gt;存储这些数据，并在链上记录日志。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="注意事项"&gt;注意事项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;空间限制&lt;/strong&gt;：&lt;code&gt;color&lt;/code&gt; 最大 50 个字符，&lt;code&gt;hobbies&lt;/code&gt; 最多 5 个条目，每个条目 50 个字符。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;费用&lt;/strong&gt;：账户创建需要用户支付少量 SOL 作为租金。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;日志&lt;/strong&gt;：&lt;code&gt;msg!&lt;/code&gt; 用于调试，实际部署时可以选择移除。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="第三步：Build"&gt;第三步：Build&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/db3059ef-9143-4093-9f82-de6e2508eac5.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="第四步：Deploy"&gt;第四步：Deploy&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/6fd0009a-84f4-4baa-ae72-e1d0a2cd9aee.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://explorer.solana.com/tx/zV4B8N8X41LNNhY5woCNpoDQp3B9gMGf1usg1BAfejctmSDWxpFAR2tWDHkWutGQ2jJZsXiRzHpJEUQhsxkbXNp?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/zV4B8N8X41LNNhY5woCNpoDQp3B9gMGf1usg1BAfejctmSDWxpFAR2tWDHkWutGQ2jJZsXiRzHpJEUQhsxkbXNp?cluster=devnet&lt;/a&gt;
&lt;img src="/uploads/photo/Paxon/cc89c097-1552-46c6-8bdf-fb24a00d8c73.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://explorer.solana.com/address/ALuosANfx2rg9YDbBB5JnvtwNyvnLTVUntXDXhyiEzE?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/address/ALuosANfx2rg9YDbBB5JnvtwNyvnLTVUntXDXhyiEzE?cluster=devnet&lt;/a&gt;
&lt;img src="/uploads/photo/Paxon/61905071-eb95-4130-ada8-ecd680f1e54c.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="第五步：Test"&gt;第五步：Test&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/Paxon/004cdf79-6917-40b3-8810-ac5adebdbe1e.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过这次实操，你已经用 Solana Playground 在 Web3 世界里打造了一张属于自己的“链上名片”！从 Rust 代码到链上部署，短短几步就解锁了 Solana 的高效开发体验。无论你是 Web3 小白还是想玩点新花样，这只是个开始——快去试试更多功能，造出更酷的链上作品吧！&lt;/p&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://beta.solpg.io/" rel="nofollow" target="_blank"&gt;https://beta.solpg.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explorer.solana.com/tx/5H4VV7RoiAb2PZ34A11DPJA3EANv7s32Yyrmn7Ez9YLUPDUeMTVCGGFzXPGApXaUrNuaNDScV6mZrh1bYYYWuwkZ?cluster=devnet" rel="nofollow" target="_blank"&gt;https://explorer.solana.com/tx/5H4VV7RoiAb2PZ34A11DPJA3EANv7s32Yyrmn7Ez9YLUPDUeMTVCGGFzXPGApXaUrNuaNDScV6mZrh1bYYYWuwkZ?cluster=devnet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/zh/docs" rel="nofollow" target="_blank"&gt;https://solana.com/zh/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solanacookbook.com/zh/" rel="nofollow" target="_blank"&gt;https://solanacookbook.com/zh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anza-xyz/platform-tools" rel="nofollow" target="_blank"&gt;https://github.com/anza-xyz/platform-tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Paxon</author>
      <pubDate>Tue, 11 Mar 2025 18:00:25 +0800</pubDate>
      <link>https://soldev.cn/topics/155</link>
      <guid>https://soldev.cn/topics/155</guid>
    </item>
    <item>
      <title>Rust 自动化测试完全指南：从基础到进阶</title>
      <description>&lt;h2 id="Rust自动化测试完全指南：从基础到进阶"&gt;Rust 自动化测试完全指南：从基础到进阶&lt;/h2&gt;
&lt;p&gt;在现代软件开发中，自动化测试是保证代码质量、减少人为错误和提高开发效率的关键实践之一。Rust 语言以其内存安全性和并发性能著称，逐渐成为系统级编程的重要工具。然而，许多开发者在 Rust 的自动化测试方面还缺乏全面的了解与实践经验。本指南将全面介绍 Rust 的自动化测试，从基础的单元测试到更复杂的集成测试和性能测试，帮助开发者掌握自动化测试的核心概念与技巧。无论你是 Rust 的新手，还是有一定经验的开发者，这篇文章都将为你提供切实可行的测试解决方案，助力你的项目开发与维护。&lt;/p&gt;

&lt;p&gt;本文旨在全面介绍 Rust 的自动化测试，包括其基础概念、工具、最佳实践和进阶技巧。文章首先介绍了 Rust 的基本测试框架，包括如何编写和运行单元测试，随后深入探讨了如何进行集成测试、性能测试以及使用 Mock 和 Test Doubles 等技术。我们还将介绍一些 Rust 生态中的常用测试工具与库，例如 cargo test、mockall 和 criterion，并探讨如何构建高效、易维护的自动化测试套件。通过本指南，读者将能够充分利用 Rust 的测试工具，提升开发效率，确保代码质量，并轻松应对开发过程中的各种挑战。&lt;/p&gt;
&lt;h2 id="编写自动化测试"&gt;编写自动化测试&lt;/h2&gt;&lt;h2 id="一、编写和运行测试"&gt;一、编写和运行测试&lt;/h2&gt;&lt;h3 id="测试（函数）"&gt;测试（函数）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;测试：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  函数&lt;/li&gt;
&lt;li&gt;  验证非测试代码的功能是否和预期一致&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;测试函数体（通常）执行的 3 个操作：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  准备数据/状态&lt;/li&gt;
&lt;li&gt;  运行被测试的代码&lt;/li&gt;
&lt;li&gt;  断言（Assert）结果&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="解剖测试函数"&gt;解剖测试函数&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;测试函数需要使用 test 属性（attribute）进行标注&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Attribute 就是一段 Rust 代码的元数据&lt;/li&gt;
&lt;li&gt;  在函数上加 #[test]，可把函数变成测试函数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="运行测试"&gt;运行测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用 cargo test 命令运行所有测试函数&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Rust 会构建一个 Test Runner 可执行文件&lt;/li&gt;
&lt;li&gt;  它会运行标注了 test 的函数，并报告其运行是否成功&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当使用 cargo 创建 library 项目的时候，会生成一个 test module，里面有一个 test 函数&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  你可以添加任意数量的 test module 或 函数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/rust
➜ cargo new adder --lib
     Created library `adder` package

~/rust
➜ cd adder

adder on  master [?] via 🦀 1.67.1
➜ code .

adder on  master [?] via 🦀 1.67.1 took 2.2s
➜


~/rust
➜ cargo new adder --lib
     Created library `adder` package

~/rust
➜ cd adder

adder on  master [?] via 🦀 1.67.1
➜ code .

adder on  master [?] via 🦀 1.67.1 took 2.2s
➜
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;lib.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn add(left: usize, right: usize) -&amp;gt; usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="测试失败"&gt;测试失败&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;  测试函数 panic 就表示失败&lt;/li&gt;
&lt;li&gt;  每个测试运行在一个新线程&lt;/li&gt;
&lt;li&gt;  当主线程看见某个测试线程挂掉了，那个测试标记为失败了。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn add(left: usize, right: usize) -&amp;gt; usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn exploration() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }

    #[test]
    fn another() {
        panic!("Make this test fail")
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 took 3.0s 
➜ cargo test
   Compiling adder v0.1.0 (/Users/qiaopengjun/rust/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.16s
     Running unittests src/lib.rs (target/debug/deps/adder-6058f7b13179a51e)

running 2 tests
test tests::exploration ... ok
test tests::another ... FAILED

failures:

---- tests::another stdout ----
thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:17:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::another

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="二、断言（Assert）"&gt;二、断言（Assert）&lt;/h2&gt;&lt;h3 id="使用 Assert! 宏检查测试结果"&gt;使用 Assert! 宏检查测试结果&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;assert! 宏，来自标准库，用来确定某个状态是否为 true&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  true：测试通过&lt;/li&gt;
&lt;li&gt;  false：调用 panic!，测试失败&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[derive(Debug)]
pub struct Rectangle {
  length: u32,
  width: u32,
}

impl Rectangle {
  pub fn can_hold(&amp;amp;self, other: &amp;amp;Rectangle) -&amp;gt; bool {
    self.length &amp;gt; other.length &amp;amp;&amp;amp; self.width &amp;gt; other.width
  }
}

#[cfg(test)]
mod tests {
  use super::*
  
  #[test]
  fn larger_can_hold_smaller() {
    let larger = Rectangle {
      length: 8,
      width: 7,
    };
    let smaller = Rectangle {
      length: 5,
      width: 1,
    };
    assert!(larger.can_hold(&amp;amp;smaller));
  }
  
  #[test]
  fn samller_cannot_hold_larger() {
    let larger = Rectangle {
      length: 8,
      width: 7,
    };
    let smaller = Rectangle {
      length: 5,
      width: 1,
    };
    assert!(!smaller.can_hold(&amp;amp;larger));
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="使用 assert_eq! 和 assert_ne! 测试相等性"&gt;使用 assert_eq! 和 assert_ne! 测试相等性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;都来自标准库&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;判断两个参数是否相等或不等&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;实际上，它们使用的就是 == 和 !== 运算符&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;断言失败，自动打印出两个参数的值&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  使用 debug 格式打印参数&lt;/li&gt;
&lt;li&gt;  要求参数实现了 PartialEq 和 Debug Traits（所有的基本类型和标准库里大部分类型都实现了）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn add_two(a: i32) -&amp;gt; i32 {
  a + 2
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  fn it_adds_two() {
    // assert_eq!(4, add_two(2));
    assert_ne!(5, add_two(2));
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="三、自定义错误消息"&gt;三、自定义错误消息&lt;/h2&gt;&lt;h3 id="添加自定义错误信息"&gt;添加自定义错误信息&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;可以向 assert!、assert_eq!、assert_ne! 添加可选的自定义消息&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  这些自定义消息和失败消息都会打印出来&lt;/li&gt;
&lt;li&gt;  assert!：第 1 参数必填，自定义消息作为第 2 个参数。&lt;/li&gt;
&lt;li&gt;  assert_eq! 和 assert_ne!：前 2 个参数必填，自定义消息作为第 3 个参数。&lt;/li&gt;
&lt;li&gt;  自定义消息参数会被传递给 format! 宏，可以使用 {} 占位符&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn greeting(name: &amp;amp;str) -&amp;gt; String {
  //format!("Hello {}!", name)
  format!("Hello!")
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  fn greetings_contain_name() {
    let result = greeting("Carol");
    // assert!(result.contains("Carol"));
    assert!(
      result.contains("Carol"),
      "Greeting didn't contain name, value was '{}'", result
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="四、用 should_panic 检查恐慌"&gt;四、用 should_panic 检查恐慌&lt;/h2&gt;&lt;h3 id="验证错误处理的情况"&gt;验证错误处理的情况&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;测试除了验证代码的返回值是否正确，还需验证代码是否如预期的处理了发生错误的情况&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;可验证代码在特定情况下是否发生了 panic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;should_panic 属性（attribute）：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  函数 panic：测试通过&lt;/li&gt;
&lt;li&gt;  函数 没有 panic：测试失败&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub struct Guess {
  value: u32,
}

impl Guess {
  pub fn new(value: u32) -&amp;gt; Guess {
    if value &amp;lt; 1 || value &amp;gt; 100 {
      panic!("Guess value must be between 1 and 100, got {}.", value)
    }
    
    Guess {value}
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  #[should_panic]
  fn greater_than_100() {
    Guess::new(200);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="让 should_panic 更精确"&gt;让 should_panic 更精确&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;为 should_panic 属性添加一个可选的 expected 参数：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  将检查失败消息中是否包含所指定的文字&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub struct Guess {
  value: u32,
}

impl Guess {
  pub fn new(value: u32) -&amp;gt; Guess {
    if value &amp;lt; 1 {
      panic!("Guess value must be greater than or equal to 1, got {}.", value)
    } else if value &amp;gt; 100 {
      panic!("Guess value must be less than or equal to 100, got {}.", value)
    }
    
    Guess {value}
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  #[should_panic(expected = "Guess value must be less than or equal to 100")]
  fn greater_than_100() {
    Guess::new(200);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="五、在测试中使用 Result"&gt;五、在测试中使用 Result
&lt;/h2&gt;&lt;h3 id="在测试中使用 Result"&gt;在测试中使用 Result
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;无需 panic，可使用 Result 作为返回类型编写测试：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  返回 Ok：测试通过&lt;/li&gt;
&lt;li&gt;  返回 Err：测试失败&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[cfg(test)]
mod tests {
  #[test]
  fn it_works() -&amp;gt; Result&amp;lt;(), String&amp;gt; {
    if 2 + 2 == 4 {
      Ok(())
    } else {
      Err(String::from("two plus two does not equal four"))
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  注意：不要在使用 Result 编写的测试上标注 #[should_panic]
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="六、控制测试运行：并行和连续执行测试"&gt;六、控制测试运行：并行和连续执行测试&lt;/h2&gt;&lt;h3 id="控制测试如何运行"&gt;控制测试如何运行&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;改变 cargo test 的行为：添加命令行参数&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;默认行为：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  并行运行&lt;/li&gt;
&lt;li&gt;  所有测试&lt;/li&gt;
&lt;li&gt;  捕获（不显示）所有输出，使读取与测试结果相关的输出更容易。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令行参数：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  针对 cargo test 的参数：紧跟 cargo test 后&lt;/li&gt;
&lt;li&gt;  针对测试可执行程序：放在 -- 之后&lt;/li&gt;
&lt;li&gt;  cargo test --help&lt;/li&gt;
&lt;li&gt;  cargo -- --help&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="并行运行测试"&gt;并行运行测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;运行多个测试：默认使用多个线程并行运行&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  运行快&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确保测试之间：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  不会互相依赖&lt;/li&gt;
&lt;li&gt;  不依赖于某个共享状态（环境、工作目录、环境变量等待）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="--test-threads 参数"&gt;--test-threads 参数&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;  传递给 二进制文件&lt;/li&gt;
&lt;li&gt;  不想以并行方式运行测试，或想对线程数进行细粒度控制&lt;/li&gt;
&lt;li&gt;  可以使用 --test-threads 参数，后边跟着线程的数量&lt;/li&gt;
&lt;li&gt;  例如：cargo test -- --test-threads=1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="显示函数输出"&gt;显示函数输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;默认，如测试通过，Rust 的 test 库会捕获所有打印到标准输出的内容&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如，如果被测试代码中用到了 println!：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  如果测试通过：不会再终端看到 println! 打印的内容&lt;/li&gt;
&lt;li&gt;  如果测试失败：会看到 println! 打印的内容和失败信息&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn prints_and_returns_10(a: i32) -&amp;gt; i32 {
  println!("I got the value {}", a);
  10
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  fn this_test_will_pass() {
    let value = prints_and_returns_10(4);
    assert_eq!(10, value);
  }
  
  #[test]
  fn this_test_will_fail() {
    let value = prints_and_returns_10(8);
    assert_eq!(5, value);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  如果想在成功的测试中看到打印的内容： --show-output&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="七、控制测试运行：按名称运行测试"&gt;七、控制测试运行：按名称运行测试&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  选择运行的测试：将测试的名称（一个或多个）作为 cargo test 的参数&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn add_two(a: i32) -&amp;gt; i32 {
  a + 2
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  fn add_two_and_two() {
    assert_eq!(4, add_two(2));
  }
  
  #[test]
  fn add_three_and_two() {
    assert_eq!(5, add_two(3));
  }
  
  #[test]
  fn one_hundred() {
    assert_eq!(102, add_two(100));
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  运行单个测试：指定测试名&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo test one_hundred
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;  运行多个测试：指定测试名的一部分（模块名也可以）&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;cargo test add&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="八、控制测试运行：忽略测试"&gt;八、控制测试运行：忽略测试&lt;/h2&gt;&lt;h3 id="忽略某些测试，运行剩余测试"&gt;忽略某些测试，运行剩余测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;  ignore 属性（attribute）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[cfg(test)]
mod tests {
  #[test]
  fn it_works() {
    assert_eq!(4, 2 + 2);
  }
  
  #[test]
  #[ignore]
  fn expensive_test() {
    assert_eq!(5, 1 + 1 + 1 + 1 + 1);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;运行被忽略（ignore）的测试：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;cargo test -- --ignored&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="九、测试组织：单元测试"&gt;九、测试组织：单元测试&lt;/h2&gt;&lt;h3 id="测试的分类"&gt;测试的分类&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Rust 对测试的分类：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  单元测试&lt;/li&gt;
&lt;li&gt;  集成测试&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;单元测试：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  小、专注&lt;/li&gt;
&lt;li&gt;  一次对一个模块进行隔离的测试&lt;/li&gt;
&lt;li&gt;  可测试 private 接口&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;集成测试：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  在库外部。和其他外部代码一样使用你的代码&lt;/li&gt;
&lt;li&gt;  只能使用 public 接口&lt;/li&gt;
&lt;li&gt;  可能在每个测试中使用到多个模块&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;单元测试&lt;/p&gt;
&lt;h3 id="#[cfg(test)] 标注"&gt;#[cfg(test)] 标注&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;tests 模块上的 #[cfg(test)] 标注：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  只有运行 cargo test 才编译和运行代码&lt;/li&gt;
&lt;li&gt;  运行 cargo build 则不会&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;集成测试在不同的目录，它不需要 #[cfg(test)] 标注&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cfg：configuration（配置）&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  告诉 Rust 下面的条目只有在指定的配置选项下才被包含&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置选项 test：由 Rust 提供，用来编译和运行测试&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  只有 cargo test 才会编译代码，包括模块中的 helper 函数 和 #[test] 标注的函数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[cfg(test)]
mod tests {
  #[test]
  fn it_works() {
    assert_eq!(4, 2 + 2);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="测试私有函数"&gt;测试私有函数&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;  Rust 允许测试私有函数&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn add_two(a: i32) -&amp;gt; i32 {
  internal_adder(a, 2)
}

fn internal_adder(a: i32, b: i32) -&amp;gt; i32 {
  a + b
}

#[cfg(test)]
mod tests {
  use super::*;
  
  #[test]
  fn it_works() {
    assert_eq!(4, internal_adder(2, 2));
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="十、集成测试"&gt;十、集成测试&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  在 Rust 里，集成测试完全位于被测试库的外部&lt;/li&gt;
&lt;li&gt;  目的：是测试被测试库的多个部分是否能正确的一起工作&lt;/li&gt;
&lt;li&gt;  集成测试的覆盖率很重要&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Tests 目录"&gt;Tests 目录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;创建集成测试：tests 目录&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;tests 目录下的每个测试文件都是单独的一个 crate&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  需要将被测试库导入&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;无需标注 #[cfg(test)]，tests 目录被特殊对待&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  只有 cargo test，才会编译 tests 目录下的文件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;src/lib.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn add(left: usize, right: usize) -&amp;gt; usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn exploration() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }

    // #[test]
    // fn another() {
    //     panic!("Make this test fail")
    // }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tests/integration_test.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use adder;

#[test]
fn it_add() {
    assert_eq!(5, adder::add(2, 3));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
➜ cargo test
   Compiling adder v0.1.0 (/Users/qiaopengjun/rust/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.11s
     Running unittests src/lib.rs (target/debug/deps/adder-6058f7b13179a51e)

running 1 test
test tests::exploration ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/integration_test.rs (target/debug/deps/integration_test-461b916f2718e782)

running 1 test
test it_add ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="运行指定的集成测试"&gt;运行指定的集成测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;  运行一个特定的集成测试：cargo test 函数名&lt;/li&gt;
&lt;li&gt;  运行某个测试文件内的所有测试：cargo test --test 文件名&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;tests/another_integration_tests.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use adder;

#[test]
fn it_adds2() {
    assert_eq!(7, adder::add(3,4));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
➜ cargo test --test integration_test
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running tests/integration_test.rs (target/debug/deps/integration_test-461b916f2718e782)

running 1 test
test it_add ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
➜ cargo test --test another_integration_tests
   Compiling adder v0.1.0 (/Users/qiaopengjun/rust/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.11s
     Running tests/another_integration_tests.rs (target/debug/deps/another_integration_tests-0a89cbf68d5b375f)

running 1 test
test it_adds2 ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="集成测试中的子模块"&gt;集成测试中的子模块&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;tests 目录下每个文件被编译成单独的 crate&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  这些文件不共享行为（与 src 下的文件规则不同）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adder on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
➜ tree
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
├── target
│   ├── CACHEDIR.TAG
│   ├── debug
│   └── tmp
└── tests
    ├── another_integration_tests.rs
    ├── common
    │   └── mod.rs
    └── integration_test.rs

27 directories, 205 files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tests/common/mod.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn setup() {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tests/another_integration_tests.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use adder;

mod common;

#[test]
fn it_adds2() {
    common::setup();
    assert_eq!(7, adder::add(3,4));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tests/integration_test.rs 文件&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use adder;

mod common;

#[test]
fn it_add() {
    common::setup();
    assert_eq!(5, adder::add(2, 3));
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="针对 binary crate 的集成测试"&gt;针对 binary crate 的集成测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果项目是 binary Crate，只含有 src/main.rs 没有 src/lib.rs：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  不能在 tests 目录下创建集成测试&lt;/li&gt;
&lt;li&gt;  无法把 main.rs 的函数导入作用域&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;只有 library crate 才能暴露函数给其它 crate 用&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;binary crate 意味着独立运行&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过本文的学习，开发者不仅能够掌握 Rust 的自动化测试基础，还能进一步了解如何使用高级测试技术来解决实际开发中的复杂问题。自动化测试是提高软件质量的有效手段，而 Rust 的强类型系统和优秀的编译器使得测试变得更加高效和安全。希望这篇指南能帮助你构建一个健壮的自动化测试体系，让你在开发过程中更加得心应手。如果你有任何问题或想法，欢迎与我们讨论，继续深入 Rust 的测试领域。&lt;/p&gt;</description>
      <author>Paxon</author>
      <pubDate>Fri, 21 Feb 2025 10:03:39 +0800</pubDate>
      <link>https://soldev.cn/topics/145</link>
      <guid>https://soldev.cn/topics/145</guid>
    </item>
  </channel>
</rss>
