Signing transactions
Advanced
Ethereum
Tutorial
Overview
Before a transaction can be sent to the Ethereum network, it must be signed and formatted into a raw ETH transaction. Transactions are signed using threshold ECDSA chain-key signatures.
A code example using the ethers-core
Rust library to sign and format an ETH EIP-1559 transaction can be found below:
#[update(guard = "caller_is_not_anonymous")]
async fn sign_transaction(req: SignRequest) -> String {
use ethers_core::types::transaction::eip1559::Eip1559TransactionRequest;
use ethers_core::types::Signature;
const EIP1559_TX_ID: u8 = 2;
let caller = ic_cdk::caller();
let data = req.data.as_ref().map(|s| decode_hex(s));
let tx = Eip1559TransactionRequest {
chain_id: Some(nat_to_u64(&req.chain_id)),
from: None,
to: Some(
Address::from_str(&req.to)
.expect("failed to parse the destination address")
.into(),
),
gas: Some(nat_to_u256(&req.gas)),
value: Some(nat_to_u256(&req.value)),
nonce: Some(nat_to_u256(&req.nonce)),
data,
access_list: Default::default(),
max_priority_fee_per_gas: Some(nat_to_u256(&req.max_priority_fee_per_gas)),
max_fee_per_gas: Some(nat_to_u256(&req.max_fee_per_gas)),
};
let mut unsigned_tx_bytes = tx.rlp().to_vec();
unsigned_tx_bytes.insert(0, EIP1559_TX_ID);
let txhash = keccak256(&unsigned_tx_bytes);
let (pubkey, signature) = pubkey_and_signature(&caller, txhash.to_vec()).await;
let signature = Signature {
v: y_parity(&txhash, &signature, &pubkey),
r: U256::from_big_endian(&signature[0..32]),
s: U256::from_big_endian(&signature[32..64]),
};
let mut signed_tx_bytes = tx.rlp_signed(&signature).to_vec();
signed_tx_bytes.insert(0, EIP1559_TX_ID);
format!("0x{}", hex::encode(&signed_tx_bytes))
}
Additional examples of signing transactions can be found in the threshold ECDSA documentation.
Next steps
Now that your transaction is signed, it can be submitted to Ethereum to be executed.