Skip to content

Commit

Permalink
feat: OpTransactionRequest (#215)
Browse files Browse the repository at this point in the history
<!--
Thank you for your Pull Request. Please provide a description above and
review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide:
https://github.com/alloy-rs/core/blob/main/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and
building the
documentation.
-->

<!-- ** Please select "Allow edits from maintainers" in the PR Options
** -->

## Motivation

Closes #27

To be able to replace `UnsingedTx` and `TxEnvelope` on Optimism network
with op-consensus types, we need a op-specific tx builder.

## Solution

Add `OpTransactionBuilder` which is a wrapper around
`TransactionBuilder`. It intentionally does not have any direct methods
allowing to set EIP-4844 fields, however they can still be set through
`AsMut` or during deserialization.

When building typed transaction, eip-4844 transactions are getting
converted to eip-1559

## PR Checklist

- [ ] Added Tests
- [ ] Added Documentation
- [ ] Breaking changes
  • Loading branch information
klkvr authored Nov 1, 2024
1 parent f726c5f commit b52866d
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 41 deletions.
1 change: 1 addition & 0 deletions crates/network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ alloy-consensus.workspace = true
alloy-network.workspace = true
alloy-primitives.workspace = true
alloy-rpc-types-eth.workspace = true
alloy-signer.workspace = true
126 changes: 87 additions & 39 deletions crates/network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

pub use alloy_network::*;

use alloy_consensus::TxType;
use alloy_consensus::{TxEnvelope, TxType, TypedTransaction};
use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
use alloy_rpc_types_eth::AccessList;
use op_alloy_consensus::OpTxType;
use op_alloy_consensus::{OpTxEnvelope, OpTxType, OpTypedTransaction};
use op_alloy_rpc_types::OpTransactionRequest;

/// Types for an Op-stack network.
#[derive(Clone, Copy, Debug)]
Expand All @@ -22,15 +23,15 @@ pub struct Optimism {
impl Network for Optimism {
type TxType = OpTxType;

type TxEnvelope = alloy_consensus::TxEnvelope;
type TxEnvelope = op_alloy_consensus::OpTxEnvelope;

type UnsignedTx = alloy_consensus::TypedTransaction;
type UnsignedTx = op_alloy_consensus::OpTypedTransaction;

type ReceiptEnvelope = op_alloy_consensus::OpReceiptEnvelope;

type Header = alloy_consensus::Header;

type TransactionRequest = alloy_rpc_types_eth::transaction::TransactionRequest;
type TransactionRequest = op_alloy_rpc_types::OpTransactionRequest;

type TransactionResponse = op_alloy_rpc_types::Transaction;

Expand All @@ -42,135 +43,143 @@ impl Network for Optimism {
alloy_rpc_types_eth::Block<Self::TransactionResponse, Self::HeaderResponse>;
}

impl TransactionBuilder<Optimism> for alloy_rpc_types_eth::transaction::TransactionRequest {
impl TransactionBuilder<Optimism> for OpTransactionRequest {
fn chain_id(&self) -> Option<ChainId> {
self.chain_id
self.as_ref().chain_id()
}

fn set_chain_id(&mut self, chain_id: ChainId) {
self.chain_id = Some(chain_id);
self.as_mut().set_chain_id(chain_id);
}

fn nonce(&self) -> Option<u64> {
self.nonce
self.as_ref().nonce()
}

fn set_nonce(&mut self, nonce: u64) {
self.nonce = Some(nonce);
self.as_mut().set_nonce(nonce);
}

fn input(&self) -> Option<&Bytes> {
self.input.input()
self.as_ref().input()
}

fn set_input<T: Into<Bytes>>(&mut self, input: T) {
self.input.input = Some(input.into());
self.as_mut().set_input(input);
}

fn from(&self) -> Option<Address> {
self.from
self.as_ref().from()
}

fn set_from(&mut self, from: Address) {
self.from = Some(from);
self.as_mut().set_from(from);
}

fn kind(&self) -> Option<TxKind> {
self.to
self.as_ref().kind()
}

fn clear_kind(&mut self) {
self.to = None;
self.as_mut().clear_kind();
}

fn set_kind(&mut self, kind: TxKind) {
self.to = Some(kind);
self.as_mut().set_kind(kind);
}

fn value(&self) -> Option<U256> {
self.value
self.as_ref().value()
}

fn set_value(&mut self, value: U256) {
self.value = Some(value)
self.as_mut().set_value(value);
}

fn gas_price(&self) -> Option<u128> {
self.gas_price
self.as_ref().gas_price()
}

fn set_gas_price(&mut self, gas_price: u128) {
self.gas_price = Some(gas_price);
self.as_mut().set_gas_price(gas_price);
}

fn max_fee_per_gas(&self) -> Option<u128> {
self.max_fee_per_gas
self.as_ref().max_fee_per_gas()
}

fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) {
self.max_fee_per_gas = Some(max_fee_per_gas);
self.as_mut().set_max_fee_per_gas(max_fee_per_gas);
}

fn max_priority_fee_per_gas(&self) -> Option<u128> {
self.max_priority_fee_per_gas
self.as_ref().max_priority_fee_per_gas()
}

fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) {
self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas);
self.as_mut().set_max_priority_fee_per_gas(max_priority_fee_per_gas);
}

fn gas_limit(&self) -> Option<u64> {
self.gas
self.as_ref().gas_limit()
}

fn set_gas_limit(&mut self, gas_limit: u64) {
self.gas = Some(gas_limit);
self.as_mut().set_gas_limit(gas_limit);
}

fn access_list(&self) -> Option<&AccessList> {
self.access_list.as_ref()
self.as_ref().access_list()
}

fn set_access_list(&mut self, access_list: AccessList) {
self.access_list = Some(access_list);
self.as_mut().set_access_list(access_list);
}

fn complete_type(&self, ty: OpTxType) -> Result<(), Vec<&'static str>> {
match ty {
OpTxType::Deposit => Err(vec!["not implemented for deposit tx"]),
_ => {
let ty = TxType::try_from(ty as u8).unwrap();
TransactionBuilder::<Ethereum>::complete_type(self, ty)
self.as_ref().complete_type(ty)
}
}
}

fn can_submit(&self) -> bool {
TransactionBuilder::<Ethereum>::can_submit(self)
self.as_ref().can_submit()
}

fn can_build(&self) -> bool {
TransactionBuilder::<Ethereum>::can_build(self)
self.as_ref().can_build()
}

#[doc(alias = "output_transaction_type")]
fn output_tx_type(&self) -> OpTxType {
OpTxType::try_from(self.preferred_type() as u8).unwrap()
match self.as_ref().preferred_type() {
TxType::Eip1559 | TxType::Eip4844 => OpTxType::Eip1559,
TxType::Eip2930 => OpTxType::Eip2930,
TxType::Eip7702 => OpTxType::Eip7702,
TxType::Legacy => OpTxType::Legacy,
}
}

#[doc(alias = "output_transaction_type_checked")]
fn output_tx_type_checked(&self) -> Option<OpTxType> {
self.buildable_type().map(|tx_ty| OpTxType::try_from(tx_ty as u8).unwrap())
self.as_ref().buildable_type().map(|tx_ty| match tx_ty {
TxType::Eip1559 | TxType::Eip4844 => OpTxType::Eip1559,
TxType::Eip2930 => OpTxType::Eip2930,
TxType::Eip7702 => OpTxType::Eip7702,
TxType::Legacy => OpTxType::Legacy,
})
}

fn prep_for_submission(&mut self) {
self.transaction_type = Some(self.preferred_type() as u8);
self.trim_conflicting_keys();
self.populate_blob_hashes();
self.as_mut().prep_for_submission();
}

fn build_unsigned(self) -> BuildResult<alloy_consensus::TypedTransaction, Optimism> {
if let Err((tx_type, missing)) = self.missing_keys() {
fn build_unsigned(self) -> BuildResult<OpTypedTransaction, Optimism> {
if let Err((tx_type, missing)) = self.as_ref().missing_keys() {
let tx_type = OpTxType::try_from(tx_type as u8).unwrap();
return Err(TransactionBuilderError::InvalidTransactionRequest(tx_type, missing)
.into_unbuilt(self));
Expand All @@ -185,3 +194,42 @@ impl TransactionBuilder<Optimism> for alloy_rpc_types_eth::transaction::Transact
Ok(wallet.sign_request(self).await?)
}
}

impl NetworkWallet<Optimism> for EthereumWallet {
fn default_signer_address(&self) -> Address {
NetworkWallet::<Ethereum>::default_signer_address(self)
}

fn has_signer_for(&self, address: &Address) -> bool {
NetworkWallet::<Ethereum>::has_signer_for(self, address)
}

fn signer_addresses(&self) -> impl Iterator<Item = Address> {
NetworkWallet::<Ethereum>::signer_addresses(self)
}

async fn sign_transaction_from(
&self,
sender: Address,
tx: OpTypedTransaction,
) -> alloy_signer::Result<OpTxEnvelope> {
let tx = match tx {
OpTypedTransaction::Legacy(tx) => TypedTransaction::Legacy(tx),
OpTypedTransaction::Eip2930(tx) => TypedTransaction::Eip2930(tx),
OpTypedTransaction::Eip1559(tx) => TypedTransaction::Eip1559(tx),
OpTypedTransaction::Eip7702(tx) => TypedTransaction::Eip7702(tx),
OpTypedTransaction::Deposit(_) => {
return Err(alloy_signer::Error::other("not implemented for deposit tx"))
}
};
let tx = NetworkWallet::<Ethereum>::sign_transaction_from(self, sender, tx).await?;

Ok(match tx {
TxEnvelope::Eip1559(tx) => OpTxEnvelope::Eip1559(tx),
TxEnvelope::Eip2930(tx) => OpTxEnvelope::Eip2930(tx),
TxEnvelope::Eip7702(tx) => OpTxEnvelope::Eip7702(tx),
TxEnvelope::Legacy(tx) => OpTxEnvelope::Legacy(tx),
_ => unreachable!(),
})
}
}
6 changes: 6 additions & 0 deletions crates/rpc-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ op-alloy-consensus = { workspace = true, features = ["serde"] }
# Alloy
alloy-serde.workspace = true
alloy-consensus.workspace = true
alloy-network.workspace = true
alloy-network-primitives.workspace = true
alloy-eips = { workspace = true, features = ["serde"] }
alloy-rpc-types-eth = { workspace = true, features = ["serde"] }
Expand All @@ -33,6 +34,8 @@ serde = { workspace = true, features = ["derive"] }
# arbitrary
arbitrary = { workspace = true, features = ["derive"], optional = true }

derive_more.workspace = true

[dev-dependencies]
rand.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
Expand All @@ -54,3 +57,6 @@ arbitrary = [
"alloy-primitives/arbitrary",
"alloy-rpc-types-eth/arbitrary",
]
k256 = [
"alloy-consensus/k256",
]
2 changes: 1 addition & 1 deletion crates/rpc-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ pub mod sync;
pub mod transaction;

pub use receipt::{OpTransactionReceipt, OpTransactionReceiptFields};
pub use transaction::{OpTransactionFields, Transaction};
pub use transaction::{OpTransactionFields, OpTransactionRequest, Transaction};
3 changes: 3 additions & 0 deletions crates/rpc-types/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use alloy_serde::OtherFields;
use op_alloy_consensus::{OpTxEnvelope, OpTxType, TxDeposit};
use serde::{Deserialize, Serialize};

mod request;
pub use request::OpTransactionRequest;

/// OP Transaction type
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
Expand Down
Loading

0 comments on commit b52866d

Please sign in to comment.