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

web3.js签名交易时from地址随机变更,仅nonce为0时正常求助

解决web3.js签名以太坊交易时nonce非0导致from地址随机的问题

问题根源

  1. 签名流程错误:你对未签名交易的序列化结果做哈希后用web3.eth.sign签名,但web3.eth.sign默认会给哈希添加以太坊消息前缀(\x19Ethereum Signed Message:\n32),导致签名后的r/s/v无法匹配交易的签名规范,最终还原出错误的from地址,nonce变化时交易哈希结构改变,错误被放大为随机地址。
  2. 手动计算v值逻辑错误:ethereumjs-tx会根据chainId自动处理EIP-155的v值偏移,手动计算会破坏签名合法性。
  3. 提前设置空签名字段:未签名交易中提前设置v/r/s为空值,干扰了交易的RLP编码结构,导致待签名哈希不正确。
  4. 手续费计算精度丢失:用浮点乘法计算txFee会导致精度损失,进而引发余额判断错误。

解决方案

  • 构建交易时只传入核心字段(nonce、gasPrice、gasLimit、to、value、data、chainId),不提前设置v/r/s。
  • 获取交易的待签名原始哈希:使用tx.hash(false)(false表示排除签名部分),这是以太坊交易签名的标准哈希。
  • 用web3.eth.sign签名该哈希,正确解析r/s/v后赋值给交易对象,让ethereumjs-tx自动处理chainId相关的v值偏移。
  • 添加签名后地址验证步骤,确保还原的from地址与预期一致。

修正后的代码

import Transaction from "ethereumjs-tx";
import { toast } from "react-toastify";
import { bufferToHex, toBuffer } from "ethereumjs-util"; // 需要安装ethereumjs-util

const createTx = (txData: any) => {
  // 指定chainId,让Transaction自动处理EIP-155签名规则
  return new Transaction(txData, { chain: parseInt(txData.chainId.toString()) });
};

await web3.eth
  .getTransactionCount(target, "pending")
  .then(async (nonce1: number) => {
    const gasPrice: bigint = await web3.eth.getGasPrice();
    const gasLimit: bigint = 21000n; // 普通转账标准gasLimit
    const txFee: bigint = gasPrice * gasLimit; // 用bigint直接计算总手续费,避免精度丢失
    const funds: bigint = 10000000000000000n - txFee;
    const chainId: bigint = await web3.eth.getChainId();
    const minEth: bigint = 1000000000000000n;

    const getBalanceBigInt = BigInt(getBalance); // 确保余额为bigint类型
    if (getBalanceBigInt > minEth && getBalanceBigInt > funds + txFee && host) {
      // 构建基础交易数据,不包含签名字段
      const txData = {
        nonce: web3.utils.toHex(nonce1),
        gasLimit: web3.utils.toHex(gasLimit),
        gasPrice: web3.utils.toHex(gasPrice),
        to: host,
        value: web3.utils.toHex(funds),
        data: "0x",
        chainId: web3.utils.toHex(chainId),
      };

      const tx = createTx(txData);
      // 获取交易的待签名哈希(标准RLP哈希,不含签名)
      const txHash = bufferToHex(tx.hash(false));
      console.log("待签名交易哈希:", txHash);

      await web3.eth
        .sign(txHash, target)
        .then(async (signed: string) => {
          // 解析签名结果:去除前缀,拆分r/s/v
          const sig = signed.slice(2);
          const r = `0x${sig.slice(0, 64)}`;
          const s = `0x${sig.slice(64, 128)}`;
          let v = parseInt(sig.slice(128, 130), 16);

          // 修正v值的基础偏移(确保符合以太坊签名规范)
          if (v < 27) {
            v += 27;
          }
          const vHex = web3.utils.toHex(v);

          console.log("r:", r);
          console.log("s:", s);
          console.log("v:", vHex);

          // 给交易对象赋值签名(转换为buffer格式)
          tx.r = toBuffer(r);
          tx.s = toBuffer(s);
          tx.v = toBuffer(vHex);

          // 验证还原的from地址是否正确
          const recoveredAddress = bufferToHex(tx.getSenderAddress());
          console.log("还原的from地址:", recoveredAddress);
          console.log("预期的from地址:", myAddr);
          if (recoveredAddress.toLowerCase() !== myAddr.toLowerCase()) {
            toast.error("签名地址不匹配");
            return;
          }

          const txFin = "0x" + tx.serialize().toString("hex");
          console.log("最终签名交易:", txFin);

          await web3.eth
            .sendSignedTransaction(txFin)
            .then((receipt: any) => {
              console.log("交易回执:", receipt);
              toast.success("交易成功");
            })
            .catch((error: any) => {
              console.log("发送失败:", error);
              toast.error("交易发送失败");
            });
        })
        .catch((error: any) => {
          toast.error("签名失败");
          console.log("签名错误:", error);
        });
    } else {
      toast.error("余额不足或参数不合法");
    }
  })
  .catch((error: any) => {
    console.log("获取nonce失败:", error);
    toast.error("获取交易nonce失败");
  });

关键优化点

  • 用bigint直接计算手续费,避免浮点运算导致的精度丢失。
  • 添加签名后地址验证,提前发现签名错误。
  • 使用ethereumjs-util工具函数处理缓冲区,避免手动字符串解析出错。
  • 采用标准转账gasLimit(21000n),减少不必要的手续费支出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:53:11