You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用solana-web3.js实现Phantom钱包同款消息签名?

如何用solana-web3.js实现与Phantom钱包一致的消息签名

问题背景

想要在solana-web3.js中实现和Phantom钱包扩展signMessage函数一致的消息签名逻辑,但现有代码的输出结果和Phantom的签名结果完全不符:

Phantom签名输出示例:

{"signature":"5uLfqwr19GdFBUyMTPeeL3HJ9U5f9TjRzAnCkzAaCyxZgNeA3C1i6c37FjPTowZRg8WSYdwANdVVXMMkWx2Hzwoy","address":"2o6AxcaMZqcS3L8oyPhS66keuxA1FrRtrSRcRxRw4U9C"}

现有不符代码:

const signMessage = (message, mnemonic) => {
// mnemonic to keypair
const seed = bip39.mnemonicToSeedSync(mnemonic);
const path = 'm/44\'/501\'/0\'/0\'';
const derivedSeed = derivePath(path, seed.toString('hex')).key;
const keyPair = Keypair.fromSeed(derivedSeed);
if (!keyPair) throw new Error('Invalid secret key')
// sign message
const address = keyPair.publicKey.toBase58()
const bufSecretKey = keyPair.secretKey
const serializedData = Buffer.from(message)
const bufSig = tweetnacl.sign(serializedData, bufSecretKey)
const signature = Buffer.from(bufSig).toString('hex')

return { signature, address}
};

现有代码输出示例:

{"signature":"f53409d847b5eae5ef3bb892b998805d2ea04b699e39bb5c126e757d1f4023488945ba210609fb661f70c10a45fdd55db09741d5e6a8839446a91af39d4fe5044c6f67696e204368616c6c656e67653a2066613666363234342d663863352d343162642d383033372d356333396562653534396366","address":"2o6AxcaMZqcS3L8oyPhS66keuxA1FrRtrSRcRxRw4U9C"}

问题原因

现有代码不符合Phantom遵循的Solana消息签名规范,核心差异有两点:

  1. 缺少固定签名前缀:Phantom会自动为待签名消息添加"Solana Signed Message:\n"前缀,这是Solana生态统一约定,用于区分交易签名和普通消息签名。
  2. 签名结果处理错误:tweetnacl.sign()返回的是「签名+原始消息」的拼接字节数组,而Phantom只返回前64字节的纯签名;同时Phantom返回的签名是Base58编码,不是现有代码用的Hex编码。

修正后的代码

const signMessage = (message, mnemonic) => {
  // 助记词转密钥对(保留原有逻辑)
  const seed = bip39.mnemonicToSeedSync(mnemonic);
  const path = "m/44'/501'/0'/0'";
  const derivedSeed = derivePath(path, seed.toString("hex")).key;
  const keyPair = Keypair.fromSeed(derivedSeed);
  if (!keyPair) throw new Error("Invalid secret key");

  // 构造符合Solana规范的待签名消息:添加固定前缀
  const encoder = new TextEncoder();
  const prefix = encoder.encode("Solana Signed Message:\n");
  const messageBytes = encoder.encode(message);
  const dataToSign = new Uint8Array([...prefix, ...messageBytes]);

  // 生成纯签名并转换为Base58格式(和Phantom一致)
  const signatureBytes = tweetnacl.sign.detached(dataToSign, keyPair.secretKey);
  const signature = bs58.encode(signatureBytes);
  const address = keyPair.publicKey.toBase58();

  return { signature, address };
};

关键说明

  • 必须添加前缀:这是Solana生态的硬性约定,所有合规钱包都会遵循,避免消息签名被恶意用作交易签名。
  • 使用detached签名:tweetnacl.sign.detached()直接返回纯签名字节数组,无需手动截取,更适配场景需求。
  • 依赖bs58包:需要先安装bs58包来完成Base58编码转换,和Phantom的输出格式对齐。

内容的提问来源于stack exchange,提问作者Yoga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 05:35:41