Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add example for encoding and decoding raw transactions #164

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ This repository contains the following examples:
- [x] [Transfer ERC20 token](./examples/transactions/examples/transfer_erc20.rs)
- [x] [Transfer ETH](./examples/transactions/examples/transfer_eth.rs)
- [x] [Sign and send a raw transaction](./examples/transactions/examples/send_raw_transaction.rs)
- [x] [Encode and decode a raw transaction](./examples/transactions/examples/encode_and_decode_raw_transaction.rs)
- [x] [Send EIP-1559 transaction](./examples/transactions/examples/send_eip1559_transaction.rs)
- [x] [Send legacy transaction](./examples/transactions/examples/send_legacy_transaction.rs)
- [x] [Send EIP-4844 transaction](./examples/transactions/examples/send_eip4844_transaction.rs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Example of encoding and decoding raw transactions.

use alloy::{
consensus::{SignableTransaction, TxEip1559, TxEnvelope},
network::{EthereumWallet, TransactionBuilder},
primitives::{
keccak256,
private::alloy_rlp::{Decodable, Encodable},
Address, TxKind, U256,
},
providers::{Provider, ProviderBuilder, WalletProvider},
rpc::types::TransactionRequest,
};
use eyre::Result;

fn build_unsigned_tx(chain_id: u64, to_address: Address) -> TxEip1559 {
TxEip1559 {
chain_id,
nonce: 0,
gas_limit: 21_000,
max_fee_per_gas: 20_000_000_000,
max_priority_fee_per_gas: 1_000_000_000,
to: TxKind::Call(to_address), // Change this to `TxKind::Create` if you'd like to deploy a contract instead
value: U256::from(100),
..Default::default()
}
}

fn unsigned_tx_to_bytes(tx: TxEip1559) -> Vec<u8> {
tx.encoded_for_signing() // To use this, have to import "alloy::primitives::private::alloy_rlp::Encodable"
}

fn bytes_to_unsigned_tx(bytes: Vec<u8>) -> TxEip1559 {
let mut slice = &bytes.as_slice()[1..];
TxEip1559::decode(&mut slice).unwrap() // To use this, have to import "alloy::primitives::private::alloy_rlp::Decodable"
}

async fn sign_tx(tx: TxEip1559, wallet: EthereumWallet) -> TxEnvelope {
let tx_request: TransactionRequest = tx.into();

tx_request.build(&wallet).await.unwrap()
}

fn signed_tx_to_bytes(signed_tx: TxEnvelope) -> Vec<u8> {
let mut encoded = Vec::new();
signed_tx.encode(&mut encoded);
let encoded = &encoded[2..];
encoded.into()
}

fn bytes_to_signed_tx(bytes: Vec<u8>) -> TxEnvelope {
let mut slice = bytes.as_slice();
TxEnvelope::decode(&mut slice).unwrap()
}

#[tokio::main]
async fn main() -> Result<()> {
// Spin up a local Anvil node.
// Ensure `anvil` is available in $PATH.
let provider = ProviderBuilder::new().on_anvil_with_wallet();

// Create two users, Alice and Bob.
let accounts = provider.get_accounts().await?;
let alice = accounts[0];
let bob = accounts[1];

// 1. Build a transaction to send 100 wei from Alice to Bob.
let tx: TxEip1559 = build_unsigned_tx(provider.get_chain_id().await?, bob);

// 2. Encode the unsigned transaction to rlp bytes.
let unsigned_tx_bytes = unsigned_tx_to_bytes(tx.clone());
assert_eq!(tx, bytes_to_unsigned_tx(unsigned_tx_bytes));

// 3. Sign the transaction using the wallet.
let signed_tx = sign_tx(tx, provider.wallet().clone()).await;

// 4. Encode the signed transaction to rlp bytes.
let signed_tx_bytes = signed_tx_to_bytes(signed_tx.clone());
assert_eq!(signed_tx, bytes_to_signed_tx(signed_tx_bytes.clone()));

// 5. Send the raw transaction and retrieve the transaction receipt.
let receipt = provider.send_tx_envelope(signed_tx).await?.get_receipt().await?;
assert_eq!(receipt.from, alice);
assert_eq!(receipt.to, Some(bob));

// 6. Comapre the transaction hash from the signed transaction with the receipt's transaction hash.
let tx_hash = keccak256(signed_tx_bytes);
assert_eq!(tx_hash, receipt.transaction_hash);

Ok(())
}
Loading