惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
H
Help Net Security
GbyAI
GbyAI
博客园_首页
G
Google Developers Blog
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
月光博客
月光博客
美团技术团队
B
Blog RSS Feed
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
博客园 - 叶小钗
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
Engineering at Meta
Engineering at Meta
Google DeepMind News
Google DeepMind News
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
P
Proofpoint News Feed

smallyu的博客

为什么买的加密货币一定要去中心化 RISC-V 虚拟机无法代替 EVM 虚拟机 Polkadot 的 Existential Deposit 机制错在哪里 利息,套利,交易策略,金融市场 LayerZero 从来不挑战比特币的地位 默认参数引起的以太坊节点运行错误 硬件钱包与资产安全 预测市场比币圈更赌场 为什么去中心化的跨链桥不可能实现 我对于 AI 时代的答案 不要投资任何隐私币 区块链技术世界的三大真理 以太坊 AA 钱包的致命问题是什么 基于 AI 语义执行的 MCP 区块链的设计 一个集成 Geth 和 CometBFT 的兼容层 我的加密货币定投策略(二) DeFi 基础: 理解 AMM 定价机制 Go 语言 GMP 调度器的原理是什么 Web3 项目分析计划 对 Psyche Network 项目的分析 continuation 教程: 理解 CPS 如何开发一个比特币符文(Runes)协议 比特币脚本开发教程 我从王垠的计算机科学视频班学到了什么 区块链技术面试题(2025年版) Rust 语言容易让新手困惑的一个“过度优化” 尝试开发一个最小 EVM 虚拟机 基于 ZK 的链上身份系统设计 一个 Web3 打赏系统的设计 鼓吹 Cursor 的人技术能力都差
Solana 智能合约开发教程 (1)
2025-06-24 · via smallyu的博客

2025-06-24

这个一个零基础的系列教程,可以从最基本的操作开始学会 Solana 智能合约的开发。

  • 第一篇》:基础环境安装、HelloWorld 合约部署、链上合约调用
  • 第二篇》:实现 USDT 合约的最小模型,自定义数据结构与方法
  • 第三篇》:使用官方 SPL 库复用合约功能,完成标准化代币的发行

我们将从最基础的操作开始,学习 Solana 智能合约的开发。你只需要普通的编程基础,理解面向对象等概念就可以,不需要事先知道其他网络的智能合约概念,也不需要知道 Rust 语言的编程理念。

1. 安装环境

访问 Solana 官方提供的安装教程:https://solana.com/docs/intro/installation

文档中提供了一键安装全部依赖的单个命令行,也有分阶段安装的详细教程。要注意其中 Solana Cli 是需要修改环境变量文件的。安装好一切后,solana 命令应该是可用的:

solana --help

2. 初始化项目

使用 anchor 命令来初始化一个智能合约的项目,这个命令行工具在上个步骤已经安装好了,可以先不用管生成的目录结构是什么样子:

anchor init hello_sol
cd hello_sol

3. 写入合约代码

programs/hello_sol/src 目录下有一个 lib.rs 文件,.rs 结尾意味着这是一个 Rust 语言的代码文件。把这些代码复制进去,注意 declare_id 中的内容是你的项目在初始化的时候,就会自动为你生成,不需要原封不动复制下面的内容:

use anchor_lang::prelude::*;

declare_id!("3Zbdw1oWu1CiMiQr3moQeT4XzMgeqmCvjH5R5wroDWQH");

#[program]
pub mod hello_sol {
    use super::*;

    pub fn say_hello(ctx: Context<Hello>) -> Result<()> {
        msg!("Hello, world!");
        Ok(())
    }
}

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

4. 编译智能合约

使用 anchor 命令编译你刚才复制进去的智能合约代码,确保编译是成功的,代码没有写错。编译过程中可能会有一些警告,那些警告不要紧,因为 Rust 语言对于代码非常严格,很小的问题都会抛出大段的警告。如果一切顺利,命令行的输出不会有错误日志:

anchor build

5. 设置本地默认网络

运行这个命令,让你本地的 solana 命令默认使用 devnet,因为 devnet 是给开发者使用的,可以用来测试自己的程序,而不需要真的花钱去买 SOL 代币:

solana config set --url https://api.devnet.solana.com

6. 创建本地账户文件

这个命令用于在你本地的默认路径下,创建一个用来部署智能合约的 Solana 账户。因为部署智能合约需要消耗手续费,这些手续费需要一个账户来支付:

solana-keygen new -o ~/.config/solana/id.json  

这个命令的运行结果中,有一行 pubkey: 开头的输出,pubkey 后面的就是你本地的账户地址。因为上一个步骤已经设置了 devnet 为默认网络,所以可以直接使用这个命令来查看你本地账户的余额:

solana balance

也可以打开 devnet 的 浏览器,搜索你刚才生成的地址。搜索之后的 URL 形如: https://explorer.solana.com/address/75sFifxBt7zw1YrDfCdPjDCGDyKEqLWrBarPCLg6PHwb?cluster=devnet

当然,你会发现自己的账户余额是 0 SOL

7. 领取 devnet 上的空投

运行这个命令,你的账户就可以收到 2 个 SOL。其中参数里的 2 就是请求发放 2 个 SOL 的意思。因为领水的额度限制,你只能一次性最多领 2 个。不用担心太少,足够我们接下来的步骤使用了。

solana airdrop 2

8. 部署合约到 devnet

