本文是一篇关于如何在Solana Devnet上进行快速、低成本和有趣的项目开发的教程。
本指南将帮助你准备好使用 Node.js、Javascript 和 Typescript 实用程序脚本编写 Solana 交易代码,然后通过 React 和 Solana Next.js 引导程序模板进行前端集成。
为什么要这样做?我们正在为前端、后端和链上应用程序的发布 盛会 做准备,我们需要熟悉我们所有的工具。如果你没有看过 Solana 开发设置指南(第一部分:CLI) 这篇文章,请先阅读它!
这是“设置系列”的第二部分,是为那些想学习如何发布 Solana 应用程序的人准备的序幕。
在 Solana 上开发是多学科的,涵盖了广泛的技术,从 CLI,到像 Next.js 这样的前端,到链下 Node.js 或其他后端脚本,当然还有 Rust/Anchor 中的链上后端程序。
🙋♂️ 为什么要关注我的设置文章? 你也可以在 Solana 文档中找到设置信息,但是,我沿途添加了自己的技巧和窍门,并将其设计为适合 我们稍后将使用的有趣的应用程序和技术堆栈!
🛠️ 我们将从 Node.js 脚本开始,然后从那里开始! 使用 TypeScript 是一个最佳实践,所以我们将使用它,但是,由于这些是用于学习的基础脚本,所以没有很多需要担心的类型!
然后,我们将深入研究一个很棒的应用程序引导程序模板,它结合了 Next.js、React、TailwindCSS、ReactQuery 和大量的 Solana 交易 javascript。最好的部分是,我们公开了大量的 React Hooks,可以在你的应用程序中按原样使用。
在本教程中:
🛠️ Javascript、Typescript 配置
🚀 Solana Node.js 和 Typescript 脚本
⚡️ Solana 前端,Next.js 集成
Next.js 应用程序 — 基本应用程序设置
安装并设置 Solana “热”钱包(和安全)
获得免费的 Solana Devnet Airdrop
Next.js 应用程序。了解钱包集成。
11. 回顾 Next.js 中的 Solana 钱包代码
12. 快速的首次交易,React Hooks
🥰 感谢阅读!… 🔥 请鼓掌并分享这篇文章,谢谢!🚀
我在这里放置了标准的 TypeScript 安装流程,但是,这也在下面创建你的仓库的说明中重复了。
你可以只阅读本节并用作参考, 因为我们将在下面开始我们的项目时再次输入。
我建议你现在做的唯一一件事是在全局安装 ts-node
CLI:
npm install -g ts-node
标准 Typescript 安装流程:
1. 初始化一个 Node.js 项目
通常在你的空 git 初始化仓库文件夹中完成此操作。
npm init -y
2. 安装 TypeScript 和所需的 Solana 依赖项
还添加 dotenv
用于密钥并将它添加到 .gitignore
。
npm install typescript @solana/web3.js @types/node --save
npm install dotenv --save
echo ".env" >> .gitignore
3. 初始化 TypeScript 配置
命令 npx tsc --init
创建一个名为 tsconfig.json
的文件。该文件包含诸如要输出的 JavaScript 版本、是否启用严格类型检查以及要包括或排除的文件或文件夹之类的设置。
tsc
TypeScript CLI 命令未在本文中使用,但是你应该可以使用它来将 TS 从 TS 转换为 JS 在输出目录中,“outDir”: “./dist”,
npx tsc --init
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./"
},
"include": ["*.ts"],
"exclude": ["node_modules"]
}
4.1 运行 Node.js 脚本(选项 1:tsc)
本节假定你已准备好运行 Node.js 脚本/文件。
通常在普通的 node.js 中,你只需使用如下命令:
node my-script.ts
但是,运行 typescript 略有不同,你可以使用此处 4.1 或 4.2 中的任一选项。
tsc
只是将你的脚本从 .ts 文件编译为 .js,然后像以前一样(在上面)使用 node 运行它。注意: 我没有在本文的大部分示例中使用此选项。但是,稍后我将把它用于无服务器函数示例。
优点是,如果你要将其转换为 无服务器函数,则应该使用此选项。如果需要,你可以更改更多配置选项,并同时拥有未编译和已编译的版本。你还可以将此与其他依赖项一起压缩。
npx tsc
node dist/my-script.js
4.2 运行 Node.js 脚本(选项 2:ts-node)
你安装 ts-node 并只需运行 typescript。我在本文中大部分时间都使用此选项,除了稍后。
优点是你可以使用它而无需任何额外的编译步骤,并且文件更少(不会将其编译为 .js,只需将其作为 .ts 运行即可)。
现在让我们创建我们的目录和仓库。
npm install -g ts-node
ts-node connect-to-devnet.ts
确保创建一个目录和 github 仓库,以便你可以保存你的工作并保持井井有条。
solana-devnet-scripts
git init
git remote add origin https://github.com/YOUR_USERNAME/solana-devnet-scripts.git
cd solana-devnet-scripts
(或你的目录名称)
npm init -y
注意:请确保你设置了本系列第一部分中所需的 Node.js 和 Solana 库。如果你没有这样做,请查看该文章。
如果没有,请添加一个 .gitignore
echo -e "node_modules\ndist\n.env*" > .gitignore
npm install typescript @solana/web3.js @types/node --save-dev
npm install dotenv --save
确保你添加了用于密钥的 dotenv
,并将 .env*
添加到 gitignore
(应该在上一步中完成)
我们正在安装两种 Typescript 选项,以便你可以学习如何使用任何一种。但是稍后我们主要使用 ts-node
。
⚠️ 我需要使用 Typescript 吗? 不,但 这是一个最佳实践,因此即使在每个脚本中都不是必需的,我们也在使用它,以使其成为一种习惯。
npx tsc -init
npm install -g ts-node
创建、编辑:tsconfig.json
以包含以下设置:
{
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./"
},
"include": ["*.ts"],
"exclude": ["node_modules"]
}
让我们开始使用一些简单的使用 Solana 的 Node.js 脚本
创建一个 scripts/
目录是个好主意。你可以随意调用它,但这是你在代码库中看到的典型名称。
还有 .env 文件,我们在其中保存密钥或常量,并且我们不希望将其检入 github(应该在你的 .gitignore 文件 .env*
中),并且永远不要检入或提交任何 .env 文件。
建立与 Solana Devnet 的连接,这是一个供开发人员使用的测试网络,并检索有关区块链状态的基本信息。
它将 Solana 集群软件的版本和最新的 slot(区块高度)记录到控制台,从而提供了一种快速验证连接性和网络状态的方法。
connect-to-devnet.ts
import { Connection, clusterApiUrl } from '@solana/web3.js';
async function checkConnection(): Promise<void> {
// Connect to Devnet
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
// Get the version of the Solana cluster
const version = await connection.getVersion();
console.log('Solana Cluster Version:', version);
// Get the latest block height
const slot = await connection.getSlot();
console.log('Current Slot:', slot);
}
checkConnection().catch((error: Error) => console.error(error));
@solana/web3.js
中的 Connection 和 clusterApiUrl 与 Solana 区块链交互。让我们从你的终端窗口运行它:
ts-node connect-to-devnet.ts
## 输出
Solana Cluster Version: { 'feature-set': 3294202862, 'solana-core': '2.2.3' }
Current Slot: 370732858
✅ 成功了!
📝 我们获得了一些其他信息,因此让我们找出其含义。
feature-set
:表示集群上启用的活动功能集的数字哈希。 随着 Solana 核心功能打开或关闭而变化。 当你想确认与链上程序或工具的兼容性时,它很有用。solana-core
:这是 devnet 验证器当前正在运行的 Solana 核心软件版本,此处为版本 2.2.3
。370,732,858
只是你运行命令时的 Current slot
编号,本质上是网络的心跳。我们将创建一个钱包, 通常称为“密钥对”(公钥和私钥)。 尽管请记住,非钱包账户也需要“密钥对”,因此并非所有密钥对都是钱包!
一个“钱包”:
我们将生成一个新的密码密钥对,供在 Solana 区块链上使用,包含一个公钥和一个秘密(私钥)。
⚠️ 小心:在任何地方记录 秘密 密钥都是很高的安全风险。 泄露可能导致钱包中所有资金的损失。 下面给出一个示例,仅用于学习(如果你取消注释日志记录)。
生成密钥对是一项非常基本的任务,但却是用于以下目的的常见例程:
此脚本默认仅出于学习目的将 公钥 以 Base58 格式记录到屏幕上。 但是,你可以取消注释以在控制台中获取公钥和私钥,其中 公钥采用 Base58 格式,私钥采用十六进制格式,从而允许用户创建和管理 Solana 钱包。
示例(注意字符串的细微差别):
公钥,Base58: Fb8uPmkJzB6T4xJHFGgiU5hVzZEYQoWfbbKtTTfS98Xc
私钥,十六进制编码版本: 626gsjka7w73t763572ksdhj73gsj68adskhjh…
⚠️ 永远不要共享密钥或让其他人看到它,例如在“屏幕共享”、屏幕截图时,并且永远不要在正常操作中登录。
⚠️ 不要 记录 密钥, 除非在此演示实例中用于本地学习。
generate-keypair.ts
import { Keypair } from '@solana/web3.js';
function generateKeypair(): void {
// Generate a new keypair
const keypair = Keypair.generate();
console.log('Public Key:', keypair.publicKey.toBase58());
// Secret key commented out for security. If you want to see what it looks like once uncomment below but never give it out. Security risk.
// console.log('Secret Key (DO NOT SHARE, SECRET, This is for local learning demo only):', Buffer.from(keypair.secretKey).toString('hex'));
}
generateKeypair();
⚠️你已经创建了一个“ 钱包”。 钱包持有 SOL,可以转移它并授权其他操作 — 但钱包未配置为存储额外的程序数据。稍后,我们将创建一个“ 账户”,该账户可以容纳任意额外数据。两者都有公钥,看起来相似,但配置不同,操作不同并且用于不同的用例。
查询指定公钥的余额,将结果从 lamports(Solana 的最小单位)转换为 SOL 以提高可读性。 它将余额记录到控制台,从而提供了一种简单的方法来检查 Devnet 账户中有多少测试 SOL 可用。
在 CLI 中它是:
solana balance <address>
你可以从 CLI 获取余额,但是此处的目的是学习如何在你在 Typescript 中编写的代码中执行此操作。
请注意脚本中的这一点,根据你的 .env 文件所在的位置更改为 dotenv.config({ path: ‘../.env’ })
或 dotenv.config();
:
// Load environment variables from .env file
dotenv.config({ path: '../.env' }); // If .env is in your app root directory
// dotenv.config(); // If .env is in your current working directory
check-balance.ts
import { Connection, PublicKey, clusterApiUrl, LAMPORTS_PER_SOL } from '@solana/web3.js';
import * as dotenv from 'dotenv';
// Load environment variables from .env file
dotenv.config({ path: '../.env' }); // location of your .env file, adjust as needed
// dotenv.config(); // If .env is in your current working directory
async function checkBalance() {
try {
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
// Load and validate public key
const publicKeyString = process.env.PUBLIC_KEY;
if (!publicKeyString) {
throw new Error('PUBLIC_KEY not found in .env file. Check if .env is in the correct directory.');
}
// Validate the public key
let publicKey;
try {
publicKey = new PublicKey(publicKeyString);
} catch (err) {
throw new Error('Invalid PUBLIC_KEY in .env file');
}
// Get balance in lamports and convert to SOL
const balance = await connection.getBalance(publicKey);
console.log('Balance:', balance / LAMPORTS_PER_SOL, 'SOL');
} catch (error) {
console.error('Error:', (error as Error).message);
}
}
checkBalance();
如果你想使用你当前设置的地址进行测试,或者使用其他地址:
solana address
## 输出
[你的地址]
检查是哪个钱包:
solana config get
将你正在检查的公钥添加到 .env 文件:
PUBLIC_KEY=YOUR_PUBLIC_KEY_HERE
运行:
ts-node check-balance.ts
## 输出(SOL 因你的钱包而异)
Balance: 5.239995 SOL
我们可以尝试获得 1 个 dev SOL 的空投。 你可以更改它,但从 devnet 获取少于 2 个 SOL,通常它不会给你超过这个数量。
⚠️ 正如我们的 CLI 教程(本设置系列的第 1 部分)中所述, 有时 devnet 会过载或受到速率限制,并且可能会给出错误。 建议从 Helius 或其他 RPC 提供商处获取 RPC devnet url/密钥,以获得更稳定、无错误的操作。 如果你想从 Helius 获取一个,请参阅我的文章:在 Solana 上构建:Helius RPC + 教程。
airdrop.ts
import { Connection, PublicKey, Keypair, clusterApiUrl, LAMPORTS_PER_SOL, VersionedTransaction } from '@solana/web3.js';
import * as dotenv from 'dotenv';
// Load environment variables from .env file
dotenv.config({ path: '../.env' }); // location of your .env file, adjust as needed
// dotenv.config(); // If .env is in your current working directory
async function requestAirdrop(): Promise<void> {
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
// Load secret key from .env and create keypair
const secretKeyHex = process.env.PAYER_SECRET_KEY;
if (!secretKeyHex) {
throw new Error('PAYER_SECRET_KEY not found in .env file');
}
const payerKeypair = Keypair.fromSecretKey(Buffer.from(secretKeyHex, 'hex'));
const publicKey = payerKeypair.publicKey;
// Request 1 SOL airdrop
const signature = await connection.requestAirdrop(publicKey, 1_000_000_000); // 1 SOL in lamports
const latestBlockhash = await connection.getLatestBlockhash();
await connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
console.log('Airdrop successful! Transaction signature:', signature);
// Check new balance
const balance = await connection.getBalance(publicKey);
console.log('New Balance:', balance / LAMPORTS_PER_SOL, 'SOL');
}
requestAirdrop().catch((error: Error) => console.error(error));
dotenv
包从 .env
文件加载环境变量。 这允许代码访问敏感数据,例如 Solana 钱包的密钥,而无需对其进行硬编码。@solana/web3.js
中的 Connection
类连接到 Solana Devnet 集群。 该连接用于在开发安全的环境中与区块链交互。⚠️ 你的脚本需要一个密钥,因为它涉及在 Solana devnet 上请求空投,这是一种链上操作,需要签名者(付款人)启动和确认交易。
首先获取你的钱包路径:
solana config get
## 输出(示例,因你的配置而异)
...
Keypair Path: /Users/yourusername/wallets/wallet-dev.json
...
现在获取你的密钥,使用上面的完整钱包路径/文件输出:
cat put-your-full-wallet-path/file-here.json | jq -r '.[]' | xargs printf "%02x"
## 输出
jhe1nbc9v6yiok7jfdiohg88silhor ...etc.
将密钥完全按照输出添加到 .env 文件:
PAYER_SECRET_KEY=YOUR_SECRET_KEY_HERE
运行它:
ts-node airdrop.ts
## 输出
Airdrop successful! Transaction signature: 57bKTADsu6XkUZzGpEsJgU5mhEsp7x9JFiTAJsjij8dXRKiab1g7N1oaUavH57krdeDtifY2iKDFhNEnZ2m477hz
New Balance: 6.239995 SOL
你可以转到 Solscan.io (devnet) 或其他区块浏览器,并将交易签名放入搜索栏中(请确保在 Solana 徽标的右上角设置为 Devnet,否则它将找不到交易)。
在下一个 Solana 实用程序脚本中,我们将组合一些操作。 我们这样做是为了举例说明,以便你可以看到如何组合多个操作来完成任务。 在现实生活中,它可能略有不同,但这取决于你的场景。
为了使其工作,我们将需要一个 ✅ 具有公钥/密钥的“from”钱包和一个 ✅ 仅具有公钥的“to”钱包。 如果没有“from”钱包或密钥,我们将创建一个新钱包并尝试获得空投来资助它。
一旦我们有一个带有资金的“from”钱包,我们将执行转移指令。
import {
Connection,
PublicKey,
Keypair,
Transaction,
SystemProgram,
clusterApiUrl,
LAMPORTS_PER_SOL,
} from '@solana/web3.js';
import * as dotenv from 'dotenv';
dotenv.config({ path: '../.env' }); // location of your .env file, adjust as needed
function getOrCreateKeypair(): Keypair {
const secretKeyHex = process.env.PAYER_SECRET_KEY;
// this returns a key pair (public & secret key)
if (secretKeyHex) {
console.log('Using existing payer key from .env');
const secretKey = Buffer.from(secretKeyHex, 'hex');
return Keypair.fromSecretKey(secretKey);
} else {
console.log('No secret key found. Generating new payer key...');
const newKeypair = Keypair.generate();
console.log('Generated Public Key:', newKeypair.publicKey.toBase58());
return newKeypair;
}
}
async function transferSOL(): Promise<void> {
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const fromKeypair = getOrCreateKeypair();
let balance = await connection.getBalance(fromKeypair.publicKey);
if (balance < 0.02 * LAMPORTS_PER_SOL) {
console.log('Insufficient funds, requesting airdrop...');
const airdropSignature = await connection.requestAirdrop(
fromKeypair.publicKey,
LAMPORTS_PER_SOL
);
const latestBlockhash = await connection.getLatestBlockhash();
await connection.confirmTransaction({
signature: airdropSignature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
balance = await connection.getBalance(fromKeypair.publicKey);
console.log(`New balance after airdrop: ${balance / LAMPORTS_PER_SOL} SOL`);
} else {
console.log('Sufficient funds available, skipping airdrop.');
}
const toPublicKey = new PublicKey(process.env.RECIPIENT_PUBLIC_KEY || '');
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toPublicKey,
lamports: 0.01 * LAMPORTS_PER_SOL,
})
);
const signature = await connection.sendTransaction(transaction, [fromKeypair]);
const latestBlockhash = await connection.getLatestBlockhash();
await connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
console.log('Transfer successful! Transaction signature:', signature);
balance = await connection.getBalance(fromKeypair.publicKey);
console.log('New Balance:', balance / LAMPORTS_PER_SOL, 'SOL');
}
transferSOL().catch(console.error);
在此脚本中,你需要:
⚠️ 注意: 如果我们有钱包的密钥,我们也可以从中获取公钥! 因此,我们不需要将 PAYER 的公钥放入 .env 文件中!
.env 文件,你应该这样写(使用完整密钥,示例密钥已缩短):
PAYER_SECRET_KEY=ce3146a9...50f2bea68a2637
RECIPIENT_PUBLIC_KEY=9sLg...ktjA9WfkQPE
如果你也具有先前的 PUBLIC_KEY=GtYM8…P9zcXQ
,则可以将其保留在其中,但我们不需要它。
ts-node transfer-sol.ts
## 输出
Using existing payer key from .env
Sufficient funds available, skipping airdrop.
Transfer successful! Transaction signature: 2r4qu6LX...NZaBjG
New Balance: 6.22999 SOL
回顾一下我们所做的事情:
new Connection(clusterApiUrl(‘devnet’), ‘confirmed’)
Keypair.fromSecretKey(…)
从以十六进制格式提供的 64 字节密钥加载现有钱包。Keypair.generate()
创建一个全新的随机钱包,除非显式导出或写入文件,否则不会保存该钱包。 当没有可用的钱包时,这很有用。 但是,除非你保留密钥对,否则退出脚本后将失去访问权限。connection.getBalance(publicKey)
查询钱包中 lamports 的当前余额,可以将其转换为 SOL 以提高可读性。connection.requestAirdrop(publicKey, lamports)
请求 Devnet 水龙头将测试 SOL 发送到钱包以用于开发目的。connection.confirmTransaction(signature)
等待已提交的交易被网络确认并包含在一个区块中。new Transaction().add(SystemProgram.transfer({…}))
使用 SystemProgram.transfer
构建一个新交易,以将 SOL 从一个地址转移到另一个地址。connection.sendTransaction(transaction, [signer])
有关交易设置的更多信息:
const signature = await connection.sendTransaction(transaction, [fromKeypair]);
将交易发送到 Solana 网络,并使用 fromKeypair
对其进行签名。 返回的 signature
是一个唯一 ID,你可以使用它来跟踪链上的交易状态。
const latestBlockhash = await connection.getLatestBlockhash();
获取最新的区块哈希和关联的 lastValidBlockHeight
。 需要这些值来确认交易尚未过期(必需)。
await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });
使用签名和区块数据确认交易,以在已知的区块范围内验证它。 此方法可以防止确认过时的交易并提高可靠性。
🥰 感谢阅读!… 🔥 请鼓掌并分享这篇文章,谢谢!🚀
用例: 你可能会创建并资助一个新账户,以初始化你的去中心化应用程序的全新钱包或状态账户,从而确保它可以免除租金并为交易做好准备。 它还有助于通过隔离账户余额和操作来安全地管理资金。
⚠️ 我们不是已经用钱包做过这件事了吗? 没有。 创建账户与创建钱包不同。
仅创建钱包密钥对(示例):
const myWallet = Keypair.generate();
创建保存数据的账户(示例):
const accountKeypair = Keypair.generate();
const tx = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: myWallet.publicKey,
newAccountPubkey: accountKeypair.publicKey,
lamports: rentExemption,
space: 100,
programId: myProgramId
})
);
create-account.ts
import { Connection, PublicKey, Keypair, Transaction, SystemProgram, clusterApiUrl, LAMPORTS_PER_SOL } from '@solana/web3.js';
import * as dotenv from 'dotenv';
// Load environment variables from .env file
dotenv.config({ path: '../.env' }); // location of your .env file
async function createNewAccount(): Promise<void> {
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
// Load secret key from .env and create keypair
const payerSecretKeyHex = process.env.PAYER_SECRET_KEY;
if (!payerSecretKeyHex) {
throw new Error('PAYER_SECRET_KEY not found in .env file');
}
const payerKeypair = Keypair.fromSecretKey(Buffer.from(payerSecretKeyHex, 'hex'));
// Generate a new account to create
const newAccountKeypair = Keypair.generate();
// Minimum balance for rent exemption (approx. 0.002 SOL)
const rentExemptionAmount = await connection.getMinimumBalanceForRentExemption(0);
// Create transaction to make the new account and fund it with 0.1 SOL
const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payerKeypair.publicKey,
newAccountPubkey: newAccountKeypair.publicKey,
lamports: rentExemptionAmount + 100_000_000, // Rent + 0.1 SOL
space: 0, // No additional space needed for this example
programId: SystemProgram.programId,
})
);
// Sign and send the transaction
const signature = await connection.sendTransaction(transaction, [payerKeypair, newAccountKeypair]); const latestBlockhash = await connection.getLatestBlockhash();
await connection.confirmTransaction({
signature,
blockhash: latestBlockhash.```
createNewAccount().catch((error: Error) => console.error(error));
⚠️ 请确保你的 .env 文件中仍然有 PAYER_SECRET_KEY。
让我们运行它,然后讨论发生了什么:
ts-node create-account.ts
## 输出 (因情况而异)
New Account Created! Public Key: 7mBp...wv4bSanp6QXkK9
Transaction signature: 3KB66jvhcAu19UDH...RFnk7YaP9
New Account Balance: 0.10089088 SO
和之前一样,你可以将这个放入 Solscan.io devnet 搜索中并查看交易。
new Connection(clusterApiUrl('devnet'), 'confirmed')
连接到 Solana Devnet 集群,用于发送和确认交易。Keypair.fromSecretKey(...)
从存储在 .env
文件中的密钥中恢复 payer 钱包。
Keypair.generate()
创建一个新的密钥对,它将成为新创建的链上账户。connection.getMinimumBalanceForRentExemption(0)
确定所需的 lamports 数量,以防止新账户因不活动而被清除。
SystemProgram.createAccount(...)
准备一个指令,用于在链上创建一个新账户,并用租金 + SOL 为其提供资金。connection.sendTransaction(transaction, [payerKeypair, newAccountKeypair])
发送交易,交易由 payer 和新账户签名。connection.getLatestBlockhash()
获取最新的区块信息,以确保交易确认的安全。connection.confirmTransaction({...})
使用签名和区块信息等待交易在链上最终完成。connection.getBalance(newAccountKeypair.publicKey)
检索新创建账户的 SOL 余额。我们有很多基本的 Node.js TypeScript 示例,所以希望你对我们正在做的事情感到更舒服。
现在让我们用 React 和 Next.js 做一个快速的前端钱包集成。
我们将设置一个简单的 Solana 模板安装。
注意: 我们不会总是使用这个。但这是一个很好的学习引导。
从命令行,我运行了:
npx create-solana-dapp solana-dapp-tutorial
你可以更改 solana-dapp-tutorial
的名称——这就是我命名我的学习应用程序的名称。
npx create-solana-dapp solana-dapp-tutorial
## 输出
create-solana-dapp 4.2.7
│
◆ Project name: solana-dapp-tutorial
│
◇ Select a framework
│ Next.js
│
◆ Select a template
│ ● next-tailwind-counter (Next.js + Tailwind CSS + Anchor Counter Example)
│ ○ next-tailwind-basic
│ ○ next-tailwind
Cloning...
Installing...
◇ Installed via npm
│
◇ Executed init script
│
◇ Initialized git repo
│
◇ Installation successful ────────────────────────────╮
│ │
│ That's it! │
│ │
│ Change to your new directory and start developing: │
│ │
│ cd ./solana-dapp-tutorial │
│ │
│ Start the app: │
│ │
│ npm run dev │
│ │
│ Run Anchor commands: │
│ │
│ npm run anchor build | test | localnet | deploy │
│ │
├──────────────────────────────────────────────────────╯
│
└ Good luck with your project!
暂时忽略 Anchor 的内容。
现在我将运行它,看看会发生什么。
npm run dev
## 输出:
> legacy-next-tailwind-counter@0.1.0 dev
> next dev
▲ Next.js 14.2.24
- Local: http://localhost:3000
✓ Starting...
✓ Ready in 3.6s
一个问题,立即注意到的,是 Next.js 14。这需要升级到 Next.js 15。
现在,为了学习,我们将坚持使用这个。
我去了 http://localhost:3000 (如果你的 3000 端口正在运行其他东西,你的端口可能会不同,使用它在输出中给你的端口)
让我们快速地从顶部看一下这个。
这被称为热钱包,因为它不如其他类型的钱包安全,但经常用于持有相对少量的 SOL 的一些应用程序和演示/学习。热钱包被使用是因为它们对于在线应用程序/任务来说很方便,尽管不如 Ledger (你连接的硬件钱包设备) 和冷钱包 (离线持有) 安全。
我们正在 devnet 上进行实验,所以这没问题。但如果可能,不要使用你的 mainnet Solana 钱包。
“想象一下,你的数字资产是存储在个人金库中的贵重物品。正如你不会让你的家的门不上锁,或者随意分享你的金库密码一样,采取严格的安全措施来保护你的加密货币持有是至关重要的。”
先决条件
免费的 Solana 空投:用测试 SOL 为你的钱包提供资金
现在我们需要免费的 devnet SOL。
有很多方法可以做到这一点,包括从 CLI(但这有时会超时并且受到速率限制),也可以通过我们正在使用的 javascript 脚本……
一个简单且相对可靠的空投是如果你使用 Github 登录 Solana 的 faucet:https://faucet.solana.com/
有关空投的更多方法,请参见 devnet SOL Solana 文档:https://solana.com/developers/guides/getstarted/solana-token-airdrop-and-faucets
正如你在上面看到的,它显示了我在 devnet 上的 dev Backpack 钱包。
如果你以前没有连接过你的浏览器钱包,你可能会看到不同的东西。
按照说明操作。
接下来,我们将看看产生这个的代码。
让我们看看产生钱包的代码。
首先,让我们回顾一下用于这个前端,React/Next.js 钱包集成的关键库。
不是一个库,而是使用了许多库 (注意:最后一个是非 Solana 的,你可以根据需要替换它):
以下是此模板中使其工作的关键代码片段。
(src/components/solana/solana-provider.tsx)
'use client'
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react'
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui'
import { useCluster } from '../cluster/cluster-data-access'
import { ReactNode, useCallback, useMemo } from 'react'
import dynamic from 'next/dynamic'
// Dynamically import WalletMultiButton to avoid SSR issues
export const WalletButton = dynamic(
async () => (await import('@solana/wallet-adapter-react-ui')).WalletMultiButton,
{ ssr: false }
)
export function SolanaProvider({ children }: { children: ReactNode }) {
const { cluster } = useCluster() // Custom hook to get cluster config
const endpoint = useMemo(() => cluster.endpoint, [cluster]) // Cluster endpoint (e.g., devnet)
const onError = useCallback((error: WalletError) => {
console.error(error) // Handle wallet errors
}, [])
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={[]} onError={onError} autoConnect={true}>
<WalletModalProvider>{children}</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
)
}
这是一个需要记住的关键部分:
// Dynamically import WalletMultiButton to avoid SSR issues
export const WalletButton = dynamic(
async () => (await import('@solana/wallet-adapter-react-ui')).WalletMultiButton,
{ ssr: false }
)
src/components/ui/ui-layout.tsx
import { WalletButton } from '../solana/solana-provider'
export function UiLayout({ children, links }: { children: ReactNode; links: { label: string; path: string }[] }) {
return (
<div className="h-full flex flex-col">
<div className="navbar bg-base-300">
<div className="flex-1">
{/* Navigation links */}
</div>
<div className="flex-none space-x-2">
<WalletButton /> {/* Connects/disconnects wallet */}
<ClusterUiSelect /> {/* Cluster selection */}
</div>
</div>
{children}
</div>
)
}
我们将回顾这个应用程序和一些代码。
通常,当我们从头开始时,我们必须开始创建实用程序来执行基本的 Solana 交易。
但是,这个 dAPP 模板已经有了!🚀
所以,我们不用尝试从头开始做所有这些,我们就在这里试验,学习如何在应用程序中完成它!😁 也少了打字!
启动此应用程序的本地实例:
npm run dev
应该能够从以下位置访问 Account 页面:
这是我最初得到的。
你可以在这里看到,我们应该深入研究代码的几个项目……
考虑一下……这是开始一个很棒的 Solana 学习和诊断仪表板!我们可能会在另一个教程中在这个基础上构建更多的东西。我们可以做什么?添加并集成大约 10 个更多的 Solana React hooks?所有这些都可以在我们未来的所有项目中使用。那太棒了!
我从 1.17 SOL 开始,并在空投按钮上添加了 2 个。🚀
但是,当我试图再次这样做时,我得到了一个错误!这是预期的,因为通常你一次只能做 2 个,具体取决于它的限制方式,可能需要等待一段时间才能再次尝试。
在此之后,我确实尝试了几次,发现在 5-10 分钟后它再次工作了。但是当你使用它时,这可能会有所不同,它会根据当前的使用情况而变化。
在我写这篇文章的时候,我做了几次空投,所以我的总 SOL 上升了(该死,如果它是真正的 SOL 就好了!真是个吊人胃口的人!)。然后,在最初写完这篇文章的第二天,我又做了更多的空投,现在是 23+。
高达 3 个是好的,但 1-2 个更可靠,并且无法使其更高。
真正有用的是,有一个文件通过 React hooks 控制着许多“Solana actions”,称为: account-data-access.tsx
我查看了它,并折叠了 React hook 函数名称,这样我们就可以概述一下这个文件是做什么的。
现在你知道我为什么想从这个开始了……当我们深入研究代码以了解是如何完成的时,大量的有用开发编码信息。
其他说明:
你可以自己查看这些函数,但我们只看一个,以确保我们理解基本模式。
这些中的每一个都在使用非 Solana 库 tanstack/react-query
进行提取,你可以用你喜欢的 fetch、axios 或其他库替换它。
每个 useQuery 都在那里,因为它正在使用 tanstack/react-query
进行提取并获取响应。
useGetTokenAccounts
:用于获取钱包中有哪些 token。
// 这行代码声明并导出一个名为 useGetTokenAccounts 的自定义 hook。
// 它接受一个带有单个属性 address 的对象,该属性是 Solana 的
export function useGetTokenAccounts({ address }: { address: PublicKey }) {
// 检索当前的 Solana 连接。
const { connection } = useConnection()
// useQuery 负责处理异步数据获取、缓存和自动更新。
return useQuery({
// 这为查询设置了一个唯一的标识符。
// 这是一个数组,包括一个静态字符串和一个包含连接的 rpcEndpoint 和地址的对象。
queryKey: ['get-token-accounts', { endpoint: connection.rpcEndpoint, address }],
//异步函数,将在执行查询时获取 token 帐户数据。
queryFn: async () => {
// 使用 Promise.all 执行两个异步操作。
// 这两个操作的结果被解构为 tokenAccounts 和 token2022Accounts。
const [tokenAccounts, token2022Accounts] = await Promise.all([\
// 调用以使用标准 token 程序获取 address 拥有的 token 帐户\
connection.getParsedTokenAccountsByOwner(address, {\
programId: TOKEN_PROGRAM_ID,\
}),\
\
// // 调用以使用 Token 2022 (更新的,带有扩展) 获取 address 拥有的 token 帐户\
connection.getParsedTokenAccountsByOwner(address, {\
programId: TOKEN_2022_PROGRAM_ID,\
}),\
\
])
// 返回合并后的结果。
return [...tokenAccounts.value, ...token2022Accounts.value]
},
})
}
🛠️ 团队,我们在这篇文章中做了很多,我将在这里结束这篇文章。
我们将在下一个第 3 部分中再写一篇关于 Anchor 和 Rust 的设置文章, 这是创建链上程序所必需的。
在那之后……我们将开始比赛,并准备好制作应用程序 🚀
我可能会更新这些以进行更改/新资源。
Solana 文档 : 用于学习和使用 Solana 区块链的官方文档,包括设置指南、API 和协议详细信息。
Solana Faucet : 一个 Web 工具,可让你免费获得 Devnet SOL,用于测试 Solana 应用程序,而无需花费真正的 token。
Solana Cookbook : 一个开发者友好的指南,其中包含以代码为中心的实用方法,用于在 Solana 上构建应用程序和智能合约。
Solana Stack Overflow : 一个社区问答网站,开发者可以在这里提问和回答与 Solana 开发相关的技术问题。
Solana Developers GitHub : Solana 核心和社区开发者协作、共享工具和维护开源 Solana 项目的 GitHub 组织。
Awesome Solana : 我最初的 repo。很大,有几年历史了,并且有一些链接已损坏,我将尽快更新。如果最近没有更新,请再次查看。
🥰 感谢阅读!… 🔥 请鼓掌并分享这篇文章,谢谢!🚀
促销:我的云电子书商店——超值的云架构师和工程师书籍,“Cloud Metrics”(800 多页)和“Cloud Audit”(800 多页)等——https://store.systemsarchitect.io
结账时享受 35% 折扣 折扣码:35BLOG2025
https://store.systemsarchitect.io
我是一位云架构师、高级开发人员和技术负责人,喜欢通过创新的解决方案解决高价值的挑战。
我总是乐于讨论项目。如果你在项目上需要帮助、有一个机会,或者只是想聊聊云问题,你可以通过 csjcode at gmail 与我联系。
我在 Medium 上的最新文章: https://medium.com/@csjcode
Cloud Cost Saving: https://medium.com/cloud-cost-savings
Cloud Architect Review: https://medium.com/cloud-architect-review
AI Dev Tips: https://medium.com/ai-dev-tips
API Dev Tips: https://medium.com/api-dev-tips
Solana Dev Tips: https://medium.com/solana-dev-tips
我在软件开发领域工作了 20 多年, 既有像 NIKE 和最初的 MP3.com 这样的 企业 环境,也有像 FreshPatents, SystemsArchitect.io, API.cc, 和 Instantiate.io 这样的 初创企业。
我的经验范围从 云电子商务, API 设计/实现, serverless, AI 集成 用于开发、内容管理、前端 UI/UX 架构 和 登录/身份验证。我为架构软件提供技术讲座、教程和分享文档。之前也持有 AWS Solutions Architect 认证。
促销:我的云电子书商店——超值的云架构师和工程师书籍,“Cloud Metrics”(800 多页)和“Cloud Audit”(800 多页)等——https://store.systemsarchitect.io
结账时享受 35% 折扣 折扣码:35BLOG2025
- 原文链接: medium.com/solana-dev-ti...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!