Skip to main content

✍️ 将数据写入 Solana - 掌握区块链的"写"权!

🎯 学习目标

上一课我们学会了"读",现在要学习"写"了!就像从观众变成导演 🎬

你将掌握:

  • 🔑 理解公钥私钥的魔法
  • 📝 创建并发送交易
  • 🎭 构建自定义指令
  • 💰 处理交易费用
🚀 准备好了吗?

写入区块链就像在石头上刻字 - 一旦写入,永远存在!让我们学会如何安全地"刻字"吧!

🔐 第一章:密钥对 - 你的数字身份证

🎭 什么是密钥对?

想象密钥对就像你的银行账户系统

🏦 你的数字银行
├── 📮 公钥 = 银行账号(可以公开分享)
│ └── 用来接收转账
└── 🔑 私钥 = 银行密码(绝对保密!)
└── 用来授权支出
⚠️ 黄金法则

永远不要分享你的私钥!

  • 公钥 = 你的收款地址 ✅ 可以分享
  • 私钥 = 你的支付密码 ❌ 绝对保密

记住:拥有私钥 = 拥有账户的一切!

💻 代码实战:生成密钥对

import { Keypair } from '@solana/web3.js';

// 🎲 生成全新的密钥对(像摇骰子一样随机)
const newKeypair = Keypair.generate();

console.log("🎉 恭喜!你的新身份:");
console.log("📮 公钥(地址):", newKeypair.publicKey.toBase58());
console.log("🔐 私钥(保密):", newKeypair.secretKey);

// 输出示例:
// 📮 公钥:7cVfgArCheMR6Cs4t6vz5rfnqd56vZq4ndaBrY5xkxXy
// 🔐 私钥:[174, 47, 154, 16, 202, ...](一串神秘数字)

🎨 私钥的三种形态

私钥可以有不同的表现形式,就像水可以是冰、液体或蒸汽:

1️⃣ 助记词形式(最友好)🌱

// 12-24个单词,容易记忆
"pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter"

// 🎯 使用场景:钱包备份,人类记忆

2️⃣ Base58 字符串(钱包常用)📝

"5MaiiCavjCmn9Hs1o3eznqDEhRwxo7pXiAYez7keQUviUkauRiTMD8DrESdrNjN8zd9mTmVhRvBJeg5vhyvgrAhG"

// 🎯 使用场景:钱包导出/导入

3️⃣ 字节数组(程序使用)🔢

[174, 47, 154, 16, 202, 193, 206, 113, 199, 190, ...]

// 🎯 使用场景:程序内部处理

🔄 转换密钥格式

// 🎯 场景:从环境变量恢复密钥对
const recoverKeypair = () => {
// 从 .env 文件读取(开发环境)
const secretString = process.env.PRIVATE_KEY || "";

// 转换步骤:
// 1. 字符串 → 数字数组
const secretArray = JSON.parse(secretString) as number[];

// 2. 数字数组 → Uint8Array
const secretKey = Uint8Array.from(secretArray);

// 3. Uint8Array → Keypair对象
const keypair = Keypair.fromSecretKey(secretKey);

console.log("✅ 密钥对恢复成功!");
console.log("📮 地址:", keypair.publicKey.toBase58());

return keypair;
};
🔒 安全提示

生产环境密钥管理

  • ❌ 不要硬编码私钥
  • ❌ 不要提交到 Git
  • ✅ 使用硬件钱包
  • ✅ 使用密钥管理服务(AWS KMS、HashiCorp Vault)
  • ✅ 使用环境变量(仅开发环境)

📮 第二章:交易 - 区块链的邮政系统

🎯 交易是什么?

把交易想象成快递包裹 📦:

📦 一个 Solana 交易包裹
├── 📋 收件人列表(涉及的账户)
├── 📝 包裹内容(指令)
├── ✍️ 寄件人签名(授权)
└── 💰 邮费(交易费)

🚀 创建你的第一笔交易

让我们创建一个转账交易 - 从 Alice 转 1 SOL 给 Bob:

import {
Connection,
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction,
Keypair,
PublicKey
} from '@solana/web3.js';

