Solana学习

2026/04/06 Web3 共 6819 字,约 20 分钟

在线工具

base58/base64解析: https://explorer.solana.com/tx/inspector

oklink: https://www.oklink.com/zh-hans/solana/tx/3HTgpR3ga1izzMzNVnnAK8T6jde5J8uPGRPxfNYNyRqXSSoxKQv5LBpNo6LKz6s83oVBZXZ9Tes5ZnChFwQ7N8ss

https://solscan.io/

Solana 核心思想

  • 链上所有东西都是 Account
  • Program = 特殊 Account(可执行)
  • 账户存储数据,程序执行逻辑
  • 与 Ethereum 不同:
    • 余额和业务数据分离
    • Program 不直接存业务状态(除非 Data Account / PDA)

用 Web2 理解 Solana

想象你的公司运营一个 SaaS 系统(Solana),用户可以通过各种小程序(智能合约)实现不同业务需求。数据库设计:

Web2 设计Solana 对应
用户表:存用户信息用户账户(Wallet):存 SOL
小程序表:存代码程序账户(Program):存合约代码
小程序用户关系表:存用户在某个小程序下的数据,ID 类似 OpenIDPDA 账户:存用户在某个程序下的状态

核心类比:Solana 把数据库表的功能塞进了 Account,设计巧妙又高效。

账户地址与签名权

地址类型

每个 Solana 地址都是 32 字节,分为两种类型:

类型isOnCurve签名方式示例
钱包账户TRUE用户用私钥签名普通钱包地址
PDA (Program Derived Address)FALSE程序调用 invoke_signedATA、合约控制的账户

isOnCurve 表示地址是否是 Ed25519 曲线上的有效点:

  • TRUE = 在曲线上 = 有对应私钥 = 可加密签名
  • FALSE = 不在曲线上 = 没有私钥 = 只能由程序通过 invoke_signed “签名”

账户地址对比

账户类型isOnCurve谁能修改 / 签名
钱包账户TRUE用户签名(私钥)
ATAFALSEProgram 调用(invoke_signed)
PDAFALSEProgram 调用(invoke_signed)
Program AccountTRUEBPF Loader

钱包 vs PDA 的本质区别

钱包地址 = Ed25519 公钥 → 有对应私钥 → 用户签名
PDA 地址 = 确定性派生 → 没有私钥 → 程序签名

账户体系

三种基础账户

类型作用类比 Web2示例
用户账户 (Native/Wallet)存 SOL用户表,存现金余额钱包
程序账户 (Program)存代码(内置/自定义)小程序表,存业务逻辑System Program、SPL Token Program
数据账户 (Data)程序存状态小程序用户关系表,存应用数据Token Account、PDA、配置账户

数据账户分类

数据账户可进一步分为两类:

类型isOnCurve说明示例
系统所有账户TRUE由 Solana 原生程序生成的账户用户钱包、Token Account
程序派生账户 (PDA)FALSE签名权限是程序的账户DEX 保险库、流动性池账户

Account 关键属性

属性类型说明
executablebooltrue = 可执行(程序),false = 不可执行
lamportsu64SOL 余额(1 SOL = 10^9 lamport)
dataVec字节数组,内容取决于账户类型
ownerPubkey谁能操作这个账户(只有程序能操作)

普通用户只需关注账户的地址,其他字段由程序管理。

浏览器查看账户信息

以 Solana Explorer 为例:

字段说明
Assigned Program Id账户的所有者(Owner)
Allocated Data Size已存储的数据大小
Executable是否可执行(通常只有程序账户为 true)

Owner 权限模型

Owner 表示哪个程序能操作这个账户,只有程序能操作账户。

用户账户的 Owner

  • Owner:System Program(11111111111111111111111111111111)
class SystemProgram {
    void transfer() {
        验证签名;      // 检查私钥签名
        验证 SOL 余额;  // 够不够转账
        转账 SOL;       // 更新 lamports
    }
}

USDC 转账的 Owner

  • Owner:SPL Token Program(TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA)
class SPLTokenProgram {
    void transfer() {
        验证签名;           // 检查私钥签名
        验证 PDA 账户;      // 你在这个程序下有记录吗
        验证 Token 余额;    // 够不够转账
        转账 Token;         // 更新 Token Account 的 data
    }
}

Account 类型

类型ownerisOnCurve说明示例
System AccountSystem ProgramTRUE钱包,支付 gas你的 SOL 钱包
Program AccountBPF LoaderTRUE存代码,可执行SPL Token Program、Jupiter
Data AccountProgram-存业务数据某个 DApp 的用户配置
Token AccountToken Program-存代币余额(泛称)-
├─ ATAToken ProgramFALSE钱包+代币标准账户你的 USDC ATA、你的 USDT ATA
├─ 手动创建 Token AccountToken ProgramTRUE任意地址的 Token Account老版本兼容/自定义地址
└─ PDA Token AccountProgramFALSE程序控制的 Token 账户DEX 的 Token 保险库、流动性池中间账户
Mint AccountToken ProgramTRUE定义代币元信息USDC Mint、$TRUMP Mint
Sysvar Account系统-只读Clock、Rent、Epoch

