Solana[part3]_solana账户&简单的交互

AI-摘要
sonia33 GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
Solana[part3]_solana账户&简单的交互
SoniaChenSolana[part3]_solana账户&简单的交互
solana 账户
- 数据账户,用来存储数据
- 系统所有账户
- 程序派生账户(PDA)
- 程序账户,用来存储可执行程序(智能合约),其数据字段为程序的字节码,
executable
标志为true
- 原生账户,指 Solana 上的原生程序,例如:system(系统程序,处理账户创建、转账等基础操作),stake(质押程序),以及 vote(投票程序)
账户结构体
Account
Account
结构体主要用于客户端(如 RPC 调用)获取账户的完整数据,包含账户的所有核心属性。
代码示例:
use solana_sdk::account::Account;
// 账户结构体定义(简化版)
pub struct Account {
// 账户中的lamports数量(Sol的最小单位)
pub lamports: u64,
// 账户存储的二进制数据
pub data: Vec<u8>,
// 账户所有者的公钥(通常是程序的公钥)
pub owner: Pubkey,
// 标识该账户是否为可执行程序(程序账户为true,数据账户为false)
pub executable: bool,
// 下一次需要支付租金的 epoch
pub rent_epoch: Epoch,
}
Account Info
AccountInfo
是智能合约程序中用于访问账户的结构体,包含更多与程序执行相关的元数据(如签名状态、可写性),通常通过参数传递给程序入口函数。
代码示例:
use solana_program::account_info::AccountInfo;
// 账户信息结构体(简化版)
pub struct AccountInfo<'a> {
// 账户的公钥
pub key: &'a Pubkey,
// 标识该账户是否为交易签名者
pub is_signer: bool,
// 标识该账户在本次交易中是否可写
pub is_writable: bool,
// 账户中lamports的可变引用
pub lamports: Rc<RefCell<&'a mut u64>>,
// 账户数据的可变引用
pub data: Rc<RefCell<&'a mut [u8]>>,
// 账户所有者的公钥
pub owner: &'a Pubkey,
// 标识该账户是否为可执行程序
pub executable: bool,
// 租金相关的epoch信息
pub rent_epoch: Epoch,
}
对比
特性 | Account(客户端) | AccountInfo(程序内) |
---|---|---|
使用场景 | 客户端查询账户数据(如 RPC 返回) | 智能合约中操作账户(函数参数) |
核心数据 | 包含账户完整静态数据 | 包含数据的可变引用及操作权限标记 |
签名 / 可写标记 | 无(客户端无需关心交易执行时的权限) | 有(is_signer /is_writable ,用于程序验证) |
数据访问方式 | 不可变(客户端只读) | 可变引用(程序可修改数据) |
要点
- 账户是用来存放数据的基本单元,所有链上数据均存储在账户中
- 每个账户都有一个独一无二的地址(公钥),由 32 字节组成
- 每个账户大小不能超过 10MB,且大小是静态的(创建后无法动态扩容,需提前规划)
- 账户数据存储需要付租金:如果账户余额低于租金阈值,会被系统回收;若存入足够 lamports(约 2 年租金),可成为
rent-exempt
(免租金)状态 - 默认的账户所有者是系统程序(
11111111111111111111111111111111
),只有所有者程序有权修改账户数据
程序派生账户(PDA)
相关文档:https://solana.com/zh/docs/core/pda
程序派生账户(PDA)是由智能合约程序通过特定算法生成的特殊账户,无对应私钥,仅由生成它的程序控制。
注意事项
- 不能直接签名交易
PDA 没有私钥,无法像普通账户那样签名交易,只能由其关联的程序控制操作(确保安全性)。 - 地址碰撞的可能性
理论上,相同程序 ID 和种子可能生成相同 PDA 地址(概率极低)。需通过唯一种子(如用户公钥 + 时间戳)避免碰撞。 - 种子长度限制
种子组合的总长度不能超过 32 字节,超过时需哈希处理(如用sha256
压缩)。 - 生成成本
PDA 通过find_program_address
函数生成(内部调用sha256
哈希),频繁生成会增加链上计算成本。 - 单一程序访问
仅生成 PDA 的程序可修改其数据,跨程序共享需特殊设计(如权限委托)。 - 存储限制
作为数据账户时,大小受 10MB 限制,超大数据需拆分到多个 PDA。
应用场景
- 用户状态管理:存储用户在 DApp 中的资产、等级等数据
- 去中心化金融 (DeFi) 协议:存储流动性池、借贷记录等
- NFT 元数据存储:关联 NFT 的属性、创作者信息等
- DAO 投票系统:记录提案、投票结果等
- 时间锁合约:存储待执行的定时交易
- 多签钱包:存储签名阈值、授权列表等
- 去中心化身份验证:存储用户身份凭证
生成 PDA 的代码示例
use solana_program::{program_pack::Pack, pubkey::Pubkey};
// 生成PDA
fn create_pda(program_id: &Pubkey, seeds: &[&[u8]]) -> (Pubkey, u8) {
// 生成PDA地址和bump(用于确保地址不在Ed25519曲线内)
Pubkey::find_program_address(seeds, program_id)
}
// 示例:用用户公钥和"profile"作为种子生成PDA
let user_pubkey = Pubkey::from_str("...").unwrap();
let seeds = &[b"profile", user_pubkey.as_ref()];
let (pda_address, bump) = create_pda(&program_id, seeds);
println!("PDA地址: {}", pda_address);
println!("Bump值: {}", bump);
Rust库
solana_client
:客户端库,用于与 Solana RPC 节点交互(查询余额、发送交易等)solana_sdk
:核心 SDK,定义账户、交易、签名等基础结构solana_program
:智能合约开发库,包含程序开发所需的账户操作、指令处理等工具
实战
-
启动本地环境
solana-test-validator
➜ ~ solana-test-validator Ledger location: test-ledger Log: test-ledger/validator.log ⠁ Initializing... Waiting for fees to stabilize 1... Identity: GmyJV396jL2hnhcooyMs9U8UeWrBSadDYGbLrd8PvAL6 Genesis Hash: ATymZxWB3G7W8Qyjp7AVBLVN2ZLk1ssiWsTNsehNFczA Version: 2.2.21 Shred Version: 31032 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 ⠒ 03:14:40 | Processed Slot: 21992 | Confirmed Slot: 21992 | Finalized Slot: 219
-
配置本地环境
# 查看当前配置 solana config get # 切换到本地测试网 solana config set --url http://127.0.0.1:8899 # 确认配置生效 solana config get # 输出应显示RPC URL为本地地址
-
创建本地账户
# 生成新账户(保存到文件) solana-keygen new --outfile my-wallet.json # 查看账户公钥 solana address --keypair my-wallet.json
-
给账户空投 SOL
# 向账户空投1 SOL(1 SOL = 1e9 lamports) solana airdrop 1 <你的账户公钥> --url http://127.0.0.1:8899 # 检查余额 solana balance <你的账户公钥>
-
使用sdk
-
获取账户信息
use solana_client::rpc_client::RpcClient; use solana_sdk::{pubkey::Pubkey, signature}; use std::str::FromStr; fn main() { // 创建solana连接 let rpc_url = "http://127.0.0.1:8899"; let client = RpcClient::new(rpc_url); // 指定你要查询的余额账户公钥 // 接收空投账户 let account_pubkey = Pubkey::from_str("FcKkQZRxD5P6JwGv58vGRAcX3CkjbX8oqFiygz6ohceU").unwrap(); // 获取账户余额 match client.get_balance(&account_pubkey) { Ok(balance) => { println!("账户余额为: {}", balance); } Err(err) => { eprintln!("获取账户余额时出错: {}", err); } } }
-
空投sol
use solana_client::rpc_client::RpcClient; use solana_sdk::{pubkey::Pubkey, signature}; use std::str::FromStr; fn main() { // 创建solana连接 let rpc_url = "http://127.0.0.1:8899"; let client = RpcClient::new(rpc_url); // 指定你要查询的余额账户公钥 // 接收空投账户 let account_pubkey = Pubkey::from_str("FcKkQZRxD5P6JwGv58vGRAcX3CkjbX8oqFiygz6ohceU").unwrap(); // 定义空投数量 let amount = 1 * 1_000_000_000; match client.request_airdrop(&account_pubkey, amount) { Ok(signature) => { println!("空投成功,签名为: {}", signature); } Err(err) => { eprintln!("空投时出错: {}", err); } } // 获取账户余额 match client.get_balance(&account_pubkey) { Ok(balance) => { println!("账户余额为: {}", balance); } Err(err) => { eprintln!("获取账户余额时出错: {}", err); } } } ---------------- 空投成功,签名为: 36wYSruZNjLDiBvxG2XGNJDEDi2fmSZs9ViwYMAhEPJdpoTJMtdg5gvgRCC4VpXuDogC8YuDywCLE1ffcCDCVVnQ 账户余额为: 500000000000000000
-
转移sol
use solana_client::rpc_client::RpcClient; use solana_sdk::signature::Signer; use solana_sdk::signature::read_keypair_file; use solana_sdk::system_instruction::transfer; use solana_sdk::transaction; use solana_sdk::{pubkey::Pubkey, signature}; use std::str::FromStr; fn main() { // 创建solana连接s let rpc_url = "http://127.0.0.1:8899"; let client = RpcClient::new(rpc_url); // 设置接收方 let receive = Pubkey::from_str("6qkpaXM6Q9z9rsJQ4qpHV8soGmw2uVTFdJ8KvHR32GDe").unwrap(); // 设置发送方,需要通过签名获取 let sender = read_keypair_file("/Users/tinachan/.config/solana/id.json") .expect("failed to read keypair file"); // 定义空投数量 最小单位是lamports let amount = 1 * 1_000_000_000; // 创建转账的指令 let transfer = transfer(&sender.pubkey(), &receive, amount); // 创建交易 let recent_blockhash = client.get_latest_blockhash().unwrap(); let transaction = transaction::Transaction::new_signed_with_payer( &[transfer], Some(&sender.pubkey()), &[&sender], recent_blockhash, ); let result = client.send_and_confirm_transaction(&transaction); match result { Ok(signature) => { println!("交易成功,签名为: {}", signature); } Err(err) => { eprintln!("交易时出错: {}", err); } } } ---------------- 交易成功,签名为: 5yPGuuN7hbVqv1wTvsSryYG6rfaW6FNWTyih2PpwpSPxywjZ6LLMBGxdTjo59ZDdt8DfPShiq4KU4KXXvPBbevLc
总结:(代补充)
-
-
通过JsonRpc获取账户信息 https://solana.com/zh/docs/rpc/http/getaccountinfo
-
查看官网文档,挑选getaccountinfo来玩玩
curl https://api.devnet.solana.com -s -X \ POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getAccountInfo", "params": [ "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", { "commitment": "finalized", "encoding": "base58" } ] } '
-
替换自己的账户pubkey以及本地运行链接
➜ sol git:(master) ✗ curl http://127.0.0.1:8899 -s -X \ POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getAccountInfo", "params": [ "6qkpaXM6Q9z9rsJQ4qpHV8soGmw2uVTFdJ8KvHR32GDe", { "commitment": "finalized", "encoding": "base58" } ] } ' {"jsonrpc":"2.0","result":{"context":{"apiVersion":"2.2.21","slot":33045},"value":{"data":["","base58"],"executable":false,"lamports":1000000000,"owner":"11111111111111111111111111111111","rentEpoch":18446744073709551615,"space":0}},"id":1}
-
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果