async function sendSol() {
// 🌐 Step 1: 连接到网络
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');

// 👥 Step 2: 准备参与者
const alice = Keypair.generate(); // 发送者(有私钥)
const bob = new PublicKey("Bob的地址"); // 接收者(只需要地址)

// 💰 Step 3: 确保 Alice 有钱(开发网空投)
console.log("💸 向 Alice 空投 2 SOL...");
const airdropSignature = await connection.requestAirdrop(
alice.publicKey,
2 * LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
console.log("✅ 空投成功!");

// 📦 Step 4: 创建交易
const transaction = new Transaction();

// 📝 Step 5: 添加转账指令
const transferInstruction = SystemProgram.transfer({
fromPubkey: alice.publicKey, // 从谁
toPubkey: bob, // 给谁
lamports: LAMPORTS_PER_SOL // 多少(1 SOL)
});

transaction.add(transferInstruction);

// 🚀 Step 6: 发送交易
console.log("📤 发送交易...");
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[alice] // 签名者数组(Alice 需要签名授权)
);

console.log("🎉 交易成功!");
console.log("🔗 查看交易:", `https://explorer.solana.com/tx/${signature}?cluster=devnet`);
}

🎨 交易的生命周期

💡 批量操作:一个交易,多个指令

交易的强大之处在于可以批量执行多个操作:

async function multipleInstructions() {
const transaction = new Transaction();

// 🎯 指令1:Alice → Bob 转 1 SOL
transaction.add(
SystemProgram.transfer({
fromPubkey: alice.publicKey,
toPubkey: bob,
lamports: LAMPORTS_PER_SOL
})
);

// 🎯 指令2:Alice → Charlie 转 0.5 SOL
transaction.add(
SystemProgram.transfer({
fromPubkey: alice.publicKey,
toPubkey: charlie,
lamports: 0.5 * LAMPORTS_PER_SOL
})
);

// 🎯 指令3:Alice → David 转 0.1 SOL
transaction.add(
SystemProgram.transfer({
fromPubkey: alice.publicKey,
toPubkey: david,
lamports: 0.1 * LAMPORTS_PER_SOL
})
);

// 🚀 一次发送,全部执行(原子性)
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[alice] // Alice 签一次,授权所有转账
);

console.log("💰 批量转账完成!一笔交易费,多个转账!");
}
🎊 批量操作的优势
  • 效率高:一次网络请求
  • 💰 省钱:只付一次交易费
  • 🔒 原子性:要么全部成功,要么全部失败

🎭 第三章:指令 - 交易的灵魂

📋 指令的结构

指令就像菜谱 🍳,告诉程序要做什么:

interface Instruction {
programId: PublicKey; // 🏭 哪个程序(厨房)
keys: AccountMeta[]; // 📦 需要哪些材料(账户)
data?: Buffer; // 📝 具体食谱(数据)
}

🔧 创建自定义指令

当调用非系统程序时,需要手动构建指令:

// 🎯 示例:调用自定义程序
async function callCustomProgram() {
// 1️⃣ 定义程序 ID
const programId = new PublicKey("你的程序地址");

// 2️⃣ 准备账户列表
const accounts: AccountMeta[] = [
{
pubkey: dataAccount, // 数据账户
isSigner: false, // 不需要签名
isWritable: true // 需要写入
},
{
pubkey: userAccount, // 用户账户
isSigner: true, // 需要签名
isWritable: false // 只读
}
];

// 3️⃣ 构造指令数据(如果需要)
const instructionData = Buffer.from([
0x01, // 指令类型
0x02, // 参数1
0x03 // 参数2
]);

// 4️⃣ 创建指令
const instruction = new TransactionInstruction({
keys: accounts,
programId,
data: instructionData
});

// 5️⃣ 添加到交易并发送
const transaction = new Transaction().add(instruction);
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[userKeypair] // 签名者
);

console.log("✅ 自定义程序调用成功!");
}

📊 账户元数据详解

interface AccountMeta {
pubkey: PublicKey; // 账户地址
isSigner: boolean; // 是否需要签名
isWritable: boolean; // 是否可写
}

让我们用表格理解这些属性的组合:

isSignerisWritable含义使用场景
签名 + 可写扣款账户、所有者账户
签名 + 只读授权验证
不签名 + 可写数据存储账户
不签名 + 只读配置账户、引用数据

💰 第四章:交易费用 - 区块链的汽油费

⛽ 费用机制

Solana 的费用就像高速公路过路费 🚗:

🛣️ Solana 高速公路
├── 🚗 交易 = 你的车
├── 💰 费用 = 过路费
├── 🏃 验证者 = 收费员
└── ⚡ 速度 = 超快(不堵车!)

💸 费用计算

// 🧮 Solana 费用公式
费用 = 基础费用 + (签名数量 × 签名费用)