SPL Token

SPL-Token 代表 Solana 网络上的所有非本地代币,包括同质代币(Fungible)和非同质代币(NFT)。

与以太坊的区别

EthereumSolana
ERC20SPL Token
ERC721SPL NFT
Token 存储在合约里每个 Token 持有者都有独立的 Token Account

Mint Account

定义 token 怎么用,数据结构:

字段说明
decimals小数位数(如 USDC 是 6)
mint_authority谁能增发(可 N/A)
freeze_authority谁能冻结(可 N/A)
supply总供应量

Owner:SPL Token Program

Token Account

每个 Token 持有者都有一个特定的 Token Account,记录该 Token 的余额。

例如:Alice 同时拥有 USDT 和 USDC,她有两个 Token Account,分别记录两种 Token 的余额。

字段说明
mint对应的 Mint Account
owner有权操作此账户的公钥
amount代币余额
delegate被授权操作一定额度的地址
delegated_amount已授权的金额

Metadata Account

定义 token 外观:

字段说明
name名字(如 “USDC”)
symbol符号(如 “$”)
uri图片/描述文件地址

Owner:Token Metadata Program(metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s)

关联:Metadata Account 的 mint 字段指向 Mint Account 地址

示例

USDC:

  • Mint Address:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
  • Mint Authority:Circle(可增发)
  • Freeze Authority:N/A
  • Metadata:名称 “USDC”,符号 “$”

$TRUMP(类似供应量固定):

  • Mint Authority:N/A(不能增发,丢权限)
  • Freeze Authority:N/A(不能冻结)

PDA (Program Derived Address)

PDA 是数据账户的一种,用来标识用户在某个程序下的身份。没有私钥,由程序控制。

核心特点

特点说明
isOnCurve = FALSE不在 Ed25519 曲线上,没有私钥
确定性派生相同 seed + program_id 产生相同地址
程序签名只有程序通过 invoke_signed “签名”

什么时候用 PDA?

当你需要”程序控制的账户”时。

场景 1:DEX 的 Token 保险库

你想做一个 Swap 合约,用户把 USDT 存进来,合约帮你交易。

  • 用户把 USDT 转给合约
  • 合约把 USDT 存到自己的保险库
  • 合约用保险库里的 USDT 去交易
  • 用户取款时,合约从保险库转给用户

这个保险库用什么地址?

方案 A:随机生成一个地址 → 私钥放哪里?放合约里?私钥泄露怎么办?
方案 B:用 PDA → 程序自己就是"签名者",不需要私钥

PDA 就是保险库的地址,程序通过 invoke_signed 授权自己操作这个账户。

场景 2:每个用户的独立状态

你写了一个 Todo DApp,每个用户存自己的任务列表。

不用 PDA:
用户 A 的任务列表 → 存在某个中心化的地方
用户 B 的任务列表 → 也在同一个地方

用 PDA:
用户 A 的任务列表 → PDA = findProgramAddress(["todo", A的公钥], 程序ID)
用户 B 的任务列表 → PDA = findProgramAddress(["todo", B的公钥], 程序ID)

每个用户的 PDA 都不同,且只有你的程序能操作。

场景 3:流动性池的中间账户

Uniswap 的 LP token 存款凭证:

用户存 ETH + USDC → 获得 LP token
资金存在流动性池的 PDA 里 → 只有 DEX 程序能操作

总结:什么时候用 PDA?

场景为什么用 PDA
托管/保险库程序控制,不需要私钥,安全
用户专属账户确定性地址,相同 seed 产生相同地址
多人共享账户程序作为唯一签名者,规则明确
任何需要”合约当签名者”的场景没有私钥泄露风险

PDA vs 普通数据账户

对比普通数据账户PDA
谁控制程序程序(一样)
地址怎么来随机生成findProgramAddress 派生
谁能签名程序程序(一样)
优势灵活确定性+无私钥泄露风险

如何生成?

PDA = findProgramAddress(
    [YourPubkey, TokenProgramID, MintAddress],
    TokenProgramID
)

参数说明:

  • YourPubkey:确定是你
  • TokenProgramID:确定是 SPL-Token Program
  • MintAddress:确定是 USDC(不是 USDT)

注意:findProgramAddress 的参数都是你自己定义的,这是这个场景下的例子,你自己的合约中还可以传别的。

PDA vs 钱包地址

对比钱包地址PDA
生成方式随机 Ed25519 公钥findProgramAddress 派生
私钥有没有
isOnCurveTRUEFALSE
签名方式私钥加密签名invoke_signed 程序签名

