SpringBoot从0-1集成Solana区块链
一、什么是 Solanaj?
Solanaj 是专为 Java 开发者设计的 Solana 区块链交互库,可无缝集成到 Spring/SpringBoot 环境中。通过封装底层 RPC 通信和链上操作逻辑,它让 Java 开发者无需深入理解区块链底层细节,即可快速实现以下核心功能:
- 钱包创建与私钥管理(支持助记词、Base58 格式私钥)
- 链上交易构建、签名与发送(原生代币 SOL 及自定义代币)
- 账户信息查询(余额、交易历史、代币持有量)
- 智能合约(Program)调用(如 NFT 铸造、DeFi 协议交互)
二、Pom 依赖引入
Solanaj 可通过 Maven 的 pom.xml 文件直接导入,方便集成到 Spring / Spring Boot 程序中,依赖配置如下:
<dependency>
<groupId>com.mmorrell</groupId>
<artifactId>solanaj</artifactId>
<version>1.19.2</version>
</dependency>
三、主要核心类
Solanaj 的核心能力通过以下关键类实现,掌握这些类是开发 Solana 应用的基础。
3.1 RpcClient
RpcClient 是与 Solana 节点交互的核心类,用于发送 RPC 请求(如发送交易、查询账户等)。创建时需指定目标节点的 URL(主网、测试网、开发网均可)。
1. 创建 RpcClient 实例
// 示例:连接 Solana 开发网节点(Quiknode 提供的节点 URL)
RpcClient client = new RpcClient("https://palpable-fittest-shadow.solana-devnet.quiknode.pro/ce7fe4c5d9d1dd80eb5aafa6a965af0957526ad4");
2. 核心用法
通过 client.getApi() 可调用节点的远程方法,最常用的是 sendTransaction()(向链上发送交易):
// 发送交易并获取交易哈希(tx 为交易哈希字符串,可用于在 Solscan 等浏览器查询交易状态)
String tx = client.getApi().sendTransaction(transaction, sendWallet);
3.2 PublicKey
PublicKey 类用于表明 Solana 区块链中的公钥,是 Solana 系统的基础标识,可用于标识账户、程序(Program)、代币铸造地址(Mint)等资源。
1. 基础用法:字符串地址转 PublicKey
将 Solana 链上的字符串格式地址(如 B2NmFbKEvEc8nZXzYdCnkrWKXQuTSr6GmWt8jPeaX2zf)转换为代码中可用的 PublicKey 对象:
// 接收方钱包的公钥(字符串地址转 PublicKey)
PublicKey destWallet = new PublicKey("B2NmFbKEvEc8nZXzYdCnkrWKXQuTSr6GmWt8jPeaX2zf");
2. 高级用法:生成 ATA 账户(关联代币账户)
ATA(Associated Token Account,关联代币账户)是 Solana 中用于存储非原生代币(如 USDC、自定义代币)的专用账户。通过 PublicKey.findProgramAddress() 可按官方规则生成 ATA 账户地址,核心规则如下:
- Seeds(种子):[拥有者地址(PublicKey), TokenProgram 地址, 代币 Mint 地址]
- Program ID:AssociatedTokenProgram.PROGRAM_ID(关联代币程序的公钥)
代码示例:
// 生成接收方的 ATA 账户地址(用于接收指定 Mint 的非原生代币)
PublicKey destAccount = PublicKey.findProgramAddress(
Arrays.asList(
destWallet.toByteArray(), // 拥有者地址(接收方钱包公钥)
TokenProgram.PROGRAM_ID.toByteArray(), // Token 程序公钥
mintKey.toByteArray() // 代币 Mint 地址(标识具体代币)
),
AssociatedTokenProgram.PROGRAM_ID // 关联代币程序公钥
).getAddress();
注意:原生代币(SOL)无需 ATA 账户,直接使用钱包公钥即可存储;非原生代币必须通过 ATA 账户接收和存储。
3.3 Transaction
Transaction 类代表一次链上交易,可理解为“操作的集合”。一个交易需包含操作指令、签名和最新区块哈希三部分,才能被 Solana 网络验证和执行。
1. 核心组成与用法
|
组成部分 |
说明 |
代码示例 |
|
TransactionInstruction |
交易包含的操作指令(一个交易可包含多个指令,按顺序执行) |
transaction.addInstruction(instruction); (添加指令) |
|
签名(Sign) |
对交易的签名,需由资源变动的主体(如转账发起者)签名,确保交易合法性 |
transaction.sign(sendWallet); (使用发送方钱包签名) |
|
RecentBlockHash |
最新区块哈希,用于防止交易被重复执行,确保链上数据有效性 |
transaction.setRecentBlockHash(client.getApi().getRecentBlockhash().getBlockhash()); |
2. 基础示例:创建并初始化交易
// 1. 创建空交易
Transaction transaction = new Transaction();
// 2. 添加操作指令(可添加多个)
transaction.addInstruction(instruction1);
transaction.addInstruction(instruction2);
// 3. 设置最新区块哈希(从节点获取)
String recentBlockHash = client.getApi().getRecentBlockhash().getBlockhash();
transaction.setRecentBlockHash(recentBlockHash);
// 4. 签名交易(使用发送方钱包)
transaction.sign(sendWallet);
3.4 TransactionInstruction
TransactionInstruction 代表单个链上操作指令(如转账、创建账户、调用智能合约),是交易的最小执行单元。每个指令需明确指定目标程序、关联账户和指令数据。
1. 核心结构
- programId:目标程序(Program)的公钥(如转账 SOL 需指定 SystemProgram.PROGRAM_ID);
- keys:指令涉及的账户列表(需指定账户角色,如“写入账户”“只读账户”);
- data:指令的二进制数据(传递给程序的参数,如转账金额)。
2. 常用封装类(简化指令创建)
Solanaj 提供了 SystemProgram 和 TokenProgram 等封装类,无需手动构建二进制数据,可直接生成常用指令:
(1)SystemProgram:处理原生代币(SOL)相关操作
最常用功能是 SOL 转账,代码示例:
// 生成 SOL 转账指令(从源地址转账 1 SOL 到目标地址,注意:SOL 最小单位是 lamports,1 SOL = 1e9 lamports)
TransactionInstruction solTransferInstruction = SystemProgram.transfer(
sourceWallet.getPublicKey(), // 源地址(转账发起者的钱包公钥)
destWallet.getPublicKey(), // 目标地址(接收者的钱包公钥)
1000000000L // 转账金额(单位:lamports,此处为 1 SOL)
);
(2)TokenProgram:处理非原生代币(自定义代币)相关操作
最常用功能是 非原生代币转账,代码示例:
// 生成非原生代币转账指令(需使用 ATA 账户)
TransactionInstruction tokenTransferInstruction = TokenProgram.transfer(
sendAccount, // 发送方的 ATA 账户(存储待转账的非原生代币)
destAccount, // 接收方的 ATA 账户(接收非原生代币)
10000000L, // 转账金额(单位:代币最小单位,需根据代币 Decimal 调整)
sendWallet.getPublicKey() // 转账发起者的钱包公钥(需对指令签名)
);
注意:非原生代币转账必须使用 ATA 账户作为发送方和接收方,不能直接使用钱包公钥。
四、示例代码:非原生代币转账
以下代码实现从发送方钱包向指定接收方钱包转账非原生代币的完整流程,包含 ATA 账户创建(若接收方无对应 ATA 账户)、交易构建、签名与发送。
import com.mmorrell.solanaj.core.Account;
import com.mmorrell.solanaj.core.PublicKey;
import com.mmorrell.solanaj.core.Transaction;
import com.mmorrell.solanaj.core.TransactionInstruction;
import com.mmorrell.solanaj.rpc.RpcClient;
import com.mmorrell.solanaj.programs.AssociatedTokenProgram;
import com.mmorrell.solanaj.programs.TokenProgram;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TokenTransferExample {
public static void main(String[] args) {
// 1. 配置核心参数
String senderPrivateKey = "你的发送方钱包私钥(Base58 格式)"; // 替换为实际私钥
PublicKey mintKey = new PublicKey("3ufbMZs57L1qfNPXTR6Nn7WA5pDzPEaLzcFo1aUpj6KE"); // 代币 Mint 地址
PublicKey destWallet = new PublicKey("B2NmFbKEvEc8nZXzYdCnkrWKXQuTSr6GmWt8jPeaX2zf"); // 接收方钱包公钥
Account sendWallet = Account.fromBase58PrivateKey(senderPrivateKey); // 初始化发送方钱包(含私钥,用于签名)
// 2. (可选)创建接收方的 ATA 账户(若接收方未创建该代币的 ATA 账户,需先创建)
TransactionInstruction createAtaInstruction = AssociatedTokenProgram.createIdempotent(
sendWallet.getPublicKey(), // 支付创建 ATA 账户费用的地址(一般为发送方)
destWallet, // ATA 账户的拥有者(接收方钱包)
mintKey // 代币 Mint 地址
);
// 3. 计算发送方和接收方的 ATA 账户地址
// 3.1 发送方的 ATA 账户
List<byte[]> senderSeeds = new ArrayList<>();
senderSeeds.add(sendWallet.getPublicKey().toByteArray());
senderSeeds.add(TokenProgram.PROGRAM_ID.toByteArray());
senderSeeds.add(mintKey.toByteArray());
PublicKey sendAtaAccount = PublicKey.findProgramAddress(
senderSeeds,
AssociatedTokenProgram.PROGRAM_ID
).getAddress();
// 3.2 接收方的 ATA 账户
PublicKey destAtaAccount = PublicKey.findProgramAddress(
Arrays.asList(
destWallet.toByteArray(),
TokenProgram.PROGRAM_ID.toByteArray(),
mintKey.toByteArray()
),
AssociatedTokenProgram.PROGRAM_ID
).getAddress();
// 4. 构建非原生代币转账指令
TransactionInstruction tokenTransferInstruction = TokenProgram.transfer(
sendAtaAccount, // 发送方 ATA 账户(存储待转账代币)
destAtaAccount, // 接收方 ATA 账户(接收代币)
10000000L, // 转账金额(单位:代币最小单位,需根据代币 Decimal 调整)
sendWallet.getPublicKey() // 转账发起者(需签名)
);
// 5. 构建完整交易
Transaction transaction = new Transaction();
transaction.addInstruction(createAtaInstruction); // 添加 ATA 账户创建指令(若已存在则不重复创建)
transaction.addInstruction(tokenTransferInstruction); // 添加代币转账指令
// 6. 初始化 RpcClient 并发送交易
RpcClient client = new RpcClient("https://palpable-fittest-shadow.solana-devnet.quiknode.pro/ce7fe4c5d9d1dd80eb5aafa6a965af0957526ad4");
// 设置最新区块哈希(确保交易有效性)
String recentBlockHash = client.getApi().getRecentBlockhash().getBlockhash();
transaction.setRecentBlockHash(recentBlockHash);
// 签名交易(使用发送方钱包私钥)
transaction.sign(sendWallet);
// 发送交易并获取交易哈希
String txHash = client.getApi().sendTransaction(transaction, sendWallet);
// 输出交易哈希(可在 Solscan 开发网:https://solscan.io/?cluster=devnet 查询交易状态)
System.err.println("交易已发送,交易哈希:" + txHash);
}
}
五、Solana 区块链的典型应用场景
掌握 SpringBoot 与 Solana 的集成后,开发者可拓展至以下场景:
1. 去中心化金融(DeFi)
- 场景:构建链上支付网关、自动做市商(AMM)前端、借贷协议管理系统。
- 技术点:通过 Solanaj 调用 DeFi 协议(如 Raydium、Marinade)的智能合约,实现代币兑换、质押、借贷等操作
2. 非同质化代币(NFT)
- 场景:NFT 铸造平台、数字藏品交易市场、版权确权系统。
- 技术点:使用 Metaplex 协议相关指令,创建 NFT 元数据、 mint 代币、查询 NFT 所有权。
3. 企业级链上应用
- 场景:供应链金融(资产上链与追踪)、跨境支付(低成本实时结算)、会员积分系统(链上积分发行与兑换)。
- 技术点:通过自定义智能合约(Program)实现业务逻辑,用 Solanaj 调用合约接口完成数据上链与查询。
4. 钱包服务
- 场景:多链钱包后端、链上资产聚合查询、交易签名服务。
- 技术点:基于 Solanaj 实现钱包创建、私钥加密存储、批量交易签名与广播。
六、开发注意事项
- 私钥安全:绝对禁止硬编码私钥,提议使用加密存储(如 Vault)或硬件钱包(Ledger)签名。
- 网络环境:开发阶段使用测试网(Devnet/Testnet),避免真实资产风险。
- 代币精度:非原生代币需根据其 Decimal 值转换金额(如 USDC 的 Decimal 为 6,1 USDC = 1000000 最小单位)。
- 交易确认:发送交易后需轮询查询确认状态(通过getTransaction()方法),避免因网络延迟导致的业务异常。说明
- 本指南所有代码均在实验环境(Solana 开发网) 下编写,不涉及任何真实资产或投资提议。
- 实际开发中,需注意保护私钥(避免硬编码到代码中),并根据代币的 Decimal 调整转账金额(如 USDC 的 Decimal 为 6,1 USDC 需表明为 1000000)。
- 可通过 Solscan(根据网络选择主网/测试网/开发网)查询交易哈希,验证交易是否成功。
收藏了,感谢分享