// 实际例子:
基础费用 = 5000 lamports
每个签名 = 5000 lamports

// 一个签名的交易:
总费用 = 5000 + (1 × 5000) = 10000 lamports = 0.00001 SOL

// 💡 对比:
// Ethereum: $5-50 😱
// Solana: $0.00025 😎

🎯 谁付钱?第一签名者规则!

const transaction = new Transaction().add(instruction);

// 👥 多个签名者的情况
const signers = [alice, bob, charlie];

// 💰 Alice(第一个)付费!
const signature = await sendAndConfirmTransaction(
connection,
transaction,
signers // Alice 在第一位 = Alice 付钱
);
💡 省钱技巧
  1. 批量操作:多个指令放一个交易
  2. 优化账户:减少不必要的账户
  3. 复用交易:相似操作可以复用

🚿 获取测试币

开发时需要测试币?这里有免费的!

// 🎯 方法1:代码空投
async function getTestSOL(publicKey: PublicKey) {
const connection = new Connection('https://api.devnet.solana.com');

console.log("🚁 空投中...");
const signature = await connection.requestAirdrop(
publicKey,
2 * LAMPORTS_PER_SOL // 2 SOL
);

await connection.confirmTransaction(signature);
console.log("💰 到账 2 SOL!");
}

// 🎯 方法2:命令行
// solana airdrop 2 <你的地址> --url devnet

// 🎯 方法3:水龙头网站
// https://solfaucet.com/

🎮 实战项目:构建转账应用

🎯 项目目标

构建一个友好的转账界面,支持:

  • 💸 SOL 转账
  • 📊 余额查询
  • 📜 交易历史

💻 完整代码实现

import React, { useState } from 'react';
import {
Connection,
PublicKey,
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction,
Keypair
} from '@solana/web3.js';

function TransferApp() {
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [status, setStatus] = useState('');
const [txSignature, setTxSignature] = useState('');

const handleTransfer = async () => {
try {
setStatus('🔄 准备交易...');

// 连接网络
const connection = new Connection(
'https://api.devnet.solana.com',
'confirmed'
);

// 获取发送者密钥对(实际应用中应安全管理)
const sender = getStoredKeypair();

// 验证接收地址
const recipientPubkey = new PublicKey(recipient);

setStatus('📦 创建交易...');

// 创建交易
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: recipientPubkey,
lamports: parseFloat(amount) * LAMPORTS_PER_SOL
})
);

setStatus('✍️ 签名并发送...');

// 发送交易
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[sender]
);

setTxSignature(signature);
setStatus('✅ 转账成功!');

} catch (error) {
setStatus(`❌ 错误: ${error.message}`);
}
};

return (
<div className="transfer-app">
<h2>💸 SOL 转账工具</h2>

<div className="form">
<input
type="text"
placeholder="接收者地址"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>

<input
type="number"
placeholder="SOL 数量"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>

<button onClick={handleTransfer}>
发送 SOL
</button>
</div>

<div className="status">
{status}
</div>

{txSignature && (
<div className="result">
<p>交易签名:{txSignature.slice(0, 20)}...</p>
<a
href={`https://explorer.solana.com/tx/${txSignature}?cluster=devnet`}
target="_blank"
>
在浏览器中查看 →
</a>
</div>
)}
</div>
);
}

🏆 挑战任务

🎯 Level 1:批量转账工具

创建一个可以同时给多人转账的工具:

interface BatchTransfer {
recipients: Array<{
address: string;
amount: number;
}>;
}

🎯 Level 2:带备注的转账

添加备注功能(使用 Memo 程序):

// 提示:使用 @solana/spl-memo
import { createMemoInstruction } from '@solana/spl-memo';

transaction.add(
createMemoInstruction("生日快乐!🎂", [sender.publicKey])
);

🎯 Level 3:定时转账

实现定时/定期转账功能(高级)

📚 学习资源

官方文档

安全最佳实践

🎊 总结

恭喜你!现在你已经掌握了:

密钥对管理

  • 生成和恢复密钥对
  • 理解公钥私钥的关系
  • 安全存储私钥

交易构建

  • 创建和发送交易
  • 批量添加指令
  • 处理签名授权

自定义指令

  • 理解指令结构
  • 调用任意程序
  • 管理账户权限

费用处理

  • 理解费用机制
  • 优化交易成本
  • 获取测试代币

🚀 下一步:学习如何创建你自己的 Solana 程序!