简单理解:PDA = 程序控制的、地址可预测的、无法被私钥操纵的账户。

Program 类型

类型owner说明
Native Program系统SOL / Token / Stake / Vote 等内置功能
BPF ProgramBPF Loader用户部署的合约
Upgradeable ProgramUpgradeable Loader可升级,有 authority

常用内置 Program:

Program地址功能
System Program11111111111111111111111111111111创建账户、转账
BPF LoaderBPFLoader21111111111111111111111111加载执行 BPF 程序
Stake ProgramStake11111111111111111111111111111111111111质押验证节点
Vote ProgramVote111111111111111111111111111111111111111投票给验证节点

交易 (Transaction)

交易结构

每笔交易包含以下关键信息:

字段说明
Instructions一个或多个指令,定义交易中的操作
Blockhash最新块哈希,确保交易在正确块上执行
Signatures签名,授权交易的执行
Transaction {
  signatures: [signature1, signature2, ...],
  message: Message {
    header: { numReadonlySigned, numReadonlyUnsigned, numRequiredSignatures },
    account_keys: [pubkey1, pubkey2, ...],
    recent_blockhash: "xxxx",
    instructions: [Instruction1, Instruction2, ...]
  }
}

指令 (Instruction)

最小执行单元:

Instruction {
  program_id: Pubkey,      // 调用的程序
  accounts: [AccountMeta], // 涉及的账户
  data: Vec<u8>            // 指令数据(程序自定义)
}

Solana 并行逻辑

  • 并行:交易不涉及同一个账户,可以同时执行
    • 例子:A 转账给 B,C 转账给 D
  • 串行:交易涉及同一个账户,必须按顺序执行
    • 例子:A 转账给 B,B 转账给 C

多指令交易

一笔交易可以包含多个指令,按顺序执行。任何一个指令失败,整个交易都失败。

例如:可以在一笔交易中打包多笔转账,或同时包含 SOL 转账 + Swap + Token 转账。

交易生命周期

  1. 构建:组装 instruction + account keys
  2. 签名:用私钥对交易消息签名
  3. 发送:提交到 RPC 节点
  4. 验证:运行时验证签名、账户权限、余额
  5. 执行:按顺序执行 instruction
  6. 确认:状态根确认完成

转账交易

SOL 转账

  • 调用 System Program
  • 修改 lamports 字段
  • Source 和 Destination 都是钱包地址(isOnCurve = TRUE)
Sender.lamports -= 1 SOL
Receiver.lamports += 1 SOL

SPL Token 转账

  • 调用 SPL Token Program
  • 修改 Token Account 的 amount 字段
  • Source 和 Destination 是 Token Account(isOnCurve = FALSE for ATA)
SenderATA.amount -= 1 USDC
ReceiverATA.amount += 1 USDC

如果 Receiver 没有 ATA,通常先调用 Create Associated Account 指令为其创建。

浏览器查看交易(SolanaScan)

字段说明
Signature交易签名,类似交易哈希
Result交易执行结果(成功/失败)
Signer执行交易的账户地址
Main Actions主要操作(转账、程序调用等)
Instruction Details具体执行的指令详情

Token 转账注意事项:

  • 必须有 Sender 和 Receiver ATA
  • Gas 用 Sender 钱包的 SOL
  • Mint 必须一致

Sysvar 账户

提供链上全局只读数据:

Sysvar说明
Clock当前 slot、epoch、时间戳
Rent当前租金费率
EpochScheduleepoch 配置
StakeHistory质押历史
RecentBlockhashes最近的 blockhash 列表

常见错误

错误原因
IllegalOwneraccount.owner 不对
no ATAToken Account 不存在
insufficient fundsToken Account.amount 不够
invalid account datadata 结构不对
borrowed account账户在同一交易中被多次可变借用
max transactions交易指令数超限

资产安全

私钥和助记词

据慢雾统计,2023 年 9 起私钥泄露安全事件导致的损失高达 8475 万美元。保管好私钥和助记词是保障资产安全的第一步。

推荐阅读:《区块链黑暗森林自救手册》

签名风险

Solana 允许将多笔转账打包成一个交易,仅需一次签名即可完成所有操作。

这一特性被黑客利用:

  1. 受害者在钓鱼网站签名
  2. Phantom Wallet 将多笔转账打包成一笔交易
  3. 受害者以为只签了一笔小额转账,实际授权了全部资产转出
  4. 一次签名,所有资产被盗

安全建议:

  • 每次签名时仔细确认操作内容
  • 阅读钱包文档中的安全提示
  • 对不明网站保持警惕

开发工具

工具用途
AnchorSolana 智能合约框架(类比 Ethereum 的 Hardhat)
Solana CLI命令行工具,部署程序、创建账户
solana/web3.jsJavaScript SDK
solana-pyPython SDK
spl-tokenToken 操作命令行工具

参考资料

Search

    Table of Contents