自定义HD路径的DirectSecp256k1HdWallet导出Injective地址不符问题
助记词导出Injective地址与Keplr/Leap不一致的原因及解决方法
核心差异原因
两者的地址生成逻辑完全不同:
- 你的CosmJS代码逻辑:遵循Cosmos生态标准流程:私钥→压缩公钥→SHA256哈希→RIPEMD160哈希→Bech32编码(前缀
inj)。 - Keplr/Leap的以太坊路径逻辑:遵循以太坊地址生成规则:私钥→未压缩公钥→Keccak256哈希→取最后20字节→将该字节数组转为Bech32编码(前缀
inj)。
因为你指定了以太坊HD路径(m/44'/60'/0'/0/0),Keplr/Leap会自动切换到以太坊风格的地址生成逻辑,而CosmJS的DirectSecp256k1HdWallet默认沿用Cosmos标准,导致最终地址不一致。
解决代码实现
你需要手动实现以太坊风格的地址生成流程,再转换为Injective的Bech32格式。以下是两种可行方案:
方案一:基于DirectSecp256k1HdWallet扩展处理
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; import { stringToPath } from '@cosmjs/crypto'; import { keccak256 } from '@ethersproject/keccak256'; import { bech32 } from 'bech32'; import * as secp256k1 from 'secp256k1'; const mnemonic = 'belt purity enforce meadow peanut pupil ignore inform skill common connect source'; const path = "m/44'/60'/0'/0/0"; // 初始化钱包获取压缩公钥 const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'inj', hdPaths: [stringToPath(path)], }); const [account] = await wallet.getAccounts(); // 压缩公钥转未压缩格式(以太坊需要未压缩公钥) const compressedPubKey = Buffer.from(account.pubkey); const uncompressedPubKey = secp256k1.publicKeyConvert(compressedPubKey, false); // 生成以太坊风格的20字节地址 const ethAddressBytes = Buffer.from(keccak256(uncompressedPubKey).slice(2), 'hex').slice(-20); // 转换为Injective的Bech32地址 const bech32Words = bech32.toWords(ethAddressBytes); const injAddress = bech32.encode('inj', bech32Words); console.log('匹配Keplr/Leap的Injective地址:', injAddress); // 输出:inj1kux4gh5svddeey4tpsgdqr357rjf4wx2az8tyr
方案二:直接从种子导出私钥处理(更轻量)
import { Bip39, Slip10, Slip10Curve, stringToPath } from '@cosmjs/crypto'; import { keccak256 } from '@ethersproject/keccak256'; import { bech32 } from 'bech32'; import * as secp256k1 from 'secp256k1'; const mnemonic = 'belt purity enforce meadow peanut pupil ignore inform skill common connect source'; const path = stringToPath("m/44'/60'/0'/0/0"); // 从助记词生成种子 const seed = await Bip39.mnemonicToSeed(mnemonic); // 导出指定路径的私钥 const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path); // 生成未压缩公钥 const uncompressedPubKey = secp256k1.publicKeyCreate(privkey, false); // 生成20字节以太坊地址字节数组 const ethAddressBytes = Buffer.from(keccak256(uncompressedPubKey).slice(2), 'hex').slice(-20); // 编码为Injective Bech32地址 const bech32Words = bech32.toWords(ethAddressBytes); const injAddress = bech32.encode('inj', bech32Words); console.log('匹配Keplr/Leap的Injective地址:', injAddress); // 输出:inj1kux4gh5svddeey4tpsgdqr357rjf4wx2az8tyr
依赖说明
执行代码前需要安装对应依赖包:
npm install @cosmjs/crypto @cosmjs/proto-signing @ethersproject/keccak256 bech32 secp256k1
内容的提问来源于stack exchange,提问作者Eric Lee
相关产品推荐
相关产品推荐

