以太坊作为全球第二大区块链平台,其原生代币ETH以及基于ERC标准的代币(如USDT、USDC等)的普及,使得个人钱包的管理变得日益重要,虽然市面上已有众多成熟的以太坊钱包应用,但对于开发者而言,使用Java语言从零开始搭建一个属于自己的以太坊钱包,不仅能深入理解区块链技术的底层原理,还能为开发更复杂的去中心化应用(DApp)或金融服务打下坚实基础,本文将详细介绍如何使用Java语言搭建一个基础的以太坊钱包。
准备工作:开发环境与依赖
在开始之前,我们需要准备以下环境和工具:
- Java开发环境:确保你的系统已安装JDK 8或更高版本,并配置好
JAVA_HOME环境变量。 - 构建工具:Maven或Gradle,本文以Maven为例。
- 以太坊客户端库:我们将使用成熟的Java以太坊库
web3j,它提供了与以太坊节点交互的丰富API,包括创建钱包、查询余额、发送交易等。 - 以太坊节点:可以连接到公共的以太坊节点(如Infura、Alchemy),或者运行本地节点(如Geth或Parity),对于开发测试,公共节点更为便捷。
创建Maven项目并引入依赖
创建一个Maven项目,并在pom.xml文件中添加web3j的核心依赖:
<dependencies>
<!-- Web3j核心库 -->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.9.8</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 用于生成 solidity 相关代码的依赖 (可选) -->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>codegen</artifactId>
<version>4.9.8</version>
</dependency>
<!-- 日志依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
生成以太坊钱包(创建账户)
以太坊钱包的核心是账户,每个账户由一对公钥和私钥组成,其中私钥用于签名交易,公钥和地址则用于接收资金。web3j提供了方便的工具来生成和管理这些密钥。
创建一个新的Java类WalletGenerator,编写以下代码来生成一个新钱包:
import org.web3j.crypto.CipherException;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;
import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
public class WalletGenerator {
public static void main(String[] args) {
try {
// 1. 生成随机密钥对
ECKeyPair ecKeyPair = Keys.createEcKeyPair();
// 2. 根据密钥对创建钱包文件 (默认使用Scrypt加密算法)
WalletFile walletFile = Wallet.createStandard("your-password", ecKeyPair); // "your-password"是你为钱包设置的密码
// 3. 将钱包文件保存到本地
String walletDir = "wallets"; // 钱包存储目录
File walletFileObj = new File(walletDir);
if (!walletFileObj.exists()) {
walletFileObj.mkdirs();
}
String fileName = walletFile.getAddress() + ".json";
WalletUtils.saveWalletFile(new File(walletDir, fileName), walletFile);
System.out.println("钱包创建成功!");
System.out.println("钱包地址: " + walletFile.getAddress());
System.out.println("私钥 (请妥善保管,切勿泄露): " + org.web3j.crypto.WalletUtils.privateKeyFromKeyPair(ecKeyPair).toString(16));
System.out.println("钱包文件已保存至: " + walletDir + File.separator + fileName);
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException | CipherException | IOException e) {
e.printStackTrace();
}
}
}
说明:
Keys.createEcKeyPair():生成随机的椭圆曲线密钥对。Wallet.createStandard(password, ecKeyPair):使用标准算法(Scrypt)创建钱包文件,需要设置一个密码。WalletUtils.saveWalletFile():将钱包文件保存到指定目录,钱包文件是一个JSON文件,包含地址、加密的私钥等信息。- 重要:私钥是控制钱包资产的关键,生成后必须极其小心地保存,绝对不要泄露给他人,在实际应用中,私钥的存储需要极高的安全性。
运行上述代码,你将在wallets目录下看到一个以钱包地址命名的.json文件,这就是你的以太坊钱包文件。
加载钱包与查询余额
生成钱包后,我们需要能够加载它并进行操作,比如查询账户余额。
创建WalletManager类:
import org.web3j.crypto.CipherException;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;
import org.web3j.utils.Numeric;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
public class WalletManager {
// 替换为你的Infura或Alchemy节点URL
private static final String INFURA_URL = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID&q
uot;;
private static final String WALLET_DIR = "wallets";
private static final String WALLET_PASSWORD = "your-password";
public static void main(String[] args) {
// 1. 连接到以太坊节点
Web3j web3j = Web3j.build(new HttpService(INFURA_URL));
try {
// 2. 加载钱包 (假设我们已经有了一个钱包文件)
String walletFileName = "YOUR_WALLET_ADDRESS.json"; // 替换为你的钱包文件名
File walletFile = new File(WALLET_DIR, walletFileName);
// 3. 解析钱包文件,获取Credentials对象
org.web3j.crypto.Credentials credentials = WalletUtils.loadCredentials(WALLET_PASSWORD, walletFile);
String walletAddress = credentials.getAddress();
System.out.println("钱包地址: " + walletAddress);
// 4. 查询账户余额
EthGetBalance balance = web3j.ethGetBalance(walletAddress, org.web3j.protocol.core.DefaultBlockParameterName.LATEST).send();
BigInteger balanceWei = balance.getBalance();
BigDecimal balanceEth = Convert.fromWei(new BigDecimal(balanceWei), Unit.ETHER);
System.out.println("账户余额: " + balanceEth + " ETH");
// 5. (可选) 查询nonce (交易次数)
EthGetTransactionCount nonce = web3j.ethGetTransactionCount(walletAddress, org.web3j.protocol.core.DefaultBlockParameterName.LATEST).send();
BigInteger transactionCount = nonce.getTransactionCount();
System.out.println("Nonce: " + transactionCount);
// 6. (可选) 发送ETH (这里仅作示例,实际发送需要足够的ETH支付gas费)
/*
String toAddress = "0xRecipientAddressHere";
BigInteger value = Convert.toWei("0.01", Unit.ETHER).toBigInteger();
BigInteger gasLimit = BigInteger.valueOf(21000); // 转账ETH通常21000 gas
BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); // 获取当前建议gas价格
EthSendTransaction transaction = web3j.ethSendTransaction(
org.web3j.protocol.core.methods.Transaction.createEtherTransaction(
walletAddress,
nonce.getTransactionCount(),
gasPrice,
gasLimit,
toAddress,
value))
.send();
if (transaction.getTransactionHash() != null) {
System.out.println("交易发送成功,哈希: " + transaction.getTransactionHash());
// 等待交易被打包
TransactionReceipt receipt = web3j.ethGetTransactionReceipt(transaction.getTransactionHash()).send().getTransactionReceipt().orElse(null);
if (receipt != null) {
System.out.println("交易状态: " + receipt.getStatus());
}
} else {
System.out.println("交易发送失败: " + transaction.getError().getMessage());
}
*/