diff --git a/prdoc/pr_6461.prdoc b/prdoc/pr_6461.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..1b3d1e8b03643c32b028c89bcb8735c97bf08ea0
--- /dev/null
+++ b/prdoc/pr_6461.prdoc
@@ -0,0 +1,12 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+title: '[pallet-revive] add support for all eth tx types'
+doc:
+- audience: Runtime Dev
+  description: Add support for 1559, 4844, and 2930 transaction types
+crates:
+- name: pallet-revive-eth-rpc
+  bump: minor
+- name: pallet-revive
+  bump: minor
+
diff --git a/substrate/frame/revive/rpc/src/client.rs b/substrate/frame/revive/rpc/src/client.rs
index 1ca1a6d37c534bdd9d05bbdac7cbb33fdfe5feb6..d37f1d760065314afe2a96ffe166109b77d572a7 100644
--- a/substrate/frame/revive/rpc/src/client.rs
+++ b/substrate/frame/revive/rpc/src/client.rs
@@ -17,13 +17,12 @@
 //! The client connects to the source substrate chain
 //! and is used by the rpc server to query and send transactions to the substrate chain.
 use crate::{
-	rlp,
 	runtime::GAS_PRICE,
 	subxt_client::{
 		revive::{calls::types::EthTransact, events::ContractEmitted},
 		runtime_types::pallet_revive::storage::ContractInfo,
 	},
-	TransactionLegacySigned, LOG_TARGET,
+	LOG_TARGET,
 };
 use futures::{stream, StreamExt};
 use jsonrpsee::types::{error::CALL_EXECUTION_FAILED_CODE, ErrorObjectOwned};