现在我们已经有了智能合约代码,有了本地账户,并且本地账户里有 SOL 余额。现在可以部署合约到 devnet 上了。运行这个命令:

anchor deploy --provider.cluster devnet 

如果部署成功,会看到 Deploy success 的字样。命令行输出中还有一行需要留意,Program Id: 后面的,就是部署之后的合约地址,你可以直接在 devnet 的浏览器上搜索这个地址,然后看到类似这个 URL 的页面,URL 中的 3Zbdw1oWu1CiMiQr3moQeT4XzMgeqmCvjH5R5wroDWQH 就是我部署的合约地址:https://explorer.solana.com/address/3Zbdw1oWu1CiMiQr3moQeT4XzMgeqmCvjH5R5wroDWQH?cluster=devnet

9. 调用链上合约

hello_sol/app 目录下,新建一个叫 app.js 的文件,把这些代码复制进去。简单来说,这段代码读取了你本地默认的账户文件,然后用你的 Solana 账户发起一笔对智能合约调用的交易,这个脚本每执行一次,就会在链上创建一笔交易。:

const anchor = require('@coral-xyz/anchor');
const fs     = require('fs');
const os     = require('os');
const path   = require('path');
const { Keypair, Connection } = anchor.web3;

const RPC_URL    = process.env.RPC_URL;
const connection = new Connection(RPC_URL, { commitment: 'confirmed' });

const secretKey = Uint8Array.from(
  JSON.parse(
    fs.readFileSync(
      path.join(os.homedir(), '.config/solana/id.json'),
      'utf8',
    ),
  ),
);

const wallet   = new anchor.Wallet(Keypair.fromSecretKey(secretKey));
const provider = new anchor.AnchorProvider(connection, wallet, {
  preflightCommitment: 'confirmed',
});
anchor.setProvider(provider);

const idlPath = path.resolve(__dirname, '../target/idl/hello_sol.json');
const idl     = JSON.parse(fs.readFileSync(idlPath, 'utf8'));
const program = new anchor.Program(idl, provider);

(async () => {
  try {
    const sig = await program.methods.sayHello().rpc();
    console.log('✅ tx', sig);
    console.log(`🌐 https://explorer.solana.com/tx/${sig}?cluster=devnet`);
  } catch (err) {
    console.error('❌', err);
  }
})();

返回 hello_sol 项目的顶层目录,执行这些命令来安装 nodejs 的依赖:

npm init -y 
npm install @coral-xyz/anchor

然后记得现在仍然是在顶层目录,运行这个命令,来执行刚才写的 app.js 脚本,脚本会到 devnet 上调用我们部署的智能合约:

export RPC_URL=https://api.devnet.solana.com
node app/app.js

这里有一个环境变量 RPC_URL 是脚本请求的 API 地址,因为 nodejs 脚本默认不走系统代理,所以对于网络受阻的同学,需要用一个比公开 RPC 更好用的 API 地址。可以使用例如 Helius 的服务,注册一个免费的账号就可以了。假如执行脚本的过程中遇到下面的错误,那就说明是网络问题,换一个好用的 RPC 地址就好了:

❌ Error: failed to get recent blockhash: TypeError: fetch failed
    at Connection.getLatestBlockhash (/Users/smallyu/work/github/hello_sol/node_modules/@solana/web3.js/lib/index.cjs.js:7236:13)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async AnchorProvider.sendAndConfirm (/Users/smallyu/work/github/hello_sol/node_modules/@coral-xyz/anchor/dist/cjs/provider.js:89:35)
    at async MethodsBuilder.rpc [as _rpcFn] (/Users/smallyu/work/github/hello_sol/node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/rpc.js:15:24)
    at async /Users/smallyu/work/github/hello_sol/app/app.js:40:17

你也许好奇为什么不需要指定调用的合约地址,这个脚本怎么知道你刚才,部署到链上的合约在哪里?注意看脚本中有一个 idlPath 的变量,你可以直接打开这个路径的文件 target/idl/hello_sol.json 查看,里面是一些合约编译后的元信息,包括合约的地址也在里面,没错合约地址是离线生成的,不需要上链,合约就有属于自己的唯一地址了。

如果执行脚本没有输出错误,就会看到终端打印出了这一次调用合约的交易哈希,以及可以直接复制访问的浏览器 URL,例如这就是一笔调用合约的交易: https://explorer.solana.com/tx/2fnPgKkv3tGKKq72hhRxmW6WFSXuofMzXfY2UYoFZXTdJi37btdESy9NzS2gjpWzXX4CL5F7QfxugpctBVaMcBFY?cluster=devnet

这笔交易页面的最下方,可以看到我们写的智能合约在被交易调用后,打印出了 Program logged: "Hello, world!" 的日志,这正是我们写在合约代码中的 msg。

10. Troubleshooting

如果在执行上述命令或者代码的过程中,遇到了错误,可以优先考虑是命令行工具版本的问题。由于区块链行业和技术迭代比较快,很容易出现版本不兼容的情况。我本地的环境和版本是:

rustup: rustup 1.28.2 (e4f3ad6f8 2025-04-28)
rustc: rustc 1.90.0-nightly (706f244db 2025-06-23)
solana: solana-cli 2.2.18 (src:8392f753; feat:3073396398, client:Agave)
archor: anchor-cli 0.31.1
node: v24.2.0
@coral-xyz/anchor(nodejs): ^0.31.1