跳到主要内容

evmSignTransaction

以太坊:签名交易

  • evmSignTransaction 用于签名 EVM 交易
  • 支持 EIP-1559 与 Legacy 交易;请确认使用的 RPC 节点支持对应交易类型

使用指定 BIP32 路径对应的账户对交易签名。签名前,用户需要在 Ukey Wallet 上核对交易目标、金额、费用等关键信息。

const result = await HardwareSDK.evmSignTransaction(
connectId,
deviceId,
params,
);

参数

可选通用参数

  • path - 必需 string | Array<number>,字符串数组最小长度限制为 3更多信息
  • transaction - 必需 EthereumTransactionEIP1559 | EthereumSignTransaction,待签名交易对象;各字段的 0x 前缀可选。
  • chainId - 可选 number,ChainId 是 ETH 中用于特定以太坊网络的唯一标识符,帮助区分不同版本的区块链。参考

示例

EIP-1559(伦敦升级后

如果同时传入 maxFeePerGasmaxPriorityFeePerGas,交易会按照 EIP-1559 引入的新类型进行签名。

HardwareSDK.evmSignTransaction(connectId, deviceId, {
path: "m/44'/60'/0'/0/0",
transaction: {
to: "0x7314e0f1c0e28474bdb6be3e2c3e0453255188f8",
value: "0x38d7ea4c68000",
data: "0x",
chainId: 1,
nonce: "0x1",
maxFeePerGas: "0x77359400",
maxPriorityFeePerGas: "0x59682f00",
gasLimit: "0x5208",
},
chainId: 1,
});

Legacy

HardwareSDK.evmSignTransaction(connectId, deviceId, {
path: "m/44'/60'/0'/0/0",
transaction: {
to: "0x7314e0f1c0e28474bdb6be3e2c3e0453255188f8",
value: "0x38d7ea4c68000",
data: "0x",
chainId: 1,
nonce: "0x1",
gasLimit: "0x5208",
gasPrice: "0x77359400",
},
chainId: 1,
});

返回结果

{
success: true,
payload: {
v: string, // 带 `0x` 前缀的十六进制字符串
r: string, // 带 `0x` 前缀的十六进制字符串
s: string, // 带 `0x` 前缀的十六进制字符串
}
}

错误

{
success: false,
payload: {
error: string, // 错误消息
code: number // 错误码
}
}

交易签名和广播示例

import type { UnsignedTransaction } from '@ethersproject/transactions';
import { TransactionTypes, serialize } from '@ethersproject/transactions';
import { keccak256 } from '@ethersproject/keccak256';
function buildSignedTxFromSignatureEvm({
tx,
signature,
}: {
tx: UnsignedTransaction;
signature: {
v: string | number; // 示例:'0x11'、'17' 或 17
r: string; // 带 `0x` 前缀
s: string; // 带 `0x` 前缀
};
}) {
const { r, s, v } = signature;
/**
* SDK Legacy 返回 {v,r,s};EIP-1559 返回 {recoveryParam,r,s}
* splitSignature 自动将 v 转换为 recoveryParam
*/
const sig = splitSignature({
v: Number(v),
r,
s,
});
const rawTx = serialize(tx, sig);
const txid = keccak256(rawTx);
return {
rawTx,
txid,
};
}

// 1. 创建交易参数
const txParams = {
to: '0x7314e0f1c0e28474bdb6be3e2c3e0453255188f8',
value: '0x38d7ea4c68000', // 0.001 ETH
data: '0x', // 空数据
chainId: 1,
nonce: '0x1',
// 对于 EIP-1559 交易
maxFeePerGas: '0x77359400', // 2 Gwei
maxPriorityFeePerGas: '0x59682f00', // 1.5 Gwei
gasLimit: '0x5208', // 21000
// 对于 Legacy 交易
// gasPrice: '0x77359400', // 2 Gwei
};

// 2. 签名交易
const result = await HardwareSDK.evmSignTransaction(connectId, deviceId, {
path: "m/44'/60'/0'/0/0",
transaction: txParams,
...deviceCommonParams
});

// 3. 构建已签名交易
const baseTx = {
to: txParams.to,
nonce: parseInt(txParams.nonce, 16),
gasLimit: txParams.gasLimit,
data: txParams.data,
value: txParams.value,
chainId: txParams.chainId,
};

// 添加 EIP-1559 特定字段
const isEIP1559 = txParams?.maxFeePerGas || txParams?.maxPriorityFeePerGas;
if (isEIP1559) {
Object.assign(baseTx, {
type: TransactionTypes.eip1559,
maxFeePerGas: txParams?.maxFeePerGas,
maxPriorityFeePerGas: txParams?.maxPriorityFeePerGas
});
} else {
Object.assign(baseTx, {
gasPrice: txParams.gasPrice,
});
}

// 4. 使用签名构建最终交易
const { rawTx, txid } = buildSignedTxFromSignatureEvm({
tx: baseTx,
signature: result.payload
});

// 5. 广播交易
// const provider = new ethers.providers.JsonRpcProvider();
// const broadcastedTx = await provider.sendTransaction(rawTx);

// 6. 等待确认
// const receipt = await broadcastedTx.wait();
// console.log('交易已确认:', receipt);