evmSignTransaction
Ethereum: Sign transaction
- Use
evmSignTransactionto sign EVM transactions - Supports EIP-1559 and Legacy transactions; make sure your RPC node supports the transaction type you use
Asks the device to sign a transaction using the private key derived by the given BIP32 path. Before signing, the user should confirm the transaction target, amount, fees, and other key details on Ukey Wallet.
const result = await HardwareSDK.evmSignTransaction(
connectId,
deviceId,
params,
);
Params
path- requiredstring | Array<number>minimum length is3. read moretransaction- requiredEthereumTransactionEIP1559 | EthereumSignTransactiontransaction object to sign; the0xprefix is optional for each field.chainId- optionalnumberidentifies the target Ethereum network and helps distinguish different chain configurations. Reference.
Examples
EIP-1559 (after The London Upgrade)
If both maxFeePerGas and maxPriorityFeePerGas are provided, the transaction will be signed as the new type introduced by 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,
});
Result
{
success: true,
payload: {
v: string, // hexadecimal string with `0x` prefix
r: string, // hexadecimal string with `0x` prefix
s: string, // hexadecimal string with `0x` prefix
}
}
Error
{
success: false,
payload: {
error: string, // error message
code: number // error code
}
}
Transaction Signing and Broadcasting example
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; // examples: '0x11', '17', or 17
r: string; // with `0x` prefix
s: string; // with `0x` prefix
};
}) {
const { r, s, v } = signature;
/**
* SDK Legacy returns {v,r,s}; EIP-1559 returns {recoveryParam,r,s}
* splitSignature automatically converts v to recoveryParam
*/
const sig = splitSignature({
v: Number(v),
r,
s,
});
const rawTx = serialize(tx, sig);
const txid = keccak256(rawTx);
return {
rawTx,
txid,
};
}
// 1. Create transaction parameters
const txParams = {
to: '0x7314e0f1c0e28474bdb6be3e2c3e0453255188f8',
value: '0x38d7ea4c68000', // 0.001 ETH
data: '0x', // Empty data
chainId: 1,
nonce: '0x1',
// For EIP-1559 transaction
maxFeePerGas: '0x77359400', // 2 Gwei
maxPriorityFeePerGas: '0x59682f00', // 1.5 Gwei
gasLimit: '0x5208', // 21000
// For Legacy transaction
// gasPrice: '0x77359400', // 2 Gwei
};
// 2. Sign the transaction
const result = await HardwareSDK.evmSignTransaction(connectId, deviceId, {
path: "m/44'/60'/0'/0/0",
transaction: txParams,
...deviceCommonParams
});
// 3. Build signed transaction
const baseTx = {
to: txParams.to,
nonce: parseInt(txParams.nonce, 16),
gasLimit: txParams.gasLimit,
data: txParams.data,
value: txParams.value,
chainId: txParams.chainId,
};
// Add EIP-1559 specific fields
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. Build final transaction with signature
const { rawTx, txid } = buildSignedTxFromSignatureEvm({
tx: baseTx,
signature: result.payload
});
// 5. Broadcast the transaction
// const provider = new ethers.providers.JsonRpcProvider();
// const broadcastedTx = await provider.sendTransaction(rawTx);
// 6. Wait for confirmation
// const receipt = await broadcastedTx.wait();
// console.log('Transaction confirmed:', receipt);