深入浅出,使用JavaScript代码实现以太坊交易签名

投稿 2026-02-16 11:54 点击数: 1

在以太坊生态系统中,交易签名是确保交易安全性和发送者身份验证的核心环节,每一笔从外部账户(EOA)发出的交易都必须经过签名,证明发送者拥有该账户的私钥,并授权该交易广播到以太坊网络,本文将深入探讨如何使用JavaScript代码(主要借助ethers.js库)来实现以太坊交易的签名过程。

理解以太坊签名的核心要素

在开始编码之前,我们需要理解几个关键概念:

  1. 账户 (Account):以太坊账户由地址(Address)和私钥(Private Key)组成,地址是公开的,类似于银行账号;私钥是保密的,类似于银行卡密码,用于证明对账户的控制权。
  2. 交易 (Transaction):包含发送者、接收者、转账金额、gas费用等信息的指令。
  3. 签名 (Signature):通过对交易数据进行哈希,然后用私钥加密哈希值得到的一段数据,它确保了交易数据的完整性和发送者的身份。
  4. 签名者 (Signer):在ethers.js中,Signer是一个抽象类,代表一个能够签名交易或消息的对象,它通常与私钥关联。
  5. 交易哈希 (Transaction Hash):交易被签名后,会生成一个唯一的标识符,用于在以太坊网络上追踪交易。
  6. 随机配图
ol>

准备工作:安装ethers.js

ethers.js是一个功能强大且易于使用的JavaScript库,用于与以太坊区块链进行交互,它是实现以太坊签名的首选工具之一,你需要在你的项目中安装它:

npm install ethers

或者,如果你在HTML文件中直接使用,可以通过CDN引入:

<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js" type="application/javascript"></script>

以太坊交易签名步骤详解

使用ethers.js签名交易通常遵循以下步骤:

步骤1:创建签名者 (Signer)

签名者是签名的关键,你可以通过私钥、助记词或硬件钱包来创建签名者,最常见的方式是通过私钥创建。

const { ethers } = require("ethers");
// 假设这是你的私钥(请务必妥善保管,不要泄露!)
const privateKey = "0x你的私钥这里";
// 创建一个provider(可选,用于获取nonce等链上数据)
// 使用Infura的Provider
const provider = new ethers.providers.JsonRpcProvider("https://mainnet.infura.io/v3/你的Infura项目ID");
// 通过私钥和provider创建签名者
const signer = new ethers.Wallet(privateKey, provider);
// 或者,如果你不需要provider,可以直接创建:
// const signer = new ethers.Wallet(privateKey);

注意:在实际应用中,私钥的存储和安全管理至关重要,切勿将私钥硬编码在客户端代码中或提交到版本控制系统。

步骤2:构建待签名交易 (Unsigned Transaction)

交易对象包含所有必要的交易信息,你需要构建一个这样的对象,它通常包括:

  • to: 接收地址
  • value: 转账金额(以wei为单位)
  • gasLimit: Gas限制
  • gasPrice: Gas价格(以wei为单位)
  • nonce: 发送者的交易nonce(防止重放攻击)
  • data: 可选的附加数据(例如合约交互的调用数据)
  • chainId: 链ID(防止交易在不同链间重放)

ethers.js提供了构建交易对象的便捷方法。signer.populateTransaction()可以帮助你填充一些默认值,如noncegasPricechainId

async function buildTransaction() {
  const recipientAddress = "0x接收方地址";
  const amountToSend = ethers.utils.parseEther("0.01"); // 转账0.01 ETH
  // 获取当前nonce
  const nonce = await signer.getTransactionCount();
  // 构建交易对象
  const unsignedTx = {
    to: recipientAddress,
    value: amountToSend,
    gasLimit: ethers.utils.hexlify(21000), // 简单转账通常21000 gas
    gasPrice: await signer.getGasPrice(), // 获取当前建议的gasPrice
    nonce: nonce,
    chainId: 1, // 以太坊主网链ID
  };
  console.log("待签名交易:", unsignedTx);
  return unsignedTx;
}
// 调用函数
buildTransaction().then(tx => {
  // 下一步将使用这个交易对象进行签名
});

步骤3:对交易进行签名

有了待签名的交易对象后,就可以使用签名者的signTransaction()方法对其进行签名了。

async function signTransaction(unsignedTx) {
  try {
    // 对交易进行签名
    const signedTx = await signer.signTransaction(unsignedTx);
    console.log("签名后的交易:", signedTx);
    // signedTx 是一个RLP编码的十六进制字符串,可以直接发送到节点
    return signedTx;
  } catch (error) {
    console.error("签名交易失败:", error);
  }
}
// 结合前面的步骤
buildTransaction()
  .then(signTransaction)
  .then(signedTx => {
    // 这里可以将signedTx发送到以太坊节点
    console.log("签名成功,交易已准备好发送!");
  });

signTransaction()方法会返回一个签名的交易字符串(RLP编码格式),这个字符串可以直接发送到以太坊节点(如通过provider.sendTransaction()或直接调用节点的eth_sendRawTransaction RPC方法)进行广播。

步骤4:发送已签名的交易(可选)

签名完成后,你可以将交易发送到网络:

async function sendSignedTransaction(signedTx) {
  try {
    // 发送已签名的交易
    const txResponse = await provider.sendTransaction(signedTx);
    console.log("交易已发送,交易哈希:", txResponse.hash);
    // 等待交易被确认
    const receipt = await txResponse.wait();
    console.log("交易已确认,区块号:", receipt.blockNumber);
  } catch (error) {
    console.error("发送交易失败:", error);
  }
}
// 完整流程示例
async function fullProcess() {
  const unsignedTx = await buildTransaction();
  const signedTx = await signTransaction(unsignedTx);
  if (signedTx) {
    await sendSignedTransaction(signedTx);
  }
}
fullProcess();

签名消息 (Message Signing)

除了交易签名,ethers.js还支持对普通消息进行签名,这在身份验证等场景下非常有用,使用signMessage()方法:

async function signMessageExample() {
  const message = "Hello, Ethereum!";
  try {
    const signature = await signer.signMessage(message);
    console.log("消息签名:", signature);
    // 验证签名
    const recoveredAddress = ethers.utils.verifyMessage(message, signature);
    console.log("从签名恢复的地址:", recoveredAddress);
    console.log("签名者地址:", signer.address);
    console.log("地址是否匹配:", recoveredAddress === signer.address);
  } catch (error) {
    console.error("签名消息失败:", error);
  }
}
signMessageExample();

安全注意事项

  1. 私钥安全:这是最重要的一点,私钥等同于账户的控制权,一旦泄露,资产将面临风险,避免在代码中硬编码私钥,使用环境变量、加密存储或硬件钱包等安全方式管理。
  2. 网络环境:确保在安全的网络环境下进行签名操作,避免中间人攻击。
  3. Gas估算:在构建交易时,合理设置gasLimitgasPrice,避免因Gas不足或价格过高导致交易失败或损失过多资金。
  4. 测试:在将真实资产转入主网地址前,务必在测试网(如Ropsten, Goerli, Sepolia)上充分测试你的签名和发送逻辑。

本文详细介绍了使用JavaScript(ethers.js库)进行以太坊交易签名的核心步骤和注意事项,从创建签名者、构建交易对象到最终签名和发送,每一步都至关重要,理解并掌握这些技术,对于开发去中心化应用(DApps)或与以太坊区块链进行交互的Web应用都是必不可少的,请始终将安全性放在首位,谨慎处理私钥和交易数据,随着Web3技术的不断发展,掌握这些基础技能将帮助你更好地构建和参与以太坊生态系统。