跳到主要内容

地址管理

📁 完整代码 examples/solana/account/address.ts

在Solana中,地址是账户的唯一标识符,用于接收SOL、SPL代币和调用智能合约。理解地址的生成和验证对于开发安全的应用程序至关重要。

地址格式

Solana地址使用以下格式:

  • 长度: 32字节(44个Base58字符)
  • 字符集: Base58编码(1-9, A-H, J-N, P-Z, a-k, m-z)
  • 示例: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM

地址生成

基础导入和依赖

dependencies
import assert from 'node:assert';
import { Keypair } from '@solana/web3.js';
import { toBase58 } from '@sola-hq/toolkit';
import nacl from 'tweetnacl';

import * as bip39 from 'bip39';
import { HDKey } from 'micro-ed25519-hdkey';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';

从助记词生成地址

mnemonicToKeypair
/**
* @param mnemonic - The mnemonic phrase to derive the keypair from.
* @param password - The password to use for the mnemonic.
* @param index - The index to use for the derivation path.
* @returns The derived keypair.
*/
function mnemonicToKeypair(mnemonic: string, password: string = '', index: number = 0) {
const seed = bip39.mnemonicToSeedSync(mnemonic, password);
const hd = HDKey.fromMasterSeed(seed.toString('hex'));
const path = `m/44'/501'/${index}'/0'`;
const derivedSeed = hd.derive(path).privateKey;
return Keypair.fromSeed(derivedSeed);
}

随机生成地址

generateRandomKeypair
/**
* @returns The generated keypair.
*/
function generateRandomKeypair() {
// 生成新的随机密钥对
const keypair = Keypair.generate();
console.log('Public Key:', keypair.publicKey.toBase58());
console.log('Secret Key:', toBase58(keypair.secretKey));
console.log('Address :', keypair.publicKey.toBase58());
return keypair;
}

完整的使用示例

main.ts
import assert from 'node:assert';
import { Keypair } from '@solana/web3.js';
import { toBase58 } from '@sola-hq/toolkit';
import nacl from 'tweetnacl';

import * as bip39 from 'bip39';
import { HDKey } from 'micro-ed25519-hdkey';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';

/**
* @param mnemonic - The mnemonic phrase to derive the keypair from.
* @param password - The password to use for the mnemonic.
* @param index - The index to use for the derivation path.
* @returns The derived keypair.
*/
function mnemonicToKeypair(mnemonic: string, password: string = '', index: number = 0) {
const seed = bip39.mnemonicToSeedSync(mnemonic, password);
const hd = HDKey.fromMasterSeed(seed.toString('hex'));
const path = `m/44'/501'/${index}'/0'`;
const derivedSeed = hd.derive(path).privateKey;
return Keypair.fromSeed(derivedSeed);
}

/**
* @returns The generated keypair.
*/
function generateRandomKeypair() {
// 生成新的随机密钥对
const keypair = Keypair.generate();
console.log('Public Key:', keypair.publicKey.toBase58());
console.log('Secret Key:', toBase58(keypair.secretKey));
console.log('Address :', keypair.publicKey.toBase58());
return keypair;
}

function main() {
const mnemonic = process.env.MNEMONIC;
const message = hexToBytes('8001000102');
const keypair = mnemonicToKeypair(mnemonic);

console.log('keypair publicKey : ', keypair.publicKey.toBase58());
console.log('keypair secretKey : ', toBase58(keypair.secretKey));
console.log('address : ', keypair.publicKey.toBase58());
const signature = nacl.sign.detached(message, keypair.secretKey);
assert.strict(nacl.sign.detached.verify(message, signature, keypair.publicKey.toBuffer()), 'tweetnacl ed25519 signature verify failed');
console.log('signature : ', bytesToHex(signature));
console.log('bs58 : ', toBase58(signature));
}

main();

从私钥恢复地址

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

function recoverKeypairFromSecretKey(secretKeyBase58: string): Keypair {
try {
const secretKeyBytes = bs58.decode(secretKeyBase58);
return Keypair.fromSecretKey(secretKeyBytes);
} catch (error) {
throw new Error('Invalid secret key format');
}
}

// 使用示例
const secretKey = 'your_secret_key_here';
const keypair = recoverKeypairFromSecretKey(secretKey);
console.log('Recovered Address:', keypair.publicKey.toBase58());

地址验证

基本格式验证

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

function isValidSolanaAddress(address: string): boolean {
try {
new PublicKey(address);
return true;
} catch {
return false;
}
}

// 测试地址
const testAddresses = [
'9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
'invalid_address',
'11111111111111111111111111111112',
'So11111111111111111111111111111111111111112'
];

testAddresses.forEach(addr => {
console.log(`${addr}: ${isValidSolanaAddress(addr)}`);
});

高级验证

function validateSolanaAddress(address: string): {
isValid: boolean;
error?: string;
publicKey?: PublicKey;
} {
try {
const publicKey = new PublicKey(address);

// 检查是否为系统程序地址
const isSystemProgram = publicKey.equals(SystemProgram.programId);

return {
isValid: true,
publicKey,
isSystemProgram
};
} catch (error) {
return {
isValid: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}

地址派生

从主密钥派生子地址

function deriveChildAddresses(mnemonic: string, count: number = 5) {
const addresses: string[] = [];

for (let i = 0; i < count; i++) {
const keypair = mnemonicToKeypair(mnemonic, '', i);
addresses.push(keypair.publicKey.toBase58());
}

return addresses;
}

// 使用示例
const mnemonic = process.env.MNEMONIC || 'your mnemonic here';
const childAddresses = deriveChildAddresses(mnemonic, 3);

console.log('Child Addresses:');
childAddresses.forEach((address, index) => {
console.log(` ${index}: ${address}`);
});

地址比较

安全比较

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

function addressesEqual(address1: string, address2: string): boolean {
try {
const pubKey1 = new PublicKey(address1);
const pubKey2 = new PublicKey(address2);
return pubKey1.equals(pubKey2);
} catch {
return false;
}
}

// 使用示例
const addr1 = '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM';
const addr2 = '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM';

console.log('Addresses Equal:', addressesEqual(addr1, addr2)); // true

最佳实践

  1. 地址验证: 始终验证用户输入的地址格式
  2. 助记词安全: 安全存储助记词,避免泄露
  3. 错误处理: 实现适当的错误处理和用户反馈
  4. 地址标准化: 使用Base58格式存储和显示地址
  5. 安全比较: 使用PublicKey.equals()进行地址比较

常见问题

Q: 为什么Solana地址这么长?

A: Solana地址是32字节的公钥,使用Base58编码后约为44个字符,确保地址的唯一性和安全性。

Q: 地址可以重复使用吗?

A: 每个私钥对应唯一的地址,地址不会重复。

Q: 如何检查地址是否存在于区块链上?

A: 使用Connection.getAccountInfo()查询账户信息,如果返回null说明地址不存在。

Q: 地址区分大小写吗?

A: Base58编码不区分大小写,但建议使用一致的格式。

参考资源