@@ -269,25 +268,26 @@ impl ClientInner {
 		let extrinsics = extrinsics.iter().flat_map(|ext| {
 			let call = ext.as_extrinsic::<EthTransact>().ok()??;
 			let transaction_hash = H256(keccak_256(&call.payload));
-			let tx = rlp::decode::<TransactionLegacySigned>(&call.payload).ok()?;
-			let from = tx.recover_eth_address().ok()?;
-			let contract_address = if tx.transaction_legacy_unsigned.to.is_none() {
-				Some(create1(&from, tx.transaction_legacy_unsigned.nonce.try_into().ok()?))
+			let signed_tx = TransactionSigned::decode(&call.payload).ok()?;
+			let from = signed_tx.recover_eth_address().ok()?;
+			let tx_info = GenericTransaction::from_signed(signed_tx.clone(), Some(from));
+			let contract_address = if tx_info.to.is_none() {
+				Some(create1(&from, tx_info.nonce.unwrap_or_default().try_into().ok()?))
 			} else {
 				None
 			};
 
-			Some((from, tx, transaction_hash, contract_address, ext))
+			Some((from, signed_tx, tx_info, transaction_hash, contract_address, ext))
 		});
 
 		// Map each extrinsic to a receipt
 		stream::iter(extrinsics)
-			.map(|(from, tx, transaction_hash, contract_address, ext)| async move {
+			.map(|(from, signed_tx, tx_info, transaction_hash, contract_address, ext)| async move {
 				let events = ext.events().await?;
 				let tx_fees =
 					events.find_first::<TransactionFeePaid>()?.ok_or(ClientError::TxFeeNotFound)?;
 
-				let gas_price = tx.transaction_legacy_unsigned.gas_price;
+				let gas_price = tx_info.gas_price.unwrap_or_default();
 				let gas_used = (tx_fees.tip.saturating_add(tx_fees.actual_fee))
 					.checked_div(gas_price.as_u128())
 					.unwrap_or_default();
@@ -324,16 +324,16 @@ impl ClientInner {
 					contract_address,
 					from,
 					logs,
-					tx.transaction_legacy_unsigned.to,
+					tx_info.to,
 					gas_price,
 					gas_used.into(),
 					success,
 					transaction_hash,
 					transaction_index.into(),
-					tx.transaction_legacy_unsigned.r#type.as_byte()
+					tx_info.r#type.unwrap_or_default()
 				);
 
-				Ok::<_, ClientError>((receipt.transaction_hash, (tx.into(), receipt)))
+				Ok::<_, ClientError>((receipt.transaction_hash, (signed_tx, receipt)))
 			})
 			.buffer_unordered(10)
 			.collect::<Vec<Result<_, _>>>()
diff --git a/substrate/frame/revive/rpc/src/example.rs b/substrate/frame/revive/rpc/src/example.rs
index 20f00465b146f1a5ecf4a3882969dd2f429e9b92..3b9a33296ef4d9073c52e44bdb895cbe57875dc5 100644
--- a/substrate/frame/revive/rpc/src/example.rs
+++ b/substrate/frame/revive/rpc/src/example.rs
@@ -20,8 +20,7 @@
 use crate::{EthRpcClient, ReceiptInfo};
 use anyhow::Context;
 use pallet_revive::evm::{
-	rlp::*, Account, BlockTag, Bytes, GenericTransaction, TransactionLegacyUnsigned, H160, H256,
-	U256,
+	Account, BlockTag, Bytes, GenericTransaction, TransactionLegacyUnsigned, H160, H256, U256,
 };
 
 /// Wait for a transaction receipt.
@@ -169,11 +168,11 @@ impl TransactionBuilder {
 
 		mutate(&mut unsigned_tx);
 
-		let tx = signer.sign_transaction(unsigned_tx.clone());
-		let bytes = tx.rlp_bytes().to_vec();
+		let tx = signer.sign_transaction(unsigned_tx.into());
+		let bytes = tx.signed_payload();
 
 		let hash = client
-			.send_raw_transaction(bytes.clone().into())
+			.send_raw_transaction(bytes.into())
 			.await
 			.with_context(|| "transaction failed")?;
 
diff --git a/substrate/frame/revive/rpc/src/lib.rs b/substrate/frame/revive/rpc/src/lib.rs
index 8d9d6fab829e0f3a3534cbf5a908d3ee1be1d609..6a324e63a8573eda22dec3f4c865fd7576568def 100644
--- a/substrate/frame/revive/rpc/src/lib.rs
+++ b/substrate/frame/revive/rpc/src/lib.rs
@@ -137,7 +137,7 @@ impl EthRpcServer for EthRpcServerImpl {
 	async fn send_raw_transaction(&self, transaction: Bytes) -> RpcResult<H256> {
 		let hash = H256(keccak_256(&transaction.0));
 
-		let tx = rlp::decode::<TransactionLegacySigned>(&transaction.0).map_err(|err| {
+		let tx = TransactionSigned::decode(&transaction.0).map_err(|err| {
 			log::debug!(target: LOG_TARGET, "Failed to decode transaction: {err:?}");
 			EthRpcError::from(err)
 		})?;
@@ -147,21 +147,10 @@ impl EthRpcServer for EthRpcServerImpl {
 			EthRpcError::InvalidSignature
 		})?;
 
+		let tx = GenericTransaction::from_signed(tx, Some(eth_addr));
+
 		// Dry run the transaction to get the weight limit and storage deposit limit
-		let TransactionLegacyUnsigned { to, input, value, .. } = tx.transaction_legacy_unsigned;
-		let dry_run = self
-			.client
-			.dry_run(
-				&GenericTransaction {
-					from: Some(eth_addr),
-					input: Some(input.clone()),
-					to,
-					value: Some(value),
-					..Default::default()
-				},
-				BlockTag::Latest.into(),
-			)
-			.await?;
+		let dry_run = self.client.dry_run(&tx, BlockTag::Latest.into()).await?;
 
 		let EthContractResult { gas_required, storage_deposit, .. } = dry_run;
 		let call = subxt_client::tx().revive().eth_transact(
@@ -174,11 +163,10 @@ impl EthRpcServer for EthRpcServerImpl {
 		Ok(hash)
 	}
 
-	async fn send_transaction(&self, transaction: GenericTransaction) -> RpcResult<H256> {
+	async fn send_transaction(&self, mut transaction: GenericTransaction) -> RpcResult<H256> {
 		log::debug!(target: LOG_TARGET, "{transaction:#?}");
-		let GenericTransaction { from, gas, gas_price, input, to, value, r#type, .. } = transaction;
 
-		let Some(from) = from else {
+		let Some(from) = transaction.from else {
 			log::debug!(target: LOG_TARGET, "Transaction must have a sender");
 			return Err(EthRpcError::InvalidTransaction.into());
 		};
@@ -189,27 +177,26 @@ impl EthRpcServer for EthRpcServerImpl {
 			.find(|account| account.address() == from)
 			.ok_or(EthRpcError::AccountNotFound(from))?;
 
-		let gas_price = gas_price.unwrap_or_else(|| U256::from(GAS_PRICE));
-		let chain_id = Some(self.client.chain_id().into());
-		let input = input.unwrap_or_default();
-		let value = value.unwrap_or_default();
-		let r#type = r#type.unwrap_or_default();
+		if transaction.gas.is_none() {
+			transaction.gas = Some(self.estimate_gas(transaction.clone(), None).await?);
+		}
 
-		let Some(gas) = gas else {
-			log::debug!(target: LOG_TARGET, "Transaction must have a gas limit");
-			return Err(EthRpcError::InvalidTransaction.into());
-		};
+		if transaction.gas_price.is_none() {
+			transaction.gas_price = Some(self.gas_price().await?);
+		}
 
-		let r#type = Type0::try_from_byte(r#type.clone())
-			.map_err(|_| EthRpcError::TransactionTypeNotSupported(r#type))?;
+		if transaction.nonce.is_none() {
+			transaction.nonce =
+				Some(self.get_transaction_count(from, BlockTag::Latest.into()).await?);
+		}
 
-		let nonce = self.get_transaction_count(from, BlockTag::Latest.into()).await?;
+		if transaction.chain_id.is_none() {
+			transaction.chain_id = Some(self.chain_id().await?);
+		}
 
-		let tx =
-			TransactionLegacyUnsigned { chain_id, gas, gas_price, input, nonce, to, value, r#type };
-		let tx = account.sign_transaction(tx);
-		let rlp_bytes = rlp::encode(&tx).to_vec();
-		self.send_raw_transaction(Bytes(rlp_bytes)).await
+		let tx = transaction.try_into_unsigned().map_err(|_| EthRpcError::InvalidTransaction)?;
+		let payload = account.sign_transaction(tx).signed_payload();
+		self.send_raw_transaction(Bytes(payload)).await
 	}
 
 	async fn get_block_by_hash(
diff --git a/substrate/frame/revive/src/evm/api.rs b/substrate/frame/revive/src/evm/api.rs
index 8185a2c8f6f874d4c2f3d8aafe38444c7a924c9b..fe18c8735bed4d8dc435f8517d86f67aa9c70393 100644
--- a/substrate/frame/revive/src/evm/api.rs
+++ b/substrate/frame/revive/src/evm/api.rs
@@ -25,9 +25,7 @@ pub use rlp;
 mod type_id;
 pub use type_id::*;
 
-#[cfg(feature = "std")]
 mod rpc_types;
-
 mod rpc_types_gen;
 pub use rpc_types_gen::*;
 
diff --git a/substrate/frame/revive/src/evm/api/account.rs b/substrate/frame/revive/src/evm/api/account.rs
index 8365ebf83cae4f539f61dd7e3971f71932b8d3ea..ba1c68ea0cf77336860a9df26910573872a4a2c3 100644
--- a/substrate/frame/revive/src/evm/api/account.rs
+++ b/substrate/frame/revive/src/evm/api/account.rs
@@ -16,10 +16,9 @@
 // limitations under the License.
 //! Utilities for working with Ethereum accounts.
 use crate::{
-	evm::{TransactionLegacySigned, TransactionLegacyUnsigned},
+	evm::{TransactionSigned, TransactionUnsigned},
 	H160,
 };
-use rlp::Encodable;
 use sp_runtime::AccountId32;
 
 /// A simple account that can sign transactions
@@ -38,6 +37,11 @@ impl From<subxt_signer::eth::Keypair> for Account {
 }
 
 impl Account {
+	/// Create a new account from a secret
+	pub fn from_secret_key(secret_key: [u8; 32]) -> Self {
+		subxt_signer::eth::Keypair::from_secret_key(secret_key).unwrap().into()
+	}
+
 	/// Get the [`H160`] address of the account.
 	pub fn address(&self) -> H160 {
 		H160::from_slice(&self.0.public_key().to_account_id().as_ref())
@@ -52,9 +56,21 @@ impl Account {
 	}
 
 	/// Sign a transaction.
-	pub fn sign_transaction(&self, tx: TransactionLegacyUnsigned) -> TransactionLegacySigned {
-		let rlp_encoded = tx.rlp_bytes();
-		let signature = self.0.sign(&rlp_encoded);
-		TransactionLegacySigned::from(tx, signature.as_ref())
+	pub fn sign_transaction(&self, tx: TransactionUnsigned) -> TransactionSigned {
+		let payload = tx.unsigned_payload();
+		let signature = self.0.sign(&payload).0;
+		tx.with_signature(signature)
 	}
 }
+
+#[test]
+fn from_secret_key_works() {
+	let account = Account::from_secret_key(hex_literal::hex!(
+		"a872f6cbd25a0e04a08b1e21098017a9e6194d101d75e13111f71410c59cd57f"
+	));
+
+	assert_eq!(
+		account.address(),
+		H160::from(hex_literal::hex!("75e480db528101a381ce68544611c169ad7eb342"))
+	)
+}
diff --git a/substrate/frame/revive/src/evm/api/rlp_codec.rs b/substrate/frame/revive/src/evm/api/rlp_codec.rs
index e5f24c28a482d78f8f066e0c289c8252f7d811d3..3442ed73accab7be5caff8f884a336c4be03b252 100644
--- a/substrate/frame/revive/src/evm/api/rlp_codec.rs
+++ b/substrate/frame/revive/src/evm/api/rlp_codec.rs
@@ -21,6 +21,73 @@ use super::*;
 use alloc::vec::Vec;
 use rlp::{Decodable, Encodable};
 
+impl TransactionUnsigned {
+	/// Return the bytes to be signed by the private key.
+	pub fn unsigned_payload(&self) -> Vec<u8> {
+		use TransactionUnsigned::*;
+		let mut s = rlp::RlpStream::new();
+		match self {
+			Transaction2930Unsigned(ref tx) => {
+				s.append(&tx.r#type.value());
+				s.append(tx);
+			},
+			Transaction1559Unsigned(ref tx) => {
+				s.append(&tx.r#type.value());
+				s.append(tx);
+			},
+			Transaction4844Unsigned(ref tx) => {
+				s.append(&tx.r#type.value());
+				s.append(tx);
+			},
+			TransactionLegacyUnsigned(ref tx) => {
+				s.append(tx);
+			},
+		}
+
+		s.out().to_vec()
+	}
+}
+
+impl TransactionSigned {
+	/// Encode the Ethereum transaction into bytes.
+	pub fn signed_payload(&self) -> Vec<u8> {
+		use TransactionSigned::*;
+		let mut s = rlp::RlpStream::new();
+		match self {
+			Transaction2930Signed(ref tx) => {
+				s.append(&tx.transaction_2930_unsigned.r#type.value());
+				s.append(tx);
+			},
+			Transaction1559Signed(ref tx) => {
+				s.append(&tx.transaction_1559_unsigned.r#type.value());
+				s.append(tx);
+			},
+			Transaction4844Signed(ref tx) => {
+				s.append(&tx.transaction_4844_unsigned.r#type.value());
+				s.append(tx);
+			},
+			TransactionLegacySigned(ref tx) => {
+				s.append(tx);
+			},
+		}
+
+		s.out().to_vec()
+	}
+
+	/// Decode the Ethereum transaction from bytes.
+	pub fn decode(data: &[u8]) -> Result<Self, rlp::DecoderError> {
+		if data.len() < 1 {
+			return Err(rlp::DecoderError::RlpIsTooShort);
+		}
+		match data[0] {
+			TYPE_EIP2930 => rlp::decode::<Transaction2930Signed>(&data[1..]).map(Into::into),
+			TYPE_EIP1559 => rlp::decode::<Transaction1559Signed>(&data[1..]).map(Into::into),
+			TYPE_EIP4844 => rlp::decode::<Transaction4844Signed>(&data[1..]).map(Into::into),
+			_ => rlp::decode::<TransactionLegacySigned>(data).map(Into::into),
+		}
+	}
+}
+
 impl TransactionLegacyUnsigned {
 	/// Get the rlp encoded bytes of a signed transaction with a dummy 65 bytes signature.
 	pub fn dummy_signed_payload(&self) -> Vec<u8> {
@@ -47,8 +114,8 @@ impl Encodable for TransactionLegacyUnsigned {
 			s.append(&self.value);
 			s.append(&self.input.0);
 			s.append(&chain_id);
-			s.append(&0_u8);
-			s.append(&0_u8);
+			s.append(&0u8);
+			s.append(&0u8);
 		} else {
 			s.begin_list(6);
 			s.append(&self.nonce);
@@ -64,7 +131,6 @@ impl Encodable for TransactionLegacyUnsigned {
 	}
 }
 
-/// See <https://eips.ethereum.org/EIPS/eip-155>
 impl Decodable for TransactionLegacyUnsigned {
 	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
 		Ok(TransactionLegacyUnsigned {
@@ -95,16 +161,18 @@ impl Decodable for TransactionLegacyUnsigned {
 
 impl Encodable for TransactionLegacySigned {
 	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		let tx = &self.transaction_legacy_unsigned;
+
 		s.begin_list(9);
-		s.append(&self.transaction_legacy_unsigned.nonce);
-		s.append(&self.transaction_legacy_unsigned.gas_price);
-		s.append(&self.transaction_legacy_unsigned.gas);
-		match self.transaction_legacy_unsigned.to {
+		s.append(&tx.nonce);
+		s.append(&tx.gas_price);
+		s.append(&tx.gas);
+		match tx.to {
 			Some(ref to) => s.append(to),
 			None => s.append_empty_data(),
 		};
-		s.append(&self.transaction_legacy_unsigned.value);
-		s.append(&self.transaction_legacy_unsigned.input.0);
+		s.append(&tx.value);
+		s.append(&tx.input.0);
 
 		s.append(&self.v);
 		s.append(&self.r);
@@ -112,6 +180,232 @@ impl Encodable for TransactionLegacySigned {
 	}
 }
 
+impl Encodable for AccessListEntry {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		s.begin_list(2);
+		s.append(&self.address);
+		s.append_list(&self.storage_keys);
+	}
+}
+
+impl Decodable for AccessListEntry {
+	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
+		Ok(AccessListEntry { address: rlp.val_at(0)?, storage_keys: rlp.list_at(1)? })
+	}
+}
+
+/// See <https://eips.ethereum.org/EIPS/eip-1559>
+impl Encodable for Transaction1559Unsigned {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		s.begin_list(9);
+		s.append(&self.chain_id);
+		s.append(&self.nonce);
+		s.append(&self.max_priority_fee_per_gas);
+		s.append(&self.max_fee_per_gas);
+		s.append(&self.gas);
+		match self.to {
+			Some(ref to) => s.append(to),
+			None => s.append_empty_data(),
+		};
+		s.append(&self.value);
+		s.append(&self.input.0);
+		s.append_list(&self.access_list);
+	}
+}
+
+/// See <https://eips.ethereum.org/EIPS/eip-1559>
+impl Encodable for Transaction1559Signed {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		let tx = &self.transaction_1559_unsigned;
+		s.begin_list(12);
+		s.append(&tx.chain_id);
+		s.append(&tx.nonce);
+		s.append(&tx.max_priority_fee_per_gas);
+		s.append(&tx.max_fee_per_gas);
+		s.append(&tx.gas);
+		match tx.to {
+			Some(ref to) => s.append(to),
+			None => s.append_empty_data(),
+		};
+		s.append(&tx.value);
+		s.append(&tx.input.0);
+		s.append_list(&tx.access_list);
+
+		s.append(&self.y_parity);
+		s.append(&self.r);
+		s.append(&self.s);
+	}
+}
+
+impl Decodable for Transaction1559Signed {
+	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
+		Ok(Transaction1559Signed {
+			transaction_1559_unsigned: {
+				Transaction1559Unsigned {
+					chain_id: rlp.val_at(0)?,
+					nonce: rlp.val_at(1)?,
+					max_priority_fee_per_gas: rlp.val_at(2)?,
+					max_fee_per_gas: rlp.val_at(3)?,
+					gas: rlp.val_at(4)?,
+					to: {
+						let to = rlp.at(5)?;
+						if to.is_empty() {
+							None
+						} else {
+							Some(to.as_val()?)
+						}
+					},
+					value: rlp.val_at(6)?,
+					input: Bytes(rlp.val_at(7)?),
+					access_list: rlp.list_at(8)?,
+					..Default::default()
+				}
+			},
+			y_parity: rlp.val_at(9)?,
+			r: rlp.val_at(10)?,
+			s: rlp.val_at(11)?,
+			..Default::default()
+		})
+	}
+}
+
+//See https://eips.ethereum.org/EIPS/eip-2930
+impl Encodable for Transaction2930Unsigned {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		s.begin_list(8);
+		s.append(&self.chain_id);
+		s.append(&self.nonce);
+		s.append(&self.gas_price);
+		s.append(&self.gas);
+		match self.to {
+			Some(ref to) => s.append(to),
+			None => s.append_empty_data(),
+		};
+		s.append(&self.value);
+		s.append(&self.input.0);
+		s.append_list(&self.access_list);
+	}
+}
+
+//See https://eips.ethereum.org/EIPS/eip-2930
+impl Encodable for Transaction2930Signed {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		let tx = &self.transaction_2930_unsigned;
+		s.begin_list(11);
+		s.append(&tx.chain_id);
+		s.append(&tx.nonce);
+		s.append(&tx.gas_price);
+		s.append(&tx.gas);
+		match tx.to {
+			Some(ref to) => s.append(to),
+			None => s.append_empty_data(),
+		};
+		s.append(&tx.value);
+		s.append(&tx.input.0);
+		s.append_list(&tx.access_list);
+		s.append(&self.y_parity);
+		s.append(&self.r);
+		s.append(&self.s);
+	}
+}
+
+impl Decodable for Transaction2930Signed {
+	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
+		Ok(Transaction2930Signed {
+			transaction_2930_unsigned: {
+				Transaction2930Unsigned {
+					chain_id: rlp.val_at(0)?,
+					nonce: rlp.val_at(1)?,
+					gas_price: rlp.val_at(2)?,
+					gas: rlp.val_at(3)?,
+					to: {
+						let to = rlp.at(4)?;
+						if to.is_empty() {
+							None
+						} else {
+							Some(to.as_val()?)
+						}
+					},
+					value: rlp.val_at(5)?,
+					input: Bytes(rlp.val_at(6)?),
+					access_list: rlp.list_at(7)?,
+					..Default::default()
+				}
+			},
+			y_parity: rlp.val_at(8)?,
+			r: rlp.val_at(9)?,
+			s: rlp.val_at(10)?,
+			..Default::default()
+		})
+	}
+}
+
+//See https://eips.ethereum.org/EIPS/eip-4844
+impl Encodable for Transaction4844Unsigned {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		s.begin_list(11);
+		s.append(&self.chain_id);
+		s.append(&self.nonce);
+		s.append(&self.max_priority_fee_per_gas);
+		s.append(&self.max_fee_per_gas);
+		s.append(&self.gas);
+		s.append(&self.to);
+		s.append(&self.value);
+		s.append(&self.input.0);
+		s.append_list(&self.access_list);
+		s.append(&self.max_fee_per_blob_gas);
+		s.append_list(&self.blob_versioned_hashes);
+	}
+}
+
+//See https://eips.ethereum.org/EIPS/eip-4844
+impl Encodable for Transaction4844Signed {
+	fn rlp_append(&self, s: &mut rlp::RlpStream) {
+		let tx = &self.transaction_4844_unsigned;
+		s.begin_list(14);
+		s.append(&tx.chain_id);
+		s.append(&tx.nonce);
+		s.append(&tx.max_priority_fee_per_gas);
+		s.append(&tx.max_fee_per_gas);
+		s.append(&tx.gas);
+		s.append(&tx.to);
+		s.append(&tx.value);
+		s.append(&tx.input.0);
+		s.append_list(&tx.access_list);
+		s.append(&tx.max_fee_per_blob_gas);
+		s.append_list(&tx.blob_versioned_hashes);
+		s.append(&self.y_parity);
+		s.append(&self.r);
+		s.append(&self.s);
+	}
+}
+
+impl Decodable for Transaction4844Signed {
+	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
+		Ok(Transaction4844Signed {
+			transaction_4844_unsigned: {
+				Transaction4844Unsigned {
+					chain_id: rlp.val_at(0)?,
+					nonce: rlp.val_at(1)?,
+					max_priority_fee_per_gas: rlp.val_at(2)?,
+					max_fee_per_gas: rlp.val_at(3)?,
+					gas: rlp.val_at(4)?,
+					to: rlp.val_at(5)?,
+					value: rlp.val_at(6)?,
+					input: Bytes(rlp.val_at(7)?),
+					access_list: rlp.list_at(8)?,
+					max_fee_per_blob_gas: rlp.val_at(9)?,
+					blob_versioned_hashes: rlp.list_at(10)?,
+					..Default::default()
+				}
+			},
+			y_parity: rlp.val_at(11)?,
+			r: rlp.val_at(12)?,
+			s: rlp.val_at(13)?,
+		})
+	}
+}
+
 /// See <https://eips.ethereum.org/EIPS/eip-155>
 impl Decodable for TransactionLegacySigned {
 	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
@@ -142,7 +436,7 @@ impl Decodable for TransactionLegacySigned {
 					value: rlp.val_at(4)?,
 					input: Bytes(rlp.val_at(5)?),
 					chain_id: extract_chain_id(v).map(|v| v.into()),
-					r#type: Type0 {},
+					r#type: TypeLegacy {},
 				}
 			},
 			v,
@@ -157,26 +451,118 @@ mod test {
 	use super::*;
 
 	#[test]
-	fn encode_decode_legacy_transaction_works() {
-		let tx = TransactionLegacyUnsigned {
-			chain_id: Some(596.into()),
-			gas: U256::from(21000),
-			nonce: U256::from(1),
-			gas_price: U256::from("0x640000006a"),
-			to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()),
-			value: U256::from(123123),
-			input: Bytes(vec![]),
-			r#type: Type0,
-		};
+	fn encode_decode_tx_works() {
+		let txs = [
+			// Legacy
+			(
+				"f86080808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d87808025a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+				r#"
+				{
+					"chainId": "0x1",
+					"gas": "0x1e241",
+					"gasPrice": "0x0",
+					"input": "0x",
+					"nonce": "0x0",
+					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+					"type": "0x0",
+					"value": "0x0",
+					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
+					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+					"v": "0x25"
+				}
+				"#
+			),
+			// type 1: EIP2930
+			(
+				"01f89b0180808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+				r#"
+				{
+					"accessList": [
+						{
+						"address": "0x0000000000000000000000000000000000000001",
+						"storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
+						}
+					],
+					"chainId": "0x1",
+					"gas": "0x1e241",
+					"gasPrice": "0x0",
+					"input": "0x",
+					"nonce": "0x0",
+					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+					"type": "0x1",
+					"value": "0x0",
+					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
+					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+					"yParity": "0x0"
+				}
+				"#
+			),
+			// type 2: EIP1559
+			(
+				"02f89c018080018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+				r#"
+				{
+					"accessList": [
+						{
+							"address": "0x0000000000000000000000000000000000000001",
+							"storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
+						}
+					],
+					"chainId": "0x1",
+					"gas": "0x1e241",
+					"gasPrice": "0x0",
+					"input": "0x",
+					"maxFeePerGas": "0x1",
+					"maxPriorityFeePerGas": "0x0",
+					"nonce": "0x0",
+					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+					"type": "0x2",
+					"value": "0x0",
+					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
+					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+					"yParity": "0x0"
 
-		let rlp_bytes = rlp::encode(&tx);
-		let decoded = rlp::decode::<TransactionLegacyUnsigned>(&rlp_bytes).unwrap();
-		assert_eq!(&tx, &decoded);
+				}
+				"#
+			),
+			// type 3: EIP4844
+			(
 
-		let tx = Account::default().sign_transaction(tx);
-		let rlp_bytes = rlp::encode(&tx);
-		let decoded = rlp::decode::<TransactionLegacySigned>(&rlp_bytes).unwrap();
-		assert_eq!(&tx, &decoded);
+				"03f8bf018002018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+				r#"
+				{
+					"accessList": [
+						{
+						"address": "0x0000000000000000000000000000000000000001",
+						"storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
+						}
+					],
+					"blobVersionedHashes": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
+					"chainId": "0x1",
+					"gas": "0x1e241",
+					"input": "0x",
+					"maxFeePerBlobGas": "0x0",
+					"maxFeePerGas": "0x1",
+					"maxPriorityFeePerGas": "0x2",
+					"nonce": "0x0",
+					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+					"type": "0x3",
+					"value": "0x0",
+					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
+					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
+					"yParity": "0x0"
+				}
+				"#
+			)
+		];
+
+		for (tx, json) in txs {
+			let raw_tx = hex::decode(tx).unwrap();
+			let tx = TransactionSigned::decode(&raw_tx).unwrap();
+			assert_eq!(tx.signed_payload(), raw_tx);
+			let expected_tx = serde_json::from_str(json).unwrap();
+			assert_eq!(tx, expected_tx);
+		}
 	}
 
 	#[test]
@@ -189,31 +575,12 @@ mod test {
 			to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()),
 			value: U256::from(123123),
 			input: Bytes(vec![]),
-			r#type: Type0,
+			r#type: TypeLegacy,
 		};
 
-		let signed_tx = Account::default().sign_transaction(tx.clone());
-		let rlp_bytes = rlp::encode(&signed_tx);
-		assert_eq!(tx.dummy_signed_payload().len(), rlp_bytes.len());
-	}
-
-	#[test]
-	fn recover_address_works() {
-		let account = Account::default();
-
-		let unsigned_tx = TransactionLegacyUnsigned {
-			value: 200_000_000_000_000_000_000u128.into(),
-			gas_price: 100_000_000_200u64.into(),
-			gas: 100_107u32.into(),
-			nonce: 3.into(),
-			to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()),
-			chain_id: Some(596.into()),
-			..Default::default()
-		};
-
-		let tx = account.sign_transaction(unsigned_tx.clone());
-		let recovered_address = tx.recover_eth_address().unwrap();
-
-		assert_eq!(account.address(), recovered_address);
+		let dummy_signed_payload = tx.dummy_signed_payload();
+		let tx: TransactionUnsigned = tx.into();
+		let payload = Account::default().sign_transaction(tx).signed_payload();
+		assert_eq!(dummy_signed_payload.len(), payload.len());
 	}
 }
diff --git a/substrate/frame/revive/src/evm/api/rpc_types.rs b/substrate/frame/revive/src/evm/api/rpc_types.rs
index dd1a2642724df48b6c3980e9c2c9cb78e2f12603..1cf8d984b68b847d512fba8dd9483745ecb507e8 100644
--- a/substrate/frame/revive/src/evm/api/rpc_types.rs
+++ b/substrate/frame/revive/src/evm/api/rpc_types.rs
@@ -16,7 +16,8 @@
 // limitations under the License.
 //! Utility impl for the RPC types.
 use super::*;
-use sp_core::U256;
+use alloc::vec::Vec;
+use sp_core::{H160, U256};
 
 impl TransactionInfo {
 	/// Create a new [`TransactionInfo`] from a receipt and a signed transaction.
@@ -138,3 +139,140 @@ fn logs_bloom_works() {
 	.unwrap();
 	assert_eq!(receipt.logs_bloom, ReceiptInfo::logs_bloom(&receipt.logs));
 }
+
+impl GenericTransaction {
+	/// Create a new [`GenericTransaction`] from a signed transaction.
+	pub fn from_signed(tx: TransactionSigned, from: Option<H160>) -> Self {
+		use TransactionSigned::*;
+		match tx {
+			TransactionLegacySigned(tx) => {
+				let tx = tx.transaction_legacy_unsigned;
+				GenericTransaction {
+					from,
+					r#type: Some(tx.r#type.as_byte()),
+					chain_id: tx.chain_id,
+					input: Some(tx.input),
+					nonce: Some(tx.nonce),
+					value: Some(tx.value),
+					to: tx.to,
+					gas: Some(tx.gas),
+					gas_price: Some(tx.gas_price),
+					..Default::default()
+				}
+			},
+			Transaction4844Signed(tx) => {
+				let tx = tx.transaction_4844_unsigned;
+				GenericTransaction {
+					from,
+					r#type: Some(tx.r#type.as_byte()),
+					chain_id: Some(tx.chain_id),
+					input: Some(tx.input),
+					nonce: Some(tx.nonce),
+					value: Some(tx.value),
+					to: Some(tx.to),
+					gas: Some(tx.gas),
+					gas_price: Some(tx.max_fee_per_blob_gas),
+					access_list: Some(tx.access_list),
+					blob_versioned_hashes: Some(tx.blob_versioned_hashes),
+					max_fee_per_blob_gas: Some(tx.max_fee_per_blob_gas),
+					max_fee_per_gas: Some(tx.max_fee_per_gas),
+					max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
+					..Default::default()
+				}
+			},
+			Transaction1559Signed(tx) => {
+				let tx = tx.transaction_1559_unsigned;
+				GenericTransaction {
+					from,
+					r#type: Some(tx.r#type.as_byte()),
+					chain_id: Some(tx.chain_id),
+					input: Some(tx.input),
+					nonce: Some(tx.nonce),
+					value: Some(tx.value),
+					to: tx.to,
+					gas: Some(tx.gas),
+					gas_price: Some(tx.gas_price),
+					access_list: Some(tx.access_list),
+					max_fee_per_gas: Some(tx.max_fee_per_gas),
+					max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
+					..Default::default()
+				}
+			},
+			Transaction2930Signed(tx) => {
+				let tx = tx.transaction_2930_unsigned;
+				GenericTransaction {
+					from,
+					r#type: Some(tx.r#type.as_byte()),
+					chain_id: Some(tx.chain_id),
+					input: Some(tx.input),
+					nonce: Some(tx.nonce),
+					value: Some(tx.value),
+					to: tx.to,
+					gas: Some(tx.gas),
+					gas_price: Some(tx.gas_price),
+					access_list: Some(tx.access_list),
+					..Default::default()
+				}
+			},
+		}
+	}
+
+	/// Convert to a [`TransactionUnsigned`].
+	pub fn try_into_unsigned(self) -> Result<TransactionUnsigned, ()> {
+		match self.r#type.unwrap_or_default().0 {
+			TYPE_LEGACY => Ok(TransactionLegacyUnsigned {
+				r#type: TypeLegacy {},
+				chain_id: self.chain_id,
+				input: self.input.unwrap_or_default(),
+				nonce: self.nonce.unwrap_or_default(),
+				value: self.value.unwrap_or_default(),
+				to: self.to,
+				gas: self.gas.unwrap_or_default(),
+				gas_price: self.gas_price.unwrap_or_default(),
+			}
+			.into()),
+			TYPE_EIP1559 => Ok(Transaction1559Unsigned {
+				r#type: TypeEip1559 {},
+				chain_id: self.chain_id.unwrap_or_default(),
+				input: self.input.unwrap_or_default(),
+				nonce: self.nonce.unwrap_or_default(),
+				value: self.value.unwrap_or_default(),
+				to: self.to,
+				gas: self.gas.unwrap_or_default(),
+				gas_price: self.gas_price.unwrap_or_default(),
+				access_list: self.access_list.unwrap_or_default(),
+				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
+				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
+			}
+			.into()),
+			TYPE_EIP2930 => Ok(Transaction2930Unsigned {
+				r#type: TypeEip2930 {},
+				chain_id: self.chain_id.unwrap_or_default(),
+				input: self.input.unwrap_or_default(),
+				nonce: self.nonce.unwrap_or_default(),
+				value: self.value.unwrap_or_default(),
+				to: self.to,
+				gas: self.gas.unwrap_or_default(),
+				gas_price: self.gas_price.unwrap_or_default(),
+				access_list: self.access_list.unwrap_or_default(),
+			}
+			.into()),
+			TYPE_EIP4844 => Ok(Transaction4844Unsigned {
+				r#type: TypeEip4844 {},
+				chain_id: self.chain_id.unwrap_or_default(),
+				input: self.input.unwrap_or_default(),
+				nonce: self.nonce.unwrap_or_default(),
+				value: self.value.unwrap_or_default(),
+				to: self.to.unwrap_or_default(),
+				gas: self.gas.unwrap_or_default(),
+				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
+				max_fee_per_blob_gas: self.max_fee_per_blob_gas.unwrap_or_default(),
+				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
+				access_list: self.access_list.unwrap_or_default(),
+				blob_versioned_hashes: self.blob_versioned_hashes.unwrap_or_default(),
+			}
+			.into()),
+			_ => Err(()),
+		}
+	}
+}
diff --git a/substrate/frame/revive/src/evm/api/rpc_types_gen.rs b/substrate/frame/revive/src/evm/api/rpc_types_gen.rs
index 48045a6acc86b279c41a6d9c8161e9df70ebbbbb..5037ec05d881c94f33cc8e0b798b9c2ef7d4c1c9 100644
--- a/substrate/frame/revive/src/evm/api/rpc_types_gen.rs
+++ b/substrate/frame/revive/src/evm/api/rpc_types_gen.rs
@@ -17,7 +17,7 @@
 //! Generated JSON-RPC types.
 #![allow(missing_docs)]
 
-use super::{byte::*, Type0, Type1, Type2, Type3};
+use super::{byte::*, TypeEip1559, TypeEip2930, TypeEip4844, TypeLegacy};
 use alloc::vec::Vec;
 use codec::{Decode, Encode};
 use derive_more::{From, TryInto};
@@ -455,7 +455,7 @@ pub struct Transaction1559Unsigned {
 	/// to address
 	pub to: Option<Address>,
 	/// type
-	pub r#type: Type2,
+	pub r#type: TypeEip1559,
 	/// value
 	pub value: U256,
 }
@@ -486,7 +486,7 @@ pub struct Transaction2930Unsigned {
 	/// to address
 	pub to: Option<Address>,
 	/// type
-	pub r#type: Type1,
+	pub r#type: TypeEip2930,
 	/// value
 	pub value: U256,
 }
@@ -530,7 +530,7 @@ pub struct Transaction4844Unsigned {
 	/// to address
 	pub to: Address,
 	/// type
-	pub r#type: Type3,
+	pub r#type: TypeEip4844,
 	/// value
 	pub value: U256,
 }
@@ -557,7 +557,7 @@ pub struct TransactionLegacyUnsigned {
 	/// to address
 	pub to: Option<Address>,
 	/// type
-	pub r#type: Type0,
+	pub r#type: TypeLegacy,
 	/// value
 	pub value: U256,
 }
@@ -622,8 +622,8 @@ pub struct Transaction1559Signed {
 	pub v: Option<U256>,
 	/// yParity
 	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
-	#[serde(rename = "yParity", skip_serializing_if = "Option::is_none")]
-	pub y_parity: Option<U256>,
+	#[serde(rename = "yParity")]
+	pub y_parity: U256,
 }
 
 /// Signed 2930 Transaction
@@ -661,8 +661,8 @@ pub struct Transaction4844Signed {
 	pub s: U256,
 	/// yParity
 	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
-	#[serde(rename = "yParity", skip_serializing_if = "Option::is_none")]
-	pub y_parity: Option<U256>,
+	#[serde(rename = "yParity")]
+	pub y_parity: U256,
 }
 
 /// Signed Legacy Transaction
diff --git a/substrate/frame/revive/src/evm/api/signature.rs b/substrate/frame/revive/src/evm/api/signature.rs
index 957d50c8e324164b404c390056581a3fd71f4946..9f39b92b461eabbed376f31ea8cc3259363368a7 100644
--- a/substrate/frame/revive/src/evm/api/signature.rs
+++ b/substrate/frame/revive/src/evm/api/signature.rs
@@ -15,49 +15,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //! Ethereum signature utilities
-use super::{TransactionLegacySigned, TransactionLegacyUnsigned};
-use rlp::Encodable;
+use super::*;
 use sp_core::{H160, U256};
 use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256};
 
-impl TransactionLegacyUnsigned {
-	/// Recover the Ethereum address, from an RLP encoded transaction and a 65 bytes signature.
-	pub fn recover_eth_address(rlp_encoded: &[u8], signature: &[u8; 65]) -> Result<H160, ()> {
-		let hash = keccak_256(rlp_encoded);
-		let mut addr = H160::default();
-		let pk = secp256k1_ecdsa_recover(&signature, &hash).map_err(|_| ())?;
-		addr.assign_from_slice(&keccak_256(&pk[..])[12..]);
-
-		Ok(addr)
-	}
-}
-
 impl TransactionLegacySigned {
-	/// Create a signed transaction from an [`TransactionLegacyUnsigned`] and a signature.
-	pub fn from(
-		transaction_legacy_unsigned: TransactionLegacyUnsigned,
-		signature: &[u8; 65],
-	) -> TransactionLegacySigned {
-		let r = U256::from_big_endian(&signature[..32]);
-		let s = U256::from_big_endian(&signature[32..64]);
-		let recovery_id = signature[64] as u32;
-		let v = transaction_legacy_unsigned
-			.chain_id
-			.map(|chain_id| chain_id * 2 + 35 + recovery_id)
-			.unwrap_or_else(|| U256::from(27) + recovery_id);
-
-		TransactionLegacySigned { transaction_legacy_unsigned, r, s, v }
-	}
-
-	/// Get the raw 65 bytes signature from the signed transaction.
-	pub fn raw_signature(&self) -> Result<[u8; 65], ()> {
-		let mut s = [0u8; 65];
-		self.r.write_as_big_endian(s[0..32].as_mut());
-		self.s.write_as_big_endian(s[32..64].as_mut());
-		s[64] = self.extract_recovery_id().ok_or(())?;
-		Ok(s)
-	}
-
 	/// Get the recovery ID from the signed transaction.
 	/// See https://eips.ethereum.org/EIPS/eip-155
 	fn extract_recovery_id(&self) -> Option<u8> {
@@ -71,10 +33,154 @@ impl TransactionLegacySigned {
 			self.v.try_into().ok()
 		}
 	}
+}
+
+impl TransactionUnsigned {
+	/// Extract the unsigned transaction from a signed transaction.
+	pub fn from_signed(tx: TransactionSigned) -> Self {
+		match tx {
+			TransactionSigned::TransactionLegacySigned(signed) =>
+				Self::TransactionLegacyUnsigned(signed.transaction_legacy_unsigned),
+			TransactionSigned::Transaction4844Signed(signed) =>
+				Self::Transaction4844Unsigned(signed.transaction_4844_unsigned),
+			TransactionSigned::Transaction1559Signed(signed) =>
+				Self::Transaction1559Unsigned(signed.transaction_1559_unsigned),
+			TransactionSigned::Transaction2930Signed(signed) =>
+				Self::Transaction2930Unsigned(signed.transaction_2930_unsigned),
+		}
+	}
+
+	/// Create a signed transaction from an [`TransactionUnsigned`] and a signature.
+	pub fn with_signature(self, signature: [u8; 65]) -> TransactionSigned {
+		let r = U256::from_big_endian(&signature[..32]);
+		let s = U256::from_big_endian(&signature[32..64]);
+		let recovery_id = signature[64];
+
+		match self {
+			TransactionUnsigned::Transaction2930Unsigned(transaction_2930_unsigned) =>
+				Transaction2930Signed {
+					transaction_2930_unsigned,
+					r,
+					s,
+					v: None,
+					y_parity: U256::from(recovery_id),
+				}
+				.into(),
+			TransactionUnsigned::Transaction1559Unsigned(transaction_1559_unsigned) =>
+				Transaction1559Signed {
+					transaction_1559_unsigned,
+					r,
+					s,
+					v: None,
+					y_parity: U256::from(recovery_id),
+				}
+				.into(),
+
+			TransactionUnsigned::Transaction4844Unsigned(transaction_4844_unsigned) =>
+				Transaction4844Signed {
+					transaction_4844_unsigned,
+					r,
+					s,
+					y_parity: U256::from(recovery_id),
+				}
+				.into(),
+
+			TransactionUnsigned::TransactionLegacyUnsigned(transaction_legacy_unsigned) => {
+				let v = transaction_legacy_unsigned
+					.chain_id
+					.map(|chain_id| {
+						chain_id
+							.saturating_mul(U256::from(2))
+							.saturating_add(U256::from(35u32 + recovery_id as u32))
+					})
+					.unwrap_or_else(|| U256::from(27u32 + recovery_id as u32));
+
+				TransactionLegacySigned { transaction_legacy_unsigned, r, s, v }.into()
+			},
+		}
+	}
+}
+
+impl TransactionSigned {
+	/// Get the raw 65 bytes signature from the signed transaction.
+	pub fn raw_signature(&self) -> Result<[u8; 65], ()> {
+		use TransactionSigned::*;
+		let (r, s, v) = match self {
+			TransactionLegacySigned(tx) => (tx.r, tx.s, tx.extract_recovery_id().ok_or(())?),
+			Transaction4844Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
+			Transaction1559Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
+			Transaction2930Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
+		};
+		let mut sig = [0u8; 65];
+		r.write_as_big_endian(sig[0..32].as_mut());
+		s.write_as_big_endian(sig[32..64].as_mut());
+		sig[64] = v;
+		Ok(sig)
+	}
 
-	/// Recover the Ethereum address from the signed transaction.
+	/// Recover the Ethereum address, from a signed transaction.
 	pub fn recover_eth_address(&self) -> Result<H160, ()> {
-		let rlp_encoded = self.transaction_legacy_unsigned.rlp_bytes();
-		TransactionLegacyUnsigned::recover_eth_address(&rlp_encoded, &self.raw_signature()?)
+		use TransactionSigned::*;
+
+		let mut s = rlp::RlpStream::new();
+		match self {
+			TransactionLegacySigned(tx) => {
+				let tx = &tx.transaction_legacy_unsigned;
+				s.append(tx);
+			},
+			Transaction4844Signed(tx) => {
+				let tx = &tx.transaction_4844_unsigned;
+				s.append(&tx.r#type.value());
+				s.append(tx);
+			},
+			Transaction1559Signed(tx) => {
+				let tx = &tx.transaction_1559_unsigned;
+				s.append(&tx.r#type.value());
+				s.append(tx);
+			},
+			Transaction2930Signed(tx) => {
+				let tx = &tx.transaction_2930_unsigned;
+				s.append(&tx.r#type.value());
+				s.append(tx);
+			},
+		}
+		let bytes = s.out().to_vec();
+		let signature = self.raw_signature()?;
+
+		let hash = keccak_256(&bytes);
+		let mut addr = H160::default();
+		let pk = secp256k1_ecdsa_recover(&signature, &hash).map_err(|_| ())?;
+		addr.assign_from_slice(&keccak_256(&pk[..])[12..]);
+		Ok(addr)
+	}
+}
+
+#[test]
+fn sign_and_recover_work() {
+	use crate::evm::TransactionUnsigned;
+	let txs = [
+		// Legacy
+		"f86080808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d87808026a07b2e762a17a71a46b422e60890a04512cf0d907ccf6b78b5bd6e6977efdc2bf5a01ea673d50bbe7c2236acb498ceb8346a8607c941f0b8cbcde7cf439aa9369f1f",
+		//// type 1: EIP2930
+		"01f89b0180808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0c45a61b3d1d00169c649e7326e02857b850efb96e587db4b9aad29afc80d0752a070ae1eb47ab4097dbed2f19172ae286492621b46ac737ee6c32fb18a00c94c9c",
+		// type 2: EIP1559
+		"02f89c018080018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a055d72bbc3047d4b9d3e4b8099f187143202407746118204cc2e0cb0c85a68baea04f6ef08a1418c70450f53398d9f0f2d78d9e9d6b8a80cba886b67132c4a744f2",
+		// type 3: EIP4844
+		"03f8bf018002018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080e1a0000000000000000000000000000000000000000000000000000000000000000001a0672b8bac466e2cf1be3148c030988d40d582763ecebbc07700dfc93bb070d8a4a07c635887005b11cb58964c04669ac2857fa633aa66f662685dadfd8bcacb0f21",
+	];
+	let account = Account::from_secret_key(hex_literal::hex!(
+		"a872f6cbd25a0e04a08b1e21098017a9e6194d101d75e13111f71410c59cd57f"
+	));
+
+	for tx in txs {
+		let raw_tx = hex::decode(tx).unwrap();
+		let tx = TransactionSigned::decode(&raw_tx).unwrap();
+
+		let address = tx.recover_eth_address();
+		assert_eq!(address.unwrap(), account.address());
+
+		let unsigned = TransactionUnsigned::from_signed(tx.clone());
+		let signed = account.sign_transaction(unsigned);
+		assert_eq!(tx, signed);
 	}
 }
diff --git a/substrate/frame/revive/src/evm/api/type_id.rs b/substrate/frame/revive/src/evm/api/type_id.rs
index 7434ca6e9b7f1ed268bd2f3b454527e6883ec071..c6e018a379b37c804b28d148f6ef8586db1d43a7 100644
--- a/substrate/frame/revive/src/evm/api/type_id.rs
+++ b/substrate/frame/revive/src/evm/api/type_id.rs
@@ -17,6 +17,7 @@
 //! Ethereum Typed Transaction types
 use super::Byte;
 use codec::{Decode, Encode};
+use paste::paste;
 use rlp::Decodable;
 use scale_info::TypeInfo;
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
@@ -29,8 +30,14 @@ macro_rules! transaction_type {
 		#[derive(Clone, Default, Debug, Eq, PartialEq)]
 		pub struct $name;
 
+		// upper case const name
+		paste! {
+			#[doc = concat!("Transaction value for type identifier: ", $value)]
+			pub const [<$name:snake:upper>]: u8 = $value;
+		}
+
 		impl $name {
-			/// Get the value of the type
+			/// Convert to u8
 			pub fn value(&self) -> u8 {
 				$value
 			}
@@ -107,7 +114,12 @@ macro_rules! transaction_type {
 	};
 }
 
-transaction_type!(Type0, 0);
-transaction_type!(Type1, 1);
-transaction_type!(Type2, 2);
-transaction_type!(Type3, 3);
+transaction_type!(TypeLegacy, 0);
+transaction_type!(TypeEip2930, 1);
+transaction_type!(TypeEip1559, 2);
+transaction_type!(TypeEip4844, 3);
+
+#[test]
+fn transaction_type() {
+	assert_eq!(TYPE_EIP2930, 1u8);
+}
diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs
index 21294fdf6baa117f085494e4819b55deb3d4a147..40c210304ca2fe84641bb1349a4b4b45527e1b19 100644
--- a/substrate/frame/revive/src/evm/runtime.rs
+++ b/substrate/frame/revive/src/evm/runtime.rs
@@ -16,7 +16,7 @@
 // limitations under the License.
 //! Runtime types for integrating `pallet-revive` with the EVM.
 use crate::{
-	evm::api::{TransactionLegacySigned, TransactionLegacyUnsigned},
+	evm::api::{GenericTransaction, TransactionSigned},
 	AccountIdOf, AddressMapper, BalanceOf, MomentOf, Weight, LOG_TARGET,
 };
 use codec::{Decode, Encode};
@@ -293,7 +293,7 @@ pub trait EthExtra {
 		CallOf<Self::Config>: From<crate::Call<Self::Config>>,
 		<Self::Config as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
 	{
-		let tx = rlp::decode::<TransactionLegacySigned>(&payload).map_err(|err| {
+		let tx = TransactionSigned::decode(&payload).map_err(|err| {
 			log::debug!(target: LOG_TARGET, "Failed to decode transaction: {err:?}");
 			InvalidTransaction::Call
 		})?;
@@ -305,33 +305,33 @@ pub trait EthExtra {
 
 		let signer =
 			<Self::Config as crate::Config>::AddressMapper::to_fallback_account_id(&signer);
-		let TransactionLegacyUnsigned { nonce, chain_id, to, value, input, gas, gas_price, .. } =
-			tx.transaction_legacy_unsigned;
+		let GenericTransaction { nonce, chain_id, to, value, input, gas, gas_price, .. } =
+			GenericTransaction::from_signed(tx, None);
 
 		if chain_id.unwrap_or_default() != <Self::Config as crate::Config>::ChainId::get().into() {
 			log::debug!(target: LOG_TARGET, "Invalid chain_id {chain_id:?}");
 			return Err(InvalidTransaction::Call);
 		}
 
-		let value = crate::Pallet::<Self::Config>::convert_evm_to_native(value).map_err(|err| {
-			log::debug!(target: LOG_TARGET, "Failed to convert value to native: {err:?}");
-			InvalidTransaction::Call
-		})?;
+		let value = crate::Pallet::<Self::Config>::convert_evm_to_native(value.unwrap_or_default())
+			.map_err(|err| {
+				log::debug!(target: LOG_TARGET, "Failed to convert value to native: {err:?}");
+				InvalidTransaction::Call
+			})?;
 
+		let data = input.unwrap_or_default().0;
 		let call = if let Some(dest) = to {
 			crate::Call::call::<Self::Config> {
 				dest,
 				value,
 				gas_limit,
 				storage_deposit_limit,
-				data: input.0,
+				data,
 			}
 		} else {
-			let blob = match polkavm::ProgramBlob::blob_length(&input.0) {
-				Some(blob_len) => blob_len
-					.try_into()
-					.ok()
-					.and_then(|blob_len| (input.0.split_at_checked(blob_len))),
+			let blob = match polkavm::ProgramBlob::blob_length(&data) {
+				Some(blob_len) =>
+					blob_len.try_into().ok().and_then(|blob_len| (data.split_at_checked(blob_len))),
 				_ => None,
 			};
 
@@ -350,18 +350,18 @@ pub trait EthExtra {
 			}
 		};
 
-		let nonce = nonce.try_into().map_err(|_| InvalidTransaction::Call)?;
+		let nonce = nonce.unwrap_or_default().try_into().map_err(|_| InvalidTransaction::Call)?;
 
 		// Fees calculated with the fixed `GAS_PRICE`
 		// When we dry-run the transaction, we set the gas to `Fee / GAS_PRICE`
 		let eth_fee_no_tip = U256::from(GAS_PRICE)
-			.saturating_mul(gas)
+			.saturating_mul(gas.unwrap_or_default())
 			.try_into()
 			.map_err(|_| InvalidTransaction::Call)?;
 
 		// Fees with the actual gas_price from the transaction.
-		let eth_fee: BalanceOf<Self::Config> = U256::from(gas_price)
-			.saturating_mul(gas)
+		let eth_fee: BalanceOf<Self::Config> = U256::from(gas_price.unwrap_or_default())
+			.saturating_mul(gas.unwrap_or_default())
 			.try_into()
 			.map_err(|_| InvalidTransaction::Call)?;
 
@@ -414,7 +414,6 @@ mod test {
 	};
 	use frame_support::{error::LookupError, traits::fungible::Mutate};
 	use pallet_revive_fixtures::compile_module;
-	use rlp::Encodable;
 	use sp_runtime::{
 		traits::{Checkable, DispatchTransaction},
 		MultiAddress, MultiSignature,
@@ -523,7 +522,7 @@ mod test {
 				100_000_000_000_000,
 			);
 
-			let payload = account.sign_transaction(tx).rlp_bytes().to_vec();
+			let payload = account.sign_transaction(tx.into()).signed_payload();
 			let call = RuntimeCall::Contracts(crate::Call::eth_transact {
 				payload,
 				gas_limit,
diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs
index caecf07c4071acd59288009efa0332da5fd613c0..b55854e2eec583ef954ac8a9df7c796cc69f4c4d 100644
--- a/substrate/frame/revive/src/lib.rs
+++ b/substrate/frame/revive/src/lib.rs
@@ -768,7 +768,7 @@ pub mod pallet {
 		///
 		/// # Parameters
 		///
-		/// * `payload`: The RLP-encoded [`crate::evm::TransactionLegacySigned`].
+		/// * `payload`: The encoded [`crate::evm::TransactionSigned`].
 		/// * `gas_limit`: The gas limit enforced during contract execution.
 		/// * `storage_deposit_limit`: The maximum balance that can be charged to the caller for
 		///   storage usage.